From e5e2d14153514fe7d9b4f2a5d7f762d1be2f5c17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20L=C3=A4ubrich?= Date: Sun, 28 Jan 2024 19:55:28 +0100 Subject: [PATCH] Add an organize-manifest mojo Maintaining a manifest manually and keeping it up-to-date is a cumbersome task. This adds a new mojo that helps to manage traditional manifest first projects by organizing the manifest by updating version bounds and removes obsolete imports. --- .../eclipse/tycho}/ArtifactTypeHelper.java | 19 +- .../org/eclipse/tycho/TargetPlatform.java | 17 ++ .../core/osgitools/TargetPlatformProject.java | 2 +- .../resolver/DefaultP2ResolutionResult.java | 2 +- .../core/resolver/target/ArtifactMatcher.java | 1 + .../publisher/PublishProductToolImpl.java | 2 +- .../tycho/p2resolver/P2ResolverImpl.java | 2 +- .../PomDependencyCollectorImpl.java | 2 +- .../p2resolver/TargetPlatformBaseImpl.java | 2 +- .../p2resolver/TargetPlatformFactoryImpl.java | 2 +- .../tycho-dependency-tools-plugin/pom.xml | 4 + .../pde/{ => list}/ListDependenciesMojo.java | 2 +- .../tycho/extras/pde/organize/BundleInfo.java | 40 ++++ .../extras/pde/organize/ExportedPackages.java | 18 ++ .../extras/pde/organize/ImportedPackage.java | 59 ++++++ .../extras/pde/organize/ImportedPackages.java | 40 ++++ .../tycho/extras/pde/organize/Mappings.java | 36 ++++ .../pde/organize/OrganizeManifestMojo.java | 187 ++++++++++++++++++ .../extras/pde/organize/RequiredBundle.java | 85 ++++++++ .../extras/pde/organize/RequiredBundles.java | 61 ++++++ 20 files changed, 564 insertions(+), 19 deletions(-) rename {tycho-core/src/main/java/org/eclipse/tycho/core/resolver/target => tycho-api/src/main/java/org/eclipse/tycho}/ArtifactTypeHelper.java (92%) rename tycho-extras/tycho-dependency-tools-plugin/src/main/java/org/eclipse/tycho/extras/pde/{ => list}/ListDependenciesMojo.java (99%) create mode 100644 tycho-extras/tycho-dependency-tools-plugin/src/main/java/org/eclipse/tycho/extras/pde/organize/BundleInfo.java create mode 100644 tycho-extras/tycho-dependency-tools-plugin/src/main/java/org/eclipse/tycho/extras/pde/organize/ExportedPackages.java create mode 100644 tycho-extras/tycho-dependency-tools-plugin/src/main/java/org/eclipse/tycho/extras/pde/organize/ImportedPackage.java create mode 100644 tycho-extras/tycho-dependency-tools-plugin/src/main/java/org/eclipse/tycho/extras/pde/organize/ImportedPackages.java create mode 100644 tycho-extras/tycho-dependency-tools-plugin/src/main/java/org/eclipse/tycho/extras/pde/organize/Mappings.java create mode 100644 tycho-extras/tycho-dependency-tools-plugin/src/main/java/org/eclipse/tycho/extras/pde/organize/OrganizeManifestMojo.java create mode 100644 tycho-extras/tycho-dependency-tools-plugin/src/main/java/org/eclipse/tycho/extras/pde/organize/RequiredBundle.java create mode 100644 tycho-extras/tycho-dependency-tools-plugin/src/main/java/org/eclipse/tycho/extras/pde/organize/RequiredBundles.java diff --git a/tycho-core/src/main/java/org/eclipse/tycho/core/resolver/target/ArtifactTypeHelper.java b/tycho-api/src/main/java/org/eclipse/tycho/ArtifactTypeHelper.java similarity index 92% rename from tycho-core/src/main/java/org/eclipse/tycho/core/resolver/target/ArtifactTypeHelper.java rename to tycho-api/src/main/java/org/eclipse/tycho/ArtifactTypeHelper.java index 5742b32f6e..b9327e5c10 100644 --- a/tycho-core/src/main/java/org/eclipse/tycho/core/resolver/target/ArtifactTypeHelper.java +++ b/tycho-api/src/main/java/org/eclipse/tycho/ArtifactTypeHelper.java @@ -10,7 +10,7 @@ * Contributors: * SAP SE - initial API and implementation *******************************************************************************/ -package org.eclipse.tycho.core.resolver.target; +package org.eclipse.tycho; import static org.eclipse.tycho.ArtifactType.TYPE_BUNDLE_FRAGMENT; import static org.eclipse.tycho.ArtifactType.TYPE_ECLIPSE_FEATURE; @@ -24,20 +24,14 @@ import org.eclipse.equinox.p2.metadata.MetadataFactory.InstallableUnitDescription; import org.eclipse.equinox.p2.metadata.Version; import org.eclipse.equinox.p2.metadata.VersionRange; -import org.eclipse.equinox.p2.publisher.eclipse.BundlesAction; import org.eclipse.equinox.p2.query.IQuery; import org.eclipse.equinox.p2.query.QueryUtil; import org.eclipse.equinox.spi.p2.publisher.PublisherHelper; -import org.eclipse.tycho.ArtifactKey; -import org.eclipse.tycho.ArtifactType; -import org.eclipse.tycho.DefaultArtifactKey; -import org.eclipse.tycho.IArtifactFacade; -import org.eclipse.tycho.IllegalArtifactReferenceException; -import org.eclipse.tycho.PackagingType; public class ArtifactTypeHelper { - // p2 installable units + public static final String CAPABILITY_NS_OSGI_BUNDLE = "osgi.bundle"; //see org.eclipse.equinox.p2.publisher.eclipse.BundlesAction.CAPABILITY_NS_OSGI_BUNDLE + public static final String OSGI_BUNDLE_CLASSIFIER = "osgi.bundle"; //see org.eclipse.equinox.p2.publisher.eclipse.BundlesAction.OSGI_BUNDLE_CLASSIFIER /** * Returns a query matching the installable units representing the specified Eclipse @@ -90,8 +84,7 @@ public static IRequirement createRequirementFor(String type, String id, VersionR } private static IRequirement createBundleRequirement(String id, VersionRange versionRange) { - return MetadataFactory.createRequirement(BundlesAction.CAPABILITY_NS_OSGI_BUNDLE, id, versionRange, null, false, - true); // optional=false, multiple=true + return MetadataFactory.createRequirement(CAPABILITY_NS_OSGI_BUNDLE, id, versionRange, null, false, true); // optional=false, multiple=true } private static IRequirement createFeatureRequirement(String id, VersionRange versionRange) { @@ -187,4 +180,8 @@ public static String getType(IArtifactFacade artifactFacade) { return ArtifactType.TYPE_INSTALLABLE_UNIT; } + public static boolean isBundle(IArtifactKey key) { + return key != null && OSGI_BUNDLE_CLASSIFIER.equals(key.getClassifier()); + } + } diff --git a/tycho-api/src/main/java/org/eclipse/tycho/TargetPlatform.java b/tycho-api/src/main/java/org/eclipse/tycho/TargetPlatform.java index ffbb72eb09..2340a74099 100644 --- a/tycho-api/src/main/java/org/eclipse/tycho/TargetPlatform.java +++ b/tycho-api/src/main/java/org/eclipse/tycho/TargetPlatform.java @@ -13,7 +13,12 @@ package org.eclipse.tycho; import java.io.File; +import java.util.stream.Stream; +import org.eclipse.equinox.p2.metadata.IInstallableUnit; +import org.eclipse.equinox.p2.metadata.VersionRange; +import org.eclipse.equinox.p2.query.IQuery; +import org.eclipse.equinox.p2.query.IQueryResult; import org.eclipse.equinox.p2.repository.artifact.IArtifactRepository; import org.eclipse.equinox.p2.repository.metadata.IMetadataRepository; import org.eclipse.equinox.spi.p2.publisher.PublisherHelper; @@ -89,6 +94,18 @@ default ResolvedArtifactKey resolvePackage(String packageName, String versionRef location); } + default Stream resolvePackages(String packageName, VersionRange versionRange) { + IQuery query = ArtifactTypeHelper.createQueryFor(PublisherHelper.CAPABILITY_NS_JAVA_PACKAGE, + packageName, versionRange); + IQueryResult result = getMetadataRepository().query(query, null); + return result.stream().flatMap(iu -> { + return iu.getArtifacts().stream().filter(key -> ArtifactTypeHelper.isBundle(key)).map(key -> { + return new DefaultArtifactKey(ArtifactType.TYPE_ECLIPSE_PLUGIN, key.getId(), + key.getVersion().toString()); + }); + }); + } + /** * @return the target platform content as a {@link IMetadataRepository}. */ diff --git a/tycho-core/src/main/java/org/eclipse/tycho/core/osgitools/TargetPlatformProject.java b/tycho-core/src/main/java/org/eclipse/tycho/core/osgitools/TargetPlatformProject.java index 1dc6feae4c..1ca7ea6995 100644 --- a/tycho-core/src/main/java/org/eclipse/tycho/core/osgitools/TargetPlatformProject.java +++ b/tycho-core/src/main/java/org/eclipse/tycho/core/osgitools/TargetPlatformProject.java @@ -22,6 +22,7 @@ import org.eclipse.equinox.p2.metadata.IArtifactKey; import org.eclipse.equinox.p2.metadata.IInstallableUnit; import org.eclipse.tycho.ArtifactKey; +import org.eclipse.tycho.ArtifactTypeHelper; import org.eclipse.tycho.DefaultArtifactKey; import org.eclipse.tycho.DependencyArtifacts; import org.eclipse.tycho.PackagingType; @@ -32,7 +33,6 @@ import org.eclipse.tycho.core.ArtifactDependencyWalker; import org.eclipse.tycho.core.TychoProject; import org.eclipse.tycho.core.osgitools.targetplatform.DefaultDependencyArtifacts; -import org.eclipse.tycho.core.resolver.target.ArtifactTypeHelper; import org.eclipse.tycho.model.Feature; import org.eclipse.tycho.targetplatform.P2TargetPlatform; diff --git a/tycho-core/src/main/java/org/eclipse/tycho/core/resolver/DefaultP2ResolutionResult.java b/tycho-core/src/main/java/org/eclipse/tycho/core/resolver/DefaultP2ResolutionResult.java index 1c0145c602..21499147c3 100644 --- a/tycho-core/src/main/java/org/eclipse/tycho/core/resolver/DefaultP2ResolutionResult.java +++ b/tycho-core/src/main/java/org/eclipse/tycho/core/resolver/DefaultP2ResolutionResult.java @@ -27,8 +27,8 @@ import org.eclipse.equinox.p2.metadata.IInstallableUnit; import org.eclipse.tycho.ArtifactKey; import org.eclipse.tycho.ArtifactType; +import org.eclipse.tycho.ArtifactTypeHelper; import org.eclipse.tycho.DefaultArtifactKey; -import org.eclipse.tycho.core.resolver.target.ArtifactTypeHelper; import org.eclipse.tycho.p2.resolver.ClassifiedArtifactKey; import org.eclipse.tycho.p2.resolver.ClassifiedLocation; import org.eclipse.tycho.targetplatform.P2TargetPlatform; diff --git a/tycho-core/src/main/java/org/eclipse/tycho/core/resolver/target/ArtifactMatcher.java b/tycho-core/src/main/java/org/eclipse/tycho/core/resolver/target/ArtifactMatcher.java index b138e48891..3026d24e33 100644 --- a/tycho-core/src/main/java/org/eclipse/tycho/core/resolver/target/ArtifactMatcher.java +++ b/tycho-core/src/main/java/org/eclipse/tycho/core/resolver/target/ArtifactMatcher.java @@ -31,6 +31,7 @@ import org.eclipse.equinox.p2.query.IQueryResult; import org.eclipse.equinox.p2.query.QueryUtil; import org.eclipse.equinox.spi.p2.publisher.PublisherHelper; +import org.eclipse.tycho.ArtifactTypeHelper; import org.eclipse.tycho.IllegalArtifactReferenceException; public class ArtifactMatcher { diff --git a/tycho-core/src/main/java/org/eclipse/tycho/p2/tools/publisher/PublishProductToolImpl.java b/tycho-core/src/main/java/org/eclipse/tycho/p2/tools/publisher/PublishProductToolImpl.java index 4d73213b55..e338c5bc6e 100644 --- a/tycho-core/src/main/java/org/eclipse/tycho/p2/tools/publisher/PublishProductToolImpl.java +++ b/tycho-core/src/main/java/org/eclipse/tycho/p2/tools/publisher/PublishProductToolImpl.java @@ -33,10 +33,10 @@ import org.eclipse.equinox.p2.repository.metadata.IMetadataRepository; import org.eclipse.tycho.ArtifactKey; import org.eclipse.tycho.ArtifactType; +import org.eclipse.tycho.ArtifactTypeHelper; import org.eclipse.tycho.BuildFailureException; import org.eclipse.tycho.DependencySeed; import org.eclipse.tycho.Interpolator; -import org.eclipse.tycho.core.resolver.target.ArtifactTypeHelper; import org.eclipse.tycho.core.shared.MavenLogger; import org.eclipse.tycho.p2.repository.PublishingRepository; import org.eclipse.tycho.p2.tools.publisher.facade.PublishProductTool; diff --git a/tycho-core/src/main/java/org/eclipse/tycho/p2resolver/P2ResolverImpl.java b/tycho-core/src/main/java/org/eclipse/tycho/p2resolver/P2ResolverImpl.java index 21db213c82..68c1a0943b 100644 --- a/tycho-core/src/main/java/org/eclipse/tycho/p2resolver/P2ResolverImpl.java +++ b/tycho-core/src/main/java/org/eclipse/tycho/p2resolver/P2ResolverImpl.java @@ -48,6 +48,7 @@ import org.eclipse.equinox.p2.query.QueryUtil; import org.eclipse.tycho.ArtifactKey; import org.eclipse.tycho.ArtifactType; +import org.eclipse.tycho.ArtifactTypeHelper; import org.eclipse.tycho.DefaultArtifactKey; import org.eclipse.tycho.DependencyResolutionException; import org.eclipse.tycho.ExecutionEnvironmentConfiguration; @@ -66,7 +67,6 @@ import org.eclipse.tycho.core.resolver.P2ResolutionResult; import org.eclipse.tycho.core.resolver.P2Resolver; import org.eclipse.tycho.core.resolver.shared.PomDependencies; -import org.eclipse.tycho.core.resolver.target.ArtifactTypeHelper; import org.eclipse.tycho.core.shared.LoggingProgressMonitor; import org.eclipse.tycho.core.shared.MavenLogger; import org.eclipse.tycho.core.shared.MultiLineLogger; diff --git a/tycho-core/src/main/java/org/eclipse/tycho/p2resolver/PomDependencyCollectorImpl.java b/tycho-core/src/main/java/org/eclipse/tycho/p2resolver/PomDependencyCollectorImpl.java index d77a7e6b63..d6c0fa00bd 100644 --- a/tycho-core/src/main/java/org/eclipse/tycho/p2resolver/PomDependencyCollectorImpl.java +++ b/tycho-core/src/main/java/org/eclipse/tycho/p2resolver/PomDependencyCollectorImpl.java @@ -37,12 +37,12 @@ import org.eclipse.equinox.p2.repository.artifact.IArtifactDescriptor; import org.eclipse.tycho.ArtifactKey; import org.eclipse.tycho.ArtifactType; +import org.eclipse.tycho.ArtifactTypeHelper; import org.eclipse.tycho.DefaultArtifactKey; import org.eclipse.tycho.IArtifactFacade; import org.eclipse.tycho.IRawArtifactFileProvider; import org.eclipse.tycho.ReactorProject; import org.eclipse.tycho.TychoConstants; -import org.eclipse.tycho.core.resolver.target.ArtifactTypeHelper; import org.eclipse.tycho.core.resolver.target.FileArtifactRepository; import org.eclipse.tycho.p2.repository.ArtifactTransferPolicies; import org.eclipse.tycho.p2.repository.FileRepositoryArtifactProvider; diff --git a/tycho-core/src/main/java/org/eclipse/tycho/p2resolver/TargetPlatformBaseImpl.java b/tycho-core/src/main/java/org/eclipse/tycho/p2resolver/TargetPlatformBaseImpl.java index bf7865521c..e6c20ed191 100644 --- a/tycho-core/src/main/java/org/eclipse/tycho/p2resolver/TargetPlatformBaseImpl.java +++ b/tycho-core/src/main/java/org/eclipse/tycho/p2resolver/TargetPlatformBaseImpl.java @@ -26,6 +26,7 @@ import org.eclipse.equinox.p2.metadata.Version; import org.eclipse.equinox.p2.metadata.VersionRange; import org.eclipse.tycho.ArtifactType; +import org.eclipse.tycho.ArtifactTypeHelper; import org.eclipse.tycho.DefaultArtifactKey; import org.eclipse.tycho.DependencyResolutionException; import org.eclipse.tycho.ExecutionEnvironmentResolutionHints; @@ -34,7 +35,6 @@ import org.eclipse.tycho.IllegalArtifactReferenceException; import org.eclipse.tycho.ReactorProjectIdentities; import org.eclipse.tycho.core.resolver.target.ArtifactMatcher; -import org.eclipse.tycho.core.resolver.target.ArtifactTypeHelper; import org.eclipse.tycho.p2.repository.LocalArtifactRepository; import org.eclipse.tycho.targetplatform.P2TargetPlatform; diff --git a/tycho-core/src/main/java/org/eclipse/tycho/p2resolver/TargetPlatformFactoryImpl.java b/tycho-core/src/main/java/org/eclipse/tycho/p2resolver/TargetPlatformFactoryImpl.java index e31cfbb8e8..e5693c3a5f 100644 --- a/tycho-core/src/main/java/org/eclipse/tycho/p2resolver/TargetPlatformFactoryImpl.java +++ b/tycho-core/src/main/java/org/eclipse/tycho/p2resolver/TargetPlatformFactoryImpl.java @@ -64,6 +64,7 @@ import org.eclipse.equinox.p2.repository.artifact.IArtifactRepositoryManager; import org.eclipse.equinox.p2.repository.metadata.IMetadataRepository; import org.eclipse.equinox.p2.repository.metadata.IMetadataRepositoryManager; +import org.eclipse.tycho.ArtifactTypeHelper; import org.eclipse.tycho.ExecutionEnvironmentConfiguration; import org.eclipse.tycho.ExecutionEnvironmentResolutionHints; import org.eclipse.tycho.IArtifactFacade; @@ -84,7 +85,6 @@ import org.eclipse.tycho.core.osgitools.MavenBundleResolver; import org.eclipse.tycho.core.osgitools.OsgiBundleProject; import org.eclipse.tycho.core.resolver.shared.ReferencedRepositoryMode; -import org.eclipse.tycho.core.resolver.target.ArtifactTypeHelper; import org.eclipse.tycho.core.resolver.target.DuplicateReactorIUsException; import org.eclipse.tycho.core.resolver.target.FileArtifactRepository; import org.eclipse.tycho.core.resolver.target.TargetPlatformFilterEvaluator; diff --git a/tycho-extras/tycho-dependency-tools-plugin/pom.xml b/tycho-extras/tycho-dependency-tools-plugin/pom.xml index 849866c9b8..82504a1aaa 100644 --- a/tycho-extras/tycho-dependency-tools-plugin/pom.xml +++ b/tycho-extras/tycho-dependency-tools-plugin/pom.xml @@ -39,6 +39,10 @@ org.eclipse.tycho tycho-core + + biz.aQute.bnd + biz.aQute.bndlib + diff --git a/tycho-extras/tycho-dependency-tools-plugin/src/main/java/org/eclipse/tycho/extras/pde/ListDependenciesMojo.java b/tycho-extras/tycho-dependency-tools-plugin/src/main/java/org/eclipse/tycho/extras/pde/list/ListDependenciesMojo.java similarity index 99% rename from tycho-extras/tycho-dependency-tools-plugin/src/main/java/org/eclipse/tycho/extras/pde/ListDependenciesMojo.java rename to tycho-extras/tycho-dependency-tools-plugin/src/main/java/org/eclipse/tycho/extras/pde/list/ListDependenciesMojo.java index 1018c64e83..09a9801d68 100644 --- a/tycho-extras/tycho-dependency-tools-plugin/src/main/java/org/eclipse/tycho/extras/pde/ListDependenciesMojo.java +++ b/tycho-extras/tycho-dependency-tools-plugin/src/main/java/org/eclipse/tycho/extras/pde/list/ListDependenciesMojo.java @@ -7,7 +7,7 @@ * * SPDX-License-Identifier: EPL-2.0 *******************************************************************************/ -package org.eclipse.tycho.extras.pde; +package org.eclipse.tycho.extras.pde.list; import java.io.BufferedWriter; import java.io.File; diff --git a/tycho-extras/tycho-dependency-tools-plugin/src/main/java/org/eclipse/tycho/extras/pde/organize/BundleInfo.java b/tycho-extras/tycho-dependency-tools-plugin/src/main/java/org/eclipse/tycho/extras/pde/organize/BundleInfo.java new file mode 100644 index 0000000000..42a3125a7a --- /dev/null +++ b/tycho-extras/tycho-dependency-tools-plugin/src/main/java/org/eclipse/tycho/extras/pde/organize/BundleInfo.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2023 Christoph Läubrich and others. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.tycho.extras.pde.organize; + +import java.util.jar.Manifest; + +import org.osgi.framework.Constants; +import org.osgi.framework.Version; + +public class BundleInfo { + + private Version version; + private Manifest manifest; + + public BundleInfo(Manifest manifest) { + this.manifest = manifest; + } + + public Manifest manifest() { + return manifest; + } + + public Version version() { + if (version == null) { + String v = manifest().getMainAttributes().getValue(Constants.BUNDLE_VERSION); + if (v == null) { + v = "0"; + } + version = Version.parseVersion(v); + } + return version; + } +} diff --git a/tycho-extras/tycho-dependency-tools-plugin/src/main/java/org/eclipse/tycho/extras/pde/organize/ExportedPackages.java b/tycho-extras/tycho-dependency-tools-plugin/src/main/java/org/eclipse/tycho/extras/pde/organize/ExportedPackages.java new file mode 100644 index 0000000000..26d6498a9c --- /dev/null +++ b/tycho-extras/tycho-dependency-tools-plugin/src/main/java/org/eclipse/tycho/extras/pde/organize/ExportedPackages.java @@ -0,0 +1,18 @@ +/******************************************************************************* + * Copyright (c) 2023 Christoph Läubrich and others. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.tycho.extras.pde.organize; + +public class ExportedPackages { + + public ExportedPackages(String manifestValue) { + // TODO Auto-generated constructor stub + } + +} diff --git a/tycho-extras/tycho-dependency-tools-plugin/src/main/java/org/eclipse/tycho/extras/pde/organize/ImportedPackage.java b/tycho-extras/tycho-dependency-tools-plugin/src/main/java/org/eclipse/tycho/extras/pde/organize/ImportedPackage.java new file mode 100644 index 0000000000..8e12b29956 --- /dev/null +++ b/tycho-extras/tycho-dependency-tools-plugin/src/main/java/org/eclipse/tycho/extras/pde/organize/ImportedPackage.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2023 Christoph Läubrich and others. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.tycho.extras.pde.organize; + +import org.eclipse.equinox.p2.metadata.VersionRange; +import org.osgi.framework.Constants; + +import aQute.bnd.header.Attrs; + +public class ImportedPackage { + + private ImportedPackages packages; + private String packageName; + private Attrs attrs; + private VersionRange version; + + public ImportedPackage(ImportedPackages packages, String packageName, Attrs attrs) { + this.packages = packages; + this.packageName = packageName; + this.attrs = attrs; + } + + public String getPackageName() { + return packageName; + } + + public VersionRange getVersionRange() { + if (version == null) { + String string = attrs.get(Constants.VERSION_ATTRIBUTE); + if (string == null) { + version = VersionRange.emptyRange; + } else { + version = VersionRange.create(string); + } + } + return version; + } + + public boolean isJava() { + return packageName.startsWith("java."); + } + + @Override + public String toString() { + VersionRange version = getVersionRange(); + if (version.equals(VersionRange.emptyRange)) { + return getPackageName(); + } + return getPackageName() + " " + getVersionRange(); + } + +} diff --git a/tycho-extras/tycho-dependency-tools-plugin/src/main/java/org/eclipse/tycho/extras/pde/organize/ImportedPackages.java b/tycho-extras/tycho-dependency-tools-plugin/src/main/java/org/eclipse/tycho/extras/pde/organize/ImportedPackages.java new file mode 100644 index 0000000000..a278720795 --- /dev/null +++ b/tycho-extras/tycho-dependency-tools-plugin/src/main/java/org/eclipse/tycho/extras/pde/organize/ImportedPackages.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2023 Christoph Läubrich and others. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.tycho.extras.pde.organize; + +import java.util.Map; +import java.util.Map.Entry; +import java.util.TreeMap; +import java.util.stream.Stream; + +import aQute.bnd.header.Attrs; +import aQute.bnd.header.OSGiHeader; +import aQute.bnd.header.Parameters; + +public class ImportedPackages { + + //import package order is not important so we can sort here by name + private Map packages = new TreeMap<>(); + + public ImportedPackages(String manifestValue) { + if (manifestValue != null) { + Parameters header = OSGiHeader.parseHeader(manifestValue); + for (Entry entry : header.entrySet()) { + packages.put(entry.getKey(), new ImportedPackage(this, entry.getKey(), entry.getValue())); + } + } + } + + public Stream packages() { + return packages.values().stream(); + + } + +} diff --git a/tycho-extras/tycho-dependency-tools-plugin/src/main/java/org/eclipse/tycho/extras/pde/organize/Mappings.java b/tycho-extras/tycho-dependency-tools-plugin/src/main/java/org/eclipse/tycho/extras/pde/organize/Mappings.java new file mode 100644 index 0000000000..e5490c1c46 --- /dev/null +++ b/tycho-extras/tycho-dependency-tools-plugin/src/main/java/org/eclipse/tycho/extras/pde/organize/Mappings.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2023 Christoph Läubrich and others. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.tycho.extras.pde.organize; + +import java.util.Collection; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +public class Mappings { + + private Map> contributedPackages = new HashMap<>(); + + private Map> contributingBundles = new HashMap<>(); + + public Stream contributedPackages(RequiredBundle bundle) { + return contributedPackages.getOrDefault(bundle, List.of()).stream() + .sorted(Comparator.comparing(ImportedPackage::getPackageName)); + } + + public void addContribution(ImportedPackage pkg, RequiredBundle requiredBundle) { + contributedPackages.computeIfAbsent(requiredBundle, x -> new HashSet<>()).add(pkg); + contributingBundles.computeIfAbsent(pkg, x -> new HashSet<>()).add(requiredBundle); + } + +} diff --git a/tycho-extras/tycho-dependency-tools-plugin/src/main/java/org/eclipse/tycho/extras/pde/organize/OrganizeManifestMojo.java b/tycho-extras/tycho-dependency-tools-plugin/src/main/java/org/eclipse/tycho/extras/pde/organize/OrganizeManifestMojo.java new file mode 100644 index 0000000000..34e95a2616 --- /dev/null +++ b/tycho-extras/tycho-dependency-tools-plugin/src/main/java/org/eclipse/tycho/extras/pde/organize/OrganizeManifestMojo.java @@ -0,0 +1,187 @@ +/******************************************************************************* + * Copyright (c) 2023 Christoph Läubrich and others. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.tycho.extras.pde.organize; + +import java.io.File; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Consumer; +import java.util.jar.Manifest; + +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugin.logging.Log; +import org.apache.maven.plugins.annotations.Component; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.annotations.ResolutionScope; +import org.apache.maven.project.MavenProject; +import org.eclipse.equinox.p2.metadata.VersionRange; +import org.eclipse.tycho.ArtifactDescriptor; +import org.eclipse.tycho.ArtifactType; +import org.eclipse.tycho.DependencyArtifacts; +import org.eclipse.tycho.PackagingType; +import org.eclipse.tycho.TargetPlatform; +import org.eclipse.tycho.core.TychoProject; +import org.eclipse.tycho.core.TychoProjectManager; +import org.eclipse.tycho.core.osgitools.DefaultReactorProject; +import org.eclipse.tycho.core.osgitools.OsgiBundleProject; +import org.osgi.framework.Constants; + +import aQute.bnd.osgi.Analyzer; +import aQute.bnd.osgi.Jar; + +@Mojo(name = "organize-manifest", requiresProject = true, threadSafe = true, requiresDependencyCollection = ResolutionScope.TEST) +public class OrganizeManifestMojo extends AbstractMojo { + + /** + * Controls if export packages without a version will get the current project version. + *

+ * Adding a version to a previously unversioned package is a compatible change, but consumers + * should be changed to use versioned package imports. + *

+ */ + @Parameter(defaultValue = "true") + private boolean addMissingVersions; + + /** + * Controls if a reexported bundle dependency should be kept even if it is not used anymore by + * this bundle + *

+ * Removing a reexported bundle is an incompatible change if consumers are using require bundle + * as well and possibly need to be adjusted. + *

+ */ + @Parameter(defaultValue = "true") + private boolean keepReexportedBundles; + + @Parameter + private boolean skip; + + @Component + private MavenProject mavenProject; + + @Component + private TychoProjectManager projectManager; + + @Override + public void execute() throws MojoExecutionException, MojoFailureException { + if (PackagingType.TYPE_ECLIPSE_PLUGIN.equals(mavenProject.getPackaging())) { + TychoProject tychoProject = projectManager.getTychoProject(mavenProject).get(); + if (tychoProject instanceof OsgiBundleProject osgi) { + DependencyArtifacts artifacts = tychoProject + .getDependencyArtifacts(DefaultReactorProject.adapt(mavenProject)); + TargetPlatform targetPlatform = projectManager.getTargetPlatform(mavenProject).get(); + File file = mavenProject.getArtifact().getFile(); + if (!file.isFile()) { + file = new File(mavenProject.getBuild().getOutputDirectory()); + if (!file.isDirectory()) { + throw new MojoFailureException( + "Artifact is not packed and output directory is not present, do you executed the compile/package phase for this project?"); + } + } + Mappings mappings = new Mappings(); + Map bundleInfos = new HashMap<>(); + RequiredBundles requiredBundles = new RequiredBundles( + osgi.getManifestValue(Constants.REQUIRE_BUNDLE, mavenProject)); + ExportedPackages exportedPackages = new ExportedPackages( + osgi.getManifestValue(Constants.EXPORT_PACKAGE, mavenProject)); + ImportedPackages importedPackages = new ImportedPackages( + osgi.getManifestValue(Constants.IMPORT_PACKAGE, mavenProject)); + ImportedPackages calculatePackages = calculatePackages(artifacts, file, requiredBundles, bundleInfos); + //now expand the required bundles... + HashSet expanded = new HashSet<>(); + requiredBundles.bundles().forEach(rb -> expandReexports(rb, bundleInfos, expanded)); + //and assign packages to required bundles... + calculatePackages.packages().forEach(pkg -> { + if (!pkg.isJava()) { + VersionRange version = pkg.getVersionRange(); + targetPlatform.resolvePackages(pkg.getPackageName(), + VersionRange.emptyRange.equals(version) ? VersionRange.emptyRange + : VersionRange.create(version.toString())) + .forEach(key -> { + requiredBundles.addPackageMapping(pkg, key, mappings); + }); + } + }); + Log log = getLog(); + log.info("====== Required Bundles Report ======="); + if (requiredBundles.isEmpty()) { + log.info("No required bundles!"); + } else { + requiredBundles.bundles().forEach(rb -> { + printRequireBundle(mappings, rb, 0, log::info); + }); + } + System.out.println("=== Imported Packages ==="); + importedPackages.packages().forEach(pkg -> System.out.println(pkg.getPackageName())); + } + } + } + + private void printRequireBundle(Mappings mappings, RequiredBundle bundle, int indent, Consumer logger) { + String repeat = " ".repeat(indent); + logger.accept(repeat + bundle); + mappings.contributedPackages(bundle).map(pkg -> repeat + " provides " + pkg).forEach(logger); + bundle.childs(false).forEach(child -> printRequireBundle(mappings, child, indent + 1, logger)); + } + + private ImportedPackages calculatePackages(DependencyArtifacts artifacts, File file, + RequiredBundles requiredBundles, Map bundleInfos) throws MojoExecutionException { + try (Jar jar = new Jar(file)) { + jar.setManifest((Manifest) null); + try (Analyzer analyzer = new Analyzer(jar)) { + analyzer.setImportPackage("*"); + List list = artifacts.getArtifacts(ArtifactType.TYPE_ECLIPSE_PLUGIN); + for (ArtifactDescriptor artifactDescriptor : list) { + File cp = artifactDescriptor.fetchArtifact().join(); + if (!cp.exists() || cp.length() == 0) { + continue; + } + Jar dependencyJar = new Jar(cp); + String bsn = dependencyJar.getBsn(); + if (bsn != null) { + bundleInfos.put(bsn, new BundleInfo(dependencyJar.getManifest())); + } + analyzer.addClasspath(dependencyJar); + } + Manifest manifest = analyzer.calcManifest(); + Log log = getLog(); + analyzer.getWarnings().forEach(log::warn); + analyzer.getErrors().forEach(log::error); + String value = manifest.getMainAttributes().getValue(Constants.IMPORT_PACKAGE); + return new ImportedPackages(value); + } + } catch (Exception e) { + throw new MojoExecutionException("Organize manifest failed", e); + } + } + + private void expandReexports(RequiredBundle bundle, Map bundleInfos, + Set expanded) { + if (expanded.add(bundle)) { + //here we need to add childs to the RequiredBundle if that bundle has reexported required bundles... + //as these packages are visible we need to consider if we can remove the parent + BundleInfo bundleInfo = bundleInfos.get(bundle.getBundleSymbolicName()); + System.out.println("Expand " + bundle.getBundleSymbolicName() + " " + bundleInfo); + String requiredBundles = bundleInfo.manifest().getMainAttributes().getValue(Constants.REQUIRE_BUNDLE); + new RequiredBundles(requiredBundles).bundles().filter(RequiredBundle::isReexport).forEach(exported -> { + bundle.addChild(exported); + expandReexports(exported, bundleInfos, expanded); + }); + } + + } + +} diff --git a/tycho-extras/tycho-dependency-tools-plugin/src/main/java/org/eclipse/tycho/extras/pde/organize/RequiredBundle.java b/tycho-extras/tycho-dependency-tools-plugin/src/main/java/org/eclipse/tycho/extras/pde/organize/RequiredBundle.java new file mode 100644 index 0000000000..100fb27478 --- /dev/null +++ b/tycho-extras/tycho-dependency-tools-plugin/src/main/java/org/eclipse/tycho/extras/pde/organize/RequiredBundle.java @@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright (c) 2023 Christoph Läubrich and others. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.tycho.extras.pde.organize; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.stream.Stream; + +import org.osgi.framework.Constants; +import org.osgi.framework.VersionRange; + +import aQute.bnd.header.Attrs; + +public final class RequiredBundle { + + private String bundleSymbolicName; + private Attrs attrs; + private VersionRange version; + private List childs = new ArrayList<>(); + + public RequiredBundle(String bundleSymbolicName, Attrs attrs) { + this.bundleSymbolicName = bundleSymbolicName; + this.attrs = attrs; + } + + public String getBundleSymbolicName() { + return bundleSymbolicName; + } + + public VersionRange getVersionRange() { + if (this.version == null) { + String version = attrs.get(Constants.BUNDLE_VERSION_ATTRIBUTE, "0"); + this.version = VersionRange.valueOf(version); + } + return this.version; + } + + public boolean isReexport() { + return Constants.VISIBILITY_REEXPORT.equals(attrs.get(Constants.VISIBILITY_DIRECTIVE + ":")); + } + + @Override + public int hashCode() { + return Objects.hash(bundleSymbolicName, getVersionRange()); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + RequiredBundle other = (RequiredBundle) obj; + return Objects.equals(bundleSymbolicName, other.bundleSymbolicName) + && Objects.equals(getVersionRange(), other.getVersionRange()); + } + + public void addChild(RequiredBundle child) { + childs.add(child); + } + + public Stream childs(boolean transitive) { + Stream stream = childs.stream(); + if (transitive) { + return stream.flatMap(c -> Stream.concat(Stream.of(c), c.childs(transitive))).distinct(); + } + return stream; + } + + @Override + public String toString() { + return getBundleSymbolicName() + " " + getVersionRange() + (isReexport() ? " (reexported)" : ""); + } + +} diff --git a/tycho-extras/tycho-dependency-tools-plugin/src/main/java/org/eclipse/tycho/extras/pde/organize/RequiredBundles.java b/tycho-extras/tycho-dependency-tools-plugin/src/main/java/org/eclipse/tycho/extras/pde/organize/RequiredBundles.java new file mode 100644 index 0000000000..8e32de0a59 --- /dev/null +++ b/tycho-extras/tycho-dependency-tools-plugin/src/main/java/org/eclipse/tycho/extras/pde/organize/RequiredBundles.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2023 Christoph Läubrich and others. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.tycho.extras.pde.organize; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.stream.Stream; + +import org.eclipse.tycho.ArtifactKey; + +import aQute.bnd.header.Attrs; +import aQute.bnd.header.OSGiHeader; +import aQute.bnd.header.Parameters; + +public class RequiredBundles { + + //The order of require bundle is important so we need to retain it + private Map bundles = new LinkedHashMap<>(); + + public RequiredBundles(String manifestValue) { + if (manifestValue != null) { + Parameters header = OSGiHeader.parseHeader(manifestValue); + for (Entry entry : header.entrySet()) { + String bsn = entry.getKey(); + Attrs attrs = entry.getValue(); + bundles.put(bsn, new RequiredBundle(bsn, attrs)); + } + } + } + + public Stream bundles() { + return bundles.values().stream(); + + } + + public void addPackageMapping(ImportedPackage pkg, ArtifactKey key, Mappings mappings) { + RequiredBundle requiredBundle = bundles.get(key.getId()); + if (requiredBundle != null) { + mappings.addContribution(pkg, requiredBundle); + } else { + //might be something contributed by a reexported bundle... + bundles.values().stream().flatMap(rb -> rb.childs(true)) + .filter(rb -> rb.getBundleSymbolicName().equals(key.getId()) + && rb.getVersionRange().toString().equals(key.getVersion())) + .forEach(rb -> mappings.addContribution(pkg, rb)); + } + } + + public boolean isEmpty() { + return bundles.isEmpty(); + } + +}