Defining a composite build only to build all subprojects

Hello!

Recently I read the article about composite builds and realized that it is what I need. But I haven’t had any success with it so I need your professional help.

I have a project which components are located in separated git repositories. These components depend on each other in some way so it is quite inconvenient to do any modification in the code. So when I’ve found out the composite build feature I tried to apply it for my case.

I did a synthetic repository which contains only git submodules for all components. Then I added a folder which contains only Gradle files to define composite build. So I got quite simple structure like this:

/all-components
 ├ /build
 │  ├ build.gradle
 │  ├ gradle.properties
 │  └ settings.gradle
 ├ /component1
 │  ├ ...
 │  └ build.gradle
 ├ /component2
 │  ├ ...
 │  └ build.gradle
 ├ /component3
 │  ├ ...
 │  └ build.gradle
 └ .gitmodules

Then I defined all included builds in the build/settings.gradle file like below:

rootProject.name = 'all-components'

includeBuild '../component1'
includeBuild '../component2'
includeBuild '../component3'

So when I performed gradle build command I’ve expected to see build-artifacts in each component’s subfolder as if I would performed manually gradle build command within each component’s subfolder. But my expectations weren’t met.

Yes, I saw examples on GitHub. They have some root project which contains source code and defines dependencies for all subprojects. In my case I don’t have a root project which depends on all subprojects. Most likely I have several independent components some of them depend on each other. And I would like to combine them by making synthetic composite build project.

Could you give me advice how to solve this problem? :slight_smile:

I think your problem is that your settings.gradle etc. files for the synthetic project are in /build. They should be at the root of the synthetic tree.

@luke_daley Thank you for reply! I thought the same, it was my first attempt. Unfortunately it does not work too. So I tried to follow official examples where all projects are located at the same level.

What actually happens when you run gradle build ? What’s the output?

I’ve just prepared the example here.

I run gradle build within all directory and have an output like this:

> Task :buildEnvironment                                    
                                                            
------------------------------------------------------------
Root project                                                
------------------------------------------------------------
                                                            
classpath                                                   
No dependencies                                             
                                                            
                                                            
BUILD SUCCESSFUL in 1s                                      
1 actionable task: 1 executed                               

UPDATE:

Also the .gradle folders are created in all project folders. But I expect that build folders will be created and I will be able to get e.g. jar files from them.

Included builds are triggered by either artifact dependencies or task dependencies.

Artifact dependencies are substituted with artifacts published by included builds, see https://docs.gradle.org/current/userguide/composite_builds.html#included_build_declaring_substitutions

Tasks dependencies need to be explicitly declared, see https://docs.gradle.org/current/userguide/composite_builds.html#included_build_task_dependencies

1 Like

Tasks dependencies work excellent if subprojects are single-project builds. Defining the buildAll task in all/build.gradle file solves my problem indeed:

task buildAll {
	dependsOn gradle.includedBuilds*.task(':build')
}

But to be honest my components are multi-project builds. I’ve just updated my example and you can see new structure. It is nearer what I have. So when I try to run the buildAll task defined above it does not do what I want.

> Task :component1:buildEnvironment                         
                                                            
------------------------------------------------------------
Root project                                                
------------------------------------------------------------
                                                            
classpath                                                   
No dependencies                                             
                                                            
> Task :component2:buildEnvironment                         
                                                            
------------------------------------------------------------
Root project                                                
------------------------------------------------------------
                                                            
classpath                                                   
No dependencies                                             
                                                            
> Task :component3:buildEnvironment                         
                                                            
------------------------------------------------------------
Root project                                                
------------------------------------------------------------
                                                            
classpath                                                   
No dependencies                                             
                                                            
                                                            
BUILD SUCCESSFUL in 1s                                      
3 actionable tasks: 3 executed                              

The output is very similar my first one, components do not have build task. How can I solve this problem gracefully? Should I define manually some kind of build task in root gradle.build file for each multi-project build? Or is there more elegant way?

When using task dependencies, you have to provide the task path, preventing your use case where build does not exist in your included build root project. A workaround in your specific case would be to create those tasks in the included builds, or to target specific subproject tasks in your composite.

But, wiring tasks is not the main use case for composite builds at the moment. Artifact dependencies is where composite builds shine.

@eskatos Thank you for your comprehensive answer. Yes, I have a quite specific case I just want to automate build of my legacy project.

Following your tip, I updated my example by adding buildAll task for each root project in multi-project builds.

Components define buildAll tasks:

task buildAll(type: GradleBuild) {
    tasks = [ 'build' ]
}

Composite build defines a root buildAll task:

task buildAll {
    dependsOn gradle.includedBuilds*.task(':buildAll')
}

So calling gradle buildAll builds all nested projects and it’s quite enough to my case. :slight_smile:

1 Like

Ah, nuts. I’m trying to do this, but I don’t have a way to force all downstream projects to add a task so that I can call it.

Is there no other way? :frowning:

@trejkaz try this in your rootproject build script

allprojects {
    it.tasks.register("myTask") { ...
   }
}

For a generalized solution using a task rule and Kotlin DSL you may want to check my answer on StackOverflow.