Invoking tasks provided by a plugin without altering the build file

I’ve got an use case for what the subject describes. I’ve got a build system that should treat a source repository as a black box, that is, invoke the build without modifying the sources. Additionally I need extra information provided by plugins that may not have been configured in the build, such as jdeps and jdeprscan.

In the Maven world this is as easy as invoking mvn groupId:artifactId:version:targetId; provided the plugin can be resolved from the repositories configured in the POM file (AFAICT). Sadly, there’s no equivalent syntax found in Gradle.

The first alternative I thought of was to create another script with the additional plugin config in place, importing the main build file, then instructing gradle to use the new build file, such that

my-custom-build.gradle

buildscript {
    repositories {
    }

    dependencies {
        classpath 'org.kordamp.gradle:jdeps-gradle-plugin:0.3.0'
    }
}
apply plugin: 'org.kordamp.jdeps'
apply from: 'build.gradle'

Then invoke gradlew -b my-custom-build.gradle jdeps which unfortunately fails. The next option was to append the required customizations to the main build file, but this will cause trouble depending on how the main build file defines plugins.

  1. If it uses buildscript then all is OK as you can have multiple buildscript blocks. Still feels odd somehow.

  2. If it uses plugins and customizations use buildscript then results in Failure with the following error printed to the output "all buildscript {} blocks must appear before any plugins {} blocks in the script

See https://docs.gradle.org/4.6/userguide/plugins.html#sec:plugins_block for information on the plugins {} block"

  1. If it uses plugins and customizations use plugins too then it results in Failure again with the following error printed to the output "only buildscript {} and other plugins {} script blocks are allowed before plugins {} blocks, no other statements are allowed

See https://docs.gradle.org/4.6/userguide/plugins.html#sec:plugins_block for information on the plugins {} block"

Thus the append option works for 1/3 of the possibilities. Is there any other way to make this behavior work, similarly as it’s done with Maven?

Hey Andres,

I think the best way to invoke build logic without touching sources would be using an init script. With shipping init scripts in a init.d folder in the distribution or gradle user home these get also executed automatically.

cheers,
René

Thanks Rene. How would such init script look like? If I understood correctly the information found at https://docs.gradle.org/current/userguide/init_scripts.html a plugin defined on an init script will be applied to the gradle object and not the Project itself.

if you apply the plugin directly in the init script, thats right. but you can get hold of the root project and navigate from there down to all the subprojects etc.

this would look sort of like this:

initscript {
    repositories {
        maven { url 'repo_url' }
    }
    dependencies {
        classpath 'org.kordamp.gradle:jdeps-gradle-plugin:0.3.0'
    }
}
rootProject {
    apply plugin: <<full qualified plugin class name>>

}

I recall there’s one limitation to this approach, you must apply the plugin by class name instead of plugin id.

Indeed. I was getting an error stating Plugin with id 'org.kordamp.jdeps' not found. with a different approach until I saw your remark for fully qualified class.

On the plus side this approach works regardless of how the build has defined plugins (plugins or buildscript block). On the down side the inconsistency of not being able to use the plugin id with the resulting error message being a bit obscure.

It would be great if init scripts would have access to plugin ids, as that data already resides in resource files available in the classpath.

Thanks again!

2 Likes