From 25737f98db2a3cd67e354243c8f9aecc658fef8c Mon Sep 17 00:00:00 2001 From: Roy Teeuwen Date: Sun, 14 Jun 2026 19:44:46 +0200 Subject: [PATCH] Add native OSGi bundle metadata to published jars Wire the bnd Gradle plugin into the shared otel.java-conventions plugin so the semconv and semconv-incubating jars are published with OSGi manifest headers (Bundle-SymbolicName, Export-Package, Import-Package). This lets the artifacts be consumed directly in OSGi containers without external re-wrapping. Fixes #494 --- buildSrc/build.gradle.kts | 1 + .../opentelemetry/gradle/OtelJavaExtension.kt | 7 ++++ .../kotlin/otel.java-conventions.gradle.kts | 34 +++++++++++++++++++ 3 files changed, 42 insertions(+) diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 51d89d43..9ae36998 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -9,6 +9,7 @@ repositories { } dependencies { + implementation("biz.aQute.bnd:biz.aQute.bnd.gradle:7.3.0") implementation("com.diffplug.spotless:spotless-plugin-gradle:8.6.0") implementation("ru.vyarus:gradle-animalsniffer-plugin:2.0.1") implementation("me.champeau.gradle:japicmp-gradle-plugin:0.4.6") diff --git a/buildSrc/src/main/kotlin/io/opentelemetry/gradle/OtelJavaExtension.kt b/buildSrc/src/main/kotlin/io/opentelemetry/gradle/OtelJavaExtension.kt index bf584e71..99cbd6d2 100644 --- a/buildSrc/src/main/kotlin/io/opentelemetry/gradle/OtelJavaExtension.kt +++ b/buildSrc/src/main/kotlin/io/opentelemetry/gradle/OtelJavaExtension.kt @@ -5,8 +5,15 @@ package io.opentelemetry.gradle +import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Property abstract class OtelJavaExtension { abstract val moduleName: Property + + /** Whether to generate OSGi bundle metadata. Enabled by default. */ + abstract val osgiEnabled: Property + + /** Extra packages imported as optional (resolution:=optional), e.g. compileOnly deps. */ + abstract val osgiOptionalPackages: ListProperty } diff --git a/buildSrc/src/main/kotlin/otel.java-conventions.gradle.kts b/buildSrc/src/main/kotlin/otel.java-conventions.gradle.kts index dc48dea0..ef114180 100644 --- a/buildSrc/src/main/kotlin/otel.java-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/otel.java-conventions.gradle.kts @@ -8,10 +8,13 @@ plugins { eclipse idea + id("biz.aQute.bnd.builder") id("otel.spotless-conventions") } val otelJava = extensions.create("otelJava") +otelJava.osgiEnabled.convention(true) +otelJava.osgiOptionalPackages.convention(emptyList()) java { toolchain { @@ -29,6 +32,13 @@ checkstyle { configProperties["rootDir"] = rootDir } +// normalize timestamps and file ordering in jars, making the outputs (including OSGi +// manifests) reproducible. see open-telemetry/opentelemetry-java#4488 +tasks.withType().configureEach { + isPreserveFileTimestamps = false + isReproducibleFileOrder = true +} + val testJavaVersion = gradle.startParameter.projectProperties.get("testJavaVersion")?.let(JavaVersion::toVersion) tasks { @@ -85,6 +95,30 @@ tasks { } } + afterEvaluate { + if (otelJava.osgiEnabled.get()) { + named("jar") { + // Configure OSGi metadata. semconv has no SPI / ServiceLoader needs, so this is the + // trimmed form of opentelemetry-java's otel.java-conventions OSGi config. + bundle { + // Modules may declare optional imports (typically compileOnly deps). The trailing + // "*" lets BND auto-import everything else (e.g. io.opentelemetry.api.*). + val optionalPackages = otelJava.osgiOptionalPackages.get() + val optionalImports = + optionalPackages.joinToString(",") { "$it.*;resolution:=optional;version=\"\${@}\"" } + val importPackages = if (optionalImports.isEmpty()) "*" else "$optionalImports,*" + + bnd( + mapOf( + "-exportcontents" to "io.opentelemetry.*", + "Import-Package" to importPackages, + ), + ) + } + } + } + } + withType().configureEach { inputs.property("moduleName", otelJava.moduleName)