composite builds in gradle

In the lab today, I got a nice Gradle question. I’m answering it here on the blog so all the students can see it. (and anyone else who is curious).

The use case

We have a common library that this year (and all future year’s robots should use). However, this is the first year of competition with the library so it is likely we will need to make changes to it and rebuild at competition.

Constraint #1 – lack of wifi

For any professionals reading this wondering why we don’t use Jitpack, github or any other hosting – internet is “tricky” at competition.; It is common for there to not be any wifi in the pit and you aren’t allowed to make your own hotspot. So we need a solution that works without internet and easily lets us re-build on the same machines.

Constraint #2 – reuse

I really want the library (StuyLib) to stay in it’s own repository and not have the build file “polluted” by any changes. Why you ask? So it stays a library and not something we copy/paste from year to year.

The solution

Composite builds. Gradle has a nice system for composite builds that lets you refer to one build from another.

Directory structure

  • git clone https://github.com/StuyPulse/StuyLib
  • git clone https://github.com/StuyPulse/fantastic-spork (or any other robot)
  • That means StuyLib and fantastic-spork are in parallel directories

Updates to StuyLib

Yes, I know, I said I didn’t want updates. The only things I added were a group and version variable. That gives the artifact a proper name. (group id = com.stuypulse, artifact id = StuyLib, version = 1.0.0). StuyLib still doesn’t know it is part of a composite build.

plugins {
   id "java"
   id "edu.wpi.first.GradleRIO" version "2020.1.2"
}

// add the following two lines
group = 'com.stuypulse'
version = '1.0.0'

sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11

Updates to Robot

At end of settings.gradle, add this line:

includeBuild '../StuyLib'

Then in build.gradle, add a dependency on StuyLib

def ROBOT_MAIN_CLASS = "com.stuypulse.robot.Main"

// add a dependency
dependencies {
  implementation('com.stuypulse:StuyLib:1.0.0')
}

Testing

First, I tested that I could build each independently:

  1. cd StuyLib
  2. ./gradlew build
  3. cd ..
  4. cd fantastic-spork
  5. ./gradlew build

Then I added a class that used existing functionality in StuyLib to fantastic-spork

package jb;

import com.stuypulse.stuylib.util.*;

public class Jeanne {
  StopWatch s;
//  String str = StopWatch.TEST;
}

I re-ran the ./gradlew build in fantastic-spork and good. Then I added one line to StopWatch:

public static final String TEST = "test";

I uncommented the line in Jeanne.java. Then for the key part. I re-ran ./gradlew build in fantastic-spork and it worked. I was able to recompile fantastic-spork and have it see a change in StuyLib without having to rebuild StuyLib.

Plus this change to StuyLib only exists on my machine which proves it is being used.