# Custom plugins: how to avoid using afterEvaluate when setting another plugin's extension configuration

**URL:** <https://discuss.gradle.org/t/custom-plugins-how-to-avoid-using-afterevaluate-when-setting-another-plugins-extension-configuration/42314>\
**Category:** Help/Discuss\
**Tags:** plugins\
**Created:** [February 25, 2022, 4:57pm UTC](https://discuss.gradle.org/t/custom-plugins-how-to-avoid-using-afterevaluate-when-setting-another-plugins-extension-configuration/42314 "2022-02-25T16:57:53Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![chefhoobajoob](https://avatars.discourse-cdn.com/v4/letter/c/c68b51/32.png) [@chefhoobajoob](https://discuss.gradle.org/u/chefhoobajoob)\
**Post date:** [February 25, 2022, 4:57pm UTC](https://discuss.gradle.org/t/custom-plugins-how-to-avoid-using-afterevaluate-when-setting-another-plugins-extension-configuration/42314/1 "2022-02-25T16:57:53Z")

</div>

I have a custom plugin that supplies several build tasks as well as automating common settings and task configurations for the [moowork node plugin](https://github.com/srs/gradle-node-plugin/blob/master/docs/node.md). It also might apply different plugins based on its extension properties.

I want consumers of this custom plugin to be able to apply the custom plugin, configure its extension, and then, based on those settings, I want to update the [moowork plugin’s configuration accordingly](https://github.com/srs/gradle-node-plugin/blob/master/docs/node.md#configuring-the-plugin).

So this build script snippet:

```gradle
apply plugin: 'the-custom-plugin'
customPluginConfig {
   buildEnvironment = 'agent'
   projectKind = 'library'
}

```

…should be equivalent to:

```gradle
apply plugin: 'com.moowork.node'
node {
  version = <customPlugin.commonNodeVersion>
  npmVersion = <customPlugin.commonNpmVersion>
  workDir = file(<customPlugin.agentWorkDir>)
  npmWorkDir = file(<customPlugin.agentWorkDir>)
  // various other settings
}
if (<customPlugin.projectKind> == 'library') {
  apply plugin: 'java-library'
} else {
  apply plugin: 'java'
}

```

I have reviewed the documentation on [lazy properties](https://docs.gradle.org/current/userguide/lazy_configuration.html?_ga=2.30174399.160807630.1645744542-1337104151.1644626250#lazy_properties), and gone through [responses to similar questions](https://discuss.gradle.org/t/unit-test-plugins-afterevaulate/37437/5), but all the examples are describing how to use lazy configuration patterns for tasks, and I can’t find any examples or documentation describing how I can defer other plugins’ extension configuration settings after evaluation, before tasks are executed, but without using `afterEvaluate`.

Any suggestions here would be very welcome

---

<div class="post-metadata">

**Author:** ![jjustinic](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/jjustinic/32/9005_2.png) [@jjustinic](https://discuss.gradle.org/u/jjustinic)\
**Post date:** [February 25, 2022, 8:05pm UTC](https://discuss.gradle.org/t/custom-plugins-how-to-avoid-using-afterevaluate-when-setting-another-plugins-extension-configuration/42314/2 "2022-02-25T20:05:28Z")

</div>

> [@chefhoobajoob](#):
>
> I have reviewed the documentation on [lazy properties](https://docs.gradle.org/current/userguide/lazy_configuration.html?_ga=2.30174399.160807630.1645744542-1337104151.1644626250#lazy_properties), and gone through [responses to similar questions](https://discuss.gradle.org/t/unit-test-plugins-afterevaulate/37437/5), but all the examples are describing how to use lazy configuration patterns for tasks, and I can’t find any examples or documentation describing how I can defer other plugins’ extension configuration settings after evaluation, before tasks are executed, but without using `afterEvaluate` .

The examples for lazy properties do actually show what you need, but they don’t spell out the exact use case. Fundamentally, there is no difference between an extension configuring a task vs. an extension configuring another extension though. You would just configure your extension’s properties as the value of the node extension’s properties.

> [@chefhoobajoob](#):
>
> I have a custom plugin that supplies several build tasks as well as automating common settings and task configurations for the [moowork node plugin](https://github.com/srs/gradle-node-plugin/blob/master/docs/node.md).

This is actually your problem in this case. The moowork node plugin was abandoned years ago and was forked to [Gradle Node Plugin](https://github.com/node-gradle/gradle-node-plugin) with a new maintainer when the original maintainer stopped updating and responding. At this point, the moowork node plugin is woefully out of date. The `2.x.x` versions should be drop-in replacements, but you would really need to get to `3.0.0+` for the plugin to support what would best handle your use case (all configuration supports lazy properties).

---

<div class="post-metadata">

**Author:** ![chefhoobajoob](https://avatars.discourse-cdn.com/v4/letter/c/c68b51/32.png) [@chefhoobajoob](https://discuss.gradle.org/u/chefhoobajoob)\
**Post date:** [February 25, 2022, 8:21pm UTC](https://discuss.gradle.org/t/custom-plugins-how-to-avoid-using-afterevaluate-when-setting-another-plugins-extension-configuration/42314/3 "2022-02-25T20:21:42Z")

</div>

> [@jjustinic](#):
>
> Fundamentally, there is no difference between an extension configuring a task vs. an extension configuring another extension though

I’m trying to sort this out, but not sure what it looks like in a standalone plugin. I’ll update this plugin to work with the newer gradle node plugin, but regarding config setting, would the custom plugin’s `apply` implementation look something like this?

```gradle
project.node {
  workDir = extension.buildEnvironment.get() == 'agent' ? file(agentDirPath) : file(devDirPath)
}

```

…also, how do I avoid doing this?

```gradle
project.afterEvaluate {
    if (extension.projectKind.get() == 'library') {
        project.apply plugin: 'java-library'
    } else {
        project.apply plugin: 'java'
        project.apply plugin: 'distribution'
       if (extension.hasExternalDistribution.get()) {
           project.distributions {
               external {
                   // distribution config
               }
           }
       }
    }
}

```

---

<div class="post-metadata">

**Author:** ![jjustinic](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/jjustinic/32/9005_2.png) [@jjustinic](https://discuss.gradle.org/u/jjustinic)\
**Post date:** [February 25, 2022, 8:35pm UTC](https://discuss.gradle.org/t/custom-plugins-how-to-avoid-using-afterevaluate-when-setting-another-plugins-extension-configuration/42314/4 "2022-02-25T20:35:33Z")

</div>

You don’t want to `get()` the value in your plugin’s `apply`. Rather, `map` the value so that the consumer task (node task) only queries the actual value when it needs it. It’d look something like this (untested):

```groovy
project.node {
    workDir = extension.buildEnvironment.map { e -> e == 'agent' ? file(agentDirPath) : file(devDirPath) }
}

```

> [@chefhoobajoob](#):
>
> …also, how do I avoid doing this?

Is it a strict requirement that your plugin applies all the other plugins? Typically, my preference would be to reverse the contract and make your plugin reactive, i.e. configure the `distributions` if the `distribution` plugin has been applied to the project or the things specifically needed for a library if `java-library` has been applied.

---

<div class="post-metadata">

**Author:** ![chefhoobajoob](https://avatars.discourse-cdn.com/v4/letter/c/c68b51/32.png) [@chefhoobajoob](https://discuss.gradle.org/u/chefhoobajoob)\
**Post date:** [February 25, 2022, 8:47pm UTC](https://discuss.gradle.org/t/custom-plugins-how-to-avoid-using-afterevaluate-when-setting-another-plugins-extension-configuration/42314/5 "2022-02-25T20:47:00Z")

</div>

> [@jjustinic](#):
>
> Is it a strict requirement that your plugin applies all the other plugins?

It will be a mix. For example, the java/java-library plugins would reasonably be expected to be applied in the build script, but there are a few other plugins (some internal, some external) that we’d like to avoid build script authors having to be repeatedly bothered with knowing when they need one plugin or the other, so the snippet I have there is meant more as an example to illustrate the use case than the specific plugins that are involved.

What is the best way to handle conditionally-applied plugins without `afterEvaluate`?

---

<div class="post-metadata">

**Author:** ![chefhoobajoob](https://avatars.discourse-cdn.com/v4/letter/c/c68b51/32.png) [@chefhoobajoob](https://discuss.gradle.org/u/chefhoobajoob)\
**Post date:** [February 26, 2022, 12:10am UTC](https://discuss.gradle.org/t/custom-plugins-how-to-avoid-using-afterevaluate-when-setting-another-plugins-extension-configuration/42314/6 "2022-02-26T00:10:34Z")

</div>

> [@jjustinic](#):
>
> Fundamentally, there is no difference between an extension configuring a task vs. an extension configuring another extension though

There are also some extension configuration setups that I’m having a harder time understanding than these simple property assignment cases like with the node plugin.

Taking distributions as an example, how would I conditionally add a custom distribution based on the custom plugin’s extension settings?

The approach below obviously wouldn’t work in the custom plugin’s `apply` method, but it’s not obvious to me from the lazy property patterns how to properly set up a distribution configuration based on the custom plugin’s extension configuration.

```gradle
project.withPlugin('distribution') {
  project.distributions {
    if (extension.hasExternalDistribution.get()) {
      external {
         // distribution config
      }
    }
  }
}

```

---

<div class="post-metadata">

**Author:** ![jjustinic](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/jjustinic/32/9005_2.png) [@jjustinic](https://discuss.gradle.org/u/jjustinic)\
**Post date:** [February 26, 2022, 12:12am UTC](https://discuss.gradle.org/t/custom-plugins-how-to-avoid-using-afterevaluate-when-setting-another-plugins-extension-configuration/42314/7 "2022-02-26T00:12:14Z")

</div>

> [@chefhoobajoob](#):
>
> What is the best way to handle conditionally-applied plugins without `afterEvaluate` ?

Using `afterEvaluate` is not necessarily bad in this case. You need the project to be evaluated to know what was ultimately configured and act on it. The danger of `afterEvaluate` is really abuse where you have many things that are trying to execute later and later after each other when order is not guaranteed. As long as you’re handling the configuration of these plugins and eliminating bad coupling, you should be fine.

However, if you just really don’t want an `afterEvaluate` you could have the extension apply the plugins on the set of your value. The drawback is that if someone sets the value, then later changes it in the `build.gradle` file or through other means (like `afterEvaluate` abuse), you’ll have plugins you didn’t want.

---

<div class="post-metadata">

**Author:** ![jjustinic](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/jjustinic/32/9005_2.png) [@jjustinic](https://discuss.gradle.org/u/jjustinic)\
**Post date:** [February 26, 2022, 12:29am UTC](https://discuss.gradle.org/t/custom-plugins-how-to-avoid-using-afterevaluate-when-setting-another-plugins-extension-configuration/42314/8 "2022-02-26T00:29:54Z")

</div>

> [@chefhoobajoob](#):
>
> Taking distributions as an example, how would I conditionally add a custom distribution based on the custom plugin’s extension settings?

The APIs are not well designed for this particular case. The expectation is that you might configure a distribution differently based on properties, but not really create a distribution or not based on other properties.

It might work well enough to always `register` the external distribution, but in the lazy configuration action, throw an `Exception` if there should not be an external distribution or explicitly set the relevant tasks to `enabled = false` based on the property. I don’t particularly like these options and there may be cases where the timing doesn’t work quite right, but that’s what I would try if you don’t have any other possibilities that you’re happy with.

---

<div class="post-metadata">

**Author:** ![chefhoobajoob](https://avatars.discourse-cdn.com/v4/letter/c/c68b51/32.png) [@chefhoobajoob](https://discuss.gradle.org/u/chefhoobajoob)\
**Post date:** [February 26, 2022, 12:41am UTC](https://discuss.gradle.org/t/custom-plugins-how-to-avoid-using-afterevaluate-when-setting-another-plugins-extension-configuration/42314/9 "2022-02-26T00:41:20Z")

</div>

@jjustinic : thank you so much for all this feedback; I think I’ll be able to get most of the lazy config setup to work where there’s a good fit and find alternatives to relying on `afterEvaluate` where there’s support for it.
