Load Gradle plugin from custom plugin, dynamically - gradle

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?

Related

How to manually add dependencies to Gradle's MavenPom/MavenPublication?

I am working on a plugin. This plugin gets attached to a project that does not apply the java plugin nor the java-library plugin but which should functionally "look" like a Java project[1]. Which means that it should publish a POM including dependencies. The exact dependencies are known and have been collected in a Configuration.
However, I cannot figure out how to manually attach dependencies to the MavenPublication such that they make it into the published pom (aside from directly editing the pom xml).
MavenPublication shadowMavenPublication = publishingExtension.getPublications().create( "mavenShadowArtifacts", MavenPublication.class );
// `shadowPublishArtifact` is a class defined in the plugin
shadowMavenPublication.artifact(
shadowPublishArtifact.getFile(),
(mavenArtifact) -> {
mavenArtifact.setClassifier( shadowPublishArtifact.getClassifier() );
mavenArtifact.setExtension( shadowPublishArtifact.getExtension() );
}
);
So at this point I have the MavenPublication and added my custom artifact to it. Internally this MavenPublication contains a number of "dependencies" as instances of MavenDependency. E.g. DefaultMavenPublication#runtimeDependencies, DefaultMavenPublication#apiDependencies, ... But those are things defined on internal-only contracts.
Using just public APIs, how can I add dependencies to get added to the pom?
P.S. As a bonus, answer the question on the Gradle forums and get points there too! :D
P.S.S. These dependencies come from another project (hibernate-core) in a multi-project build. The user has configured those dependencies themselves. I just "consume" those dependencies with a series of "dependency substitutions". That "source project" defines some exclusions to its dependencies. How can I access those exclusions do be able to transfer them to the dependencies I am creating for this copy project (hibernate-core-jakarta)?
Thanks!
[1] Its a long back-story, but the gist is that this plugin integrates the JakartaTransformer. The project is completely generated using the transformer. The tasks added by those 2 plugins cause problems.
MavenPublication class has pom property - You need to construct (or provide in Your plugin some API for that purpose) pom with all necessary dependencies. It will be published alongside with artifact.
As far as I know, dependencies are attached to the POM by evaluating the configurations of a software component: MavenPublication.from(SoftwareComponent) (source: DefaultMavenPublication).
The idea would be to provide a customized software component. This is only possible through a custom plugin, according to Creating and publishing custom components.

Gradle: Combine jars from sub-projects

I have a multi module gradle project. The project contains two subproject, i.e. a spring-boot application server and an npm front-end ui (which is just static javascript).
I can build both sub-projects. I can define:
implementation(project(':ui'))
in the dependencies section of the spring application and I get a running jar in the server projects build folder successfully serving the frontend.
However, I want to be able not to combine the two not within the server sub-project, but rather in the enclosing project.
I thought of something like:
build.gradle:
allprojects {
group = 'com.example.webapp'
version = '0.0.1-SNAPSHOT'
}
dependencies {
implementation(project(':server'))
implementation(project(':ui'))
}
settings.gradle:
rootProject.name = 'webapp'
include 'server', 'ui'
I think, I am completely wrong. Everything I find about gradle is either completely basic, or assumes way more than what I understood about it so far.
EDIT:
With my solution approach I am getting the following error:
A problem occurred evaluating root project 'webapp'.
Could not find method implementation() for arguments [project ':server'] on object of type org.gradle.api.internal.artifacts.dsl.dependencies.DefaultDependencyHandler.
EDIT 2:
The basic idea is from https://ordina-jworks.github.io/architecture/2018/10/12/spring-boot-angular-gradle.html
The implementation not found is caused by the lack of plugins applied to your root project. The implementation configuration is created by the java plugins in Gradle.
What you are trying to achieve requires a good understanding of Gradle and all the magic provided by the Spring Boot plugin.
You are effectively trying to reproduce some of that integration in your root project, without the help of the plugins.
An approach that might be easier would be to migrate your application project to be the root project and then have the ui as a subproject.

How to determine plugin id for 3rd party gradle plugin library?

For integration test requirement I want to use gradle plugin 'gradle-processes':
https://github.com/johnrengelman/gradle-processes
So I have added version(0.3.0 is only version I see without our organization) in classpath dependency :
dependencies {
classpath 'com.github.jengelman.gradle.plugins:gradle-processes:0.3.0'
}
along with
apply plugin: 'com.github.jengelman.gradle-processes'
But gradle run fails stating that:
Plugin with id 'com.github.jengelman.gradle-processes' not found.
In my local machine repo I can see the jar get downloaded properly, only issue is I am unable to understand what plugin Id I should apply.
Even I went through
https://github.com/johnrengelman/gradle-processes but couldn't get the answer.
Any help to understand how plugin id get determined for a plugin will be really helpful.
Thanks,
Understood the plugin Id concept, so may be useful reference for someone else
https://guides.gradle.org/writing-gradle-plugins/#declare_a_plugin_identifier

Gradle - Adding new configuration to classpath throws error

I have created a custom Gradle plugin which creates a new configuration for some dependencies that I want to treat separately. Since these used to be in the compile configuration I have added the new configuration to the classpath (from the Java plugin) like so:
project.sourceSets.all { sourceSet ->
sourceSet.compileClasspath += myConfiguration
}
My configuration extends the compile configuration. My reasoning for this was that if there were any other 3rd party plugins that did "something" to the compile configuration then it would also affect my new configuration, since it is also an instance of compile.
It seems that later on another plugin, the Spring PropDeps Plugin, also modifies the classpath and the build fails with:
Failed to apply plugin [class 'org.springframework.build.gradle.propdep.PropDepsPlugin']
Cannot change dependencies of configuration ':my-project:compile' after it has been resolved.
Looking at the source code for that plugin they are doing the same steps that I am in my custom plugin to create a configuration, namely create the configuration and add it to the classpath as soon as the plugin is applied, see here.
It would seem crazy if only one plugin could add a new configuration to the classpath. What am I doing wrong here?
Note that my custom plugin is applied in the allprojects block whereas the Spring plugin is applied in the subprojects block - not sure if this matters.
Hmmm ... as I was writing the question something struck me about both extending the compile configuration and adding to the compile classpath. This seemed like I was adding a circular dependency. Low and behold, when I did not extend the configuration the build worked!

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

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.

Resources