diff --git a/ui/org.eclipse.pde.core/META-INF/MANIFEST.MF b/ui/org.eclipse.pde.core/META-INF/MANIFEST.MF index e38bbb16355..58917c063b4 100644 --- a/ui/org.eclipse.pde.core/META-INF/MANIFEST.MF +++ b/ui/org.eclipse.pde.core/META-INF/MANIFEST.MF @@ -89,6 +89,9 @@ Import-Package: aQute.bnd.build;version="[4.4.0,5.0.0)", aQute.bnd.service;version="[4.7.0,5.0.0)", aQute.bnd.version;version="[2.2.0,3.0.0)", aQute.service.reporter;version="[1.2.0,2.0.0)", + org.eclipse.equinox.internal.p2.publisher.eclipse, + org.eclipse.equinox.p2.publisher, + org.eclipse.equinox.p2.publisher.eclipse, org.osgi.service.repository;version="[1.1.0,2.0.0)", org.osgi.util.promise;version="[1.3.0,2.0.0)" Require-Bundle: diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/core/target/TargetBundle.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/core/target/TargetBundle.java index b9da82fe329..3d86ae68d73 100644 --- a/ui/org.eclipse.pde.core/src/org/eclipse/pde/core/target/TargetBundle.java +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/core/target/TargetBundle.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2009, 2017 IBM Corporation and others. + * Copyright (c) 2009, 2023 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -10,6 +10,7 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Christoph Läubrich - supply the manifest as a string if requested *******************************************************************************/ package org.eclipse.pde.core.target; @@ -18,6 +19,8 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.StringWriter; import java.util.Map; import org.eclipse.core.runtime.CoreException; @@ -171,7 +174,22 @@ private void initialize(File file) throws CoreException { } Map manifest = ManifestUtils.loadManifest(file); try { - fInfo = new BundleInfo(file.toURI()); + fInfo = new BundleInfo(file.toURI()) { + private String manifestString; + + @Override + public String getManifest() { + if (manifestString == null) { + try { + StringWriter writer = new StringWriter(); + ManifestUtils.writeManifest(manifest, writer); + manifestString = writer.toString(); + } catch (IOException e) { + } + } + return manifestString; + } + }; // Attempt to retrieve additional bundle information from the manifest String header = manifest.get(Constants.BUNDLE_SYMBOLICNAME); if (header != null) { diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/DirectoryBundleContainer.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/DirectoryBundleContainer.java index 3045aeb36db..9cecfa8ce49 100644 --- a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/DirectoryBundleContainer.java +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/DirectoryBundleContainer.java @@ -23,6 +23,10 @@ import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubMonitor; +import org.eclipse.equinox.p2.metadata.IInstallableUnit; +import org.eclipse.equinox.p2.metadata.IInstallableUnitQueryable; +import org.eclipse.equinox.p2.query.IQuery; +import org.eclipse.equinox.p2.query.IQueryResult; import org.eclipse.osgi.util.NLS; import org.eclipse.pde.core.target.ITargetDefinition; import org.eclipse.pde.core.target.TargetBundle; @@ -34,7 +38,7 @@ * * @since 3.5 */ -public class DirectoryBundleContainer extends AbstractBundleContainer { +public class DirectoryBundleContainer extends AbstractBundleContainer implements IInstallableUnitQueryable { /** * Constant describing the type of bundle container @@ -173,4 +177,12 @@ public void reload() { clearResolutionStatus(); } + @Override + public IQueryResult query(IQuery query, IProgressMonitor monitor) { + if (fBundles == null || fFeatures == null) { + resolve(null, monitor); + } + return query.perform(InstallableUnitGenerator.generateInstallableUnits(fBundles, fFeatures).iterator()); + } + } diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/FeatureBundleContainer.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/FeatureBundleContainer.java index 2e4ec4a7ad9..ceb0e95d244 100644 --- a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/FeatureBundleContainer.java +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/FeatureBundleContainer.java @@ -22,6 +22,10 @@ import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.Status; +import org.eclipse.equinox.p2.metadata.IInstallableUnit; +import org.eclipse.equinox.p2.metadata.IInstallableUnitQueryable; +import org.eclipse.equinox.p2.query.IQuery; +import org.eclipse.equinox.p2.query.IQueryResult; import org.eclipse.osgi.util.NLS; import org.eclipse.pde.core.target.ITargetDefinition; import org.eclipse.pde.core.target.ITargetLocation; @@ -41,7 +45,7 @@ * * @since 3.5 */ -public class FeatureBundleContainer extends AbstractBundleContainer { +public class FeatureBundleContainer extends AbstractBundleContainer implements IInstallableUnitQueryable { /** * Constant describing the type of bundle container @@ -239,4 +243,12 @@ public String[] getVMArguments() { public void reload() { clearResolutionStatus(); } + + @Override + public IQueryResult query(IQuery query, IProgressMonitor monitor) { + if (fBundles == null || fFeatures == null) { + resolve(null, monitor); + } + return query.perform(InstallableUnitGenerator.generateInstallableUnits(fBundles, fFeatures).iterator()); + } } diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/InstallableUnitGenerator.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/InstallableUnitGenerator.java new file mode 100644 index 00000000000..b697af77158 --- /dev/null +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/InstallableUnitGenerator.java @@ -0,0 +1,149 @@ +/******************************************************************************* + * 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 + * + * Contributors: + * Christoph Läubrich - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.internal.core.target; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Map.Entry; +import java.util.Set; +import java.util.jar.Attributes; +import java.util.jar.Manifest; +import java.util.stream.Stream; + +import org.eclipse.equinox.frameworkadmin.BundleInfo; +import org.eclipse.equinox.internal.p2.publisher.eclipse.FeatureManifestParser; +import org.eclipse.equinox.internal.p2.publisher.eclipse.FeatureParser; +import org.eclipse.equinox.p2.metadata.IInstallableUnit; +import org.eclipse.equinox.p2.publisher.IPublisherInfo; +import org.eclipse.equinox.p2.publisher.PublisherInfo; +import org.eclipse.equinox.p2.publisher.PublisherResult; +import org.eclipse.equinox.p2.publisher.eclipse.BundlesAction; +import org.eclipse.equinox.p2.publisher.eclipse.Feature; +import org.eclipse.equinox.p2.publisher.eclipse.FeaturesAction; +import org.eclipse.equinox.p2.query.QueryUtil; +import org.eclipse.osgi.framework.util.CaseInsensitiveDictionaryMap; +import org.eclipse.osgi.service.resolver.BundleDescription; +import org.eclipse.pde.core.IModel; +import org.eclipse.pde.core.target.TargetBundle; +import org.eclipse.pde.core.target.TargetFeature; +import org.eclipse.pde.internal.core.ifeature.IFeature; +import org.eclipse.pde.internal.core.ifeature.IFeatureModel; +import org.xml.sax.SAXException; + +/** + * Class to generate {@link IInstallableUnit}s from target objects + */ +@SuppressWarnings("restriction") +public class InstallableUnitGenerator { + + public static Stream generateInstallableUnits(TargetBundle[] bundles, TargetFeature[] features) { + return Stream.concat(generateInstallableUnits(bundles), generateInstallableUnits(features)); + } + + public static Stream generateInstallableUnits(TargetBundle[] bundles) { + if (bundles == null || bundles.length == 0) { + return Stream.empty(); + } + return Arrays.stream(bundles).flatMap(InstallableUnitGenerator::generateInstallableUnits); + } + + public static Stream generateInstallableUnits(TargetBundle targetBundle) { + BundleInfo bundleInfo = targetBundle.getBundleInfo(); + if (bundleInfo != null) { + String manifest = bundleInfo.getManifest(); + if (manifest != null) { + try { + Manifest parsed = new Manifest(new ByteArrayInputStream(manifest.getBytes(StandardCharsets.UTF_8))); + Attributes mainAttributes = parsed.getMainAttributes(); + CaseInsensitiveDictionaryMap headers = new CaseInsensitiveDictionaryMap<>( + mainAttributes.size()); + Set> entrySet = mainAttributes.entrySet(); + for (Entry entry : entrySet) { + headers.put(entry.getKey().toString(), entry.getValue().toString()); + } + PublisherInfo publisherInfo = new PublisherInfo(); + publisherInfo.setArtifactOptions(IPublisherInfo.A_INDEX); + BundleDescription bundleDescription = BundlesAction.createBundleDescription(headers, null); + IInstallableUnit iu = BundlesAction.createBundleIU(bundleDescription, + BundlesAction.createBundleArtifactKey(bundleDescription.getSymbolicName(), + bundleDescription.getVersion().toString()), + publisherInfo); + return Stream.of(iu); + } catch (IOException e) { + // can't use it then... + } + } + } + return Stream.empty(); + } + + public static Stream generateInstallableUnits(TargetFeature[] features) { + if (features == null || features.length == 0) { + return Stream.empty(); + } + return Arrays.stream(features).flatMap(InstallableUnitGenerator::generateInstallableUnits); + } + + public static Stream generateInstallableUnits(TargetFeature targetFeature) { + String location = targetFeature.getLocation(); + if (location != null) { + Feature feature = new FeatureParser().parse(new File(location)); + if (feature != null) { + feature.setLocation(location); + return generateInstallableUnits(feature); + } + } + IModel model = targetFeature.getFeatureModel(); + if (model instanceof IFeatureModel featureModel) { + IFeature feature = featureModel.getFeature(); + if (feature != null) { + StringWriter stringWriter = new StringWriter(); + PrintWriter printWriter = new PrintWriter(stringWriter); + feature.write("", printWriter); //$NON-NLS-1$ + printWriter.flush(); + try { + generateInstallableUnits(new FeatureManifestParser().parse( + new ByteArrayInputStream(stringWriter.toString().getBytes(StandardCharsets.UTF_8)), + new URL("file:/dummy")));//$NON-NLS-1$ + } catch (SAXException | IOException e) { + // can't use it then... + } + } + } + return Stream.empty(); + } + + private static Stream generateInstallableUnits(Feature feature) { + PublisherInfo publisherInfo = new PublisherInfo(); + publisherInfo.setArtifactOptions(IPublisherInfo.A_INDEX); + FeaturesAction action = new FeaturesAction(new Feature[] { feature }) { + @Override + protected void publishFeatureArtifacts(Feature feature, IInstallableUnit featureIU, + IPublisherInfo publisherInfo) { + // so not call super as we don't wan't to copy anything --> Bug + // in P2 with IPublisherInfo.A_INDEX option + } + }; + PublisherResult results = new PublisherResult(); + action.perform(publisherInfo, results, null); + return results.query(QueryUtil.ALL_UNITS, null).stream(); + } + +} diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/P2TargetUtils.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/P2TargetUtils.java index 47055914382..fb0df32c8c5 100644 --- a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/P2TargetUtils.java +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/P2TargetUtils.java @@ -32,11 +32,13 @@ import java.util.WeakHashMap; import java.util.concurrent.ConcurrentHashMap; +import org.eclipse.core.runtime.Adapters; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.MultiStatus; +import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubMonitor; @@ -59,6 +61,7 @@ import org.eclipse.equinox.p2.engine.spi.ProvisioningAction; import org.eclipse.equinox.p2.metadata.IArtifactKey; import org.eclipse.equinox.p2.metadata.IInstallableUnit; +import org.eclipse.equinox.p2.metadata.IInstallableUnitQueryable; import org.eclipse.equinox.p2.metadata.IProvidedCapability; import org.eclipse.equinox.p2.metadata.IRequirement; import org.eclipse.equinox.p2.metadata.MetadataFactory; @@ -1084,6 +1087,7 @@ private void resolveWithPlanner(ITargetDefinition target, IProgressMonitor monit context.setProperty(ProvisioningContext.FOLLOW_REPOSITORY_REFERENCES, Boolean.toString(true)); context.setMetadataRepositories(getMetadataRepositories(target)); context.setArtifactRepositories(getArtifactRepositories(target)); + context.setExtraInstallableUnits(getAdditionalProvisionIUs(target)); IProvisioningPlan plan = planner.getProvisioningPlan(request, context, subMonitor.split(20)); IStatus status = plan.getStatus(); @@ -1547,6 +1551,24 @@ private URI[] getMetadataRepositories(ITargetDefinition target) throws CoreExcep return result.toArray(new URI[result.size()]); } + private List getAdditionalProvisionIUs(ITargetDefinition target) { + List result = new ArrayList<>(); + ITargetLocation[] containers = target.getTargetLocations(); + if (containers != null) { + for (ITargetLocation container : containers) { + if (container instanceof IUBundleContainer) { + // this is already handled by getMetadataRepositories(..) + continue; + } + IInstallableUnitQueryable queryable = Adapters.adapt(container, IInstallableUnitQueryable.class); + if (queryable != null) { + queryable.query(QueryUtil.ALL_UNITS, new NullProgressMonitor()).forEach(result::add); + } + } + } + return result; + } + private static final String NATIVE_ARTIFACTS = "nativeArtifacts"; //$NON-NLS-1$ private static final String NATIVE_TYPE = "org.eclipse.equinox.p2.native"; //$NON-NLS-1$ private static final String PARM_OPERAND = "operand"; //$NON-NLS-1$ diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/ProfileBundleContainer.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/ProfileBundleContainer.java index 26b307fcaa6..8bacb71f66d 100644 --- a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/ProfileBundleContainer.java +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/ProfileBundleContainer.java @@ -35,6 +35,10 @@ import org.eclipse.core.runtime.SubMonitor; import org.eclipse.core.runtime.URIUtil; import org.eclipse.equinox.frameworkadmin.BundleInfo; +import org.eclipse.equinox.p2.metadata.IInstallableUnit; +import org.eclipse.equinox.p2.metadata.IInstallableUnitQueryable; +import org.eclipse.equinox.p2.query.IQuery; +import org.eclipse.equinox.p2.query.IQueryResult; import org.eclipse.osgi.util.NLS; import org.eclipse.pde.core.target.ITargetDefinition; import org.eclipse.pde.core.target.TargetBundle; @@ -47,7 +51,7 @@ * * @since 3.5 */ -public class ProfileBundleContainer extends AbstractBundleContainer { +public class ProfileBundleContainer extends AbstractBundleContainer implements IInstallableUnitQueryable { // The following constants are duplicated from org.eclipse.equinox.internal.p2.core.Activator private static final String CONFIG_INI = "config.ini"; //$NON-NLS-1$ @@ -418,4 +422,12 @@ public void reload() { clearResolutionStatus(); } + @Override + public IQueryResult query(IQuery query, IProgressMonitor monitor) { + if (fBundles == null || fFeatures == null) { + resolve(null, monitor); + } + return query.perform(InstallableUnitGenerator.generateInstallableUnits(fBundles, fFeatures).iterator()); + } + } diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/RepositoryBundleContainer.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/RepositoryBundleContainer.java index bf4a9cd41a2..47105b5cb98 100644 --- a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/RepositoryBundleContainer.java +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/RepositoryBundleContainer.java @@ -36,6 +36,10 @@ import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.Status; import org.eclipse.equinox.internal.p2.repository.CacheManager; +import org.eclipse.equinox.p2.metadata.IInstallableUnit; +import org.eclipse.equinox.p2.metadata.IInstallableUnitQueryable; +import org.eclipse.equinox.p2.query.IQuery; +import org.eclipse.equinox.p2.query.IQueryResult; import org.eclipse.pde.core.target.ITargetDefinition; import org.eclipse.pde.core.target.TargetBundle; import org.eclipse.pde.core.target.TargetFeature; @@ -51,7 +55,7 @@ import aQute.bnd.osgi.resource.ResourceUtils.ContentCapability; @SuppressWarnings("restriction") -public class RepositoryBundleContainer extends AbstractBundleContainer { +public class RepositoryBundleContainer extends AbstractBundleContainer implements IInstallableUnitQueryable { public static final String ATTRIBUTE_URI = "uri"; //$NON-NLS-1$ public static final String ELEMENT_REQUIRE = "require"; //$NON-NLS-1$ @@ -88,7 +92,7 @@ protected TargetBundle[] resolveBundles(ITargetDefinition definition, IProgressM } bundles.add(new TargetBundle(file)); } catch (IOException | URISyntaxException e) { - throw new CoreException(Status.error("Can't fetch bundle from " + url, e)); + throw new CoreException(Status.error("Can't fetch bundle from " + url, e)); //$NON-NLS-1$ } } return bundles.toArray(TargetBundle[]::new); @@ -120,10 +124,10 @@ public ResourcesRepository getRepository(IProgressMonitor monitor) throws CoreEx if (e instanceof RuntimeException runtime) { throw runtime; } - throw new CoreException(Status.error("Loading repository from " + location + " failed: " + e, e)); + throw new CoreException(Status.error("Loading repository from " + location + " failed: " + e, e)); //$NON-NLS-1$ //$NON-NLS-2$ } } catch (URISyntaxException e) { - throw new CoreException(Status.error("Invalid URI: " + location, e)); + throw new CoreException(Status.error("Invalid URI: " + location, e)); //$NON-NLS-1$ } } @@ -180,4 +184,15 @@ public void reload() { fResolutionStatus = null; fBundles = null; } + + @Override + public IQueryResult query(IQuery query, IProgressMonitor monitor) { + // FIXME actually we should provide the whole repository content here + // and not only what is selected by the user but current PDE + // architecture make this hard to accomplish + if (fBundles == null || fFeatures == null) { + resolve(null, monitor); + } + return query.perform(InstallableUnitGenerator.generateInstallableUnits(fBundles, fFeatures).iterator()); + } } diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/TargetReferenceBundleContainer.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/TargetReferenceBundleContainer.java index 44da8a83dc5..0f450b892d7 100644 --- a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/TargetReferenceBundleContainer.java +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/TargetReferenceBundleContainer.java @@ -15,20 +15,30 @@ package org.eclipse.pde.internal.core.target; import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.function.Predicate; +import org.eclipse.core.runtime.Adapters; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.MultiStatus; import org.eclipse.core.runtime.Status; +import org.eclipse.equinox.p2.metadata.IInstallableUnit; +import org.eclipse.equinox.p2.metadata.IInstallableUnitQueryable; +import org.eclipse.equinox.p2.query.CompoundQueryable; +import org.eclipse.equinox.p2.query.IQuery; +import org.eclipse.equinox.p2.query.IQueryResult; import org.eclipse.pde.core.target.ITargetDefinition; +import org.eclipse.pde.core.target.ITargetLocation; import org.eclipse.pde.core.target.TargetBundle; import org.eclipse.pde.core.target.TargetFeature; import org.eclipse.pde.internal.core.PDECore; -public class TargetReferenceBundleContainer extends AbstractBundleContainer { +public class TargetReferenceBundleContainer extends AbstractBundleContainer implements IInstallableUnitQueryable { public static final String ATTRIBUTE_URI = "uri"; //$NON-NLS-1$ @@ -144,4 +154,24 @@ public boolean equals(Object obj) { return uri.equals(((TargetReferenceBundleContainer) obj).getUri()); } + @SuppressWarnings("restriction") + @Override + public IQueryResult query(IQuery query, IProgressMonitor monitor) { + try { + ITargetDefinition definition = getTargetDefinition(); + synchronized (definition) { + ITargetLocation[] locations = definition.getTargetLocations(); + if (locations != null && locations.length > 0) { + List queryableLocations = Arrays.stream(locations) + .map(loc -> Adapters.adapt(loc, IInstallableUnitQueryable.class)).filter(Objects::nonNull) + .toList(); + return new CompoundQueryable<>(queryableLocations).query(query, monitor); + } + } + } catch (CoreException e) { + // we can't use this then... + } + return query.perform(Collections.emptyIterator()); + } + } \ No newline at end of file