Back to Home

Getting rid of binary dependencies with composite builds in Gradle 3.1

gradle · java

Getting rid of binary dependencies with composite builds in Gradle 3.1

    Since the advent of Gradle, there have been 2 ways to break your assembly into components: through binary dependencies and using multi-project builds. Each of these methods has its pros and cons. In the case of binary dependencies, there is a need to publish artifacts, which complicates the assembly. When using a multi-project assembly, it becomes
    more difficult to isolate the components from each other.


    Composite assemblies


    In the upcoming version 3.1 in Gradle, a new approach to organizing assemblies consisting of several components appears: composite assemblies ( orig . Composite Builds).


    Composite assemblies allow you to:


    • Quickly put the corrected version of the library sources in another project without the need to assemble it, publish and edit the assembly.
    • Divide large projects into several small, isolated assemblies, on each of which you can work individually or simultaneously.
    • Separate plug-in development to build the system from the project, it is used (analogue buildSrc)


    Let's look at a simple example of using the new feature.


    To do this, create a simple projection:


    --app/
    |-src/main/java/Main.java
    |-build.gradle
    - lib/
    |-src/main/java/A.java
    |-build.gradle
    |-settings.gradle

    lib / build.gradle:


    apply plugin: 'java'
    group "ru.shadam"
    version "1.0"
    task wrapper(type: Wrapper) {
        gradleVersion = '3.1-rc-1'
    }

    app / build.gradle


    apply plugin: 'java'
    apply plugin: 'application'
    mainClassName='Main'
    dependencies {
        compile 'ru.shadam:lib1:1.0'
    }
    task wrapper(type: Wrapper) {
        gradleVersion = '3.1-rc-1'
    }

    Now, if we try to run appusing the command, ./gradlew runGradle will swear at an unresolved dependency:


    $ ./gradlew run
    :compileJava
    FAILURE: Build failed with an exception.
    * What went wrong:
    Could not resolve all dependencies for configuration ':compileClasspath'.
    > Cannot resolve external dependency ru.shadam:lib1:1.0 because no repositories are defined.
      Required by:
          project :
    * 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: 1.027 secs

    But, if we add a new flag - include-build, then Gradle will resolve the dependencies automatically:


    $ ./gradlew run --include-build ../lib1
    [composite-build] Configuring build: C:\Users\sala\projects\gradle-compose\lib1
    :compileJava
    :lib1:compileJava UP-TO-DATE
    :lib1:processResources UP-TO-DATE
    :lib1:classes UP-TO-DATE
    :lib1:jar UP-TO-DATE
    :compileJava UP-TO-DATE
    :processResources UP-TO-DATE
    :classes UP-TO-DATE
    :run
    Hello
    BUILD SUCCESSFUL
    Total time: 1.092 secs

    Advanced use cases.


    Embed --include-build in the script


    The above option is more suitable for single use - here and now. I don’t want to specify flags every time - even if I sew them in a wrapper.


    For this, gradle suggests using a configuration using settings.gradle. So, the above flag can be replaced with the following settings.gradle:


    includeBuild('../lib1')

    Lookups


    What if the project that you want to include does not specify the coordinates of the artifact? (group, version)


    Substitutions come to the rescue:


    includeBuild('../lib1') {
        dependencySubstitution {
            substitute('ru.shadam:lib1') with project(':')
        }
    }

    This feature allows you to substitute any dependency on the ru.shadam:lib1dependency on the project lib1.


    Dependencies between tasks


    In the case of a composite assembly, projects are isolated from each other, so you cannot declare dependencies between assemblies directly.


    In this regard, a new syntax has appeared for accessing included assemblies. For example, you can determine the dependence on the task of the included assembly:


     task run {
        dependsOn gradle.includedBuild('lib1').task(':jar')
    }

    What doesn't work yet?


    • Projects that have published artifacts that do not match the default configuration are not supported as included projects. link
    • While there is no support in the IDE (but support for generating a project using the command ./gradlew idea)
    • Native builds are not supported.

    Team plans


    • Add the ability to call tasks directly from included assemblies.
    • Add the ability to execute included builds in parallel
    • Add change detection in enabled assemblies when using a flag -t.
    • Make the implicit buildSrc project an included build.

    Used materials


    Read Next