Gradle and Multi-Project structure
Asked Answered
H

2

61

I'm trying to understand how should I approach the following project setup:

┌Top Android Project
│
├── Project 1 - (Pure Java Modules)
│    │
│    ├── Module A1
│    ├── Module B1
│    :
│    └── Module Z1
│  
├── Project 2 - (Android Libraries Modules)
│    │
│    ├── Module A2
│    ├── Module B2
│    :
│    └── Module Z2
│  
└── Module - Actual Android Project

In the current setup I have there is a build.gradle in each of the Modules, what I really hate about this setup is that all the content of the build.gradle is duplicated between modules.

Fact is that I would like the same logic in most of them, 'Pure Java Modules' are all infra modules, which I would like to Jar the output, the JavaDoc, and sources, and deploy to some remote repository (* default).

On the other hand, some modules of the 'Pure Java Modules' I would like to have a second agenda, for example, aside from the default* build I would like to deploy a jar with dependencies for a specific project, or something like that.

While building the Actual Android Project, I would like the modules to compile in the default* build, and finally to configure a default build.gradle for all of my Android projects, which are quite a few, and I would not like to duplicate that file.

===============================================================

I guess what I'm looking for is something like Maven parent pom, but since I don't fully understand how Gradle works, I'm opening this to you guys to share your thoughts...

Taking under consideration that duplicating same build file is (I dare say) unacceptable, due to the fact that I might want to change something in all the build logic of all the modules

What would be the best approach to handle this sort of setup?

Huang answered 8/7, 2013 at 21:55 Comment(0)
E
59

Most of this is pretty normal to the http://www.gradle.org/docs/current/userguide/multi_project_builds.html page. However you are going to need to add

evaluationDependsOn(':project1')
evaluationDependsOn(':project2')

so that gradle will evaluate project1 and project2 before module. In all the projects that contain code you will need to have an empty build.gradle file. This will also allow you to customize a project if needed.

Example: https://github.com/ethankhall/AndroidComplexBuild

Add a build.gradle at the root of your projects. So you need 4 that have useful information in it.

/build.gradle
/settings.gradle
/project1/build.gradle
/project2/build.gradle
/module/build.gradle

in /build.gradle put

dependencies {
    project(":module")
}

in /settings.gradle put

include ':module'
include ':project1', ':project1:A1', ':project1:B1', ':project1:Z1'
include ':project2', ':project2:A2', ':project2:B2', ':project2:Z2'

in /project1/build.gradle put

apply plugin: 'java'

subprojects {
    apply plugin: 'java'

    sourceCompatibility = JavaVersion.VERSION_1_6
    targetCompatibility = JavaVersion.VERSION_1_6

    repositories{
        mavenCentral()
    }   

    //Anything else you would need here that would be shared across all subprojects
}

/project2/build.gradle

buildscript {
    repositories {
        mavenCentral()
    }   

    dependencies {
        classpath 'com.android.tools.build:gradle:0.4.2'
    }   
}

subprojects {
    apply plugin: 'android-library'

    android {
        compileSdkVersion 17
        buildToolsVersion "17.0"
    }   

    sourceCompatibility = JavaVersion.VERSION_1_6
    targetCompatibility = JavaVersion.VERSION_1_6

    repositories{
        mavenCentral()
    }   

    //Anything else you would need here that would be shared across all subprojects
}

in /module/build.gradle put

buildscript {
    repositories {
        mavenCentral()
    }   

    dependencies {
        classpath 'com.android.tools.build:gradle:0.4.2'
    }   
}

evaluationDependsOn(':project1')
evaluationDependsOn(':project2')

apply plugin: 'android'

android {
    compileSdkVersion 17
    buildToolsVersion "17.0"
}

dependencies {
    compile project(":project1:A1")
    compile project(":project1:B1")
    compile project(":project1:Z1")

    compile project(":project2:A2")
    compile project(":project2:B2")
    compile project(":project2:Z2")
}
Eleventh answered 9/7, 2013 at 0:33 Comment(14)
My friend, that is a greatly detailed answer, I've been reading the Multi-Project section of Gradle last night, and realized that the solution would have to go that way. Thanks.Huang
Thanks for the sample project on GitHub, saved me a lot of Copy-Paste :) BTW, is there a reason for the settings.build in the Project1 folder?Huang
I was messing around. it shouldn't be there.Eleventh
Theoretically, if I have settings.build in a sub folder, and I run a build script in that sub folder, would Gradle use the settings in the sub folder, or the parent folder? (specifically asking about IntelliJ)Huang
I think it's just the one in /. If you were to call build in project1 it would do :project1:build (on project1, task build)Eleventh
so the settings in project1 would be used?Huang
I've implemented the setup you have suggested, with a twist, project1 and project2 are external to the "Top Android Project" (these are two git repositories) and they are imported into the project. When I execute the build all the output(buildDir I think) of the modules of both project1, and project2 are under "Top Android Project/project(1 || 2)/module(A || B || Z)" thus causes duplication in the output in case I have two Top Projects... am I clear enough?Huang
the settings in project1 shouldn't be used. Yeah gradle will build everything in one directory. (buildDir). I don't quite understand what you mean by duplication.Eleventh
let us continue this discussion in chatEleventh
You could move your buildscriptpart of the code to your top-level build.gradle file, to solve some of the duplication :)Lynnalynne
@arcone really? I didn't know that it would apply it down to all subsequent projects. I will have to play with that. Thanks!Eleventh
@ethan If you figure out some more awesome stuff to extract out there, please tell me :)Lynnalynne
@Eleventh : well done. Very well thought through and informative answer.Newsom
ethan? did you just answer that one with a complete github repo? Kudos!Engeddi
L
8

If you have folders matching that structure you can apply the same build logic to multiple projects.

If your settings.gradle contains

include ':project2:moduleA2'

then ':project2' is also a project, and it can have its own build.gradle, in which you can write:

subprojects { project ->
    apply plugin 'android-library'

    // more configuration
}

If you don't apply any plugin to ':project2' itself then this project simply won't output anything (which is probably what you want) but that way you can configure all its sub-project in a simple go.

Then you can also have all your submodule put some logic that's specific to them

You can also technically do that in project2/build.gradle if you want to keep it all in the same file. You can read http://www.gradle.org/docs/current/userguide/multi_project_builds.html to see how to configure subprojects from a parent build.gradle file, accessing either all subprojects or a specific one, or using filtering.

Lack answered 8/7, 2013 at 23:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.