Skip to content

Build: upgrade to Gradle 9 + AGP 8.7.2#1270

Merged
msridhar merged 12 commits intouber:masterfrom
msridhar:chore/gradle9-agp8
Sep 3, 2025
Merged

Build: upgrade to Gradle 9 + AGP 8.7.2#1270
msridhar merged 12 commits intouber:masterfrom
msridhar:chore/gradle9-agp8

Conversation

@msridhar
Copy link
Copy Markdown
Collaborator

@msridhar msridhar commented Aug 31, 2025

  • Gradle wrapper: 9.0.0 with SHA256
  • Root build: bump Android Gradle Plugin to 8.7.2
  • Android DSL: add namespace, migrate lintOptions->lint, remove BaseVariant APIs
  • Manifests: remove package attributes (AGP 8 requires Gradle namespace)
  • Gradle 9 removal of internal APIs: replace org.gradle.util.VersionNumber with use of semver4j library
  • Rewrite jar-infer and library-model integration test build scripts to not mutate the output jar file after creation for test module

Summary by CodeRabbit

  • Chores
    • Upgraded Gradle wrapper to 9.0.0 and Android Gradle Plugin to 8.7.2.
    • Updated toolchains resolver plugin to 1.0.0.
  • Refactor
    • Migrated Android modules to use the namespace DSL (removed package from manifests).
    • Replaced per-variant Java compile tweaks with a global configuration compatible with newer AGP.
    • Switched build steps to dedicated tasks for generating and embedding analysis artifacts, improving incrementality and reliability.
    • Updated version parsing in build logic to a more robust semantic versioning approach.
  • Style
    • Minor comment cleanup in a sample activity.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Aug 31, 2025

Walkthrough

Upgrades Gradle wrapper to 9.0.0, Android Gradle Plugin to 8.7.2, and toolchains resolver to 1.0.0. Refactors dependency version parsing to semver4j. Adds Android namespaces and removes manifest package attributes. Reworks JarInfer and library-model generator to generate .astubx via Exec tasks and embed into jars during packaging.

Changes

Cohort / File(s) Change summary
Build system version bumps
build.gradle, gradle/wrapper/gradle-wrapper.properties, settings.gradle
Updated AGP to 8.7.2; Gradle wrapper to 9.0.0 (with new SHA256); Foojay resolver plugin to 1.0.0.
Version parsing refactor
gradle/dependencies.gradle
Replaced org.gradle.util.VersionNumber with semver4j; added buildscript classpath for semver4j; introduced parseSemver helper; updated epApiVersion validation and comparison logic accordingly.
Android namespace updates and manifest cleanup
jar-infer/test-android-lib-jarinfer/build.gradle, jar-infer/test-android-lib-jarinfer/src/main/AndroidManifest.xml, sample-app/build.gradle, sample-app/src/main/AndroidManifest.xml
Added android namespace properties; removed manifest package attributes; migrated lintOptions to lint DSL; replaced per-variant JavaCompile config with global tasks.withType(JavaCompile).
JarInfer pipeline refactor (Java lib)
jar-infer/test-java-lib-jarinfer/build.gradle
Introduced baseJar and generateJarInferAstubx (Exec) tasks; wired CLI shadowJar artifact; jar depends on generation and embeds generated astubx into com/uber/nullaway/jarinfer/provider; removed previous jar.doLast/javaexec approach.
Library model generator pipeline refactor
library-model/test-library-model-generator/build.gradle
Added generateLibModelsAstubx (Exec) task; wired CLI shadowJar artifact; jar depends on generation and embeds astubx into com/uber/nullaway/libmodel/provider; removed prior jar.doLast/javaexec and explicit assemble dependency.
Comment-only code tweak
sample-app/src/main/java/com/uber/myapplication/MainActivity.java
Updated a comment; no functional changes.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Dev as Developer
  participant G as Gradle
  participant B as baseJar (Jar)
  participant C as CLI shadowJar (Jar)
  participant E as generate...Astubx (Exec)
  participant J as jar (Jar)
  participant O as Output JAR

  Dev->>G: Run :jar
  activate G
  G->>B: Build base artifact
  B-->>G: base-*.jar
  G->>C: Build CLI shaded JAR
  C-->>G: *-cli-all.jar
  G->>E: Exec java -jar CLI -i base.jar -o *.astubx
  E-->>G: generated *.astubx
  G->>J: Package JAR with astubx (into provider path)
  J-->>O: Published JAR containing astubx
  deactivate G

  note over E,J: Applied in both:\n- jar-infer/test-java-lib-jarinfer\n- library-model/test-library-model-generator
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested reviewers

  • yuxincs
✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@codecov
Copy link
Copy Markdown

codecov bot commented Aug 31, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 88.55%. Comparing base (41b46ba) to head (f424355).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff            @@
##             master    #1270   +/-   ##
=========================================
  Coverage     88.55%   88.55%           
  Complexity     2431     2431           
=========================================
  Files            91       91           
  Lines          8013     8013           
  Branches       1598     1598           
=========================================
  Hits           7096     7096           
  Misses          458      458           
  Partials        459      459           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@msridhar msridhar force-pushed the chore/gradle9-agp8 branch 2 times, most recently from 72e3ff4 to c183145 Compare September 1, 2025 22:30
msridhar added a commit that referenced this pull request Sep 2, 2025
This PR implements the following new policy for jobs to run on CI:

* We only run the build on the latest LTS JDK version (currently 21)
* Run tests on the latest LTS JDK version (the standard `test` task)
* Run tests on any older LTS versions supported by the latest version of
Error Prone (currently JDK 17)
* Test on latest JDK version (currently 24)
* Test on oldest supported Error Prone version, on oldest JDK that it
supports, only for core NullAway tests (JDK 11, Error Prone 2.14.0)
* Test on Linux, Mac, and Windows

This is in preparation for updating to Gradle 9 (#1270), which can only
run on JDK 17+.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- Chores
- Simplified CI: trimmed matrix to macOS and Windows, standardized job
names, and unified build/test steps.
  - Adjusted coverage gating to run on Linux with repository filter.
  - Removed matrix-based gating for local publishing.
  - Minor cleanup of formatting and step structure.

- Tests
  - Updated JDK test targets: replaced 21 with 17; 24 remains.
  - Kept certain JDK-specific tests disabled, aligned with new targets.
- Removed a version-specific test exclusion, enabling the affected test
to run across versions.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
- Gradle wrapper: 9.0.0 with SHA256
- Root build: bump Android Gradle Plugin to 8.7.2
- Android DSL: add namespace, migrate lintOptions->lint, remove BaseVariant APIs
- Manifests: remove package attributes (AGP 8 requires Gradle namespace)
- Gradle 9 removal of internal APIs: replace org.gradle.util.VersionNumber with local comparator
- Jar post-processing: replace jar.doLast { javaexec/exec } with Exec tasks and java -jar
- settings.gradle: always include all modules (removed wrapper guard)

Note: Build succeeds; some tests in integration modules may still fail and will be handled separately.
@msridhar msridhar marked this pull request as ready for review September 2, 2025 20:42
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (10)
jar-infer/nullaway-integration-test/build.gradle (1)

37-39: Prefer lazy, type-safe cross-project task wiring

Using a string task path works, but prefer a lazy reference to avoid accidental eager realization and catch renames at compile time.

-tasks.withType(Test).configureEach {
-    dependsOn(":jar-infer:test-java-lib-jarinfer:embedAstubxIntoJar")
-}
+tasks.withType(Test).configureEach {
+    dependsOn(project(":jar-infer:test-java-lib-jarinfer").tasks.named("embedAstubxIntoJar"))
+}
jar-infer/test-android-lib-jarinfer/build.gradle (1)

20-25: Consider bumping SDK levels used via deps.build to modern values.

This module still inherits compile/target SDK 30 from gradle/dependencies.gradle; AGP 8.7 supports API up to 35 and expects Build Tools 34.0.0. Suggest raising compile/target SDK to ≥34 to avoid tooling incompatibilities. (developer.android.com)

gradle/dependencies.gradle (3)

17-23: Avoid adding buildscript classpath from an applied script; prefer build-logic or a tiny local comparator.

Adding semver4j via buildscript in a shared Gradle script can hurt configuration cache and complicate classpath resolution. Either:

  • move this to buildSrc/precompiled plugin, or
  • replace with a small inlined comparator (split on dots, compare ints/prerelease), avoiding extra dependencies.

32-45: Harden epApiVersion parsing error path.

Wrap Semver parsing in try/catch and throw a clear IllegalArgumentException when the property isn’t valid semver (even in LOOSE mode), to improve DX.

-    def requested = parseSemver(epApiVersion)
+    def requested
+    try {
+        requested = parseSemver(epApiVersion)
+    } catch (Throwable t) {
+        throw new IllegalArgumentException("Invalid epApiVersion: '${epApiVersion}'. Expected a semantic version.", t)
+    }

99-105: Android SDK constants are outdated for AGP 8.7.

These drive compile/target/min across modules. Consider:

  • compileSdkVersion: 34 or 35
  • targetSdkVersion: 34 or 35
    Keep minSdk as-is if tests require. (developer.android.com)
jar-infer/test-java-lib-jarinfer/build.gradle (3)

57-59: Finalize/dependency wiring can be simplified.

With the Jar task including the file, you can drop finalizedBy chains and just dependOn generateJarInferAstubx inside jar.

-jar.finalizedBy generateJarInferAstubx
-generateJarInferAstubx.configure { finalizedBy embedAstubxIntoJar }
+// handled in jar {} above

67-67: Duplicate dependency on CLI assemble.

This duplicates the dependsOn already present in generateJarInferAstubx; safe to remove.

-jar.dependsOn ":jar-infer:jar-infer-cli:assemble"
+// redundant; removed

41-43: Hardcoded CLI JAR path may drift.

If the CLI jar name includes version/classifier, this path will break. Consider resolving the artifact from the CLI project’s configurations or expose a dedicated configuration/Outgoing artifact.

library-model/test-library-model-generator/build.gradle (1)

54-56: Finalize chain is OK; consider removing redundant jar.dependsOn at Line 64.

With the above dependsOn/finalizedBy wiring, the extra “jar.dependsOn ':library-model:library-model-generator-cli:assemble'” (Line 64) is redundant. Drop it to reduce configuration work.

library-model/library-model-generator-integration-test/build.gradle (1)

35-37: Good safeguard to ensure embedding runs before tests.

Depending on “:library-model:test-library-model-generator:embedLibModelsAstubxIntoJar” guarantees the JAR is updated pre-test. Optionally, depend on that project’s “jar” task and wire embedding via the Jar task (as suggested in the other file) to avoid cross-project Exec execution from tests.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 41b46ba and b6092f5.

📒 Files selected for processing (13)
  • build.gradle (1 hunks)
  • gradle/dependencies.gradle (2 hunks)
  • gradle/wrapper/gradle-wrapper.properties (1 hunks)
  • jar-infer/nullaway-integration-test/build.gradle (1 hunks)
  • jar-infer/test-android-lib-jarinfer/build.gradle (1 hunks)
  • jar-infer/test-android-lib-jarinfer/src/main/AndroidManifest.xml (1 hunks)
  • jar-infer/test-java-lib-jarinfer/build.gradle (1 hunks)
  • library-model/library-model-generator-integration-test/build.gradle (1 hunks)
  • library-model/test-library-model-generator/build.gradle (1 hunks)
  • sample-app/build.gradle (3 hunks)
  • sample-app/src/main/AndroidManifest.xml (2 hunks)
  • sample-app/src/main/java/com/uber/myapplication/MainActivity.java (1 hunks)
  • settings.gradle (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build and test on windows-latest
  • GitHub Check: Build caffeine with snapshot
  • GitHub Check: Build and test on ubuntu-latest
  • GitHub Check: Build and test on macos-latest
🔇 Additional comments (12)
build.gradle (1)

25-25: Remove outdated compatibility warning: Android Gradle Plugin 8.7.2 meets Gradle 9.0.0’s minimum AGP requirement (8.4.0) and is officially supported with Gradle 9.0.0 (docs.gradle.org)

Likely an incorrect or invalid review comment.

sample-app/src/main/java/com/uber/myapplication/MainActivity.java (1)

36-36: No-op comment tweak — fine to keep

This is purely a comment change; no runtime effect.

settings.gradle (1)

11-11: Good: foojay resolver 1.0.0 is Gradle 9–ready

Version 1.0.0 explicitly preps for Gradle 9 and requires JDK 17+, which aligns with this repo. No action needed.

Reference: Plugin page notes “prepare for Gradle 9 compatibility.” (plugins.gradle.org)

jar-infer/test-android-lib-jarinfer/src/main/AndroidManifest.xml (1)

1-1: Confirmed: all AndroidManifests omit package and every module declares namespace
No package= attributes remain in any AndroidManifest.xml, and all Gradle modules using AGP plugins include an explicit namespace setting.

jar-infer/test-android-lib-jarinfer/build.gradle (1)

21-21: Namespace addition is correct for AGP 8+; LGTM.

Switching from manifest package to Gradle namespace is the right move. Relative component names (like ".FooActivity") resolve against this namespace. (developer.android.com)

gradle/wrapper/gradle-wrapper.properties (2)

3-4: Gradle 9.0.0 upgrade: ensure JDK ≥17 in CI and AGP compatibility.

  • Gradle 9 requires Java 17+ to run. Verify CI/toolchains use JDK 17 or newer. (docs.gradle.org, fossies.org)
  • Gradle 9’s minimum supported AGP is 8.4; your build uses AGP 8.7, which satisfies this. (docs.gradle.org)
  • AGP 8.7’s documented minimum Gradle is 8.9; running on 9.0 is typically fine but not explicitly listed—smoke test recommended. (developer.android.com)

3-4: Double-check wrapper SHA and regenerate via wrapper task.

Confirm the SHA256 matches services.gradle.org for 9.0.0 and that gradlew/gradlew.bat were refreshed with the wrapper task.

sample-app/src/main/AndroidManifest.xml (1)

2-3: Package removal is correct; dot-prefixed Activity names still resolve.

With AGP 8+, use Gradle namespace and omit manifest package; ".MainActivity" resolves against the module’s namespace. (developer.android.com)

gradle/dependencies.gradle (2)

47-63: Version map updates look fine.

Switch to semver4j doesn’t affect the constants here; values compile. Noting gson 2.13.1 is valid. (mvnrepository.com, google.github.io)


74-85: Gradle 9 removes org.gradle.util.VersionNumber; your replacement approach is correct.

Semver-based comparison is an appropriate substitute. (docs.gradle.org)

sample-app/build.gradle (2)

25-25: Namespace addition for AGP 8+ is correct.


39-41: Lint DSL migration looks good.

Comment on lines +35 to +47
def generateJarInferAstubx = tasks.register('generateJarInferAstubx', Exec) {
dependsOn ":jar-infer:jar-infer-cli:assemble"
dependsOn jar
doFirst {
commandLine "java",
"-jar",
"${rootProject.projectDir}/jar-infer/jar-infer-cli/build/libs/jar-infer-cli.jar",
"-i",
jar.archiveFile.get().asFile.absolutePath,
"-o",
"${jar.destinationDirectory.get()}/${astubxPath}"
}
exec {
workingDir "./build/libs"
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Use toolchains and declare inputs/outputs for reproducible, cacheable execution.

  • Exec with bare "java" depends on PATH; use JavaToolchainService to locate a JDK 17+ launcher.
  • Declare inputs (the jar) and outputs (the .astubx) so the task can be up-to-date/cached.
 def generateJarInferAstubx = tasks.register('generateJarInferAstubx', Exec) {
   dependsOn ":jar-infer:jar-infer-cli:assemble"
   dependsOn jar
+  // cacheability
+  inputs.file(jar.archiveFile)
+  outputs.file(layout.buildDirectory.file("generated/jarinfer/${astubxPath}"))
   doFirst {
-    commandLine "java",
+    // resolve java via toolchains for stability
+    def launcher = project.extensions.findByType(org.gradle.jvm.toolchain.JavaToolchainService)
+        .launcherFor { languageVersion = JavaLanguageVersion.of(17) }.get()
+    commandLine launcher.executablePath.asFile.absolutePath,
                 "-jar",
                 "${rootProject.projectDir}/jar-infer/jar-infer-cli/build/libs/jar-infer-cli.jar",
                 "-i",
                 jar.archiveFile.get().asFile.absolutePath,
                 "-o",
-                "${jar.destinationDirectory.get()}/${astubxPath}"
+                layout.buildDirectory.file("generated/jarinfer/${astubxPath}").get().asFile.absolutePath
   }
 }
🤖 Prompt for AI Agents
In jar-infer/test-java-lib-jarinfer/build.gradle around lines 35 to 47, the Exec
task uses a bare "java" and doesn't declare task inputs/outputs; change it to
obtain a Java 17+ launcher via the JavaToolchainService (e.g., request a
launcher for JavaLanguageVersion.of(17)) and use that launcher's executable path
instead of the plain "java" string, and declare inputs.file pointing to
jar.archiveFile and outputs.file pointing to the generated
"${jar.destinationDirectory.get()}/${astubxPath}" so Gradle can determine
up-to-date/caching behavior; keep the commandLine invocation but set the
executable from the toolchain launcher and add proper inputs/outputs to the task
configuration.

Comment on lines +34 to +45
def generateLibModelsAstubx = tasks.register('generateLibModelsAstubx', Exec) {
dependsOn ":library-model:library-model-generator-cli:assemble"
dependsOn jar
doFirst {
commandLine "java",
"-jar",
"${rootProject.projectDir}/library-model/library-model-generator-cli/build/libs/library-model-generator-cli.jar",
testInputsPath,
"${jar.destinationDirectory.get()}/${astubxPath}"
}
exec {
workingDir "./build/libs"
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Avoid hard-coded CLI JAR path; add task inputs/outputs for incrementality.

The Exec uses a fixed path to library-model-generator-cli.jar, which will break if the jar is versioned or renamed. Also, the task has no declared inputs/outputs, so it won’t be up-to-date/cacheable.

Suggested fix: derive the CLI jar from the producer task and declare inputs/outputs.

-def generateLibModelsAstubx = tasks.register('generateLibModelsAstubx', Exec) {
-    dependsOn ":library-model:library-model-generator-cli:assemble"
-    dependsOn jar
-    doFirst {
-        commandLine "java",
-                "-jar",
-                "${rootProject.projectDir}/library-model/library-model-generator-cli/build/libs/library-model-generator-cli.jar",
-                testInputsPath,
-                "${jar.destinationDirectory.get()}/${astubxPath}"
-    }
-}
+def cliJar = project(":library-model:library-model-generator-cli")
+    .tasks.named("jar", Jar)
+    .flatMap { it.archiveFile }
+
+def generateLibModelsAstubx = tasks.register('generateLibModelsAstubx', Exec) {
+    dependsOn cliJar.map { it.asFile } // ensures producer jar is built
+    dependsOn jar
+    inputs.dir(testInputsPath)
+    outputs.file(layout.buildDirectory.file("libs/${astubxPath}"))
+    doFirst {
+        commandLine "java",
+            "-jar",
+            cliJar.get().asFile.absolutePath,
+            testInputsPath,
+            layout.buildDirectory.file("libs/${astubxPath}").get().asFile.absolutePath
+    }
+}
🤖 Prompt for AI Agents
In library-model/test-library-model-generator/build.gradle around lines 34 to
45, the Exec task hard-codes the CLI jar path and has no declared
inputs/outputs; change it to derive the CLI jar from the producer project's jar
task (use the
project(':library-model:library-model-generator-cli').tasks.named('jar')
archiveFile provider) and use that provider when building the command line, and
declare task inputs (the CLI archiveFile and testInputsPath) and outputs (the
generated astubx output file) so Gradle can properly track up-to-dateness and
caching.

Comment on lines +46 to +56
def embedLibModelsAstubxIntoJar = tasks.register('embedLibModelsAstubxIntoJar', Exec) {
dependsOn generateLibModelsAstubx
workingDir "${buildDir}/libs"
doFirst {
commandLine "jar", "uf", "test-library-model-generator.jar", astubxPath
}
}

jar.finalizedBy generateLibModelsAstubx
generateLibModelsAstubx.configure { finalizedBy embedLibModelsAstubxIntoJar }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Prefer wiring generated file into the Jar task instead of shelling out to “jar uf”.

Invoking the external “jar” tool is brittle (PATH/toolchain dependent), non-incremental, and not configuration-cache friendly. Have the Jar task include the generated file instead.

Minimal change if you keep this task: at least declare inputs/outputs and use jar.destinationDirectory to avoid hard-coded build paths.

-def embedLibModelsAstubxIntoJar = tasks.register('embedLibModelsAstubxIntoJar', Exec) {
-    dependsOn generateLibModelsAstubx
-    workingDir "${buildDir}/libs"
-    doFirst {
-        commandLine "jar", "uf", "test-library-model-generator.jar", astubxPath
-    }
-}
+def embedLibModelsAstubxIntoJar = tasks.register('embedLibModelsAstubxIntoJar', Exec) {
+    dependsOn generateLibModelsAstubx
+    inputs.files(jar.flatMap { it.archiveFile }, layout.buildDirectory.file("libs/${astubxPath}"))
+    outputs.file(jar.flatMap { it.archiveFile })
+    doFirst {
+        workingDir(jar.get().destinationDirectory.get().asFile)
+        commandLine "jar", "uf", jar.get().archiveFile.get().asFile.name, astubxPath
+    }
+}

Preferred alternative (no external exec, incremental):

-jar.finalizedBy generateLibModelsAstubx
-generateLibModelsAstubx.configure { finalizedBy embedLibModelsAstubxIntoJar }
+tasks.named("jar", Jar).configure {
+    dependsOn(generateLibModelsAstubx)
+    from(layout.buildDirectory.file("libs/${astubxPath}")) {
+        into "com/uber/nullaway/libmodel/provider"
+    }
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def embedLibModelsAstubxIntoJar = tasks.register('embedLibModelsAstubxIntoJar', Exec) {
dependsOn generateLibModelsAstubx
workingDir "${buildDir}/libs"
doFirst {
commandLine "jar", "uf", "test-library-model-generator.jar", astubxPath
}
}
jar.finalizedBy generateLibModelsAstubx
generateLibModelsAstubx.configure { finalizedBy embedLibModelsAstubxIntoJar }
def embedLibModelsAstubxIntoJar = tasks.register('embedLibModelsAstubxIntoJar', Exec) {
dependsOn generateLibModelsAstubx
inputs.files(jar.flatMap { it.archiveFile }, layout.buildDirectory.file("libs/${astubxPath}"))
outputs.file(jar.flatMap { it.archiveFile })
doFirst {
workingDir(jar.get().destinationDirectory.get().asFile)
commandLine "jar", "uf", jar.get().archiveFile.get().asFile.name, astubxPath
}
}
jar.finalizedBy generateLibModelsAstubx
generateLibModelsAstubx.configure { finalizedBy embedLibModelsAstubxIntoJar }
🤖 Prompt for AI Agents
In library-model/test-library-model-generator/build.gradle around lines 46–56,
replace the Exec task that shells out to "jar uf" with wiring the generated file
into the existing Jar task: stop calling the external jar tool, make
generateLibModelsAstubx declare its produced file as an output (and its inputs),
have the Jar task dependOn generateLibModelsAstubx and include the generated
file via the jar task's from/configuration (or point jar to
jar.destinationDirectory rather than hard-coding build paths), and if you keep
an auxiliary task ensure it declares proper inputs/outputs and does not rely on
hard-coded ${buildDir}/libs paths so the operation is incremental and
configuration-cache friendly.

Comment on lines +53 to +73
// Configure Java compilation for all Android JavaCompile tasks (AGP 8+ compatible)
tasks.withType(JavaCompile).configureEach {
options.compilerArgs += [
"-XDcompilePolicy=simple",
"--should-stop=ifError=FLOW",
"-Xplugin:ErrorProne -XepOpt:NullAway:AnnotatedPackages=com.uber",
]
options.fork = true
options.forkOptions.jvmArgs = [
"--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED",
"--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED",
"--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED",
"--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED",
"--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED",
"--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED",
"--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
"--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
"--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
"--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED"
]
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix Error Prone/NullAway args: split tokens and enable the check.

Currently the two flags are concatenated into one token, so javac won’t recognize them. Also, NullAway isn’t enabled unless you add “-Xep:NullAway:ERROR”.

 tasks.withType(JavaCompile).configureEach {
-    options.compilerArgs += [
-        "-XDcompilePolicy=simple",
-        "--should-stop=ifError=FLOW",
-        "-Xplugin:ErrorProne -XepOpt:NullAway:AnnotatedPackages=com.uber",
-    ]
+    options.compilerArgs += [
+        "-XDcompilePolicy=simple",
+        "--should-stop=ifError=FLOW",
+        "-Xplugin:ErrorProne",
+        "-Xep:NullAway:ERROR",
+        "-XepOpt:NullAway:AnnotatedPackages=com.uber",
+    ]
     options.fork = true
     options.forkOptions.jvmArgs = [
         "--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED",
         "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED",
         "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED",
         "--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED",
         "--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED",
         "--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED",
         "--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
         "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
         "--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
         "--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED"
     ]
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Configure Java compilation for all Android JavaCompile tasks (AGP 8+ compatible)
tasks.withType(JavaCompile).configureEach {
options.compilerArgs += [
"-XDcompilePolicy=simple",
"--should-stop=ifError=FLOW",
"-Xplugin:ErrorProne -XepOpt:NullAway:AnnotatedPackages=com.uber",
]
options.fork = true
options.forkOptions.jvmArgs = [
"--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED",
"--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED",
"--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED",
"--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED",
"--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED",
"--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED",
"--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
"--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
"--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
"--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED"
]
}
tasks.withType(JavaCompile).configureEach {
options.compilerArgs += [
"-XDcompilePolicy=simple",
"--should-stop=ifError=FLOW",
"-Xplugin:ErrorProne",
"-Xep:NullAway:ERROR",
"-XepOpt:NullAway:AnnotatedPackages=com.uber",
]
options.fork = true
options.forkOptions.jvmArgs = [
"--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED",
"--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED",
"--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED",
"--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED",
"--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED",
"--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED",
"--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
"--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
"--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
"--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED"
]
}
🤖 Prompt for AI Agents
In sample-app/build.gradle around lines 53 to 73, the ErrorProne/NullAway JVM
compiler args are combined into a single token so javac won’t parse them; split
the concatenated string into separate compiler argument tokens (e.g.
"-Xplugin:ErrorProne" and "-XepOpt:NullAway:AnnotatedPackages=com.uber" as two
entries) and add the NullAway enforcement flag "-Xep:NullAway:ERROR" as its own
token so NullAway is enabled as an error.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
jar-infer/test-java-lib-jarinfer/build.gradle (2)

42-51: Use Java Toolchains for a cross-platform, reproducible launcher (Windows-safe).

System.getProperty("java.home")/bin/java can be brittle on Windows and divorces the build from toolchains. Prefer a JavaLauncher from JavaToolchainService and set executable/args.

-    doFirst {
-        // use the same java as Gradle itself
-        def javaBin = new File(System.getProperty("java.home"), "bin/java")
-
-        commandLine javaBin.absolutePath,
-                "-jar",
-                jarInferCliJar.get().asFile.absolutePath,
-                "-i", baseJarFile.get().asFile.absolutePath,
-                "-o", outputAstubxFile.get().asFile.absolutePath
-    }
+    doFirst {
+        def toolchains = project.extensions.getByType(org.gradle.jvm.toolchain.JavaToolchainService)
+        // Pick the version your CLI targets (17 is a common baseline)
+        def launcher = toolchains.launcherFor { languageVersion = JavaLanguageVersion.of(17) }.get()
+        executable = launcher.executablePath.asFile.absolutePath
+        args "-jar",
+             jarInferCliJar.get().asFile.absolutePath,
+             "-i", baseJarFile.get().asFile.absolutePath,
+             "-o", outputAstubxFile.get().asFile.absolutePath
+    }

62-64: Good: embed via Jar task instead of post-processing.

dependsOn + from(...)/into(...) is the right, cacheable approach; avoids “jar uf” mutations.

🧹 Nitpick comments (2)
jar-infer/test-java-lib-jarinfer/build.gradle (2)

28-34: Avoid hard dependency on ShadowJar type to reduce classpath coupling.

Referencing com.github.jengelman...ShadowJar here can break if the plugin isn’t on this project’s script classpath. Use an untyped TaskProvider and cast at usage.

-def shadowJarTask = cliProj.tasks.named(
-        "shadowJar",
-        com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
-        )
-def jarInferCliJar = shadowJarTask.flatMap { it.archiveFile }
+def shadowJarTask = cliProj.tasks.named("shadowJar")
+def jarInferCliJar = shadowJarTask.flatMap { (it as org.gradle.api.tasks.bundling.AbstractArchiveTask).archiveFile }

57-59: Optional: remove volatile manifest fields to improve reproducibility.

Build-Jdk and Build-OS vary across machines and reduce artifact reproducibility. Consider dropping them or gating behind a flag.

         attributes(
-                'Created-By' : "Gradle ${gradle.gradleVersion}",
-                'Build-Jdk'  : "${System.properties['java.version']} (${System.properties['java.vendor']} ${System.properties['java.vm.version']})",
-                'Build-OS'   : "${System.properties['os.name']} ${System.properties['os.arch']} ${System.properties['os.version']}"
+                'Created-By' : "Gradle ${gradle.gradleVersion}"
         )
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 597523a and f424355.

📒 Files selected for processing (2)
  • jar-infer/test-java-lib-jarinfer/build.gradle (1 hunks)
  • library-model/test-library-model-generator/build.gradle (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • library-model/test-library-model-generator/build.gradle
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Build and test on macos-latest
  • GitHub Check: Build and test on ubuntu-latest
  • GitHub Check: Build caffeine with snapshot
  • GitHub Check: Build and test on windows-latest
  • GitHub Check: Build spring-framework with snapshot
🔇 Additional comments (3)
jar-infer/test-java-lib-jarinfer/build.gradle (3)

21-21: LGTM: deterministic, lazily-evaluated output location.

Using layout.buildDirectory with a Provider keeps configuration lazy and cache-friendly.


23-26: Base JAR task looks good.

Isolating a “base” archive for analysis avoids mutating the main jar and keeps inputs stable.


38-41: Nice: proper inputs/outputs declared for caching.

Declaring baseJar and CLI jar as inputs and the astubx as an output enables up-to-date checks and build cache.

@msridhar msridhar merged commit 88297f9 into uber:master Sep 3, 2025
9 of 11 checks passed
@msridhar msridhar deleted the chore/gradle9-agp8 branch September 3, 2025 16:00
@coderabbitai coderabbitai bot mentioned this pull request Sep 3, 2025
@msridhar msridhar mentioned this pull request Sep 14, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants