Custom plugin & multi-project project


(denis.zhdanov) #1

Hello,

Have a question regarding custom plugin appliance at a multi-project environment.

Consider a build.gradle of a root project:

subprojects {
    apply plugin: 'java'
      buildscript {
        repositories {
            maven {
                url 'http://myurl/nexus/content/repositories/releases/'
            }
        }
        dependencies {
            classpath 'mygroup:nonnull-instrumenter:1.0'
        }
    }
    apply plugin: 'nonnull'
      sourceCompatibility = 1.7
      ...
}

Here ‘nonnull’ is my custom plugin. I get the following on attempt to build from a root project dir:

FAILURE: Build failed with an exception.
  * Where:
Build file '/Users/denis/project/java/xxx/build.gradle' line: 31
  * What went wrong:
A problem occurred evaluating root project 'search'.
> Plugin with id 'nonnull' not found.
  * Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
  BUILD FAILED
  Total time: 4.269 secs

Note that here line 31 mentioned at the error message is a line with text “apply plugin: ‘nonnull’”.

The problem is fixed if I add the same buildscript initialization at the root project:

buildscript {
    repositories {
        maven {
            url 'http://myurl/nexus/content/repositories/releases/'
        }
    }
    dependencies {
        classpath 'mygroup:nonnull-instrumenter:1.0'
    }
}
    subprojects {
    apply plugin: 'java'
      buildscript {
        repositories {
            maven {
                url 'http://myurl/nexus/content/repositories/releases/'
            }
        }
        dependencies {
            classpath 'mygroup:nonnull-instrumenter:1.0'
        }
    }
    apply plugin: 'nonnull'
      sourceCompatibility = 1.7
      ...
}

It seems that the problem is that root project is unaware of my custom plugin but it’s not clear why does it try to evaluate it at all because the plugin appliance statement is located at ‘subprojects’ block. Tried to google the answer but no luck so far. Any ideas?

Regards, Denis


(Peter Niederwieser) #2

I think the problem is different, namely that ‘buildscript’ needs to be top-level and cannot be nested inside ‘subprojects’. (‘buildscript’ is very special; it gets extracted from the build script before compiling/evaluating the script.) Hence your nested ‘buildscript’ block doesn’t have any effect, but the one for the root project also affects subprojects (due to Gradle’s current class loader hierarchy).


(denis.zhdanov) #3

Thanks, that works!

Btw, /unrelated to the thread’s question/ is it possible to get a classloader which knows either about compiled project source classes or project binary dependencies (including transitive)? It looks like defining classloader for plugin’s class is aware only of compiled project source classes :frowning:


(Peter Niederwieser) #4

Are you perhaps looking for ‘project.buildScript.classLoader’?


(denis.zhdanov) #5

Thank you for the advice but it seems I was not clear enough. Basically, I need to get a classloader which is backed by the same classpath as the one used during compilation


(Peter Niederwieser) #6

During compilation of what exactly? The build script? The project’s source code?


(denis.zhdanov) #7

The project’s source code. But I think I found the solution - url classloader for project.configurations.getByName(“compile”).dependencies

Denis


(Peter Niederwieser) #8

I’d recommend to create a class loader based on ‘sourceSets.main.compileClasspath’. This is the “source of truth” and defaults to ‘configurations.compile’ (but is sometimes reconfigured). Depending on what you are doing, you might also have to take other source sets into account, e.g. ‘test’ or a manually declared source set.


(denis.zhdanov) #9

How can I access sourceSets in case of java plugin? I don’t see a method like getSourceSets() at Project class given to the plugin’s apply() method :frowning:


(Peter Niederwieser) #10

The concept of source sets is introduced by the Java (base) plugin. In a plugin implemented in Groovy, you would do:

plugins.withType(JavaPlugin) { // only execute if/when Java plugin has been applied
        def mainSourceSet = project.sourceSets.main
            ...
    }

In Java, the inner part becomes:

JavaPluginConvention javaConvention = project.getConvention().getPlugins().get("java");
    SourceSet mainSourceSet = javaConvention.getSourceSets().getByName("main");

(denis.zhdanov) #11

Thanks, Peter!

That did the trick

Regards, Denis