otel.java-conventions.gradle.kts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. import io.opentelemetry.instrumentation.gradle.OtelJavaExtension
  2. import java.time.Duration
  3. import org.gradle.api.tasks.testing.logging.TestExceptionFormat
  4. plugins {
  5. `java-library`
  6. groovy
  7. checkstyle
  8. codenarc
  9. idea
  10. id("org.gradle.test-retry")
  11. id("otel.errorprone-conventions")
  12. id("otel.spotless-conventions")
  13. }
  14. val otelJava = extensions.create<OtelJavaExtension>("otelJava")
  15. afterEvaluate {
  16. if (findProperty("mavenGroupId") == "io.opentelemetry.javaagent.instrumentation") {
  17. base.archivesName.set("opentelemetry-javaagent-${base.archivesName.get()}")
  18. } else {
  19. base.archivesName.set("opentelemetry-${base.archivesName.get()}")
  20. }
  21. }
  22. // Version to use to compile code and run tests.
  23. val DEFAULT_JAVA_VERSION = JavaVersion.VERSION_11
  24. java {
  25. toolchain {
  26. languageVersion.set(otelJava.minJavaVersionSupported.map { JavaLanguageVersion.of(Math.max(it.majorVersion.toInt(), DEFAULT_JAVA_VERSION.majorVersion.toInt())) })
  27. }
  28. // See https://docs.gradle.org/current/userguide/upgrading_version_5.html, Automatic target JVM version
  29. disableAutoTargetJvm()
  30. withJavadocJar()
  31. withSourcesJar()
  32. }
  33. tasks.withType<JavaCompile>().configureEach {
  34. with(options) {
  35. release.set(otelJava.minJavaVersionSupported.map { it.majorVersion.toInt() })
  36. compilerArgs.add("-Werror")
  37. }
  38. }
  39. // Groovy and Scala compilers don't actually understand --release option
  40. afterEvaluate {
  41. tasks.withType<GroovyCompile>().configureEach {
  42. sourceCompatibility = otelJava.minJavaVersionSupported.get().majorVersion
  43. targetCompatibility = otelJava.minJavaVersionSupported.get().majorVersion
  44. }
  45. tasks.withType<ScalaCompile>().configureEach {
  46. sourceCompatibility = otelJava.minJavaVersionSupported.get().majorVersion
  47. targetCompatibility = otelJava.minJavaVersionSupported.get().majorVersion
  48. }
  49. }
  50. evaluationDependsOn(":dependencyManagement")
  51. val dependencyManagementConf = configurations.create("dependencyManagement") {
  52. isCanBeConsumed = false
  53. isCanBeResolved = false
  54. isVisible = false
  55. }
  56. afterEvaluate {
  57. configurations.configureEach {
  58. if (isCanBeResolved && !isCanBeConsumed) {
  59. extendsFrom(dependencyManagementConf)
  60. }
  61. }
  62. }
  63. // Force 4.0, or 4.1 to the highest version of that branch. Since 4.0 and 4.1 often have
  64. // compatibility issues we can't just force to the highest version using normal BOM dependencies.
  65. abstract class NettyAlignmentRule : ComponentMetadataRule {
  66. override fun execute(ctx: ComponentMetadataContext) {
  67. with(ctx.details) {
  68. if (id.group == "io.netty" && id.name != "netty") {
  69. if (id.version.startsWith("4.1.")) {
  70. belongsTo("io.netty:netty-bom:4.1.65.Final", false)
  71. } else if (id.version.startsWith("4.0.")) {
  72. belongsTo("io.netty:netty-bom:4.0.56.Final", false)
  73. }
  74. }
  75. }
  76. }
  77. }
  78. dependencies {
  79. add(dependencyManagementConf.name, platform(project(":dependencyManagement")))
  80. components.all<NettyAlignmentRule>()
  81. compileOnly("org.checkerframework:checker-qual")
  82. testImplementation("org.junit.jupiter:junit-jupiter-api")
  83. testImplementation("org.junit.jupiter:junit-jupiter-params")
  84. testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
  85. testRuntimeOnly("org.junit.vintage:junit-vintage-engine")
  86. testImplementation("org.objenesis:objenesis")
  87. testImplementation("org.spockframework:spock-core")
  88. testImplementation("ch.qos.logback:logback-classic")
  89. testImplementation("org.slf4j:log4j-over-slf4j")
  90. testImplementation("org.slf4j:jcl-over-slf4j")
  91. testImplementation("org.slf4j:jul-to-slf4j")
  92. testImplementation("info.solidsoft.spock:spock-global-unroll")
  93. testImplementation("com.github.stefanbirkner:system-rules")
  94. }
  95. tasks {
  96. named<Jar>("jar") {
  97. // By default Gradle Jar task can put multiple files with the same name
  98. // into a Jar. This may lead to confusion. For example if auto-service
  99. // annotation processing creates files with same name in `scala` and
  100. // `java` directory this would result in Jar having two files with the
  101. // same name in it. Which in turn would result in only one of those
  102. // files being actually considered when that Jar is used leading to very
  103. // confusing failures. Instead we should 'fail early' and avoid building such Jars.
  104. duplicatesStrategy = DuplicatesStrategy.FAIL
  105. manifest {
  106. attributes(
  107. "Implementation-Title" to project.name,
  108. "Implementation-Version" to project.version,
  109. "Implementation-Vendor" to "OpenTelemetry",
  110. "Implementation-URL" to "https://github.com/open-telemetry/opentelemetry-java-instrumentation"
  111. )
  112. }
  113. }
  114. named<Javadoc>("javadoc") {
  115. with(options as StandardJavadocDocletOptions) {
  116. source = "8"
  117. encoding = "UTF-8"
  118. docEncoding = "UTF-8"
  119. charSet = "UTF-8"
  120. breakIterator(true)
  121. links("https://docs.oracle.com/javase/8/docs/api/")
  122. addStringOption("Xdoclint:none", "-quiet")
  123. // non-standard option to fail on warnings, see https://bugs.openjdk.java.net/browse/JDK-8200363
  124. addStringOption("Xwerror", "-quiet")
  125. }
  126. }
  127. withType<AbstractArchiveTask>().configureEach {
  128. isPreserveFileTimestamps = false
  129. isReproducibleFileOrder = true
  130. }
  131. }
  132. normalization {
  133. runtimeClasspath {
  134. metaInf {
  135. ignoreAttribute("Implementation-Version")
  136. }
  137. }
  138. }
  139. fun isJavaVersionAllowed(version: JavaVersion): Boolean {
  140. if (otelJava.minJavaVersionSupported.get().compareTo(version) > 0) {
  141. return false
  142. }
  143. if (otelJava.maxJavaVersionForTests.isPresent() && otelJava.maxJavaVersionForTests.get().compareTo(version) < 0) {
  144. return false
  145. }
  146. return true
  147. }
  148. val testJavaVersion = gradle.startParameter.projectProperties.get("testJavaVersion")?.let(JavaVersion::toVersion)
  149. val resourceClassesCsv = listOf("Host", "Os", "Process", "ProcessRuntime").map { "io.opentelemetry.sdk.extension.resources.${it}ResourceProvider" }.joinToString(",")
  150. tasks.withType<Test>().configureEach {
  151. useJUnitPlatform()
  152. // There's no real harm in setting this for all tests even if any happen to not be using context
  153. // propagation.
  154. jvmArgs("-Dio.opentelemetry.context.enableStrictContext=${rootProject.findProperty("enableStrictContext") ?: false}")
  155. // TODO(anuraaga): Have agent map unshaded to shaded.
  156. jvmArgs("-Dio.opentelemetry.javaagent.shaded.io.opentelemetry.context.enableStrictContext=${rootProject.findProperty("enableStrictContext") ?: false}")
  157. // Disable default resource providers since they cause lots of output we don't need.
  158. jvmArgs("-Dotel.java.disabled.resource.providers=${resourceClassesCsv}")
  159. val trustStore = project(":testing-common").file("src/misc/testing-keystore.p12")
  160. inputs.file(trustStore)
  161. // Work around payara not working when this is set for some reason.
  162. if (project.name != "jaxrs-2.0-payara-testing") {
  163. jvmArgs("-Djavax.net.ssl.trustStore=${trustStore.absolutePath}")
  164. jvmArgs("-Djavax.net.ssl.trustStorePassword=testing")
  165. }
  166. // All tests must complete within 15 minutes.
  167. // This value is quite big because with lower values (3 mins) we were experiencing large number of false positives
  168. timeout.set(Duration.ofMinutes(15))
  169. retry {
  170. // You can see tests that were retried by this mechanism in the collected test reports and build scans.
  171. maxRetries.set(if (System.getenv("CI") != null) 5 else 0)
  172. }
  173. reports {
  174. junitXml.isOutputPerTestCase = true
  175. }
  176. testLogging {
  177. exceptionFormat = TestExceptionFormat.FULL
  178. }
  179. }
  180. afterEvaluate {
  181. tasks.withType<Test>().configureEach {
  182. if (testJavaVersion != null) {
  183. javaLauncher.set(javaToolchains.launcherFor {
  184. languageVersion.set(JavaLanguageVersion.of(testJavaVersion.majorVersion))
  185. })
  186. isEnabled = isJavaVersionAllowed(testJavaVersion)
  187. } else {
  188. // We default to testing with Java 11 for most tests, but some tests don't support it, where we change
  189. // the default test task's version so commands like `./gradlew check` can test all projects regardless
  190. // of Java version.
  191. if (!isJavaVersionAllowed(DEFAULT_JAVA_VERSION) && otelJava.maxJavaVersionForTests.isPresent) {
  192. javaLauncher.set(javaToolchains.launcherFor {
  193. languageVersion.set(JavaLanguageVersion.of(otelJava.maxJavaVersionForTests.get().majorVersion))
  194. })
  195. }
  196. }
  197. if (plugins.hasPlugin("org.unbroken-dome.test-sets") && configurations.findByName("latestDepTestRuntime") != null) {
  198. doFirst {
  199. val testArtifacts = configurations.testRuntimeClasspath.get().resolvedConfiguration.resolvedArtifacts
  200. val latestTestArtifacts = configurations.getByName("latestDepTestRuntimeClasspath").resolvedConfiguration.resolvedArtifacts
  201. if (testArtifacts == latestTestArtifacts) {
  202. throw IllegalStateException("latestDepTest dependencies are identical to test")
  203. }
  204. }
  205. }
  206. }
  207. }
  208. codenarc {
  209. configFile = rootProject.file("gradle/enforcement/codenarc.groovy")
  210. toolVersion = "2.0.0"
  211. }
  212. checkstyle {
  213. configFile = rootProject.file("gradle/enforcement/checkstyle.xml")
  214. // this version should match the version of google_checks.xml used as basis for above configuration
  215. toolVersion = "8.37"
  216. maxWarnings = 0
  217. }
  218. idea {
  219. module {
  220. setDownloadJavadoc(false)
  221. setDownloadSources(false)
  222. }
  223. }
  224. when (projectDir.name) {
  225. "bootstrap", "javaagent", "library", "testing" -> {
  226. // We don't use this group anywhere in our config, but we need to make sure it is unique per
  227. // instrumentation so Gradle doesn't merge projects with same name due to a bug in Gradle.
  228. // https://github.com/gradle/gradle/issues/847
  229. // In otel.publish-conventions, we set the maven group, which is what matters, to the correct
  230. // value.
  231. group = "io.opentelemetry.${projectDir.parentFile.name}"
  232. }
  233. }
  234. configurations.configureEach {
  235. resolutionStrategy {
  236. // While you might think preferProjectModules would do this, it doesn't. If this gets hard to
  237. // manage, we could consider having the io.opentelemetry.instrumentation add information about
  238. // what modules they add to reference generically.
  239. dependencySubstitution {
  240. substitute(module("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api")).using(project(":instrumentation-api"))
  241. substitute(module("io.opentelemetry.javaagent:opentelemetry-javaagent-instrumentation-api")).using(project(":javaagent-instrumentation-api"))
  242. substitute(module("io.opentelemetry.javaagent:opentelemetry-javaagent-bootstrap")).using(project(":javaagent-bootstrap"))
  243. substitute(module("io.opentelemetry.javaagent:opentelemetry-javaagent-extension-api")).using(project(":javaagent-extension-api"))
  244. substitute(module("io.opentelemetry.javaagent:opentelemetry-javaagent-tooling")).using(project(":javaagent-tooling"))
  245. substitute(module("io.opentelemetry.javaagent:opentelemetry-agent-for-testing")).using(project(":testing:agent-for-testing"))
  246. substitute(module("io.opentelemetry.javaagent:opentelemetry-testing-common")).using(project(":testing-common"))
  247. }
  248. // The above substitutions ensure dependencies managed by this BOM for external projects refer to this repo's projects here.
  249. // Excluding the bom as well helps ensure if we miss a substitution, we get a resolution failure instead of using the
  250. // wrong version.
  251. exclude("io.opentelemetry.instrumentation", "opentelemetry-instrumentation-bom-alpha")
  252. }
  253. }