Allow Gradle plugin to use its configuration to conditionally apply other plugins - gradle

In the plugin I would like to be able to choose what other plugins apply (e.g. Nexus plugin or Bintray plugin, but not both) based on a configuration placed by a user in the configuration closure for my plugin.
I normally get the configuration for my plugin after the project has been evaluated. That is too late as I would like to allow users to override default configuration for those other plugins in their own (so they have to be applied earlier).
I could split my plugin into two separate plugins (within the same JAR), and require to apply the first one, provide configuration closure, apply the second one and use the configuration provided for the first plugin to decide what 3rd party plugins to apply.
Is there a better way to get the configuration for my plugin early enough to be able to conditionally apply other plugins (and allow them to use their configuration closures)?

You may use gradle 61.5. Init script plugins. This will allow your plugin runs before project configuration.
Example in the gradle documentation remove repositories from your build script. This means that you can read your configuration in a Init script plugin and decide which plugins to apply. Of course your configuration name should be fixed therefore your Init script plugin should be able to read it.

Related

Load Gradle plugin from custom plugin, dynamically

I am developing a Gradle plugin (https://github.com/hkhc/jarbird), which apply some other plugins in the code according to different scenarios.
I can do that by putting the plugin components as implementation dependencies in my plugin project. Then apply the project with project.apply() method with plugin ID or plugin class object.
However, this means unnecessary downloads of plugin components when I don't need those plugins. So I am finding a way to resolve the plugins dynamically.
I tried to do that by adding the dependency as compileOnly in the build script of my custom plugin, and load it in project.apply() of my plugin.
val artifactoryConfiguration = project.buildscript.configurations.detachedConfiguration(
DefaultExternalModuleDependency(
"org.jfrog.buildinfo",
"build-info-extractor-gradle",
"4.23.4"
)
)
artifactoryConfiguration.resolve()
When I made the coordinate wrong intentionally, I got ModuleVersionNotFoundException. So I am sure the resolve did take place. However, project.apply(ArtifactoryPlugin::class.java) still cause ClassNotFoundException when executing the plugin. It seems Gradle cannot load the plugin from a random detached configuration.
I got stuck at this point, and don't know how to make Gradle load a plugin that I resolve dynamically in my custom plugin.
Do I get the direction wrong or I missed something that makes this approach work?

Custom Gradle plugin - sharing data beetwen plugins

I developed a custom Gradle plugin to read some data from a file but I would like to develop the second one plugin to sending this data to the external server(plugin to sending data should depend on the plugin to reading data). I know that one Gradle plugin can detect the second plugin if there are using in the application. I don't have any idea how to pass or get data(string or dto) from one to another plugin. Is this even possible?
The plugin should provide the reading data logic in a form of a task: readData. Then the second plugin should create a task like sendData.
Once both tasks are created and are working independently of one another, then configure them to depend on one another:
tasks.named("sendData") {
dependsOn(tasks.named("readData")
}
In the above, in order to send data, the reading part needs to happen first.
Reference: https://docs.gradle.org/current/userguide/more_about_tasks.html
You could put all of your DTO's and tasks etc into a "base" plugin. This plugin does not add any tasks to the model, it simply makes them available on the classpath and possibly adds a common extension object to the model
Your "reader" plugin applies the "base" plugin and adds the "reader" tasks to the model
Your "publisher" plugin applies the "base" plugin and adds the "publisher" to tasks the model. These tasks will depend on any "reader" tasks in the model (eg via tasks.withType(...))
Note: Gradle will only ever apply a plugin once to a project. So if "reader" and "publisher" are applied to the same project, the "base" plugin will only be applied once
Gradle uses this pattern internally. Many plugins apply the "base" plugin which adds the clean, check and assemble lifecycle tasks. All the java plugins apply the "java-base" plugin which adds the sourceSets to the model and also applies the "base" plugin

Gradle externalized configuration

We have multiple projects/services where we do repeat same configuration over and over again as a part of build.gradle file. Examples could be configuration for spotless plugin, docker, junit/jacoco, versioning, groovy tasks, etc.
I wonder is there a way to externalize it or move to single place so that if needed we can update configuration once instead of doing the same across each and every project.
Very naive idea is to have master-build.gradle file stored in it's own git repo and where needed we can refer it as a git submodule with capabilities to extend/rewrite. Open for any ideas. Thanks!
Gradle scripts can be reused by creating external script files and importing them using apply from: my-script.gradle. apply from also accepts an URL, so you can use something like
apply from: 'https://github.com/user/myproject/raw/master/hello.gradle'
Note tough that using plain references (URLs; or GIT repo URLs) is sub-optimal; a better approach is to identify your build dependencies (and these scripts ARE dependencies!) using group:artifact:version coordinates- that is achieved by writing a plugin and publishing to a repo (eg. to https://plugins.gradle.org/).

How can you chain/stitch Maven plugins?

I want to chain two Maven plugins which should execute in sequence. The output from the first plugin should be used as input for the second plugin. Let me explain:
I want to write a plugin which generates resources and sources, such as configuration files, Java classes, ... Let's call this plugin generator-plugin.
This plugin needs input information to generate all this. This information can be retrieved from file system or from a SQL database. Possibly, in the future one might introduce several other input sources. My idea is to write two plugins, one for getting all information from the file system and another from a SQL database.
This gives:
information-plugin-file ---\
|--- generator-plugin
information-plugin-sql ---/
How can this be done with Maven? Can you chain plugins? I am familiar with writing basic Mojo's, but I have no idea how to approach this, hence this question.
One possibility is to output to a standardized file in information-plugin-file/information-plugin-sql and let the subsequent generator-plugin plugin read from the same file (the Unix way of working, everything is a file).
But I am looking for more direct, Maven specific approaches of doing this. Are there such approaches?
With regards to execution order, all plugins will run in the generate-sources phases and will be defined in correct order in the <plugins> section. So that is already covered I think.
AFAIK, plugins in maven are designed to be totally independent, so the following methods of sharing the information can be used:
Sharing via maven properties:
Its possible to set a property in the first plugin, and probably it will be accessible from within the second plugin
import org.apache.maven.project.MavenProject;
// now inject it into your mojo of the first plugin
#Parameter(defaultValue = "${project}")
private MavenProject project;
// Inside the "execute" method:
project.getProperties().setProperty("mySampleProperty", <SOME_VALUE_GOES_HERE>);
Sharing via Files
The first plugin can generate some output file in the 'target' folder
And the second plugin can read this file
Write a "wrapping" plugin that executes other plugins (like both first and second plugin). After all mojos are just java code that can be called from the aggregator plugin
You can find Here more information about this method
I believe the only way you can have something ordered in Maven is through lifecycles. You could have your first plugin (for the input information) run in the generate-sources phase, and the second in process-sources phase.

Storing Gradle configuration common to multiple projects

I have a few projects in separate git repositories.
Is there a way to store common gradle configuration that they are using in one place?
The common configuration includes repositories, tasks etc.
Yes, it's possible. You can define a gradle file with all the necessary data and expose it, e.g. www.company.com/master.gradle.
Then apply it in the following way:
apply from: 'www.company.com/master.gradle'
in all gradle scripts that require these global settings. This link may be also useful.

Resources