diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index 1635125b51..e2d57446eb 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -2,6 +2,29 @@
This page describes the noteworthy improvements provided by each release of Eclipse Tycho.
+## 4.0.3
+
+### new option to filter added repository-references when assembling a p2-repository
+
+The repository references automatically added to a assembled p2-repository (via `tycho-p2-repository-plugin`'s `addIUTargetRepositoryReferences` or `addPomRepositoryReferences`)
+can now be filtered by their location using exclusion and inclusion patterns and therefore allows more fine-grained control which references are added.
+```xml
+
+ org.eclipse.tycho
+ tycho-p2-repository-plugin
+ ${tycho-version}
+
+ ... other configuration options ...
+
+
+ https://foo.bar.org/hidden/**
+ https://foo.bar.org/secret/**
+
+ %regex[http(s)?:\/\/foo\.bar\.org\/.*]
+
+
+
+
## 4.0.2
- new option to include referenced repositories when resolving the target platform
- Add dummy parameter to prevent warnings with jgit as timestamp provider
diff --git a/tycho-its/projects/p2Repository.repositoryRef.filter/category.xml b/tycho-its/projects/p2Repository.repositoryRef.filter/category.xml
new file mode 100644
index 0000000000..6f6665dcc6
--- /dev/null
+++ b/tycho-its/projects/p2Repository.repositoryRef.filter/category.xml
@@ -0,0 +1,9 @@
+
+
+
+
+ Test Category Description
+
+
+
+
diff --git a/tycho-its/projects/p2Repository.repositoryRef.filter/pom.xml b/tycho-its/projects/p2Repository.repositoryRef.filter/pom.xml
new file mode 100644
index 0000000000..33764661d8
--- /dev/null
+++ b/tycho-its/projects/p2Repository.repositoryRef.filter/pom.xml
@@ -0,0 +1,55 @@
+
+
+ 4.0.0
+
+ 1.0.0
+ tycho-its-project.p2Repository.repositoryRef.location
+ repositoryRef.location
+ eclipse-repository
+
+
+
+ repo1
+ https://download.eclipse.org/tm4e/releases/0.8.1
+ p2
+
+
+ repo2
+ https://download.eclipse.org/lsp4e/releases/0.24.1
+ p2
+
+
+ repo3
+ https://download.eclipse.org/lsp4j/updates/releases/0.21.1
+ p2
+
+
+
+
+
+ org.eclipse.tycho
+ tycho-maven-plugin
+ ${tycho-version}
+ true
+
+
+ org.eclipse.tycho
+ tycho-p2-repository-plugin
+ ${tycho-version}
+
+ false
+ true
+
+
+ https://download.eclipse.org/lsp4e/**
+ https://download.eclipse.org/lsp4j/**
+
+ %regex[http(s)?:\/\/download\.eclipse\.org\/.*]
+
+
+
+
+
+
diff --git a/tycho-its/src/test/java/org/eclipse/tycho/test/p2Repository/RepoRefLocationP2RepositoryIntegrationTest.java b/tycho-its/src/test/java/org/eclipse/tycho/test/p2Repository/RepoRefLocationP2RepositoryIntegrationTest.java
index 401858bfb9..802954f6b7 100644
--- a/tycho-its/src/test/java/org/eclipse/tycho/test/p2Repository/RepoRefLocationP2RepositoryIntegrationTest.java
+++ b/tycho-its/src/test/java/org/eclipse/tycho/test/p2Repository/RepoRefLocationP2RepositoryIntegrationTest.java
@@ -12,92 +12,63 @@
*******************************************************************************/
package org.eclipse.tycho.test.p2Repository;
+import static org.eclipse.equinox.p2.repository.IRepository.ENABLED;
+import static org.eclipse.equinox.p2.repository.IRepository.NONE;
+import static org.eclipse.equinox.p2.repository.IRepository.TYPE_ARTIFACT;
+import static org.eclipse.equinox.p2.repository.IRepository.TYPE_METADATA;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
import java.io.File;
import java.util.List;
-import java.util.Objects;
import org.apache.maven.it.Verifier;
import org.eclipse.tycho.test.AbstractTychoIntegrationTest;
+import org.eclipse.tycho.test.util.P2RepositoryTool;
+import org.eclipse.tycho.test.util.P2RepositoryTool.RepositoryReference;
import org.eclipse.tycho.test.util.ResourceUtil;
-import org.junit.BeforeClass;
import org.junit.Test;
-import de.pdark.decentxml.Document;
-import de.pdark.decentxml.Element;
-import de.pdark.decentxml.XMLParser;
-
public class RepoRefLocationP2RepositoryIntegrationTest extends AbstractTychoIntegrationTest {
- private static Verifier verifier;
-
- private static class RepositoryReferenceData {
- private String uri;
- private String type;
- private String enabled;
-
- public RepositoryReferenceData(String uri, String type, String enabled) {
- this.uri = uri;
- this.type = type;
- this.enabled = enabled;
- }
+ @Test
+ public void testRefLocation() throws Exception {
+ Verifier verifier = getVerifier("/p2Repository.repositoryRef.location", false);
+ verifier.addCliOption("-Dtest-data-repo=" + ResourceUtil.P2Repositories.ECLIPSE_LATEST.toString());
+ verifier.executeGoal("package");
+ verifier.verifyErrorFreeLog();
- @Override
- public boolean equals(Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (getClass() != obj.getClass())
- return false;
- RepositoryReferenceData other = (RepositoryReferenceData) obj;
- return Objects.equals(enabled, other.enabled) && Objects.equals(type, other.type)
- && Objects.equals(uri, other.uri);
- }
+ P2RepositoryTool p2Repo = P2RepositoryTool.forEclipseRepositoryModule(new File(verifier.getBasedir()));
+ List allRepositoryReferences = p2Repo.getAllRepositoryReferences();
- @Override
- public String toString() {
- return "[uri=" + uri + ", type=" + type + ", enabled=" + enabled + "]";
- }
+ assertEquals(4, allRepositoryReferences.size());
+ assertThat(allRepositoryReferences,
+ containsInAnyOrder(new RepositoryReference("http://some.where", TYPE_ARTIFACT, NONE),
+ new RepositoryReference("http://some.where", TYPE_METADATA, NONE),
+ new RepositoryReference("http://some.where.else", TYPE_ARTIFACT, ENABLED),
+ new RepositoryReference("http://some.where.else", TYPE_METADATA, ENABLED)));
}
- @BeforeClass
- public static void executeBuild() throws Exception {
- verifier = new RepoRefLocationP2RepositoryIntegrationTest().getVerifier("/p2Repository.repositoryRef.location",
- false);
- verifier.addCliOption("-Dtest-data-repo=" + ResourceUtil.P2Repositories.ECLIPSE_LATEST.toString());
+ @Test
+ public void testReferenceFiltering() throws Exception {
+ // Of course it is actually a bit pointless to filter explicitly specified
+ // references, but it makes the test simple/faster instead of preparing a
+ // target-definition with IU-location so that it can be added automatically,
+ // which is the main use-case.
+ Verifier verifier = getVerifier("/p2Repository.repositoryRef.filter", false);
verifier.executeGoal("package");
verifier.verifyErrorFreeLog();
- }
- @Test
- public void testRefLocation() throws Exception {
- File target = new File(verifier.getBasedir(), "target");
- File repository = new File(target, "repository");
- File contentXml = new File(repository, "content.xml");
- assertTrue(contentXml.isFile());
- File artifactXml = new File(repository, "artifacts.xml");
- assertTrue(artifactXml.isFile());
- assertTrue(new File(target, "category.xml").isFile());
+ P2RepositoryTool p2Repo = P2RepositoryTool.forEclipseRepositoryModule(new File(verifier.getBasedir()));
+ List allRepositoryReferences = p2Repo.getAllRepositoryReferences();
- Document artifactsDocument = XMLParser.parse(contentXml);
- // See MetadataRepositoryIO.Writer#writeRepositoryReferences
- List repositories = artifactsDocument.getChild("repository").getChild("references")
- .getChildren("repository");
- assertEquals(4, repositories.size());
- List actual = repositories.stream()
- .map(e -> new RepositoryReferenceData(e.getAttributeValue("uri"), e.getAttributeValue("type"),
- e.getAttributeValue("options")))
- .toList();
- assertThat(actual,
- containsInAnyOrder(new RepositoryReferenceData("http://some.where", "1", "0"),
- new RepositoryReferenceData("http://some.where", "0", "0"),
- new RepositoryReferenceData("http://some.where.else", "1", "1"),
- new RepositoryReferenceData("http://some.where.else", "0", "1")));
+ assertEquals(4, allRepositoryReferences.size());
+ assertThat(allRepositoryReferences, containsInAnyOrder( //
+ new RepositoryReference("https://download.eclipse.org/tm4e/releases/0.8.1", TYPE_ARTIFACT, ENABLED),
+ new RepositoryReference("https://download.eclipse.org/tm4e/releases/0.8.1", TYPE_METADATA, ENABLED),
+ new RepositoryReference("https://some.where/from/category", TYPE_ARTIFACT, ENABLED),
+ new RepositoryReference("https://some.where/from/category", TYPE_METADATA, ENABLED)));
}
}
diff --git a/tycho-its/src/test/java/org/eclipse/tycho/test/p2Repository/RepositoryIncludeTest.java b/tycho-its/src/test/java/org/eclipse/tycho/test/p2Repository/RepositoryIncludeTest.java
index d19c5035e3..97707c1529 100644
--- a/tycho-its/src/test/java/org/eclipse/tycho/test/p2Repository/RepositoryIncludeTest.java
+++ b/tycho-its/src/test/java/org/eclipse/tycho/test/p2Repository/RepositoryIncludeTest.java
@@ -31,7 +31,7 @@ public void testFilterProvided() throws Exception {
P2RepositoryTool p2Repo = P2RepositoryTool
.forEclipseRepositoryModule(new File(verifier.getBasedir(), "repository"));
p2Repo.getUniqueIU("bundle");
- p2Repo.assertNumberOfUnits(1, u -> u.id.equals("a.jre.javase") || u.id.endsWith(".test.category"));
+ p2Repo.assertNumberOfUnits(1, u -> u.id().equals("a.jre.javase") || u.id().endsWith(".test.category"));
assertTrue("Bundle artifact not found!", p2Repo.findBundleArtifact("bundle").isPresent());
p2Repo.assertNumberOfBundles(1);
p2Repo.assertNumberOfFeatures(0);
diff --git a/tycho-p2-repository-plugin/src/main/java/org/eclipse/tycho/plugins/p2/repository/AssembleRepositoryMojo.java b/tycho-p2-repository-plugin/src/main/java/org/eclipse/tycho/plugins/p2/repository/AssembleRepositoryMojo.java
index bf4c13b3f3..c3160e3cd2 100644
--- a/tycho-p2-repository-plugin/src/main/java/org/eclipse/tycho/plugins/p2/repository/AssembleRepositoryMojo.java
+++ b/tycho-p2-repository-plugin/src/main/java/org/eclipse/tycho/plugins/p2/repository/AssembleRepositoryMojo.java
@@ -19,6 +19,8 @@
import java.util.Collection;
import java.util.List;
import java.util.Map;
+import java.util.function.Predicate;
+import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.apache.maven.model.Repository;
@@ -29,6 +31,7 @@
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.codehaus.plexus.util.FileUtils;
+import org.codehaus.plexus.util.MatchPatterns;
import org.eclipse.tycho.PackagingType;
import org.eclipse.tycho.ReactorProject;
import org.eclipse.tycho.TychoConstants;
@@ -73,6 +76,12 @@
*/
@Mojo(name = "assemble-repository", defaultPhase = LifecyclePhase.PACKAGE, threadSafe = true)
public class AssembleRepositoryMojo extends AbstractRepositoryMojo {
+
+ public static class RepositoryReferenceFilter {
+ List exclude;
+ List include;
+ }
+
private static final Object LOCK = new Object();
/**
*
@@ -217,6 +226,40 @@ public class AssembleRepositoryMojo extends AbstractRepositoryMojo {
@Parameter()
private boolean addIUTargetRepositoryReferences;
+ /**
+ * A list of patterns to filter the automatically derived repository references by including or
+ * excluding their location to control if they are eventually added to the assembled repository.
+ *
+ * Each pattern is either an inclusion or exclusion and an arbitrary number of
+ * each can be specified. The location of a repository reference must match at least one
+ * inclusion-pattern (if any is specified) and must not be match by any
+ * exclusion-pattern, in order to be eventually added to the assembled repository.
+ * The specified filters are only applied to those repository references derived from the
+ * target-definition or pom file, when {@link #addIUTargetRepositoryReferences} respectively
+ * {@link #addPomRepositoryReferences} is set to {@code true}.
+ *
+ *
+ * Configuration example
+ *
+ *
+ * <repositoryReferenceFilter>
+ * <exclude>
+ * <location>https://foo.bar.org/hidden/**</location>
+ * <location>https://foo.bar.org/secret/**</location>
+ * </exclude>
+ * <include>%regex[http(s)?:\/\/foo\.bar\.org\/.*]</include>
+ * </repositoryReferenceFilter>
+ *
+ *
+ * It contains two exclusion patterns using {@code ANT}-style syntax and one
+ * inclusion using a {@code Java RegEx} {@link Pattern} (enclosed in
+ * {@code %regex[]}). The inclusion pattern uses the shorthand
+ * notation for singleton lists.
+ *
+ */
+ @Parameter
+ private RepositoryReferenceFilter repositoryReferenceFilter = null;
+
/**
* If enabled, an
* OSGi
@@ -265,11 +308,14 @@ public void execute() throws MojoExecutionException, MojoFailureException {
.flatMap(List::stream)//
.map(ref -> new RepositoryReference(ref.getName(), ref.getLocation(), ref.isEnabled()))//
.collect(Collectors.toCollection(ArrayList::new));
+ Predicate autoReferencesFilter = buildRepositoryReferenceLocationFilter();
if (addPomRepositoryReferences) {
for (Repository pomRepo : getProject().getRepositories()) {
if ("p2".equals(pomRepo.getLayout())) {
- repositoryReferences
- .add(new RepositoryReference(pomRepo.getName(), pomRepo.getUrl(), true));
+ String locationURL = pomRepo.getUrl();
+ if (autoReferencesFilter.test(locationURL)) {
+ repositoryReferences.add(new RepositoryReference(pomRepo.getName(), locationURL, true));
+ }
}
}
}
@@ -278,9 +324,11 @@ public void execute() throws MojoExecutionException, MojoFailureException {
.getTargetPlatformConfiguration(getProject()).getTargets()) {
for (Location location : targetDefinitionFile.getLocations()) {
if (location instanceof InstallableUnitLocation iu) {
- for (org.eclipse.tycho.targetplatform.TargetDefinition.Repository iuRepo : iu
- .getRepositories()) {
- repositoryReferences.add(new RepositoryReference(null, iuRepo.getLocation(), true));
+ for (var iuRepo : iu.getRepositories()) {
+ String locationURL = iuRepo.getLocation();
+ if (autoReferencesFilter.test(locationURL)) {
+ repositoryReferences.add(new RepositoryReference(null, locationURL, true));
+ }
}
}
}
@@ -351,4 +399,20 @@ private List getCategories(final File categoriesDirectory) {
return eclipseRepositoryProject.loadCategories(categoriesDirectory);
}
+ private Predicate buildRepositoryReferenceLocationFilter() {
+ Predicate filter = l -> true;
+ if (repositoryReferenceFilter != null) {
+ if (repositoryReferenceFilter.include != null && !repositoryReferenceFilter.include.isEmpty()) {
+ MatchPatterns inclusionPattern = MatchPatterns.from(repositoryReferenceFilter.include);
+ filter = l -> inclusionPattern.matches(l, true);
+ }
+ if (repositoryReferenceFilter.exclude != null && !repositoryReferenceFilter.exclude.isEmpty()) {
+ MatchPatterns exclusionPattern = MatchPatterns.from(repositoryReferenceFilter.exclude);
+ Predicate exclusionFilter = l -> !exclusionPattern.matches(l, true);
+ filter = filter.and(exclusionFilter);
+ }
+ }
+ return filter;
+ }
+
}
diff --git a/tycho-testing-harness/src/main/java/org/eclipse/tycho/test/util/P2RepositoryTool.java b/tycho-testing-harness/src/main/java/org/eclipse/tycho/test/util/P2RepositoryTool.java
index 3d27c89ee2..d4ecba057d 100644
--- a/tycho-testing-harness/src/main/java/org/eclipse/tycho/test/util/P2RepositoryTool.java
+++ b/tycho-testing-harness/src/main/java/org/eclipse/tycho/test/util/P2RepositoryTool.java
@@ -7,11 +7,11 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-import java.util.Objects;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
+import java.util.stream.IntStream;
import java.util.stream.Stream;
import javax.xml.parsers.DocumentBuilderFactory;
@@ -28,7 +28,7 @@
public class P2RepositoryTool {
- private static final ThreadLocal xPathTool = ThreadLocal
+ private static final ThreadLocal XPATH_TOOL = ThreadLocal
.withInitial(() -> XPathFactory.newInstance().newXPath());
private static final Pattern strictVersionRangePattern = Pattern.compile("\\[([^,]*),\\1\\]");
private final File repoLocation;
@@ -213,6 +213,21 @@ public List getAllProvidedPackages() throws Exception {
return getValues(contentXml, "/repository/units/unit/provides/provided[@namespace='java.package']/@name");
}
+ public List getAllRepositoryReferences() throws Exception {
+ loadMetadata();
+ // See MetadataRepositoryIO.Writer#writeRepositoryReferences
+ List references = getNodes(contentXml, "/repository/references/repository");
+ List result = new ArrayList<>();
+ for (Node reference : references) {
+ String uri = getAttribute(reference, "@uri");
+ int type = Integer.parseInt(getAttribute(reference, "@type"));
+ int options = Integer.parseInt(getAttribute(reference, "@options"));
+ result.add(new RepositoryReference(uri, type, options));
+ }
+
+ return result;
+ }
+
private void loadMetadata() throws Exception {
if (contentXml != null)
return;
@@ -223,27 +238,17 @@ private void loadMetadata() throws Exception {
}
private static XPath getXPathTool() {
- return xPathTool.get();
+ return XPATH_TOOL.get();
}
static List getNodes(Object startingPoint, String expression) throws XPathExpressionException {
NodeList nodeList = (NodeList) getXPathTool().evaluate(expression, startingPoint, XPathConstants.NODESET);
- List result = new ArrayList<>(nodeList.getLength());
- for (int ix = 0; ix < nodeList.getLength(); ++ix) {
- result.add(nodeList.item(ix));
- }
- return result;
+ return IntStream.range(0, nodeList.getLength()).mapToObj(nodeList::item).toList();
}
static List getValues(Object startingPoint, String expression) throws XPathExpressionException {
- NodeList nodeList = (NodeList) getXPathTool().evaluate(expression, startingPoint, XPathConstants.NODESET);
-
- List result = new ArrayList<>(nodeList.getLength());
- for (int ix = 0; ix < nodeList.getLength(); ++ix) {
- result.add(nodeList.item(ix).getNodeValue());
- }
- return result;
+ return getNodes(startingPoint, expression).stream().map(Node::getNodeValue).toList();
}
static String getAttribute(Node node, String expression) throws XPathExpressionException {
@@ -300,14 +305,7 @@ public List getProperties() throws Exception {
}
public List getRequiredIds() throws Exception {
- List result = new ArrayList<>();
-
- List requiredIds = getNodes(unitElement, "requires/required/@name");
- for (Node id : requiredIds) {
- result.add(id.getNodeValue());
- }
-
- return result;
+ return getValues(unitElement, "requires/required/@name");
}
/**
@@ -371,33 +369,10 @@ public List getProvidedCapabilities() throws Exception {
}
}
- public static final class IdAndVersion {
- public final String id;
- public final String version;
-
- public IdAndVersion(String id, String version) {
- this.id = id;
- this.version = version;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(id, version);
- }
-
- @Override
- public boolean equals(Object obj) {
- return this == obj || //
- (obj instanceof IdAndVersion other && //
- Objects.equals(id, other.id) && //
- Objects.equals(version, other.version));
- }
-
- @Override
- public String toString() {
- return "id=" + id + ", version=" + version;
- }
+ public static final record IdAndVersion(String id, String version) {
+ }
+ public static final record RepositoryReference(String uri, int type, int options) {
}
public static IdAndVersion withIdAndVersion(String id, String version) {