From 8d40754e8b9cc35a9e839a6cc49874ada8cd7510 Mon Sep 17 00:00:00 2001 From: BJ Hargrave Date: Tue, 14 Jun 2022 09:03:14 -0400 Subject: [PATCH 1/2] gradle: Handle Providers in manifest attribute values BundleTaskExtension supports using the Jar manifest property to set attributes in the manifest main attribute section. However it did not properly handle the use of a Provider to supply the value. This fix is now in line with how the Gradle Manifest object handles converting attribute values to a String. Signed-off-by: BJ Hargrave --- .../java/aQute/bnd/gradle/BundleTaskExtension.java | 12 +++++++++++- .../testresources/builderplugin1/build.gradle | 2 +- .../testresources/builderplugin2/build.gradle | 6 ++++-- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/gradle-plugins/biz.aQute.bnd.gradle/src/main/java/aQute/bnd/gradle/BundleTaskExtension.java b/gradle-plugins/biz.aQute.bnd.gradle/src/main/java/aQute/bnd/gradle/BundleTaskExtension.java index 9520a06943..0650955299 100644 --- a/gradle-plugins/biz.aQute.bnd.gradle/src/main/java/aQute/bnd/gradle/BundleTaskExtension.java +++ b/gradle-plugins/biz.aQute.bnd.gradle/src/main/java/aQute/bnd/gradle/BundleTaskExtension.java @@ -374,7 +374,7 @@ public void execute(Task t) { .ofNullable(manifest.getEffectiveManifest() .getAttributes()) .filterKey(key -> !Objects.equals(key, "Manifest-Version")) - .mapValue(Object::toString) + .mapValue(this::unwrapAttributeValue) .collect(MapStream.toMap((k1, k2) -> { throw new IllegalStateException("Duplicate key " + k1); }, UTF8Properties::new))); @@ -532,6 +532,16 @@ private org.gradle.api.java.archives.Manifest mergeManifest(Manifest builtManife return mergeManifest; } + private String unwrapAttributeValue(Object value) { + while (value instanceof Provider) { + value = ((Provider) value).getOrNull(); + } + if (value == null) { + return null; + } + return value.toString(); + } + private void failTask(String msg, File archiveFile) { IO.delete(archiveFile); throw new GradleException(msg); diff --git a/gradle-plugins/biz.aQute.bnd.gradle/testresources/builderplugin1/build.gradle b/gradle-plugins/biz.aQute.bnd.gradle/testresources/builderplugin1/build.gradle index 91b5f30354..9f9e130675 100644 --- a/gradle-plugins/biz.aQute.bnd.gradle/testresources/builderplugin1/build.gradle +++ b/gradle-plugins/biz.aQute.bnd.gradle/testresources/builderplugin1/build.gradle @@ -25,7 +25,7 @@ dependencies { jar { ext.taskprop = 'prop.task' manifest { - attributes('Implementation-Title': project.archivesBaseName, + attributes('Implementation-Title': providers.provider({ -> project.archivesBaseName}), 'Implementation-Version': project.version, '-includeresource': '{${.}/bar.txt}', '-include': '${.}/other.bnd', diff --git a/gradle-plugins/biz.aQute.bnd.gradle/testresources/builderplugin2/build.gradle b/gradle-plugins/biz.aQute.bnd.gradle/testresources/builderplugin2/build.gradle index 0476a1e248..2d4938f265 100644 --- a/gradle-plugins/biz.aQute.bnd.gradle/testresources/builderplugin2/build.gradle +++ b/gradle-plugins/biz.aQute.bnd.gradle/testresources/builderplugin2/build.gradle @@ -19,7 +19,6 @@ repositories { ext { extraInstructions = provider { '''\ -YY-Sealed: true ZZ-Delivered: true ''' } @@ -28,7 +27,7 @@ ZZ-Delivered: true // Not a bundle. def jarTask = tasks.named('jar', Jar) { manifest { - attributes('XX-Signed': true) + attributes('XX-Signed': provider({true})) } } @@ -37,6 +36,9 @@ task bundle(type: Bundle) { group = 'build' from jarTask.map { zipTree(it.archiveFile) } archiveClassifier = 'bundle' + manifest { + attributes('YY-Sealed': provider({true})) + } bundle { bnd = jarTask.flatMap { jar -> From 8874576a83ddfef6053a806e6819bb929b54a07d Mon Sep 17 00:00:00 2001 From: BJ Hargrave Date: Tue, 14 Jun 2022 12:01:03 -0400 Subject: [PATCH 2/2] gradle: Make default Bundle-SymbolicName and Bundle-Version inputs If Bundle-SymbolicName and/or Bundle-Version are not specified in the bnd instructions, then BundleTaskExtension will set default values based upon the Jar task's archiveBaseName, archiveClassifier, and archiveVersion properties. However, these Jar task properties are not considered inputs to the Jar task. They are just considered as parts of the archiveFile property which is an output property. Setting the defaults as input properties means that Gradle will consider them as part of the build cache key. This is important since the values can end up in the build jar's manifest. Fixes https://github.com/bndtools/bnd/issues/5279 Signed-off-by: BJ Hargrave --- .../aQute/bnd/gradle/BundleTaskExtension.java | 80 +++++++++++++------ .../aQute/bnd/gradle/TestBundlePlugin.groovy | 6 +- .../testresources/builderplugin2/build.gradle | 5 +- 3 files changed, 60 insertions(+), 31 deletions(-) diff --git a/gradle-plugins/biz.aQute.bnd.gradle/src/main/java/aQute/bnd/gradle/BundleTaskExtension.java b/gradle-plugins/biz.aQute.bnd.gradle/src/main/java/aQute/bnd/gradle/BundleTaskExtension.java index 0650955299..df09d6121c 100644 --- a/gradle-plugins/biz.aQute.bnd.gradle/src/main/java/aQute/bnd/gradle/BundleTaskExtension.java +++ b/gradle-plugins/biz.aQute.bnd.gradle/src/main/java/aQute/bnd/gradle/BundleTaskExtension.java @@ -8,7 +8,6 @@ import static aQute.bnd.gradle.BndUtils.unwrap; import static aQute.bnd.gradle.BndUtils.unwrapFile; import static aQute.bnd.gradle.BndUtils.unwrapFileOptional; -import static aQute.bnd.gradle.BndUtils.unwrapOptional; import static java.util.Collections.singletonList; import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.toList; @@ -29,6 +28,17 @@ import java.util.jar.Manifest; import java.util.zip.ZipFile; +import aQute.bnd.exceptions.Exceptions; +import aQute.bnd.osgi.Builder; +import aQute.bnd.osgi.Constants; +import aQute.bnd.osgi.Jar; +import aQute.bnd.osgi.Processor; +import aQute.bnd.stream.MapStream; +import aQute.bnd.unmodifiable.Maps; +import aQute.bnd.version.MavenVersion; +import aQute.lib.io.IO; +import aQute.lib.strings.Strings; +import aQute.lib.utf8properties.UTF8Properties; import org.gradle.api.Action; import org.gradle.api.GradleException; import org.gradle.api.Project; @@ -52,18 +62,6 @@ import org.gradle.api.tasks.TaskInputFilePropertyBuilder; import org.gradle.work.NormalizeLineEndings; -import aQute.bnd.exceptions.Exceptions; -import aQute.bnd.osgi.Builder; -import aQute.bnd.osgi.Constants; -import aQute.bnd.osgi.Jar; -import aQute.bnd.osgi.Processor; -import aQute.bnd.stream.MapStream; -import aQute.bnd.unmodifiable.Maps; -import aQute.bnd.version.MavenVersion; -import aQute.lib.io.IO; -import aQute.lib.strings.Strings; -import aQute.lib.utf8properties.UTF8Properties; - /** * BundleTaskExtension for Gradle. *

@@ -96,6 +94,8 @@ public class BundleTaskExtension { private final ConfigurableFileCollection classpath; private final Provider bnd; private final MapProperty properties; + private final Provider defaultBundleSymbolicName; + private final Provider defaultBundleVersion; /** * The bndfile property. @@ -131,7 +131,7 @@ public ConfigurableFileCollection getClasspath() { * If the bndfile property points an existing file, this property is * ignored. Otherwise, the bnd instructions in this property will be used. * - * @return The property for the bnd instructions. + * @return The provider for the bnd instructions. */ @Input @org.gradle.api.tasks.Optional @@ -211,6 +211,13 @@ public BundleTaskExtension(org.gradle.api.tasks.bundling.Jar task) { classpath(mainSourceSet.getCompileClasspath()); properties = objects.mapProperty(String.class, Object.class) .convention(Maps.of("project", "__convention__")); + defaultBundleSymbolicName = task.getArchiveBaseName() + .zip(task.getArchiveClassifier(), (baseName, classifier) -> classifier.isEmpty() ? baseName : baseName + "-" + classifier); + defaultBundleVersion = task.getArchiveVersion() + .orElse("0") + .map(version -> MavenVersion.parseMavenString(version) + .getOSGiVersion() + .toString()); // need to programmatically add to inputs since @InputFiles in a // extension is not processed task.getInputs() @@ -229,6 +236,10 @@ public BundleTaskExtension(org.gradle.api.tasks.bundling.Jar task) { .property("bnd", getBnd()); task.getInputs() .property("properties", getProperties()); + task.getInputs() + .property("default Bundle-SymbolicName", getDefaultBundleSymbolicName()); + task.getInputs() + .property("default Bundle-Version", getDefaultBundleVersion()); } /** @@ -331,6 +342,30 @@ File getBuildFile() { return buildFile; } + /** + * The default value for the Bundle-SymbolicName manifest header. + *

+ * If the Bundle-SymbolicName manifest header is not set in the bnd instructions, + * the value of this provider will be used. + * + * @return The provider for the default Bundle-SymbolicName manifest header. + */ + Provider getDefaultBundleSymbolicName() { + return defaultBundleSymbolicName; + } + + /** + * The default value for the Bundle-Version manifest header. + *

+ * If the Bundle-Version manifest header is not set in the bnd instructions, + * the value of this provider will be used. + * + * @return The provider for the default Bundle-Version manifest header. + */ + Provider getDefaultBundleVersion() { + return defaultBundleVersion; + } + ProjectLayout getLayout() { return layout; } @@ -412,9 +447,6 @@ public void execute(Task t) { } File archiveFile = unwrapFile(getTask().getArchiveFile()); String archiveFileName = unwrap(getTask().getArchiveFileName()); - String archiveBaseName = unwrap(getTask().getArchiveBaseName()); - String archiveClassifier = unwrap(getTask().getArchiveClassifier()); - String archiveVersion = unwrapOptional(getTask().getArchiveVersion()).orElse(null); // Include entire contents of Jar task generated jar // (except the manifest) @@ -472,22 +504,18 @@ public void execute(Task t) { .toArray(new File[0])); getTask().getLogger() .debug("builder sourcepath: {}", builder.getSourcePath()); - // set bundle symbolic name from tasks's archiveBaseName - // property if necessary + // set bundle symbolic name if necessary String bundleSymbolicName = builder.getProperty(Constants.BUNDLE_SYMBOLICNAME); if (isEmpty(bundleSymbolicName)) { - bundleSymbolicName = archiveClassifier.isEmpty() ? archiveBaseName - : archiveBaseName + "-" + archiveClassifier; + bundleSymbolicName = unwrap(getDefaultBundleSymbolicName()); builder.setProperty(Constants.BUNDLE_SYMBOLICNAME, bundleSymbolicName); } - // set bundle version from task's archiveVersion if - // necessary + // set bundle version if necessary String bundleVersion = builder.getProperty(Constants.BUNDLE_VERSION); if (isEmpty(bundleVersion)) { - builder.setProperty(Constants.BUNDLE_VERSION, MavenVersion.parseMavenString(archiveVersion) - .getOSGiVersion() - .toString()); + bundleVersion = unwrap(getDefaultBundleVersion()); + builder.setProperty(Constants.BUNDLE_VERSION, bundleVersion); } getTask().getLogger() diff --git a/gradle-plugins/biz.aQute.bnd.gradle/src/test/groovy/aQute/bnd/gradle/TestBundlePlugin.groovy b/gradle-plugins/biz.aQute.bnd.gradle/src/test/groovy/aQute/bnd/gradle/TestBundlePlugin.groovy index 2ef48f9a16..ab81e4faf2 100644 --- a/gradle-plugins/biz.aQute.bnd.gradle/src/test/groovy/aQute/bnd/gradle/TestBundlePlugin.groovy +++ b/gradle-plugins/biz.aQute.bnd.gradle/src/test/groovy/aQute/bnd/gradle/TestBundlePlugin.groovy @@ -147,17 +147,19 @@ class TestBundlePlugin extends Specification { result.task(":bundle").outcome == SUCCESS result.task(":jar").outcome == SUCCESS - File jartask_result = new File(testProjectBuildDir, "libs/${testProject}-1.0.0.jar") + File jartask_result = new File(testProjectBuildDir, "libs/${testProject}.jar") jartask_result.isFile() JarFile jartask_jar = new JarFile(jartask_result) Attributes jartask_manifest = jartask_jar.getManifest().getMainAttributes() - File bundletask_bundle = new File(testProjectBuildDir, "libs/${testProject}-1.0.0-bundle.jar") + File bundletask_bundle = new File(testProjectBuildDir, "libs/${testProject}-bundle.jar") bundletask_bundle.isFile() JarFile bundletask_jar = new JarFile(bundletask_bundle) Attributes bundletask_manifest = bundletask_jar.getManifest().getMainAttributes() jartask_manifest.getValue("XX-Signed") == "true" + bundletask_manifest.getValue("Bundle-SymbolicName") == "${testProject}-bundle" + bundletask_manifest.getValue("Bundle-Version") == "0.0.0" bundletask_manifest.getValue("XX-Signed") == "true" bundletask_manifest.getValue("YY-Sealed") == "true" bundletask_manifest.getValue("ZZ-Delivered") == "true" diff --git a/gradle-plugins/biz.aQute.bnd.gradle/testresources/builderplugin2/build.gradle b/gradle-plugins/biz.aQute.bnd.gradle/testresources/builderplugin2/build.gradle index 2d4938f265..659acfd0b8 100644 --- a/gradle-plugins/biz.aQute.bnd.gradle/testresources/builderplugin2/build.gradle +++ b/gradle-plugins/biz.aQute.bnd.gradle/testresources/builderplugin2/build.gradle @@ -10,7 +10,6 @@ plugins { } group = 'test.bnd.gradle' -version = '1.0.0' repositories { mavenCentral() @@ -46,9 +45,9 @@ task bundle(type: Bundle) { "-include: jar:${file.asFile.toURI()}!/META-INF/MANIFEST.MF" } } - + bnd extraInstructions - + bnd ''' Bundle-Name: ${project.group}:${task.archiveBaseName}-${task.archiveClassifier} '''