Skip to main content

Library Integration

Living Documentation

APIs and control systems change season to season, and sometimes mid-season. Specific class names, file paths, and code examples throughout this section reflect FRC 190's codebase at the time of writing and may become outdated or obsolete as WPILib, CTRE, and other vendor libraries evolve. Treat this section as a guide to the underlying concepts, not a guaranteed match to the current source.

GompeiLib is a separate Gradle project from any season's robot code, which means every season repo has to actually pull it in as a dependency before any of it is usable. 190 supports two ways to do that, and uses both, for different reasons.

The Local Subproject Path

Day to day, a season repo consumes GompeiLib as a local Gradle subproject, its source living right inside the season repo's own checkout rather than being downloaded from anywhere. Two files wire this together. First, settings.gradle tells Gradle that a project named :gompeilib exists, and points it at the lib/ directory instead of a folder named gompeilib:

include(":gompeilib")
project(":gompeilib").projectDir = file("lib")

Then the season repo's own build.gradle depends on that subproject directly:

dependencies {
implementation project(':gompeilib')
aspect project(':gompeilib')
...
}

The aspect line matters as much as implementation does. GompeiLib's tracing annotations (@Trace, seen wherever a subsystem's periodic() needs its execution time measured) are woven in by AspectJ at compile time, not read by reflection at runtime, and that weaving has to happen again in the consuming project, not just inside GompeiLib's own build. Without the aspect line, @Trace annotations copied over with GompeiLib's source would silently do nothing in the season repo, compiling fine but never actually recording anything.

The Published Artifact Path

The alternative is consuming GompeiLib the way any other Maven dependency gets consumed: as a versioned artifact rather than source sitting in the same checkout. GompeiLib's own build.gradle is set up to publish exactly that way:

Show code
group = 'edu.wpi.team190'
version = project.hasProperty("releaseVersion") ? project.property("releaseVersion") : "0.0.0-SNAPSHOT"

publishing {
publications {
mavenJava(MavenPublication) {
from components.java
groupId = group
artifactId = "gompeilib"
version = version
}
}

repositories {
if (System.getenv("CI") == "true") {
maven {
name = "GitHubPackages"
url = uri("https://maven.pkg.github.com/Team-190/GompeiLib")
...
}
}
}
}

Cutting a GitHub Release on the GompeiLib repository is what actually triggers a publish (covered in full in Making Changes), and the release tag becomes the artifact's version directly, -PreleaseVersion=$RELEASE_VERSION is passed straight from the tag name into the Gradle build. A season repo could, in principle, depend on a specific published version from maven.pkg.github.com/Team-190/GompeiLib the same way it depends on any vendor library, pinning to a known-good release instead of whatever lib/ currently contains.

Why the Local Path Is the Default

190 uses the local subproject path for day-to-day development, and the published path is really there for anyone consuming a specific, frozen release rather than actively developing against it. Working out of lib/ means a change to GompeiLib and the season code that depends on it can be made, built, and tested together in one checkout, with no publish-a-new-version-then-bump-the-dependency round trip between them. That tight loop is exactly what makes in-season fixes practical, and it's also why keeping lib/ and the canonical GompeiLib repository from silently drifting apart matters enough to have its own dedicated sync process, covered next in Sync.