diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index b221efd393..0e0f5a96d8 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -6,6 +6,25 @@ If you are reading this in the browser, then you can quickly jump to specific ve
## 5.0.0 (under development)
+## Support for version ranges and no version for units in target definitions
+
+In target definitions Tycho now supports to use a range as version of a unit or to skip the version entirely in `InstallableUnit` locations, just like Eclipse-PDE.
+Specifying no version is equivalent to `0.0.0` which resolves to the latest version available.
+All of the following variants to specify a version are now possible:
+```
+
+
+
+
+
+
+
+
+
+
+
+```
+
## new `update-manifest` mojo
It is recommended to use as the lower bound the dependency the code was
diff --git a/tycho-core/src/main/java/org/eclipse/tycho/p2resolver/InstallableUnitResolver.java b/tycho-core/src/main/java/org/eclipse/tycho/p2resolver/InstallableUnitResolver.java
index 6edba2fa89..68e424901c 100644
--- a/tycho-core/src/main/java/org/eclipse/tycho/p2resolver/InstallableUnitResolver.java
+++ b/tycho-core/src/main/java/org/eclipse/tycho/p2resolver/InstallableUnitResolver.java
@@ -275,7 +275,7 @@ private static IInstallableUnit findUnits(Unit unitReference, IQueryable findUnit(Unit unitReference, IQueryable units)
throws TargetDefinitionSyntaxException {
- Version version = parseVersion(unitReference);
+ VersionRange version = parseVersion(unitReference);
// the createIUQuery treats 0.0.0 version as "any version", and all other versions as exact versions
IQuery matchingIUQuery = QueryUtil.createIUQuery(unitReference.getId(), version);
@@ -285,12 +285,20 @@ private static IQueryResult findUnit(Unit unitReference, IQuer
return queryResult;
}
- private static Version parseVersion(Unit unitReference) throws TargetDefinitionSyntaxException {
+ private static VersionRange parseVersion(Unit unitReference) throws TargetDefinitionSyntaxException {
+ String version = unitReference.getVersion();
try {
- return Version.parseVersion(unitReference.getVersion());
+ if ("0.0.0".equals(version)) {
+ return VersionRange.emptyRange;
+ } else if (version.contains(",")) { // a real version range
+ return VersionRange.create(version);
+ } else { // an explicit/exact version -> create strict version range
+ Version v = Version.parseVersion(version);
+ return new VersionRange(v, true, v, true);
+ }
} catch (IllegalArgumentException e) {
- throw new TargetDefinitionSyntaxException(NLS.bind("Cannot parse version \"{0}\" of unit \"{1}\"",
- unitReference.getVersion(), unitReference.getId()), e);
+ throw new TargetDefinitionSyntaxException(
+ NLS.bind("Cannot parse version \"{0}\" of unit \"{1}\"", version, unitReference.getId()), e);
}
}
diff --git a/tycho-core/src/test/java/org/eclipse/tycho/p2resolver/TargetPlatformFactoryTest.java b/tycho-core/src/test/java/org/eclipse/tycho/p2resolver/TargetPlatformFactoryTest.java
index 1fa142563c..9228f24ba1 100644
--- a/tycho-core/src/test/java/org/eclipse/tycho/p2resolver/TargetPlatformFactoryTest.java
+++ b/tycho-core/src/test/java/org/eclipse/tycho/p2resolver/TargetPlatformFactoryTest.java
@@ -42,6 +42,7 @@
import org.eclipse.equinox.p2.core.IProvisioningAgent;
import org.eclipse.equinox.p2.metadata.IInstallableUnit;
import org.eclipse.equinox.p2.metadata.IVersionedId;
+import org.eclipse.equinox.p2.metadata.VersionedId;
import org.eclipse.tycho.ArtifactType;
import org.eclipse.tycho.DefaultArtifactKey;
import org.eclipse.tycho.IDependencyMetadata.DependencyMetadataType;
@@ -298,7 +299,7 @@ public void testDuplicateReactorUnits() throws Exception {
@Ignore("This test don't work because maven provides a 'not real' local repo to the test")
public void testMavenArtifactsInTargetDefinitionResolveToMavenPath() throws Exception {
File targetDefinition = resourceFile("targetresolver/mavenDep.target");
- tpConfig.getTargetDefinitions().add(TargetDefinitionFile.read(targetDefinition));
+ tpConfig.addTargetDefinition(TargetDefinitionFile.read(targetDefinition));
P2TargetPlatform targetPlatform = subject.createTargetPlatform(tpConfig, NOOP_EE_RESOLUTION_HANDLER, List.of());
File artifactLocation = targetPlatform.getArtifactLocation(
new DefaultArtifactKey(ArtifactType.TYPE_ECLIPSE_PLUGIN, "org.apache.commons.logging", "1.2.0"));
@@ -307,6 +308,17 @@ public void testMavenArtifactsInTargetDefinitionResolveToMavenPath() throws Exce
assertTrue(p2ArtifactPath.startsWith(localM2Repo));
}
+ @Test
+ public void testUnitsWithVersionRangeAndNoVersionInTargetDefinition() throws Exception {
+ File targetDefinition = resourceFile("targetresolver/versionRanges.target");
+ tpConfig.addTargetDefinition(TargetDefinitionFile.read(targetDefinition));
+ P2TargetPlatform tp = subject.createTargetPlatform(tpConfig, NOOP_EE_RESOLUTION_HANDLER, List.of());
+ Set ius = tp.getInstallableUnits();
+ assertThat(ius, hasItem(unitWithIdAndVersion(new VersionedId("jakarta.activation-api", "2.1.3"))));
+ assertThat(tp.getInstallableUnits(),
+ hasItem(unitWithIdAndVersion(new VersionedId("jakarta.inject.jakarta.inject-api", "1.0.5"))));
+ }
+
private static TargetDefinition plannerTargetDefinition(TestRepositories repository, IVersionedId unit) {
TargetDefinition.Location location = new TargetDefinitionResolverIncludeModeTest.PlannerLocationStub(repository,
unit);
diff --git a/tycho-core/src/test/resources/targetresolver/versionRanges.target b/tycho-core/src/test/resources/targetresolver/versionRanges.target
new file mode 100644
index 0000000000..2e50d0f9a6
--- /dev/null
+++ b/tycho-core/src/test/resources/targetresolver/versionRanges.target
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tycho-targetplatform/src/main/java/org/eclipse/tycho/targetplatform/TargetDefinitionFile.java b/tycho-targetplatform/src/main/java/org/eclipse/tycho/targetplatform/TargetDefinitionFile.java
index 383456df63..5864add332 100644
--- a/tycho-targetplatform/src/main/java/org/eclipse/tycho/targetplatform/TargetDefinitionFile.java
+++ b/tycho-targetplatform/src/main/java/org/eclipse/tycho/targetplatform/TargetDefinitionFile.java
@@ -525,6 +525,9 @@ private static IULocation parseIULocation(Element dom) {
for (Element unitDom : getChildren(dom, "unit")) {
String id = unitDom.getAttribute("id");
String version = unitDom.getAttribute("version");
+ if (version == null || version.isBlank()) {
+ version = "0.0.0";
+ }
units.add(new Unit(id, version));
}
final List repositories = new ArrayList<>();