Gradle show content of compile configuration of a sub project - gradle

Try to show a configuration content of sub project foo:
task foo {
project(':foo').configurations.compile.files.each {
println it.name
}
project(':foo').configurations.compileClasspath.files.each {
println it.name
}
}
Error
* What went wrong:
A problem occurred evaluating project ':foo'.
> Cannot change dependencies of configuration ':foo:compile' after it has been resolved.
I am confused with configurations.compile and configurations.compileClasspath. Are they the same? both are FileCollection?
For current project (not :foo), they contain the same set of files.

Your problem is based on the same technical detail as most of the Gradle problems at StackOverflow: the difference between the configuration phase and the execution phase.
If you define a task (like foo in your example), the code in the following closure does NOT define what the task will do on execution. Instead, it is executed right after the task definition and should be used to configure the task. This is why the code will be executed every time you run Gradle on the project (in the configuration phase). On the other hand, the task in your example will do absolutely nothing when its executed (in the execution phase). Only task actions (defined by the task type), doFirst and doLast closures are executed during execution phase. Also note, that (task) configuration will be done for every task, but only selected tasks (via command line or dependencies) will be executed.
Knowing the above, we can understand the problem in your code: It tries to print the contents of a configuration of a subproject during the configuration phase of the root project. The configuration phase of the subproject, where you add dependencies, comes after the configuration phase of the root project, where you resolve the configuration (which is necessary to show the contents). But you can't add dependencies to configurations that are already resolved.
This means, that your code gets executed, resolves the configuration and prints nothing, because at the moment of execution the configuration is empty. After that, in your subprojects dependencies closure, you are trying to add a dependency to the configuration that is already resolved, which causes the error. Comment out all (compile) dependencies in your subproject foo and the error should disappear.
However, this is not what you want. You want to list the files of the configuration after they have been added. To achieve this, you can easily move your task code to a doLast (or doFirst) closure:
task foo {
doLast {
project(':foo').configurations.compile.files.each {
println it.name
}
}
}
Now, your configuration is resolved in the execution phase of your root project, which comes after the configuration phase of the subproject, so there won't be any problems adding dependencies. Please note, that the task will only be executed if you specify it (e.g. calling gradle foo / gradle build foo or using dependsOn foo on another task which will be executed).
Regarding the difference between the compile and the compileClasspath configuration, you can take a look at the docs of the Gradle Java Plugin, which defines both configurations: Configurations can extend other configurations, which means that they will contain all dependencies the other configuration contains, but can also contain additional dependencies. If you only use compile dependencies, compile and compileClasspath will be the same, but e.g. a compileOnly dependency will only be listed under compileClasspath.

Related

Gradle - copy jar to another folder as part of the 'jar' task

I have a multi project gradle setup. I want to simply copy the generated jar file of any project anytime the jar is rebuild and thought this would work in my root project's subprojects closure:
task copyJarToGenerated(type: Copy) {
from jar
into "../my-generated-jars/"
}
copyJarToGenerated.mustRunAfter jar
But if I run the 'clean' task then 'jar' task of any sub project, my jar is generated under build/libs as usual but not copied.
Looking at the gradle output, it only runs compileJava, processResources, classes then jar. It isn't running copyJarToGenerated.
The method mustRunAfter does not define a task dependency, it just enforces a specific order. It basically says 'if both tasks are executed in a build (for whatever reason), then they are executed in the specified order'.
So you need to define the task dependency on your own:
jar.finalizedBy copyJarToGenerated
You could also just add copyJarToGenerated as a dependency of the lifecycle task build:
build.dependsOn copyJarToGenerated
Since you specify the task jar in the method from of your task, it is registered as a task input and therefor registered as a task dependency implicitly. So you won't need to define order with mustRunAfter anymore.
You may also consider using the property destinationDirectory of the task jar instead of creating a new task at all.

Task that executes a specific task in all included builds

I have a root project that includes quite a number of other projects via includeBuild in settings.gradle. All the subprojects have a task named publishToMavenLocal.
How can I define a task publishToMavenLocal in the root project that calls each publishToMavenLocal of each subproject without the need to manually define dependsOn for every subproject specifically?
This use-case is actually covered in the documentation on composite builds. The following code adapts the example from the documentation to your use-case:
task publishToMavenLocal {
dependsOn gradle.includedBuilds*.task(':publishToMavenLocal')
}

Calling Gradle task for only one subproject

How do I configure subprojects for Gradle and run a Gradle task from the build.gradle only for the current directory I'm executing the task from?
For example, suppose I have two projects in a parent folder:
root-project
project-1
project-2
build.gradle
settings.gradle
I have project 1 and 2 included in the settings.gradle file:
include(
'project-1',
'project-2'
)
I have a task configured in the build.gradle file that I would like to call. I've nested the tasks within a subprojects{} block:
subprojects {
...
task check(type: JavaExec) {
main = MAIN_CLASS
classpath = sourceSets.main.runtimeClasspath
}
...
}
However, the build.gradle is run for all subprojects that way.
How can I define all possible subprojects but only call a Gradle task for the current project I'm executing from (i.e., avoid calling the task for all subprojects)? I would like to do this with the current task name only (e.g., gradle check).
Invoking the task in a single project with gradle check is not possible.
You have two options:
Leverage the model Gradle proposes, where tasks can be prefixed by the project name in a multi project build which removes the need to navigate to subprojects.
Which means invoking gradle :project-1:check or gradle :project-2:check depending on which task you want to run.
Define the tasks to have different name in each subprojects. See task rules on how to define tasks with dynamic names.
Which means invoking gradle check1 or gradle check2

Run task before repositories are added and dependencies are resolved

I'm working on compiling python bindings using gradle.
There is a plugin by linkedin that facilitates that.
They also include a project called the pivy-importer that converts python dependencies into an ivy repository.
I've created a gradle plugin that wraps the pivy-importer so that it can run as a python task.
My repositories are declared like this:
repositories {
pyGradlePyPi()
ivy {
name 'pypi-local' //optional, but nice
url "${project.buildDir.path}/pythonIvy"
layout "pattern", {
ivy "[organisation]/[module]/[revision]/[module]-[revision].ivy"
artifact "[organisation]/[module]/[revision]/[artifact]-[revision](-[classifier]).[ext]"
m2compatible = true
}
}
}
The problem, however, is that the repositories are being loaded before the plugin executes.
The first task that the python task runs is pinRequirements so I was adding my cusom pythonImporter task before that like this:
pinRequirements.dependsOn pythonImporter
However, even when I do that, the console shows that the pythonImporter task is running before but as soon as it tries to assemble the dependencies, it can't find them even though they do exist in the file system.
If you rerun the task again, however, it passes because the first run added the repository to the file system.
TL;DR
I need a way to run a task before dependencies are looked up under using a project's configured repositories are loaded.
I moved the tasks execution to my buildSrc subproject and made the build task depend upon its execution.
This works because the buildSrc project is always evaluated before the rest of the projects so you can do "before build" logic there.

Gradle batch task that invokes subproject and other tasks in order

I am writing gradle 1.4 build file for multimodule project. So there is root build.gradle that defines something like:
subprojects {
apply plugin: 'java'
...
which defines build task for all submodules. Submodules are included in settings.gradle and each module has its build file with defined dependencies.
Everything by-the-book, so far:) Now, in the main build file I've added some additional project-scope tasks, like: aggregateJavadoc (collects all javadocs into one) or bundleJar (creates bundle jar from all classes), etc. Each on works when invoked manually.
Now I need a task release that will
build all submodules (as invoked from command line - meaning, i dont want to manually write execute() for each submodule)
invoke additional tasks (using execute() I presume).
I tried dependsOn but the order of listed tasks is not followed. Also, dependent modules seems to be executed after release task execution. I tried several other ideas and failed.
Question: what would be the best way to create such batch task, that has to invoke something on all submodules and additionally to perform some more tasks? What would be the best gradle-friendly solution? Thanx!
It happened that this can be solved with simple dependency management.
So, we have many modules. Now, lets create additional tasks that depends on modules being build:
task aggregateJavadoc(type: Javadoc) {
dependsOn subprojects.build
task bundleJar(type: Jar) {
dependsOn subprojects.build
Finally, our release task would simply look like this:
task release() {
dependsOn subprojects.build
dependsOn aggregateJavadoc
dependsOn bundleJar
...
}
This will build subprojects first; not because it is listed first, but because additional tasks depends on building. Order of additional task is not important. This make sense the most to me.
EDIT
if one of your subprojects (i.e. modules) is non-java module, then you will have a problem building this project. What I do is to group submodules, like this:
def javaModules() {
subprojects.findAll {it.name.contains('jodd-')}
}
and then instead to refer to subprojects, use javaModules everywhere! For example:
configure(javaModules()) {
apply plugin: 'java'
...
and
task prj {
dependsOn javaModules().build
}
btw, I am using this 'dummy' task prj for dependsOn on all those additional projects that depends on building, to prevent repeating.

Resources