Skip to content

Commit 050dbca

Browse files
authored
Migrate build to Gradle 9 (#935)
1 parent 0276deb commit 050dbca

32 files changed

Lines changed: 708 additions & 566 deletions

.github/workflows/ci.yaml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,41 @@ on:
55
- main
66
pull_request:
77
jobs:
8+
format:
9+
runs-on: ubuntu-latest
10+
name: Formatting
11+
steps:
12+
- uses: actions/checkout@v4
13+
14+
- uses: actions/setup-java@v4
15+
with:
16+
distribution: temurin
17+
java-version: 17
18+
19+
- uses: gradle/actions/setup-gradle@v4
20+
with:
21+
gradle-version: 9.4.1
22+
23+
- run: gradle spotlessCheck --no-daemon
24+
25+
kotlin_plugin:
26+
runs-on: ubuntu-latest
27+
name: scip-kotlinc
28+
steps:
29+
- uses: actions/checkout@v4
30+
31+
- uses: actions/setup-java@v4
32+
with:
33+
distribution: temurin
34+
java-version: 17
35+
36+
- uses: gradle/actions/setup-gradle@v4
37+
with:
38+
gradle-version: 9.4.1
39+
40+
- name: scip-kotlinc tests
41+
run: gradle :scip-kotlinc:test --no-daemon
42+
843
docker_test:
944
runs-on: ubuntu-latest
1045
name: Docker CLI tests

.github/workflows/nix.yaml

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -107,21 +107,6 @@ jobs:
107107
- run: du -h index.scip
108108
working-directory: examples/bazel-example
109109

110-
kotlin_plugin:
111-
runs-on: ubuntu-latest
112-
name: scip-kotlinc
113-
steps:
114-
- uses: actions/checkout@v4
115-
116-
- uses: DeterminateSystems/nix-installer-action@v22
117-
with:
118-
summarize: false
119-
120-
- uses: DeterminateSystems/magic-nix-cache-action@v13
121-
122-
- name: scip-kotlinc tests
123-
run: gradle :scip-kotlinc:test --no-daemon
124-
125110
snapshots:
126111
runs-on: ubuntu-latest
127112
name: Snapshots

CONTRIBUTING.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ These are the main components of the project.
5858
| `gradle :scip-snapshots:saveSnapshots --no-daemon` | terminal | Regenerate Java and Kotlin snapshot goldens. |
5959
| `gradle :scip-java:installDist --no-daemon` | terminal | Build a local `scip-java` distribution under `scip-java/build/install/`. |
6060
| `gradle :scip-java:run --args='--cwd DIRECTORY'` | terminal | Run `scip-java` against a given Gradle/Maven build. |
61-
| `google-java-format --replace $(git ls-files '*.java')` | terminal | Format Java sources (from `nix develop`). Enforced by `nix flake check`. |
62-
| `ktfmt --kotlinlang-style $(git ls-files '*.kt')` | terminal | Format Kotlin sources (from `nix develop`). Enforced by `nix flake check`. |
61+
| `gradle spotlessApply` | terminal | Format Java and Kotlin sources with Spotless. |
62+
| `gradle spotlessCheck` | terminal | Check Java and Kotlin formatting. Enforced by GitHub Actions. |
6363

6464
## Import the project into IntelliJ
6565

bin/docker-setup.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ unzip -d /opt/maven maven.zip
1010
rm maven.zip
1111
mv /opt/maven/*/* /opt/maven
1212

13-
curl -fLo gradle.zip https://services.gradle.org/distributions/gradle-8.14.4-bin.zip
13+
curl -fLo gradle.zip https://services.gradle.org/distributions/gradle-9.4.1-bin.zip
1414
unzip -d /opt/gradle gradle.zip
1515
rm gradle.zip
1616
mv /opt/gradle/*/* /opt/gradle

build-logic/build.gradle.kts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import org.gradle.plugin.use.PluginDependency
2+
3+
plugins {
4+
`kotlin-dsl`
5+
}
6+
7+
dependencies {
8+
implementation(plugin(libs.plugins.kotlin.jvm))
9+
implementation(plugin(libs.plugins.shadow))
10+
implementation(plugin(libs.plugins.vanniktech.maven.publish))
11+
}
12+
13+
// Maps a version-catalog plugin alias to its plugin-marker dependency so the
14+
// external plugin can be applied by id from the precompiled convention plugins.
15+
fun plugin(dependency: Provider<PluginDependency>): Provider<String> =
16+
dependency.map { "${it.pluginId}:${it.pluginId}.gradle.plugin:${it.version}" }

build-logic/settings.gradle.kts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
dependencyResolutionManagement {
2+
repositories {
3+
gradlePluginPortal()
4+
mavenCentral()
5+
}
6+
versionCatalogs {
7+
create("libs") {
8+
from(files("../gradle/libs.versions.toml"))
9+
}
10+
}
11+
}
12+
13+
rootProject.name = "build-logic"
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.sourcegraph.buildlogic
2+
3+
import org.gradle.api.Project
4+
import org.gradle.api.Task
5+
import org.gradle.api.tasks.TaskProvider
6+
7+
/**
8+
* Registers a task that writes [content] (plus a trailing newline) to
9+
* build/generated/[relativePath], tracking the content for up-to-date checks.
10+
*/
11+
fun Project.registerGeneratedFile(
12+
name: String,
13+
relativePath: String,
14+
content: String,
15+
): TaskProvider<Task> {
16+
val output = layout.buildDirectory.file("generated/$relativePath")
17+
return tasks.register(name) {
18+
inputs.property("content", content)
19+
outputs.file(output)
20+
doLast {
21+
val file = output.get().asFile
22+
file.parentFile.mkdirs()
23+
file.writeText("$content\n")
24+
}
25+
}
26+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.sourcegraph.buildlogic
2+
3+
import java.io.File
4+
import java.util.Properties
5+
6+
/**
7+
* Reads the JVM flags scip-javac needs to access internal javac APIs on Java 9+, keeping
8+
* gradle/javac-internals.properties the single source of truth.
9+
*/
10+
object JavacInternals {
11+
fun propertiesFile(rootDir: File): File = rootDir.resolve("gradle/javac-internals.properties")
12+
13+
fun jvmOptions(rootDir: File): List<String> {
14+
val file = propertiesFile(rootDir)
15+
val raw =
16+
Properties()
17+
.apply { file.inputStream().use { load(it) } }
18+
.getProperty("javac.jvmOptions") ?: error("Missing javac.jvmOptions in $file")
19+
return raw.split(',').map { it.trim() }.filter { it.isNotEmpty() }
20+
}
21+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package com.sourcegraph.buildlogic
2+
3+
import java.io.File
4+
import org.gradle.api.Task
5+
import org.gradle.api.artifacts.Configuration
6+
import org.gradle.api.file.Directory
7+
import org.gradle.api.file.FileSystemLocation
8+
import org.gradle.api.provider.Provider
9+
import org.gradle.api.tasks.compile.JavaCompile
10+
11+
/**
12+
* Wires this [JavaCompile] task to run the scip-javac compiler plugin: puts the shaded plugin jar
13+
* [javacShadowJar] on the compile classpath and forks javac with the internal-API flags from
14+
* gradle/javac-internals.properties, recording [targetroot] as an output directory.
15+
*
16+
* Callers still add the case-specific `-Xplugin:scip ...` arguments and annotation processors.
17+
*/
18+
fun JavaCompile.useScipJavac(
19+
rootDir: File,
20+
javacShadowJar: Configuration,
21+
targetroot: Provider<Directory>,
22+
) {
23+
classpath = classpath.plus(javacShadowJar)
24+
outputs.dir(targetroot)
25+
options.isFork = true
26+
options.forkOptions.jvmArgs =
27+
(options.forkOptions.jvmArgs ?: emptyList()) + JavacInternals.jvmOptions(rootDir)
28+
}
29+
30+
/**
31+
* Builds the `kotlinc` arguments that load the scip-kotlinc compiler plugin from [pluginClasspath]
32+
* (the resolved shaded jar) and point it at [sourceroot]/[targetroot].
33+
*
34+
* The mapping lives here, in compiled build logic, rather than in a build script: a `.map {}`
35+
* lambda declared in a `.gradle.kts` file captures a hidden reference to the script object, which
36+
* the configuration cache cannot serialize.
37+
*/
38+
fun scipKotlincPluginArgs(
39+
pluginClasspath: Provider<Set<FileSystemLocation>>,
40+
sourceroot: String,
41+
targetroot: String,
42+
): Provider<List<String>> =
43+
pluginClasspath.map { locations ->
44+
listOf(
45+
"-Xplugin=${locations.single().asFile.absolutePath}",
46+
"-P",
47+
"plugin:scip-kotlinc:sourceroot=$sourceroot",
48+
"-P",
49+
"plugin:scip-kotlinc:targetroot=$targetroot",
50+
)
51+
}
52+
53+
/**
54+
* Registers a `doFirst` action that empties [dir] (deletes then recreates it) before the task runs.
55+
*
56+
* The action lives here, in compiled build logic, rather than in a `.gradle.kts` script: a lambda
57+
* declared in a build script captures a hidden reference to the script object, which the
58+
* configuration cache cannot serialize.
59+
*/
60+
fun Task.cleanDirectoryBeforeRunning(dir: Provider<Directory>) {
61+
doFirst {
62+
val file = dir.get().asFile
63+
file.deleteRecursively()
64+
file.mkdirs()
65+
}
66+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package com.sourcegraph.buildlogic
2+
3+
import org.gradle.api.Project
4+
import org.gradle.api.artifacts.Configuration
5+
import org.gradle.api.file.Directory
6+
import org.gradle.api.provider.Provider
7+
8+
/**
9+
* Declares a resolvable [Configuration] consuming the [configuration] artifact published by the
10+
* project at [producerPath]. The returned `FileCollection` resolves to that project's output and
11+
* carries the task dependency that builds it, so consumers avoid `evaluationDependsOn` and reaching
12+
* across project boundaries, keeping evaluation order and the configuration cache intact.
13+
*/
14+
fun Project.projectArtifact(
15+
producerPath: String,
16+
configuration: String,
17+
name: String,
18+
): Configuration {
19+
val bucket = configurations.dependencyScope("${name}Classpath")
20+
dependencies.add(
21+
bucket.name,
22+
dependencies.project(mapOf("path" to producerPath, "configuration" to configuration)),
23+
)
24+
return configurations.resolvable(name) { extendsFrom(bucket.get()) }.get()
25+
}
26+
27+
/**
28+
* Resolvable view of another project's shaded jar (the `scip.shadow-producer` convention plugin).
29+
*/
30+
fun Project.shadowJarArtifact(producerPath: String, name: String): Configuration =
31+
projectArtifact(producerPath, "shadowJarElements", name)
32+
33+
/**
34+
* Publishes [directory] (produced by [producedBy]) as a consumable artifact in a [name]
35+
* configuration, so a sibling project can resolve it with [projectArtifact].
36+
*/
37+
fun Project.publishDirectoryArtifact(
38+
name: String,
39+
directory: Provider<Directory>,
40+
producedBy: Any,
41+
) {
42+
val elements = configurations.consumable(name).get()
43+
artifacts.add(elements.name, directory) { builtBy(producedBy) }
44+
}

0 commit comments

Comments
 (0)