diff --git a/.gitignore b/.gitignore index c0dcdb32d1b..593d2f3c0f3 100644 --- a/.gitignore +++ b/.gitignore @@ -77,3 +77,6 @@ mise*.local.toml .config/mise*.toml # asdf .tool-versions + +# Exclude kotlin build files +.kotlin diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 16e46b0f700..563339e915d 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -24,7 +24,7 @@ gradlePlugin { } create("call-site-instrumentation-plugin") { id = "call-site-instrumentation" - implementationClass = "datadog.gradle.plugin.CallSiteInstrumentationPlugin" + implementationClass = "datadog.gradle.plugin.csi.CallSiteInstrumentationPlugin" } create("tracer-version-plugin") { id = "datadog.tracer-version" diff --git a/buildSrc/call-site-instrumentation-plugin/src/main/java/datadog/trace/plugin/csi/util/CallSiteUtils.java b/buildSrc/call-site-instrumentation-plugin/src/main/java/datadog/trace/plugin/csi/util/CallSiteUtils.java index 5f2e2fa8d75..1f9ecdc5aad 100644 --- a/buildSrc/call-site-instrumentation-plugin/src/main/java/datadog/trace/plugin/csi/util/CallSiteUtils.java +++ b/buildSrc/call-site-instrumentation-plugin/src/main/java/datadog/trace/plugin/csi/util/CallSiteUtils.java @@ -3,6 +3,7 @@ import java.io.File; import java.net.MalformedURLException; import java.net.URL; +import java.nio.file.Files; import java.nio.file.Path; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -47,7 +48,20 @@ public static String repeat(final char value, int count) { public static URL toURL(final Path path) { try { - return path.toUri().toURL(); + URL url = path.toUri().toURL(); + // URLClassLoader interprets URLs ending with '/' as directories. If the trailing '/' is + // missing, a directory URL is treated as a JAR. If the path does yet exist on disk + // assumes that paths not ending with ".jar" are directories. + boolean shouldAddSlash = + Files.exists(path) ? Files.isDirectory(path) : !path.toString().endsWith(".jar"); + + if (shouldAddSlash) { + String urlString = url.toString(); + if (!urlString.endsWith("/")) { + url = new URL(urlString + "/"); + } + } + return url; } catch (MalformedURLException e) { throw new RuntimeException(e); } diff --git a/buildSrc/src/main/kotlin/datadog/gradle/plugin/CallSiteInstrumentationPlugin.kt b/buildSrc/src/main/kotlin/datadog/gradle/plugin/CallSiteInstrumentationPlugin.kt deleted file mode 100644 index ad1d97f1240..00000000000 --- a/buildSrc/src/main/kotlin/datadog/gradle/plugin/CallSiteInstrumentationPlugin.kt +++ /dev/null @@ -1,243 +0,0 @@ -package datadog.gradle.plugin - -import org.gradle.api.GradleException -import org.gradle.api.JavaVersion -import org.gradle.api.Plugin -import org.gradle.api.Project -import org.gradle.api.file.DirectoryProperty -import org.gradle.api.file.ProjectLayout -import org.gradle.api.model.ObjectFactory -import org.gradle.api.plugins.JavaPluginExtension -import org.gradle.api.provider.ListProperty -import org.gradle.api.provider.Property -import org.gradle.api.tasks.JavaExec -import org.gradle.api.tasks.SourceSet -import org.gradle.api.tasks.SourceSetContainer -import org.gradle.api.tasks.compile.AbstractCompile -import org.gradle.api.tasks.testing.Test -import org.gradle.jvm.tasks.Jar -import org.gradle.jvm.toolchain.JavaLanguageVersion -import org.gradle.jvm.toolchain.JavaToolchainService -import java.io.File -import java.nio.file.Files -import java.nio.file.Paths -import javax.inject.Inject - -private const val CALL_SITE_INSTRUMENTER_MAIN_CLASS = "datadog.trace.plugin.csi.PluginApplication" -private const val CALL_SITE_CLASS_SUFFIX = "CallSite" -private const val CALL_SITE_CONSOLE_REPORTER = "CONSOLE" -private const val CALL_SITE_ERROR_CONSOLE_REPORTER = "ERROR_CONSOLE" - -/** - * This extension allows to configure the Call Site Instrumenter plugin execution. - */ -abstract class CallSiteInstrumentationExtension @Inject constructor(objectFactory: ObjectFactory, layout: ProjectLayout) { - /** - * The location of the source code to generate call site ({@code /src/main/java} by default). - */ - val srcFolder: DirectoryProperty = objectFactory.directoryProperty().convention( - layout.projectDirectory.dir("src").dir("main").dir("java") - ) - /** - * The location to generate call site source code ({@code /build/generated/sources/csi} by default). - */ - val targetFolder: DirectoryProperty = objectFactory.directoryProperty().convention( - layout.buildDirectory.dir("generated${File.separatorChar}sources${File.separatorChar}csi") - ) - /** - * The generated call site source file suffix (#CALL_SITE_CLASS_SUFFIX by default). - */ - val suffix: Property = objectFactory.property(String::class.java).convention(CALL_SITE_CLASS_SUFFIX) - /** - * The reporters to use after call site instrumenter run (only #CALL_SITE_CONSOLE_REPORTER and #CALL_SITE_ERROR_CONSOLE_REPORTER supported for now). - */ - val reporters: ListProperty = objectFactory.listProperty(String::class.java).convention(listOf( - CALL_SITE_ERROR_CONSOLE_REPORTER - )) - /** - * The location of the dd-trace-java project to look for the call site instrumenter (optional, current project root folder used if not set). - */ - abstract val rootFolder: Property - - /** - * The JVM to use to run the call site instrumenter (optional, default JVM used if not set). - */ - abstract val javaVersion: Property - - /** - * The JVM arguments to run the call site instrumenter. - */ - val jvmArgs: ListProperty = objectFactory.listProperty(String::class.java).convention(listOf("-Xmx128m", "-Xms64m")) -} - -abstract class CallSiteInstrumentationPlugin : Plugin{ - @get:Inject - abstract val javaToolchains: JavaToolchainService - - override fun apply(project: Project) { - // Create plugin extension - val extension = project.extensions.create("csi", CallSiteInstrumentationExtension::class.java) - project.afterEvaluate { - configureSourceSets(project, extension) - createTasks(project, extension) - } - } - - private fun configureSourceSets(project: Project, extension: CallSiteInstrumentationExtension) { - // create a new source set for the csi files - val targetFolder = newBuildFolder(project, extension.targetFolder.get().asFile.toString()) - val sourceSets = getSourceSets(project) - val mainSourceSet = sourceSets.named(SourceSet.MAIN_SOURCE_SET_NAME).get() - val csiSourceSet = sourceSets.create("csi") { - compileClasspath += mainSourceSet.output // mainly needed for the plugin tests - annotationProcessorPath += mainSourceSet.annotationProcessorPath - java.srcDir(targetFolder) - - } - project.configurations.named(csiSourceSet.compileClasspathConfigurationName) { - extendsFrom(project.configurations.named(mainSourceSet.compileClasspathConfigurationName).get()) - } - - project.tasks.named(csiSourceSet.getCompileTaskName("java"), AbstractCompile::class.java) { - sourceCompatibility = JavaVersion.VERSION_1_8.toString() - targetCompatibility = JavaVersion.VERSION_1_8.toString() - } - - // add csi classes to test classpath - sourceSets.named(SourceSet.TEST_SOURCE_SET_NAME) { - compileClasspath += csiSourceSet.output.classesDirs - runtimeClasspath += csiSourceSet.output.classesDirs - } - project.dependencies.add("testImplementation", csiSourceSet.output) - - // include classes in final JAR - project.tasks.named("jar", Jar::class.java) { - from(csiSourceSet.output.classesDirs) - } - } - - private fun newBuildFolder(project: Project, name: String): File { - val folder = project.layout.buildDirectory.dir(name).get().asFile - if (!folder.exists()) { - if (!folder.mkdirs()) { - throw GradleException("Cannot create folder $folder") - } - } - return folder - } - - private fun newTempFile(folder: File, name: String): File { - val file = File(folder, name) - if (!file.exists() && !file.createNewFile()) { - throw GradleException("Cannot create temporary file: $file") - } - file.deleteOnExit() - return file - } - - private fun getSourceSets(project: Project): SourceSetContainer { - return project.extensions.getByType(JavaPluginExtension::class.java).sourceSets - } - - private fun createTasks(project: Project, extension: CallSiteInstrumentationExtension) { - registerGenerateCallSiteTask(project, extension, "compileJava") - val targetFolder = extension.targetFolder.get().asFile - project.tasks.withType(AbstractCompile::class.java).matching { - task -> task.name.startsWith("compileTest") - }.configureEach { - inputs.dir(extension.targetFolder) - classpath += project.files(targetFolder) - } - project.tasks.withType(Test::class.java).configureEach { - inputs.dir(extension.targetFolder) - classpath += project.files(targetFolder) - } - } - - private fun configureLanguage(task: JavaExec, version: JavaLanguageVersion) { - task.javaLauncher.set(javaToolchains.launcherFor { - languageVersion.set(version) - }) - } - - private fun registerGenerateCallSiteTask(project: Project, - extension: CallSiteInstrumentationExtension, - compileTaskName: String) { - val taskName = compileTaskName.replace("compile", "generateCallSite") - val rootFolder = extension.rootFolder.getOrElse(project.rootDir) - val pluginJarFile = Paths.get( - rootFolder.toString(), - "buildSrc", - "call-site-instrumentation-plugin", - "build", - "libs", - "call-site-instrumentation-plugin-all.jar" - ).toFile() - val compileTask = project.tasks.named(compileTaskName, AbstractCompile::class.java) - val callSiteGeneratorTask = project.tasks.register(taskName, JavaExec::class.java) { - // Task description - group = "call site instrumentation" - description = "Generates call sites from ${compileTaskName}" - // Task input & output - val output = extension.targetFolder - val inputProvider = compileTask.map { it.destinationDirectory.get() } - inputs.dir(inputProvider) - outputs.dir(output) - // JavaExec configuration - if (extension.javaVersion.isPresent) { - configureLanguage(this, extension.javaVersion.get()) - } - jvmArgumentProviders.add({ extension.jvmArgs.get() }) - classpath(pluginJarFile) - mainClass.set(CALL_SITE_INSTRUMENTER_MAIN_CLASS) - // Write the call site instrumenter arguments into a temporary file - doFirst { - val programClassPath = getProgramClasspath(project).map { it.toString() } - val arguments = listOf( - extension.srcFolder.get().asFile.toString(), - inputProvider.get().asFile.toString(), - output.get().asFile.toString(), - extension.suffix.get(), - extension.reporters.get().joinToString(",") - ) + programClassPath - - val argumentFile = newTempFile(temporaryDir, "call-site-arguments") - Files.write(argumentFile.toPath(), arguments) - args(argumentFile.toString()) - } - - // make task depends on compile - dependsOn(compileTask) - } - - // make all sourcesets' class tasks depend on call site generator - val sourceSets = getSourceSets(project) - sourceSets.named(SourceSet.MAIN_SOURCE_SET_NAME) { - project.tasks.named(classesTaskName) { - dependsOn(callSiteGeneratorTask) - } - } - - // compile generated sources - sourceSets.named("csi") { - project.tasks.named(compileJavaTaskName) { - dependsOn(callSiteGeneratorTask) - } - } - } - - private fun getProgramClasspath(project: Project): List { - val classpath = ArrayList() - // 1. Compilation outputs - exclude latestDep and forked test variants - project.tasks.withType(AbstractCompile::class.java) - .filter { task -> !task.name.contains("LatestDep", ignoreCase = true) && !task.name.contains("Forked", ignoreCase = true) } - .map { it.destinationDirectory.asFile.get() } - .forEach(classpath::add) - // 2. Compile time dependencies - exclude latestDep and forked test variants - project.tasks.withType(AbstractCompile::class.java) - .filter { task -> !task.name.contains("LatestDep", ignoreCase = true) && !task.name.contains("Forked", ignoreCase = true) } - .flatMap { it.classpath } - .forEach(classpath::add) - return classpath - } -} diff --git a/buildSrc/src/main/kotlin/datadog/gradle/plugin/csi/CallSiteInstrumentationExtension.kt b/buildSrc/src/main/kotlin/datadog/gradle/plugin/csi/CallSiteInstrumentationExtension.kt new file mode 100644 index 00000000000..989e31caf86 --- /dev/null +++ b/buildSrc/src/main/kotlin/datadog/gradle/plugin/csi/CallSiteInstrumentationExtension.kt @@ -0,0 +1,83 @@ +package datadog.gradle.plugin.csi + +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.ProjectLayout +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.Property +import org.gradle.jvm.toolchain.JavaLanguageVersion +import org.gradle.kotlin.dsl.listProperty +import org.gradle.kotlin.dsl.property +import java.io.File +import javax.inject.Inject + + +/** + * This extension allows to configure the Call Site Instrumenter plugin execution. + */ +abstract class CallSiteInstrumentationExtension @Inject constructor( + objectFactory: ObjectFactory, + layout: ProjectLayout +) { + companion object { + const val CALL_SITE_CLASS_SUFFIX = "CallSite" + const val CALL_SITE_CONSOLE_REPORTER = "CONSOLE" + const val CALL_SITE_ERROR_CONSOLE_REPORTER = "ERROR_CONSOLE" + } + + /** + * The location of the source code to generate call site ({@code /src/main/java} by default). + */ + val srcFolder: DirectoryProperty = objectFactory.directoryProperty().convention( + layout.projectDirectory.dir("src").dir("main").dir("java") + ) + + /** + * The location to generate call site source code ({@code /build/generated/sources/csi} by default). + */ + val targetFolder: DirectoryProperty = objectFactory.directoryProperty().convention( + layout.buildDirectory.dir("generated/sources/$CSI_SOURCE_SET") + ) + + /** + * The generated call site source file suffix (#CALL_SITE_CLASS_SUFFIX by default). + */ + val suffix: Property = objectFactory.property().convention(CALL_SITE_CLASS_SUFFIX) + + /** + * The reporters to use after call site instrumenter run (only #CALL_SITE_CONSOLE_REPORTER and #CALL_SITE_ERROR_CONSOLE_REPORTER supported for now). + */ + val reporters: ListProperty = objectFactory.listProperty().convention( + listOf( + CALL_SITE_ERROR_CONSOLE_REPORTER + ) + ) + + /** + * The location of the dd-trace-java project to look for the call site instrumenter (optional, current project root folder used if not set). + */ + abstract val rootFolder: Property + + /** + * The JVM to use to run the call site instrumenter (optional, default JVM used if not set). + */ + val javaVersion: Property = + objectFactory.property().convention(JavaLanguageVersion.current()) + + /** + * The JVM arguments to run the call site instrumenter. + */ + val jvmArgs: ListProperty = + objectFactory.listProperty().convention(listOf("-Xmx128m", "-Xms64m")) + + /** + * The paths used to look for the call site instrumenter dependencies. + * + * The plugin includes by default **only** the `main` and `test` source sets, and their + * related compilation classpath. As we don't want other test configurations by default. + * + * However, it's possible to contribute additional paths to look for dependencies. + */ + val additionalPaths: ConfigurableFileCollection = objectFactory.fileCollection() +} diff --git a/buildSrc/src/main/kotlin/datadog/gradle/plugin/csi/CallSiteInstrumentationPlugin.kt b/buildSrc/src/main/kotlin/datadog/gradle/plugin/csi/CallSiteInstrumentationPlugin.kt new file mode 100644 index 00000000000..b9bfafa2596 --- /dev/null +++ b/buildSrc/src/main/kotlin/datadog/gradle/plugin/csi/CallSiteInstrumentationPlugin.kt @@ -0,0 +1,238 @@ +package datadog.gradle.plugin.csi + +import org.gradle.api.GradleException +import org.gradle.api.JavaVersion +import org.gradle.api.NamedDomainObjectSet +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration +import org.gradle.api.plugins.JavaPlugin +import org.gradle.api.plugins.JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME +import org.gradle.api.plugins.JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME +import org.gradle.api.plugins.JavaPluginExtension +import org.gradle.api.plugins.jvm.JvmTestSuite +import org.gradle.api.tasks.JavaExec +import org.gradle.api.tasks.SourceSet.MAIN_SOURCE_SET_NAME +import org.gradle.api.tasks.SourceSet.TEST_SOURCE_SET_NAME +import org.gradle.api.tasks.SourceSetContainer +import org.gradle.api.tasks.TaskProvider +import org.gradle.api.tasks.compile.AbstractCompile +import org.gradle.internal.configuration.problems.projectPathFrom +import org.gradle.jvm.tasks.Jar +import org.gradle.jvm.toolchain.JavaToolchainService +import org.gradle.kotlin.dsl.apply +import org.gradle.kotlin.dsl.create +import org.gradle.kotlin.dsl.getByType +import org.gradle.kotlin.dsl.named +import org.gradle.kotlin.dsl.register +import org.gradle.kotlin.dsl.withType +import org.gradle.testing.base.TestingExtension +import java.io.File +import java.nio.file.Files +import java.nio.file.Paths +import java.util.Locale +import javax.inject.Inject + +private const val CALL_SITE_INSTRUMENTER_MAIN_CLASS = "datadog.trace.plugin.csi.PluginApplication" +const val CSI = "csi" +const val CSI_SOURCE_SET = CSI + +abstract class CallSiteInstrumentationPlugin : Plugin { + @get:Inject + abstract val javaToolchains: JavaToolchainService + + override fun apply(project: Project) { + project.pluginManager.apply(JavaPlugin::class) + + // Create plugin extension + val csiExtension = project.extensions.create(CSI) + configureSourceSets(project, csiExtension) + registerGenerateCallSiteTask(project, csiExtension, project.tasks.named("compileJava")) + configureTestConfigurations(project, csiExtension) + } + + private fun configureSourceSets(project: Project, extension: CallSiteInstrumentationExtension) { + // create a new source set for the csi files + val sourceSets = project.sourceSets + val mainSourceSet = sourceSets.named(MAIN_SOURCE_SET_NAME).get() + val csiSourceSet = sourceSets.create(CSI_SOURCE_SET) { + compileClasspath += mainSourceSet.output // mainly needed for the plugin tests + annotationProcessorPath += mainSourceSet.annotationProcessorPath + java.srcDir(extension.targetFolder) + } + + project.configurations.named(csiSourceSet.compileClasspathConfigurationName) { + extendsFrom(project.configurations.named(mainSourceSet.compileClasspathConfigurationName).get()) + } + + project.tasks.named(csiSourceSet.getCompileTaskName("java")) { + sourceCompatibility = JavaVersion.VERSION_1_8.toString() + targetCompatibility = JavaVersion.VERSION_1_8.toString() + } + + // add csi classes to test classpath + sourceSets.named(TEST_SOURCE_SET_NAME) { + compileClasspath += csiSourceSet.output.classesDirs + runtimeClasspath += csiSourceSet.output.classesDirs + } + project.dependencies.add("testImplementation", csiSourceSet.output) + + // include classes in final JAR + project.tasks.named("jar") { + from(csiSourceSet.output.classesDirs) + } + } + + private fun newTempFile(folder: File, name: String): File { + val file = File(folder, name) + if (!file.exists() && !file.createNewFile()) { + throw GradleException("Cannot create temporary file: $file") + } + file.deleteOnExit() + return file + } + + private fun configureTestConfigurations(project: Project, csiExtension: CallSiteInstrumentationExtension) { + project.pluginManager.withPlugin("jvm-test-suite") { + project.extensions.getByType().suites.withType().configureEach { + project.logger.info("Configuring jvm test suite '{}' to use csiExtension.targetFolder", name) + dependencies { + compileOnly.add(project.files(csiExtension.targetFolder)) + runtimeOnly.add(project.files(csiExtension.targetFolder)) + } + } + } + } + + private fun registerGenerateCallSiteTask( + project: Project, + csiExtension: CallSiteInstrumentationExtension, + mainCompileTask: TaskProvider + ) { + val genTaskName = mainCompileTask.name.replace("compile", "generateCallSite") + val pluginJarFile = Paths.get( + csiExtension.rootFolder.getOrElse(project.rootDir).toString(), + "buildSrc", + "call-site-instrumentation-plugin", + "build", + "libs", + "call-site-instrumentation-plugin-all.jar" + ) + + val callSiteGeneratorTask = project.tasks.register(genTaskName) { + // Task description + group = "call site instrumentation" + description = "Generates call sites from ${mainCompileTask.name}" + + // Remote Debug + if (project.providers.gradleProperty("debugCsiJar").isPresent) { + jvmArgs("-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=localhost:5005") + } + + // Task input & output + val output = csiExtension.targetFolder + val inputProvider = mainCompileTask.flatMap { it.destinationDirectory } + inputs.dir(inputProvider) + inputs.dir(csiExtension.srcFolder) + inputs.dir(csiExtension.rootFolder).optional() + inputs.file(pluginJarFile) + inputs.property("csi.suffix", csiExtension.suffix) + inputs.property("csi.javaVersion", csiExtension.javaVersion) + inputs.property("csi.jvmArgs", csiExtension.jvmArgs) + inputs.property("csi.reporters", csiExtension.reporters) + outputs.dir(output) + + // JavaExec configuration + javaLauncher.set(javaToolchains.launcherFor { + languageVersion.set(csiExtension.javaVersion) + }) + + jvmArgumentProviders.add({ csiExtension.jvmArgs.get() }) + classpath(pluginJarFile) + mainClass.set(CALL_SITE_INSTRUMENTER_MAIN_CLASS) + + // Write the call site instrumenter arguments into a temporary file + doFirst { + val callsitesClassPath = project.files( + project.sourceSets.named(MAIN_SOURCE_SET_NAME).map { it.output }, + project.defaultConfigurations, + csiExtension.additionalPaths, + ) + + if (logger.isInfoEnabled) { + logger.info( + "Aggregated CSI classpath:\n{}", + callsitesClassPath.toSet().sorted().joinToString("\n") { it.toString() } + ) + } + + val argFile = buildList { + add(csiExtension.srcFolder.get().asFile.toString()) + add(inputProvider.get().asFile.toString()) + add(output.get().asFile.toString()) + add(csiExtension.suffix.get()) + add(csiExtension.reporters.get().joinToString(",")) + + // module program classpath + addAll(callsitesClassPath.map { it.toString() }) + } + + val argumentFile = newTempFile(temporaryDir, "call-site-arguments") + Files.write(argumentFile.toPath(), argFile) + args(argumentFile.toString()) + } + + // make task depends on compile + dependsOn(mainCompileTask) + } + + // Workaround for instrument plugin modifying compile tasks + project.pluginManager.withPlugin("instrument") { + callSiteGeneratorTask.configure { + dependsOn("instrumentJava") + } + } + + // make all sourcesets' class tasks depend on call site generator + val sourceSets = project.sourceSets + sourceSets.named(MAIN_SOURCE_SET_NAME) { + project.tasks.named(classesTaskName) { + dependsOn(callSiteGeneratorTask) + } + } + + // compile generated sources + sourceSets.named(CSI_SOURCE_SET) { + project.tasks.named(compileJavaTaskName) { + dependsOn(callSiteGeneratorTask) + } + } + } + + private val Project.defaultConfigurations: NamedDomainObjectSet + get() = project.configurations.matching { + // Includes all main* source sets, but only the test (as wee don;t want other ) + // * For main => runtimeClasspath, compileClasspath + // * For test => testRuntimeClasspath, testCompileClasspath + // * For other main* => "main_javaXXRuntimeClasspath", "main_javaXXCompileClasspath" + + when (it.name) { + // Regular main and test source sets + RUNTIME_CLASSPATH_CONFIGURATION_NAME, + COMPILE_CLASSPATH_CONFIGURATION_NAME, + TEST_SOURCE_SET_NAME + RUNTIME_CLASSPATH_CONFIGURATION_NAME.capitalize(), + TEST_SOURCE_SET_NAME + COMPILE_CLASSPATH_CONFIGURATION_NAME.capitalize() -> true + + else -> false + } + } + + private fun String.capitalize(): String = replaceFirstChar { + if (it.isLowerCase()) it.titlecase( + Locale.getDefault() + ) else it.toString() + } + + private val Project.sourceSets: SourceSetContainer + get() = project.extensions.getByType().sourceSets +} diff --git a/dd-java-agent/instrumentation/scala/build.gradle b/dd-java-agent/instrumentation/scala/build.gradle index f2f65b6e316..9d34895f960 100644 --- a/dd-java-agent/instrumentation/scala/build.gradle +++ b/dd-java-agent/instrumentation/scala/build.gradle @@ -63,6 +63,10 @@ final testTasks = scalaVersions.collect { scalaLibrary -> } } + csi { + additionalPaths.from(classPathConfiguration) + } + return tasks.register("test$version", Test) { classpath = classpath .filter { !it.toString().contains('scala-library') } // exclude default scala-library