Gradle Parser Improvements
July 18, 2026 ยท View on GitHub
Summary
Rust now intentionally differs from or improves on current Python ScanCode Gradle handling in several concrete ways:
- classifies
compileOnly-style Gradle scopes as non-runtime instead of treating everything excepttest*as runtime - extracts Gradle POM license metadata into package license fields so CycloneDX output can carry component license expressions
- resolves TOML-backed
libs.versions.tomlversion-catalog aliases such aslibs.androidx.appcompatto real Maven package identifiers - preserves parent path segments for local project dependencies like
project(":libs:download") - merges all discovered
dependencies {}blocks in a build file instead of only parsing the first one - parses Kotlin DSL quoted configuration names such as
"implementation"("io.ktor:ktor-client-core:3.2.3")instead of dropping those central dependency declarations
Python Status
- Current Python Gradle handling is token-based and safe, but still shallow in the areas tracked by upstream issues.
- Upstream explicitly tracks:
- incorrect runtime classification for
compileOnly - missing Gradle SBOM component licenses
- incorrect package identifiers for Android/version-catalog dependency aliases
- incorrect runtime classification for
- Repeated
dependencies {}blocks are semantically additive in Gradle, so stopping after the first block loses declared dependencies from real-world Groovy and Kotlin builds. - The misbucketed template-POM issue grouped with this batch is upstream-confirmed, but it is actually a Maven placeholder-detection problem rather than a Gradle parser problem.
Rust Improvements
Runtime scope classification
compileOnly,compileOnlyApi,annotationProcessor,kapt, andkspare now treated as non-runtime dependencies.test*scopes remain non-runtime and optional.- This fixes the upstream
compileOnlymisclassification bug instead of reproducing it.
License propagation for SBOM output
- Rust now extracts Gradle
pom { licenses { ... } }metadata from both Groovy and Kotlin DSL fixtures. - Recognizable SPDX-like Gradle license declarations are promoted into:
declared_license_expressiondeclared_license_expression_spdxextracted_license_statement
- CycloneDX output already consumes
declared_license_expression_spdx, so this closes the local package-to-SBOM license gap for Gradle metadata that is present in the build file.
Version catalog alias resolution
- Rust now resolves TOML-backed
libs.versions.tomlaliases from nearby version catalogs. - Example:
implementation libs.androidx.appcompatnow resolves topkg:maven/androidx.appcompat/appcompat@1.7.0instead of a truncated identifier. - This is intentionally limited to static TOML-backed catalogs and does not attempt full semantic evaluation of arbitrary Gradle settings/build logic.
Local project identifiers
- Local project references such as
project(":libs:download")now preserve their parent path segments as namespace data (pkg:maven/libs/download) instead of collapsing to only the last segment.
All dependencies blocks in one build file
- Rust now parses every discovered
dependencies {}block in a singlebuild.gradle/build.gradle.ktsfile instead of stopping after the first block. - This includes repeated top-level blocks and later nested blocks that the current token parser already recognizes lexically.
Kotlin DSL quoted configuration names
- Kotlin DSL also allows configuration names to be called as quoted strings, for example
"implementation"("group:artifact:version")and"implementation"(project(":core")). - Rust now treats these quoted configuration names the same way it treats unquoted identifiers, including ordinary Maven coordinates, nested
project(":...")references, and trailing exclusion closures. - This keeps centralized multi-project Gradle builds scannable when they route module dependencies through root-level
project(":module") { dependencies { ... } }blocks using quoted Kotlin DSL configuration calls.
Template POM guardrail
- Rust also skips placeholder-only Maven coordinates like
${groupId}/${artifactId}/${version}instead of emitting junk package identifiers.
Multiproject topology from settings.gradle(.kts)
- Rust now parses literal
include/includeFlatproject paths fromsettings.gradle/settings.gradle.ktsintoextra_data.projects(datasourcegradle_settings). - Assembly builds a Gradle multiproject topology domain from those declarations, synthesizes package identity for each resolved
build.gradle(.kts)using settings root name / project path plus buildgroup/version, and attributes nested sources to the deepest enclosing project while skipping each module's immediatebuild/output directory. - Non-literal / dynamic includes and custom
project(...).projectDirremaps are intentionally skipped rather than guessed.
Coverage
Coverage spans scope classification, all discovered dependency-block parsing, Kotlin DSL quoted configuration names, version-catalog alias resolution, Gradle POM license extraction, placeholder-coordinate guardrails, and settings-driven multiproject topology.