Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -96,14 +96,16 @@ private static Set<File> sourceDirs(Project project, SourceSetType sourceSetType
additionalSourceDirs = new String[] {
"build/generated/sources/proto/test/grpc",
"build/generated/sources/proto/test/java",
"gen-src/test/java"
"gen-src/test/java",
"gen-src/test/javaThrift"
};
} else {
assert sourceSetType == SourceSetType.MAIN;
additionalSourceDirs = new String[] {
"build/generated/sources/proto/main/grpc",
"build/generated/sources/proto/main/java",
"gen-src/main/java"
"gen-src/main/java",
"gen-src/main/javaThrift"
};
}
for (String path : additionalSourceDirs) {
Expand Down
5 changes: 4 additions & 1 deletion gradle/scripts/lib/java-rpc-thrift.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,10 @@ configure(projectsWithFlags('java')) {
includeDirs = [includeDirs]
}
includeDirs = project.files(includeDirs)
def javaOutputDir = project.file("${project.ext.genSrcDir}/${scope}/java")
def javaOutputDir = project.file("${project.ext.genSrcDir}/${scope}/javaThrift")
if (!sourceSet.java.srcDirs.contains(javaOutputDir)) {
sourceSet.java.srcDir javaOutputDir
}
def jsonOutputDir = project.file("${project.ext.genSrcDir}/${scope}/resources")
task.configure {
// configure inputs and outputs
Expand Down
36 changes: 35 additions & 1 deletion gradle/scripts/lib/java-shade.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,13 @@ private void configureShadowTask(Project project, ShadowJar task, boolean isMain


relocations.each { props ->
task.relocate props['from'], props['to']
logger.debug("[${project.name}] Relocating ${props['name']} from ${props['from']} to ${props['to']} " +
"in ${isMain ? 'main' : 'test'}")
task.relocate(props['from'], props['to']) {
exclude {
return isGenerated(project, it.path, isMain)
}
}
}
Comment on lines +374 to 381
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Find Shadow plugin version
rg "com.gradleup.shadow|com.github.jengelman.gradle.plugins.shadow" --type=gradle,toml -C2

Repository: line/armeria

Length of output: 91


🏁 Script executed:

# Check how relocate is used elsewhere in the codebase
rg "\.relocate\(" --type=groovy -C5

Repository: line/armeria

Length of output: 859


🌐 Web query:

Shadow Gradle plugin relocate exclude closure predicate API

💡 Result:

In the ShadowJar relocate(from, to) { … } block, exclude/include are path-pattern filters, not predicate closures. You pass Ant-style path match patterns (and optionally %regex[...] patterns), e.g.:

tasks.shadowJar {
  relocate('junit.textui', 'a') {
    exclude 'junit.textui.TestRunner'   // Ant-path style
  }
  relocate('org.foo', 'a') {
    include '%regex[org/foo/.*Factory[0-9].*]'  // regex form
  }
}

[1]

If what you want is a closure/predicate-style exclude, that API exists for dependency filtering (i.e., which dependencies get merged), via tasks.shadowJar { dependencies { exclude { dep -> … } } }, e.g.:

tasks.shadowJar {
  dependencies {
    exclude { it.moduleGroup == 'org.apache.logging.log4j' }
  }
}

[2]


Remove the closure-based exclude from relocate — Shadow plugin does not support predicate closures in relocate blocks.

The exclude/include methods in ShadowJar's relocate configuration accept only Ant-style path patterns or regex patterns (e.g., exclude 'com/foo/Bar'), not closure-based predicates. The code at lines 376–379 will be silently ignored or cause a runtime error.

To exclude generated sources during relocation, either:

  1. Use string patterns if all generated sources follow a predictable path pattern, or
  2. Implement a custom Relocator that performs the generated-source check in canRelocatePath or canRelocateClass methods.

The isGenerated function (lines 411–437) is correctly implemented but needs to be applied via the correct API.

🤖 Prompt for AI Agents
In `@gradle/scripts/lib/java-shade.gradle` around lines 374 - 381, The relocate
block currently uses a closure-based exclude (task.relocate(...) { exclude { ...
} }) which ShadowJar does not support; remove the closure-based exclude and
instead implement the generated-source check via a custom Relocator and register
it with task.relocate, or convert to string/regex excludes if generated classes
follow a predictable package/path. Specifically, remove the exclude closure from
the task.relocate call and create a class implementing Shadow's Relocator
interface (override canRelocatePath or canRelocateClass to call
isGenerated(project, path, isMain)), then add that Relocator instance to
task.relocate so generated files are skipped during relocation.


dependencies {
Expand Down Expand Up @@ -402,6 +408,34 @@ private void configureShadowTask(Project project, ShadowJar task, boolean isMain
}
}

boolean isGenerated(Project project, String path, boolean isMain) {
if (path.endsWith("package-info.class")) {
return false
}
if (!path.endsWith('.class')) {
return false
}
// Remove the .class extension.
def className = path.substring(0, path.length() - 6)
// Remove the inner class part.
def topLevelClassName = className.split('\\$')[0]

def javaSourcePath = "${topLevelClassName}.java"
def scope = isMain ? 'main' : 'test'
def thriftGenSrcDir = "${project.ext.genSrcDir}/${scope}/javaThrift"
def grpcGenSrcDir = "${project.buildDir}/generated/sources/proto/${scope}/grpc"
def protoGenSrcDir = "${project.buildDir}/generated/sources/proto/${scope}/java"

// Check if the source file exists in any of the generated source directories.
if (new File("${thriftGenSrcDir}/${javaSourcePath}").exists() ||
new File("${grpcGenSrcDir}/${javaSourcePath}").exists() ||
new File("${protoGenSrcDir}/${javaSourcePath}").exists()) {
logger.debug("[${project.name}] Excluding generated class from shading: ${path}")
return true
}
return false
}

/**
* Finds the dependencies of {@code project} recursively and adds the found dependencies to
* the configuration named as {@code 'shadedJarTestImplementation'}.
Expand Down
Loading