Skip to content

Commit f8562e4

Browse files
authored
Avoid overlapping outputs for indexing tasks (#9725)
* chore: Refactor instrumentation index tasks to avoid overlapping inputs * Emit index in proper generated source location and use the location as source dir. * Deduplicate code. * chore: Refactor dd-java-agent index tasks and included agent to avoid overlapping output * Expand the included agent jars in another location buildDir/included, this location contributes to the resources. * Emit index in separate location, modified index to receive input folder and output folder, this location contributes to the resources. * Fix indexer logging. * style: Access properties via `it` in Groovy DSL * chore: Fix indexer warning logs For logs like ``` [main] WARN datadog.trace.bootstrap.AgentJarIndex - Detected duplicate content 'datadog.trace.civisibility.writer.ddintake.*' under 'trace', already seen in <root>. Ensure your content is under a distinct directory. ``` * chore: Fix indexer warning log, due to duplicate license files For logs like ``` Detected duplicate content under 'META-INF.AL2.0'. Ensure your content is under a distinct directory. Detected duplicate content under 'META-INF.LGPL2.1'. Ensure your content is under a distinct directory. ``` * chore: Fix indexer warning log, due to duplicate dd-javac-plugin-client files For logs like ``` [main] WARN datadog.trace.bootstrap.AgentJarIndex - Detected duplicate content under 'datadog.compiler.utils.*'. Ensure your content is under a distinct directory. [main] WARN datadog.trace.bootstrap.AgentJarIndex - Detected duplicate content under 'datadog.compiler.annotations.*'. Ensure your content is under a distinct ``` * fix: Use Sync task rather than Copy, which may leave deleted files in place * fix: Replace weak configuration using tasks.matching {} by proper filetree.builtBy call * style: Fix star import
1 parent baf05d8 commit f8562e4

File tree

7 files changed

+142
-92
lines changed

7 files changed

+142
-92
lines changed

dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/AgentJarIndex.java

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@
1515
import java.nio.file.attribute.BasicFileAttributes;
1616
import java.util.ArrayList;
1717
import java.util.Arrays;
18+
import java.util.HashMap;
1819
import java.util.HashSet;
1920
import java.util.List;
21+
import java.util.Map;
2022
import java.util.Set;
2123
import java.util.jar.JarFile;
2224
import java.util.zip.ZipEntry;
@@ -101,6 +103,7 @@ static class IndexGenerator extends SimpleFileVisitor<Path> {
101103

102104
private Path prefixRoot;
103105
private int prefixId;
106+
private Map<Integer, String> prefixMappings = new HashMap<>();
104107

105108
IndexGenerator(Path resourcesDir) {
106109
this.resourcesDir = resourcesDir;
@@ -125,6 +128,7 @@ public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
125128
prefixRoot = dir;
126129
prefixes.add(dir.getFileName() + "/");
127130
prefixId = prefixes.size();
131+
prefixMappings.put(prefixId, dir.getFileName().toString());
128132
}
129133
return FileVisitResult.CONTINUE;
130134
}
@@ -143,10 +147,16 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
143147
String entryKey = computeEntryKey(prefixRoot.relativize(file));
144148
if (null != entryKey) {
145149
int existingPrefixId = prefixTrie.apply(entryKey);
146-
if (-1 != existingPrefixId && prefixId != existingPrefixId) {
150+
// warn if two subsections contain content under the same package prefix
151+
// because we're then unable to redirect requests to the right submodule
152+
// (ignore the two 'datadog.compiler' packages which allow duplication)
153+
if (existingPrefixId > 0 && prefixId != existingPrefixId) {
147154
log.warn(
148-
"Detected duplicate content under '{}'. Ensure your content is under a distinct directory.",
149-
entryKey);
155+
"Detected duplicate content '{}' under '{}', already seen in {}. Ensure your content is under a distinct directory.",
156+
entryKey,
157+
resourcesDir.relativize(file).getName(0), // prefix
158+
prefixMappings.get(existingPrefixId) // previous prefix
159+
);
150160
}
151161
prefixTrie.put(entryKey, prefixId);
152162
if (entryKey.endsWith("*")) {
@@ -185,10 +195,18 @@ private static String computeEntryKey(Path path) {
185195
}
186196

187197
public static void main(String[] args) throws IOException {
198+
if (args.length < 1) {
199+
throw new IllegalArgumentException("Expected: resources-dir");
200+
}
201+
188202
Path resourcesDir = Paths.get(args[0]).toAbsolutePath();
203+
Path indexDir = resourcesDir;
204+
if (args.length == 2) {
205+
indexDir = Paths.get(args[1]).toAbsolutePath();
206+
}
189207
IndexGenerator indexGenerator = new IndexGenerator(resourcesDir);
190208
Files.walkFileTree(resourcesDir, indexGenerator);
191-
indexGenerator.writeIndex(resourcesDir.resolve(AGENT_INDEX_FILE_NAME));
209+
indexGenerator.writeIndex(indexDir.resolve(AGENT_INDEX_FILE_NAME));
192210
}
193211
}
194212
}

dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/InstrumenterIndex.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -334,18 +334,18 @@ public void writeIndex(Path indexFile) throws IOException {
334334
*/
335335
public static void main(String[] args) throws IOException {
336336
if (args.length < 1) {
337-
throw new IllegalArgumentException("Expected: resources-dir");
337+
throw new IllegalArgumentException("Expected: index-dir");
338338
}
339339

340-
Path resourcesDir = Paths.get(args[0]).toAbsolutePath();
340+
Path indexDir = Paths.get(args[0]).toAbsolutePath();
341341

342342
// satisfy some instrumenters that cache matchers in initializers
343343
HierarchyMatchers.registerIfAbsent(HierarchyMatchers.simpleChecks());
344344
SharedTypePools.registerIfAbsent(SharedTypePools.simpleCache());
345345

346346
IndexGenerator indexGenerator = new IndexGenerator();
347347
indexGenerator.buildIndex();
348-
indexGenerator.writeIndex(resourcesDir.resolve(INSTRUMENTER_INDEX_NAME));
348+
indexGenerator.writeIndex(indexDir.resolve(INSTRUMENTER_INDEX_NAME));
349349
}
350350
}
351351
}

dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/KnownTypesIndex.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,18 +154,18 @@ public void writeIndex(Path indexFile) throws IOException {
154154
*/
155155
public static void main(String[] args) throws IOException {
156156
if (args.length < 1) {
157-
throw new IllegalArgumentException("Expected: resources-dir");
157+
throw new IllegalArgumentException("Expected: index-dir");
158158
}
159159

160-
Path resourcesDir = Paths.get(args[0]).toAbsolutePath();
160+
Path indexDir = Paths.get(args[0]).toAbsolutePath();
161161

162162
// satisfy some instrumenters that cache matchers in initializers
163163
HierarchyMatchers.registerIfAbsent(HierarchyMatchers.simpleChecks());
164164
SharedTypePools.registerIfAbsent(SharedTypePools.simpleCache());
165165

166166
IndexGenerator indexGenerator = new IndexGenerator();
167167
indexGenerator.buildIndex();
168-
indexGenerator.writeIndex(resourcesDir.resolve(KNOWN_TYPES_INDEX_NAME));
168+
indexGenerator.writeIndex(indexDir.resolve(KNOWN_TYPES_INDEX_NAME));
169169
}
170170
}
171171
}

dd-java-agent/build.gradle

Lines changed: 69 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,21 @@ configurations {
1717
traceShadowInclude
1818
}
1919

20+
def includedAgentDir = project.layout.buildDirectory.dir("generated/included")
21+
def includedJarFileTree = fileTree(includedAgentDir)
22+
23+
tasks.named("processResources") {
24+
dependsOn(includedJarFileTree)
25+
}
26+
2027
// The special pre-check should be compiled with Java 6 to detect unsupported Java versions
2128
// and prevent issues for users that still using them.
2229
sourceSets {
2330
"main_java6" {
2431
java.srcDirs "${project.projectDir}/src/main/java6"
32+
2533
}
34+
main.resources.srcDir(includedAgentDir)
2635
}
2736

2837
def java6CompileTask = tasks.named("compileMain_java6Java") {
@@ -52,9 +61,6 @@ def generalShadowJarConfig(ShadowJar shadowJarTask) {
5261

5362
duplicatesStrategy = DuplicatesStrategy.FAIL
5463

55-
// Include AgentPreCheck compiled with Java 6.
56-
from sourceSets.main_java6.output
57-
5864
// Remove some cruft from the final jar.
5965
// These patterns should NOT include **/META-INF/maven/**/pom.properties, which is
6066
// used to report our own dependencies, but we should remove the top-level metadata
@@ -114,29 +120,32 @@ def generalShadowJarConfig(ShadowJar shadowJarTask) {
114120
}
115121
}
116122

117-
def includeShadowJar(TaskProvider<ShadowJar> includedShadowJarTask, String destinationDir) {
118-
def opentracingFound = new AtomicBoolean()
119-
project.tasks.named("processResources", ProcessResources) {
120-
doFirst {
123+
def includeShadowJar(TaskProvider<ShadowJar> includedShadowJarTask, String agentDir, FileTree includedJarFileTree) {
124+
def expandTask = project.tasks.register("expandAgentShadowJar${agentDir.capitalize()}", Sync) {
125+
it.group = LifecycleBasePlugin.BUILD_GROUP
126+
it.description = "Expand the included shadow jar into the agent jar under ${agentDir}"
127+
128+
def opentracingFound = new AtomicBoolean()
129+
it.doFirst("detect-open-tracing") {
121130
eachFile {
122131
// We seem unlikely to use this name somewhere else.
123132
if (it.path.contains("opentracing") && it.name.contains("Format\$Builtin")) {
124133
opentracingFound.set(true)
125134
}
126135
}
127136
}
128-
doLast {
137+
it.doLast("fail-on-detected-opentracing") {
129138
if (opentracingFound.get()) {
130139
throw new GradleException("OpenTracing direct dependency found!")
131140
}
132141
}
133142

134-
from(zipTree(includedShadowJarTask.map { it.archiveFile })) {
135-
into destinationDir
143+
it.into providers.provider { new File(includedJarFileTree.dir, agentDir) }
144+
it.from(zipTree(includedShadowJarTask.map { it.archiveFile })) {
136145
rename '(^.*)\\.class$', '$1.classdata'
137146
// Rename LICENSE file since it clashes with license dir on non-case sensitive FSs (i.e. Mac)
138147
rename '^LICENSE$', 'LICENSE.renamed'
139-
if (destinationDir == 'inst') {
148+
if (agentDir == 'inst') {
140149
// byte-buddy now ships classes optimized for Java8+ under META-INF/versions/9
141150
// since we target Java8+ we can promote these classes over the pre-Java8 ones
142151
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
@@ -148,39 +157,41 @@ def includeShadowJar(TaskProvider<ShadowJar> includedShadowJarTask, String desti
148157
}
149158
}
150159

151-
dependsOn includedShadowJarTask
160+
it.dependsOn includedShadowJarTask
152161
}
153162

163+
includedJarFileTree.builtBy(expandTask)
164+
154165
includedShadowJarTask.configure {
155166
generalShadowJarConfig(it as ShadowJar)
156167
}
157168
}
158169

159-
def includeSubprojShadowJar(Project includedProjectJar, String destinationDir) {
170+
def includeSubprojShadowJar(Project includedProjectJar, String destinationDir, FileTree includedJarFileTree) {
160171
evaluationDependsOn(includedProjectJar.path)
161-
includeShadowJar(includedProjectJar.tasks.named("shadowJar", ShadowJar), destinationDir)
172+
includeShadowJar(includedProjectJar.tasks.named("shadowJar", ShadowJar), destinationDir, includedJarFileTree)
162173
}
163174

164-
includeSubprojShadowJar(project(':dd-java-agent:instrumentation'), 'inst')
165-
includeSubprojShadowJar(project(':dd-java-agent:agent-jmxfetch'), 'metrics')
166-
includeSubprojShadowJar(project(':dd-java-agent:agent-profiling'), 'profiling')
167-
includeSubprojShadowJar(project(':dd-java-agent:appsec'), 'appsec')
168-
includeSubprojShadowJar(project(':dd-java-agent:agent-aiguard'), 'aiguard')
169-
includeSubprojShadowJar(project(':dd-java-agent:agent-iast'), 'iast')
170-
includeSubprojShadowJar(project(':dd-java-agent:agent-debugger'), 'debugger')
171-
includeSubprojShadowJar(project(':dd-java-agent:agent-ci-visibility'), 'ci-visibility')
172-
includeSubprojShadowJar(project(':dd-java-agent:agent-llmobs'), 'llm-obs')
173-
includeSubprojShadowJar(project(':dd-java-agent:agent-logs-intake'), 'logs-intake')
174-
includeSubprojShadowJar(project(':dd-java-agent:cws-tls'), 'cws-tls')
175+
includeSubprojShadowJar(project(':dd-java-agent:instrumentation'), 'inst', includedJarFileTree)
176+
includeSubprojShadowJar(project(':dd-java-agent:agent-jmxfetch'), 'metrics', includedJarFileTree)
177+
includeSubprojShadowJar(project(':dd-java-agent:agent-profiling'), 'profiling', includedJarFileTree)
178+
includeSubprojShadowJar(project(':dd-java-agent:appsec'), 'appsec', includedJarFileTree)
179+
includeSubprojShadowJar(project(':dd-java-agent:agent-aiguard'), 'aiguard', includedJarFileTree)
180+
includeSubprojShadowJar(project(':dd-java-agent:agent-iast'), 'iast', includedJarFileTree)
181+
includeSubprojShadowJar(project(':dd-java-agent:agent-debugger'), 'debugger', includedJarFileTree)
182+
includeSubprojShadowJar(project(':dd-java-agent:agent-ci-visibility'), 'ci-visibility', includedJarFileTree)
183+
includeSubprojShadowJar(project(':dd-java-agent:agent-llmobs'), 'llm-obs', includedJarFileTree)
184+
includeSubprojShadowJar(project(':dd-java-agent:agent-logs-intake'), 'logs-intake', includedJarFileTree)
185+
includeSubprojShadowJar(project(':dd-java-agent:cws-tls'), 'cws-tls', includedJarFileTree)
175186

176187
def sharedShadowJar = tasks.register('sharedShadowJar', ShadowJar) {
177-
configurations = [project.configurations.sharedShadowInclude]
188+
it.configurations = [project.configurations.sharedShadowInclude]
178189
// Put the jar in a different directory so we don't overwrite the normal shadow jar and
179190
// break caching, and also to not interfere with CI scripts that copy everything in the
180191
// libs directory
181192
it.destinationDirectory.set(project.layout.buildDirectory.dir("shared-lib"))
182193
// Add a classifier so we don't confuse the jar file with the normal shadow jar
183-
archiveClassifier = 'shared'
194+
it.archiveClassifier = 'shared'
184195
it.dependencies {
185196
exclude(project(':dd-java-agent:agent-bootstrap'))
186197
exclude(project(':dd-java-agent:agent-logging'))
@@ -192,18 +203,21 @@ def sharedShadowJar = tasks.register('sharedShadowJar', ShadowJar) {
192203
exclude(dependency('org.slf4j::'))
193204
}
194205
}
195-
includeShadowJar(sharedShadowJar, 'shared')
206+
includeShadowJar(sharedShadowJar, 'shared', includedJarFileTree)
196207

197208
// place the tracer in its own shadow jar separate to instrumentation
198209
def traceShadowJar = tasks.register('traceShadowJar', ShadowJar) {
199-
configurations = [project.configurations.traceShadowInclude]
210+
it.configurations = [project.configurations.traceShadowInclude]
200211
it.destinationDirectory.set(project.layout.buildDirectory.dir("trace-lib"))
201-
archiveClassifier = 'trace'
212+
it.archiveClassifier = 'trace'
202213
it.dependencies deps.excludeShared
203214
}
204-
includeShadowJar(traceShadowJar, 'trace')
215+
includeShadowJar(traceShadowJar, 'trace', includedJarFileTree)
205216

206217
tasks.named("shadowJar", ShadowJar) {
218+
// Include AgentPreCheck compiled with Java 6.
219+
from sourceSets.main_java6.output
220+
207221
generalShadowJarConfig(it)
208222

209223
configurations = [project.configurations.shadowInclude]
@@ -221,24 +235,35 @@ tasks.named("shadowJar", ShadowJar) {
221235
}
222236
}
223237

224-
tasks.register('generateAgentJarIndex', JavaExec) {
225-
def indexName = 'dd-java-agent.index'
226-
def contentDir = "${sourceSets.main.output.resourcesDir}"
227-
def indexFile = "${contentDir}/${indexName}"
238+
// temporary config to add slf4j-simple so we get logging while indexing
239+
project.configurations.register('slf4j-simple') {
240+
it.dependencies.add(project.dependencyFactory.create("org.slf4j:slf4j-simple:${libs.versions.slf4j.get()}"))
241+
}
242+
243+
def generateAgentJarIndex = tasks.register('generateAgentJarIndex', JavaExec) {
244+
def destinationDir = project.layout.buildDirectory.dir("generated/${it.name}")
228245

229-
it.group = 'Build'
246+
it.group = LifecycleBasePlugin.BUILD_GROUP
230247
it.description = "Generate dd-java-agent.index"
231-
it.inputs.files(fileTree(contentDir).exclude(indexName))
232-
it.outputs.files(indexFile)
233248
it.mainClass = 'datadog.trace.bootstrap.AgentJarIndex$IndexGenerator'
234-
it.classpath = project.configurations.shadowInclude
235-
it.args = [contentDir]
236249

237-
dependsOn 'processResources'
238-
dependsOn 'writeVersionNumberFile'
250+
it.inputs.files(includedJarFileTree)
251+
it.inputs.files(it.classpath)
252+
it.outputs.dir(destinationDir)
253+
it.classpath = objects.fileCollection().tap {
254+
it.from(project.configurations.named("shadowInclude"))
255+
it.from(project.configurations.named('slf4j-simple'))
256+
}
257+
// debuggable within gradle using:
258+
// it.jvmArgs("-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005")
259+
it.argumentProviders.add(new CommandLineArgumentProvider() {
260+
@Override
261+
Iterable<String> asArguments() {
262+
return [includedAgentDir.get().asFile.path, destinationDir.get().asFile.path,]
263+
}
264+
})
239265
}
240-
241-
compileJava.dependsOn 'generateAgentJarIndex'
266+
sourceSets.main.resources.srcDir(generateAgentJarIndex)
242267

243268
subprojects { Project subProj ->
244269
// Don't need javadoc task run for internal projects.

dd-java-agent/cws-tls/build.gradle

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
2+
import org.apache.maven.model.License
23

34
plugins {
45
id 'com.gradleup.shadow'
@@ -22,6 +23,10 @@ dependencies {
2223

2324
tasks.named("shadowJar", ShadowJar) {
2425
dependencies deps.excludeShared
25-
// exclude this since it's available in the instrumentation jar
26-
exclude 'com/sun/jna/**/*'
26+
27+
// exclude 'jna' this since it's available in the instrumentation jar
28+
dependencies {
29+
exclude(dependency("net.java.dev.jna:jna"))
30+
exclude(dependency("net.java.dev.jna:jna-platform"))
31+
}
2732
}

0 commit comments

Comments
 (0)