From 0424619b551e921b374f3ad3d32247881da65079 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20L=C3=A4ubrich?= Date: Mon, 29 Apr 2024 07:50:04 +0200 Subject: [PATCH] VersionRange for root features is too tight when expanded version given Currently the (disabled by default) requirement for a root feature uses an exact version range when the feature version is expanded (e.g. Tycho) but the purpose of a root feature is that it can be updated what such a strict range makes it impossible (if activated). This now adds a new IVersionRangeAdvice that allows to modify the result of a product included requirements version range. --- .../p2/publisher/eclipse/ProductAction.java | 18 +++++++++- .../p2/publisher/AbstractPublisherAction.java | 10 ++++-- .../actions/IVersionRangeAdvice.java | 36 +++++++++++++++++++ .../p2/publisher/actions/RootIUAction.java | 9 +++++ .../publisher/actions/ProductActionTest.java | 27 ++++++++++++-- 5 files changed, 94 insertions(+), 6 deletions(-) create mode 100644 bundles/org.eclipse.equinox.p2.publisher/src/org/eclipse/equinox/p2/publisher/actions/IVersionRangeAdvice.java diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/ProductAction.java b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/ProductAction.java index f8e2f36fa0..66b25a4b97 100644 --- a/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/ProductAction.java +++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/ProductAction.java @@ -276,7 +276,7 @@ protected File getExecutablesLocation() { return null; } - protected class RootFeatureAdvice implements IFilterAdvice { + protected class RootFeatureAdvice implements IFilterAdvice, IVersionRangeAdvice { private final Collection rootFeatures; @@ -322,5 +322,21 @@ private String getFilter(IInstallableUnit unit) { } return parameter.toString(); } + + @Override + public Optional getVersionRange(String namespace, String fid) { + if (NS_FEATURE.equals(namespace)) { + for (IVersionedId featureId : rootFeatures) { + if (fid.equals(featureId.getId())) { + Version fversion = featureId.getVersion(); + if (fversion == null || Version.emptyVersion.equals(fversion)) { + return Optional.of(VersionRange.emptyRange); + } + return Optional.of(new VersionRange(fversion, true, Version.MAX_VERSION, true)); + } + } + } + return Optional.empty(); + } } } diff --git a/bundles/org.eclipse.equinox.p2.publisher/src/org/eclipse/equinox/p2/publisher/AbstractPublisherAction.java b/bundles/org.eclipse.equinox.p2.publisher/src/org/eclipse/equinox/p2/publisher/AbstractPublisherAction.java index 3e28c1b9aa..015ec88e7c 100644 --- a/bundles/org.eclipse.equinox.p2.publisher/src/org/eclipse/equinox/p2/publisher/AbstractPublisherAction.java +++ b/bundles/org.eclipse.equinox.p2.publisher/src/org/eclipse/equinox/p2/publisher/AbstractPublisherAction.java @@ -212,9 +212,7 @@ protected Collection createIURequirements(Collection filter = getFilterAdvice(next); result.add(MetadataFactory.createRequirement(IInstallableUnit.NAMESPACE_IU_ID, next.getId(), range, filter, false, false)); @@ -223,6 +221,12 @@ protected Collection createIURequirements(Collection getFilterAdvice(IVersionedId name) { if (info == null) return null; diff --git a/bundles/org.eclipse.equinox.p2.publisher/src/org/eclipse/equinox/p2/publisher/actions/IVersionRangeAdvice.java b/bundles/org.eclipse.equinox.p2.publisher/src/org/eclipse/equinox/p2/publisher/actions/IVersionRangeAdvice.java new file mode 100644 index 0000000000..8d2b69461d --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.publisher/src/org/eclipse/equinox/p2/publisher/actions/IVersionRangeAdvice.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2024 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.equinox.p2.publisher.actions; + +import java.util.Optional; +import org.eclipse.equinox.p2.metadata.IInstallableUnit; +import org.eclipse.equinox.p2.metadata.VersionRange; +import org.eclipse.equinox.p2.publisher.IPublisherAdvice; +import org.eclipse.equinox.spi.p2.publisher.PublisherHelper; + +public interface IVersionRangeAdvice extends IPublisherAdvice { + + public static final String NS_FEATURE = PublisherHelper.NAMESPACE_ECLIPSE_TYPE + "." //$NON-NLS-1$ + + PublisherHelper.TYPE_ECLIPSE_FEATURE; + public static final String NS_IU = IInstallableUnit.NAMESPACE_IU_ID; + + /** + * Returns the {@link VersionRange} for the given id in the given namespace. + * + * @param namespace the namespace in which to look for advice + * @param id the id for the item in the given namespace + * @return an {@link Optional} describing the {@link VersionRange} found. + */ + public Optional getVersionRange(String namespace, String id); + +} diff --git a/bundles/org.eclipse.equinox.p2.publisher/src/org/eclipse/equinox/p2/publisher/actions/RootIUAction.java b/bundles/org.eclipse.equinox.p2.publisher/src/org/eclipse/equinox/p2/publisher/actions/RootIUAction.java index 296572c917..92a13b821f 100644 --- a/bundles/org.eclipse.equinox.p2.publisher/src/org/eclipse/equinox/p2/publisher/actions/RootIUAction.java +++ b/bundles/org.eclipse.equinox.p2.publisher/src/org/eclipse/equinox/p2/publisher/actions/RootIUAction.java @@ -172,6 +172,15 @@ private InstallableUnitDescription createTopLevelIUDescription(Collection advice.getVersionRange(namespace, versionedId.getId()).stream()).findFirst() + .orElseGet(() -> super.getVersionRange(versionedId)); + } + private Version getVersionAdvice(String iuID) { if (versionAdvice == null) { versionAdvice = info.getAdvice(null, true, null, null, IVersionAdvice.class); diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ProductActionTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ProductActionTest.java index 19a72b7b5b..202bdb47f4 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ProductActionTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ProductActionTest.java @@ -30,6 +30,7 @@ import java.net.URI; import java.util.Arrays; import java.util.Collection; +import java.util.List; import java.util.Optional; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; @@ -42,7 +43,10 @@ import org.eclipse.equinox.p2.metadata.IRequirement; import org.eclipse.equinox.p2.metadata.ITouchpointData; import org.eclipse.equinox.p2.metadata.IUpdateDescriptor; +import org.eclipse.equinox.p2.metadata.IVersionedId; import org.eclipse.equinox.p2.metadata.Version; +import org.eclipse.equinox.p2.metadata.VersionRange; +import org.eclipse.equinox.p2.metadata.VersionedId; import org.eclipse.equinox.p2.metadata.expression.IMatchExpression; import org.eclipse.equinox.p2.publisher.AbstractPublisherAction; import org.eclipse.equinox.p2.publisher.IPublisherInfo; @@ -440,7 +444,22 @@ public void testRequiredEEAsSpecified() throws Exception { public void testInstallModeRootFeature() throws Exception { ProductFile productFile = new ProductFile( - TestData.getFile("ProductActionTest", "brandedProduct/branded.product").toString()); + TestData.getFile("ProductActionTest", "brandedProduct/branded.product").toString()) { + @Override + public List getFeatures(int options) { + if (options == ROOT_FEATURES) { + List features = super.getFeatures(options); + return features.stream().map(v -> { + // we simulate an expanded feature version here... + if ("org.example".equals(v.getId())) { + return new VersionedId(v.getId(), "1.2.3"); + } + return v; + }).toList(); + } + return super.getFeatures(options); + } + }; addContextIU("org.eclipse.platform.feature.group", "1.2.3"); addContextIU("org.example.feature.group", "1.0.0"); performProductAction(productFile); @@ -452,11 +471,15 @@ public void testInstallModeRootFeature() throws Exception { .map(IRequiredCapability.class::cast).filter(it -> it.getName().equals("org.example.feature.group")) .findFirst(); assertTrue(installModeRootFeatureRequirement.isPresent()); - IMatchExpression filter = installModeRootFeatureRequirement.get().getFilter(); + IRequiredCapability capability = installModeRootFeatureRequirement.get(); + IMatchExpression filter = capability.getFilter(); assertNotNull(filter); String filterString = filter.getParameters()[0].toString(); assertEquals("(|(branded.product.install.mode.root=true)(org.eclipse.equinox.p2.install.mode.root=true))", filterString); + VersionRange versionRange = capability.getRange(); + assertTrue("Version Range is too strict: " + versionRange, + versionRange.isIncluded(Version.parseVersion("9999"))); } public void testInstallModeRootFeatureWithFilter() throws Exception {