diff --git a/tycho-core/src/main/java/org/eclipse/m2e/pde/target/shared/MavenBundleWrapper.java b/tycho-core/src/main/java/org/eclipse/m2e/pde/target/shared/MavenBundleWrapper.java
index 4bf026ec04..4b3a5c77d4 100644
--- a/tycho-core/src/main/java/org/eclipse/m2e/pde/target/shared/MavenBundleWrapper.java
+++ b/tycho-core/src/main/java/org/eclipse/m2e/pde/target/shared/MavenBundleWrapper.java
@@ -39,11 +39,14 @@
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.collection.CollectRequest;
import org.eclipse.aether.graph.Dependency;
+import org.eclipse.aether.graph.DependencyFilter;
import org.eclipse.aether.graph.DependencyNode;
import org.eclipse.aether.graph.DependencyVisitor;
+import org.eclipse.aether.impl.SyncContextFactory;
import org.eclipse.aether.repository.RemoteRepository;
+import org.eclipse.aether.resolution.ArtifactRequest;
+import org.eclipse.aether.resolution.ArtifactResolutionException;
import org.eclipse.aether.resolution.DependencyRequest;
-import org.eclipse.aether.spi.synccontext.SyncContextFactory;
import org.eclipse.core.runtime.Platform;
import org.eclipse.m2e.pde.target.shared.ProcessingMessage.Type;
import org.osgi.framework.Constants;
@@ -53,206 +56,248 @@
import aQute.bnd.version.Version;
/**
- * The {@link MavenBundleWrapper} handle the hard part of the target support
- * that is wrapping an existing jar into a bundle that is:
+ * The {@link MavenBundleWrapper} handle the hard part of the target support that is wrapping an
+ * existing jar into a bundle that is:
*
* - Find all dependencies of an artifact
* - For each dependency check if it also needs wrapping
- * - Depending on the target and used instructions, the wrapping might be
- * different
- * -
- *
- The code is generic enough so we can have the exact same implementation
- * at Tycho, e.g. we only use maven, BND and java API but nothing from m2e!
+ * - Depending on the target and used instructions, the wrapping might be different
+ * -
+ *
- The code is generic enough so we can have the exact same implementation at Tycho, e.g. we
+ * only use maven, BND and java API but nothing from m2e!
*
*/
public class MavenBundleWrapper {
- private MavenBundleWrapper() {
- }
+ private MavenBundleWrapper() {
+ }
- /**
- * Wraps an artifact (and possible its dependents if required) to produce a
- * manifest with OSGi metadata.
- *
- * @param artifact the artifact to wrap
- * @param instructionsLookup a lookup for bnd instructions
- * @param repositories the repositories that should be used to resolve
- * dependencies
- * @param repoSystem the repository system for lookup dependent items
- * @param repositorySession the session to use
- * @param syncContextFactory the sync context factory to acquire exclusive
- * access to the wrapped artifact and its dependencies
- * @return the wrapped artifact
- * @throws Exception if wrapping the artifact fails for any reason
- */
- public static WrappedBundle getWrappedArtifact(Artifact artifact,
- Function instructionsLookup, List repositories,
- RepositorySystem repoSystem, RepositorySystemSession repositorySession,
- SyncContextFactory syncContextFactory) throws Exception {
- CollectRequest collectRequest = new CollectRequest();
- collectRequest.setRoot(new Dependency(artifact, null));
- collectRequest.setRepositories(repositories);
- DependencyNode node = repoSystem.collectDependencies(repositorySession, collectRequest).getRoot();
+ /**
+ * Wraps an artifact (and possible its dependents if required) to produce a manifest with OSGi
+ * metadata.
+ *
+ * @param artifact
+ * the artifact to wrap
+ * @param instructionsLookup
+ * a lookup for bnd instructions
+ * @param repositories
+ * the repositories that should be used to resolve dependencies
+ * @param repoSystem
+ * the repository system for lookup dependent items
+ * @param repositorySession
+ * the session to use
+ * @param syncContextFactory
+ * the sync context factory to acquire exclusive access to the wrapped artifact and
+ * its dependencies
+ * @return the wrapped artifact
+ * @throws Exception
+ * if wrapping the artifact fails for any reason
+ */
+ public static WrappedBundle getWrappedArtifact(Artifact artifact,
+ Function instructionsLookup, List repositories,
+ RepositorySystem repoSystem, RepositorySystemSession repositorySession,
+ SyncContextFactory syncContextFactory) throws Exception {
+ CollectRequest collectRequest = new CollectRequest();
+ collectRequest.setRoot(new Dependency(artifact, null));
+ collectRequest.setRepositories(repositories);
+ DependencyNode node = repoSystem.collectDependencies(repositorySession, collectRequest).getRoot();
- DependencyRequest dependencyRequest = new DependencyRequest();
- dependencyRequest.setRoot(node);
- repoSystem.resolveDependencies(repositorySession, dependencyRequest);
+ DependencyRequest dependencyRequest = new DependencyRequest();
+ dependencyRequest.setRoot(node);
+ dependencyRequest.setFilter(new DependencyFilter() {
- try (SyncContext syncContext = syncContextFactory.newInstance(repositorySession, false)) {
- Set lockList = new HashSet<>();
- node.accept(new DependencyVisitor() {
+ @Override
+ public boolean accept(DependencyNode node, List parents) {
+ ArtifactRequest request = new ArtifactRequest();
+ request.setRepositories(repositories);
+ Artifact nodeArtifact = node.getArtifact();
+ request.setArtifact(nodeArtifact);
+ try {
+ repoSystem.resolveArtifact(repositorySession, request);
+ } catch (ArtifactResolutionException e) {
+ return false;
+ }
+ return true;
+ }
+ });
+ repoSystem.resolveDependencies(repositorySession, dependencyRequest);
- @Override
- public boolean visitLeave(DependencyNode n) {
- return true;
- }
+ try (SyncContext syncContext = syncContextFactory.newInstance(repositorySession, false)) {
+ Set lockList = new HashSet<>();
+ node.accept(new DependencyVisitor() {
- @Override
- public boolean visitEnter(DependencyNode n) {
- lockList.add(n.getArtifact());
- return true;
- }
- });
- syncContext.acquire(lockList, null);
- Map visited = new HashMap<>();
- WrappedBundle wrappedNode = getWrappedNode(node, instructionsLookup, visited);
- for (WrappedBundle wrap : visited.values()) {
- wrap.getJar().close();
- }
- return wrappedNode;
- }
- }
+ @Override
+ public boolean visitLeave(DependencyNode n) {
+ return true;
+ }
- private static WrappedBundle getWrappedNode(DependencyNode node,
- Function instructionsLookup, Map visited)
- throws Exception {
- WrappedBundle wrappedNode = visited.get(node);
- if (wrappedNode != null) {
- return wrappedNode;
- }
- Artifact artifact = node.getArtifact();
- File originalFile = artifact.getFile();
- Jar jar = new Jar(originalFile);
- Manifest originalManifest = jar.getManifest();
- if (originalManifest != null
- && originalManifest.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME) != null) {
- // already a bundle!
- visited.put(node,
- wrappedNode = new WrappedBundle(node, List.of(), null, originalFile.toPath(), jar, List.of()));
- return wrappedNode;
- }
- List children = node.getChildren();
- List depends = new ArrayList<>();
- for (DependencyNode child : children) {
- depends.add(getWrappedNode(child, instructionsLookup, visited));
- }
- WrappedBundle wrappedNodeAfterVisit = visited.get(node);
- if (wrappedNodeAfterVisit != null) {
- return wrappedNodeAfterVisit;
- }
- Properties instructions = instructionsLookup.apply(node);
- String key = getInstructionsKey(instructions, depends);
- try (Jar analyzerJar = jar) {
- // now we know the key and the depends we enter the critical section of checking
- // if the data is already there or needs to be refreshed
- File parent = new File(originalFile.getParent(), "bnd-" + key);
- File wrapArtifactFile = new File(parent, originalFile.getName());
- Jar cached = getCachedJar(wrapArtifactFile.toPath(), originalFile.toPath());
- if (cached == null) {
- List messages = new ArrayList<>();
- wrapArtifactFile.getParentFile().mkdirs();
- try (Analyzer analyzer = new Analyzer(analyzerJar);) {
- analyzer.setProperty("mvnGroupId", artifact.getGroupId());
- analyzer.setProperty("mvnArtifactId", artifact.getArtifactId());
- analyzer.setProperty("mvnVersion", artifact.getBaseVersion());
- analyzer.setProperty("mvnClassifier", artifact.getClassifier());
- String versionString = createOSGiVersion(artifact).toString();
- analyzer.setProperty("generatedOSGiVersion", versionString);
- for (String property : instructions.stringPropertyNames()) {
- // See https://github.com/bndtools/bnd/issues/5659
- String trimValue = instructions.getProperty(property).trim();
- analyzer.setProperty(property, trimValue);
- }
- for (WrappedBundle dep : depends) {
- analyzer.addClasspath(dep.getJar());
- analyzer.removeClose(dep.getJar());
- }
- analyzerJar.setManifest(analyzer.calcManifest());
- analyzerJar.write(wrapArtifactFile);
- for (String err : analyzer.getErrors()) {
- if (err.contains("Classes found in the wrong directory")) {
- // ignore message from BND not supporting MR jars...
- continue;
- }
- messages.add(new ProcessingMessage(artifact, Type.ERROR, err));
- }
- for (String warn : analyzer.getWarnings()) {
- messages.add(new ProcessingMessage(artifact, Type.WARN, warn));
- }
- }
- wrapArtifactFile.setLastModified(originalFile.lastModified());
- visited.put(node, wrappedNode = new WrappedBundle(node, depends, key, wrapArtifactFile.toPath(),
- new Jar(wrapArtifactFile), messages));
- } else {
- visited.put(node, wrappedNode = new WrappedBundle(node, depends, key, wrapArtifactFile.toPath(),
- new Jar(wrapArtifactFile), List.of()));
- }
- return wrappedNode;
- }
- }
+ @Override
+ public boolean visitEnter(DependencyNode n) {
+ lockList.add(n.getArtifact());
+ return true;
+ }
+ });
+ syncContext.acquire(lockList, null);
+ Map visited = new HashMap<>();
+ WrappedBundle wrappedNode = getWrappedNode(node, instructionsLookup, visited);
+ for (WrappedBundle wrap : visited.values()) {
+ Jar jar = wrap.getJar();
+ if (jar != null) {
+ jar.close();
+ }
+ }
+ return wrappedNode;
+ }
+ }
- private static Jar getCachedJar(Path cacheFile, Path sourceFile) {
- try {
- if (!isOutdated(cacheFile, sourceFile)) {
- return new Jar(cacheFile.toFile());
- }
- } catch (IOException e) {
- // if any I/O error occurs we assume we need to regenerate the data...
- Platform.getLog(MavenBundleWrapper.class)
- .error("Reading cached data for " + cacheFile + " failed, will regenerate the data ...", e);
- }
- return null;
- }
+ private static WrappedBundle getWrappedNode(DependencyNode node,
+ Function instructionsLookup, Map visited)
+ throws Exception {
+ WrappedBundle wrappedNode = visited.get(node);
+ if (wrappedNode != null) {
+ return wrappedNode;
+ }
+ Artifact artifact = node.getArtifact();
+ File originalFile = artifact.getFile();
+ if (originalFile == null) {
+ if (node.getDependency().isOptional()) {
+ visited.put(node,
+ wrappedNode = new WrappedBundle(node, List.of(), null, null, null,
+ List.of(new ProcessingMessage(artifact, Type.WARN,
+ "Optional artifact " + node.getArtifact() + " was not found"))));
+ } else {
+ visited.put(node, wrappedNode = new WrappedBundle(node, List.of(), null, null, null, List.of(
+ new ProcessingMessage(artifact, Type.ERROR, "Artifact " + node.getArtifact() + " not found"))));
+ }
+ return wrappedNode;
+ }
+ Jar jar = new Jar(originalFile);
+ Manifest originalManifest = jar.getManifest();
+ if (originalManifest != null
+ && originalManifest.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME) != null) {
+ // already a bundle!
+ visited.put(node,
+ wrappedNode = new WrappedBundle(node, List.of(), null, originalFile.toPath(), jar, List.of()));
+ return wrappedNode;
+ }
+ List children = node.getChildren();
+ List depends = new ArrayList<>();
+ for (DependencyNode child : children) {
+ depends.add(getWrappedNode(child, instructionsLookup, visited));
+ }
+ WrappedBundle wrappedNodeAfterVisit = visited.get(node);
+ if (wrappedNodeAfterVisit != null) {
+ return wrappedNodeAfterVisit;
+ }
+ Properties instructions = instructionsLookup.apply(node);
+ String key = getInstructionsKey(instructions, depends);
+ try (Jar analyzerJar = jar) {
+ // now we know the key and the depends we enter the critical section of checking
+ // if the data is already there or needs to be refreshed
+ File parent = new File(originalFile.getParent(), "bnd-" + key);
+ File wrapArtifactFile = new File(parent, originalFile.getName());
+ Jar cached = getCachedJar(wrapArtifactFile.toPath(), originalFile.toPath());
+ if (cached == null) {
+ List messages = new ArrayList<>();
+ wrapArtifactFile.getParentFile().mkdirs();
+ try (Analyzer analyzer = new Analyzer(analyzerJar);) {
+ analyzer.setProperty("mvnGroupId", artifact.getGroupId());
+ analyzer.setProperty("mvnArtifactId", artifact.getArtifactId());
+ analyzer.setProperty("mvnVersion", artifact.getBaseVersion());
+ analyzer.setProperty("mvnClassifier", artifact.getClassifier());
+ String versionString = createOSGiVersion(artifact).toString();
+ analyzer.setProperty("generatedOSGiVersion", versionString);
+ for (String property : instructions.stringPropertyNames()) {
+ // See https://github.com/bndtools/bnd/issues/5659
+ String trimValue = instructions.getProperty(property).trim();
+ analyzer.setProperty(property, trimValue);
+ }
+ for (WrappedBundle dep : depends) {
+ Jar depJar = dep.getJar();
+ if (depJar == null) {
+ messages.add(new ProcessingMessage(artifact, Type.WARN,
+ "Dependency " + dep.getNode().getDependency() + " was ignored!"));
+ continue;
+ }
+ analyzer.addClasspath(depJar);
+ analyzer.removeClose(depJar);
+ }
+ analyzerJar.setManifest(analyzer.calcManifest());
+ analyzerJar.write(wrapArtifactFile);
+ for (String err : analyzer.getErrors()) {
+ if (err.contains("Classes found in the wrong directory")) {
+ // ignore message from BND not supporting MR jars...
+ continue;
+ }
+ messages.add(new ProcessingMessage(artifact, Type.ERROR, err));
+ }
+ for (String warn : analyzer.getWarnings()) {
+ messages.add(new ProcessingMessage(artifact, Type.WARN, warn));
+ }
+ }
+ wrapArtifactFile.setLastModified(originalFile.lastModified());
+ visited.put(node, wrappedNode = new WrappedBundle(node, depends, key, wrapArtifactFile.toPath(),
+ new Jar(wrapArtifactFile), messages));
+ } else {
+ visited.put(node, wrappedNode = new WrappedBundle(node, depends, key, wrapArtifactFile.toPath(),
+ new Jar(wrapArtifactFile), List.of()));
+ }
+ return wrappedNode;
+ }
+ }
- private static String getInstructionsKey(Properties properties, List depends) {
- Stream instructionsStream = properties == null ? Stream.empty()
- : properties.stringPropertyNames().stream().sorted(String.CASE_INSENSITIVE_ORDER)
- .map(key -> key.toLowerCase() + ":" + properties.getProperty(key));
- Stream dependsStream = depends.stream().map(WrappedBundle::getInstructionsKey).filter(Objects::nonNull)
- .sorted(String.CASE_INSENSITIVE_ORDER).distinct();
- String string = Stream.concat(instructionsStream, dependsStream).collect(Collectors.joining("#"));
- return DigestUtils.md5Hex(string);
- }
+ private static Jar getCachedJar(Path cacheFile, Path sourceFile) {
+ try {
+ if (!isOutdated(cacheFile, sourceFile)) {
+ return new Jar(cacheFile.toFile());
+ }
+ } catch (IOException e) {
+ // if any I/O error occurs we assume we need to regenerate the data...
+ Platform.getLog(MavenBundleWrapper.class)
+ .error("Reading cached data for " + cacheFile + " failed, will regenerate the data ...", e);
+ }
+ return null;
+ }
- public static Version createOSGiVersion(Artifact artifact) {
- String version = artifact.getVersion();
- return createOSGiVersion(version);
- }
+ private static String getInstructionsKey(Properties properties, List depends) {
+ Stream instructionsStream = properties == null ? Stream.empty()
+ : properties.stringPropertyNames().stream().sorted(String.CASE_INSENSITIVE_ORDER)
+ .map(key -> key.toLowerCase() + ":" + properties.getProperty(key));
+ Stream dependsStream = depends.stream().map(WrappedBundle::getInstructionsKey).filter(Objects::nonNull)
+ .sorted(String.CASE_INSENSITIVE_ORDER).distinct();
+ String string = Stream.concat(instructionsStream, dependsStream).collect(Collectors.joining("#"));
+ return DigestUtils.md5Hex(string);
+ }
- public static Version createOSGiVersion(Model model) {
- return createOSGiVersion(model.getVersion());
- }
+ public static Version createOSGiVersion(Artifact artifact) {
+ String version = artifact.getVersion();
+ return createOSGiVersion(version);
+ }
- private static final Pattern DASH = Pattern.compile("-");
+ public static Version createOSGiVersion(Model model) {
+ return createOSGiVersion(model.getVersion());
+ }
- public static Version createOSGiVersion(String version) {
- if (version == null || version.isEmpty()) {
- return new Version(0, 0, 1);
- }
- try {
- version = DASH.matcher(version).replaceFirst(".");
- return Version.parseVersion(version);
- } catch (IllegalArgumentException e) {
- return new Version(0, 0, 1, version);
- }
- }
+ private static final Pattern DASH = Pattern.compile("-");
- public static boolean isOutdated(Path cacheFile, Path sourceFile) throws IOException {
- if (Files.exists(cacheFile)) {
- FileTime sourceTimeStamp = Files.getLastModifiedTime(sourceFile);
- FileTime cacheTimeStamp = Files.getLastModifiedTime(cacheFile);
- return sourceTimeStamp.compareTo(cacheTimeStamp) > 0;
- }
- return true;
- }
+ public static Version createOSGiVersion(String version) {
+ if (version == null || version.isEmpty()) {
+ return new Version(0, 0, 1);
+ }
+ try {
+ version = DASH.matcher(version).replaceFirst(".");
+ return Version.parseVersion(version);
+ } catch (IllegalArgumentException e) {
+ return new Version(0, 0, 1, version);
+ }
+ }
+
+ public static boolean isOutdated(Path cacheFile, Path sourceFile) throws IOException {
+ if (Files.exists(cacheFile)) {
+ FileTime sourceTimeStamp = Files.getLastModifiedTime(sourceFile);
+ FileTime cacheTimeStamp = Files.getLastModifiedTime(cacheFile);
+ return sourceTimeStamp.compareTo(cacheTimeStamp) > 0;
+ }
+ return true;
+ }
}
diff --git a/tycho-core/src/main/java/org/eclipse/m2e/pde/target/shared/WrappedBundle.java b/tycho-core/src/main/java/org/eclipse/m2e/pde/target/shared/WrappedBundle.java
index 05137989c0..ed849c59d2 100644
--- a/tycho-core/src/main/java/org/eclipse/m2e/pde/target/shared/WrappedBundle.java
+++ b/tycho-core/src/main/java/org/eclipse/m2e/pde/target/shared/WrappedBundle.java
@@ -23,54 +23,65 @@
public final class WrappedBundle {
- private final DependencyNode node;
- private final List depends;
- private final String instructionsKey;
- private final Path file;
- private final Jar jar;
- private final List messages;
+ private final DependencyNode node;
+ private final List depends;
+ private final String instructionsKey;
+ private final Path file;
+ private final Jar jar;
+ private final List messages;
- WrappedBundle(DependencyNode node, List depends, String key, Path file, Jar jar,
- List messages) {
- this.node = node;
- this.depends = depends;
- this.instructionsKey = key;
- this.file = file;
- this.jar = jar;
- this.messages = messages;
- }
+ WrappedBundle(DependencyNode node, List depends, String key, Path file, Jar jar,
+ List messages) {
+ this.node = node;
+ this.depends = depends;
+ this.instructionsKey = key;
+ this.file = file;
+ this.jar = jar;
+ this.messages = messages;
+ }
- String getInstructionsKey() {
- return instructionsKey;
- }
+ String getInstructionsKey() {
+ return instructionsKey;
+ }
- Jar getJar() {
- return jar;
- }
+ Jar getJar() {
+ return jar;
+ }
- /** @return the location of the wrapped bundle's files */
- public Path getFile() {
- return file;
- }
+ DependencyNode getNode() {
+ return node;
+ }
- /** @return the messages that where produced */
- public Stream messages() {
- return Stream.concat(messages.stream(), depends.stream().flatMap(dep -> dep.messages()));
- }
+ /** @return the location of the wrapped bundle's files */
+ public Path getFile() {
+ return file;
+ }
- @Override
- public int hashCode() {
- return Objects.hash(instructionsKey, node);
- }
+ /**
+ * @param includeDependent
+ * if true
includes messages from dependent items.
+ * @return the messages that where produced
+ */
+ public Stream messages(boolean includeDependent) {
+ if (includeDependent) {
+ return Stream.concat(messages.stream(), depends.stream().flatMap(dep -> dep.messages(true)));
+ }
+ return messages.stream();
+ }
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- return obj instanceof WrappedBundle other //
- && Objects.equals(instructionsKey, other.instructionsKey) //
- && Objects.equals(node, other.node);
- }
+ @Override
+ public int hashCode() {
+ return Objects.hash(instructionsKey, node);
+ }
-}
\ No newline at end of file
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ return obj instanceof WrappedBundle other //
+ && Objects.equals(instructionsKey, other.instructionsKey) //
+ && Objects.equals(node, other.node);
+ }
+
+}
diff --git a/tycho-core/src/main/java/org/eclipse/tycho/core/resolver/MavenTargetDefinitionContent.java b/tycho-core/src/main/java/org/eclipse/tycho/core/resolver/MavenTargetDefinitionContent.java
index fc4e016f03..59b20febe9 100644
--- a/tycho-core/src/main/java/org/eclipse/tycho/core/resolver/MavenTargetDefinitionContent.java
+++ b/tycho-core/src/main/java/org/eclipse/tycho/core/resolver/MavenTargetDefinitionContent.java
@@ -34,6 +34,7 @@
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
+import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import javax.xml.parsers.ParserConfigurationException;
@@ -62,6 +63,7 @@
import org.eclipse.equinox.p2.repository.artifact.IArtifactRepository;
import org.eclipse.equinox.p2.repository.metadata.IMetadataRepository;
import org.eclipse.m2e.pde.target.shared.MavenBundleWrapper;
+import org.eclipse.m2e.pde.target.shared.ProcessingMessage;
import org.eclipse.m2e.pde.target.shared.WrappedBundle;
import org.eclipse.osgi.service.resolver.BundleDescription;
import org.eclipse.tycho.IArtifactFacade;
@@ -215,6 +217,15 @@ public MavenTargetDefinitionContent(MavenGAVLocation location, MavenDependencies
mavenArtifact.getVersion()),
instructionsLookup, repositories, repositorySystem2,
mavenSession.getRepositorySession(), syncContextFactory);
+ List directErrors = wrappedBundle.messages(false)
+ .filter(msg -> msg.type() == ProcessingMessage.Type.ERROR).toList();
+ if (directErrors.isEmpty()) {
+ wrappedBundle.messages(true).map(ProcessingMessage::message)
+ .forEach(msg -> logger.warn(asDebugString(mavenArtifact) + ": " + msg));
+ } else {
+ throw new RuntimeException(directErrors.stream().map(ProcessingMessage::message)
+ .collect(Collectors.joining(System.lineSeparator())));
+ }
File file = wrappedBundle.getFile().toFile();
BundleDescription description = BundlesAction.createBundleDescription(file);
WrappedArtifact wrappedArtifact = new WrappedArtifact(file, mavenArtifact,
diff --git a/tycho-core/src/test/java/org/eclipse/m2e/pde/target/tests/AbstractMavenTargetTest.java b/tycho-core/src/test/java/org/eclipse/m2e/pde/target/tests/AbstractMavenTargetTest.java
index cb260a500f..5df3b892e2 100644
--- a/tycho-core/src/test/java/org/eclipse/m2e/pde/target/tests/AbstractMavenTargetTest.java
+++ b/tycho-core/src/test/java/org/eclipse/m2e/pde/target/tests/AbstractMavenTargetTest.java
@@ -36,6 +36,8 @@
import java.util.stream.Collectors;
import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.URIUtil;
import org.eclipse.equinox.frameworkadmin.BundleInfo;
import org.eclipse.m2e.pde.target.tests.spi.TargetLocationLoader;
@@ -47,179 +49,198 @@
import org.junit.rules.TemporaryFolder;
public abstract class AbstractMavenTargetTest {
- static final String SOURCE_BUNDLE_SUFFIX = ".source";
- static final TargetBundle[] EMPTY = {};
-
- @Rule
- public TemporaryFolder temporaryFolder = new TemporaryFolder();
-
- private static ServiceLoader LOCATION_LOADER = ServiceLoader.load(TargetLocationLoader.class,
- AbstractMavenTargetTest.class.getClassLoader());
- private static TargetLocationLoader loader;
-
- ITargetLocation resolveMavenTarget(String targetXML) throws Exception {
- return getLoader().resolveMavenTarget(targetXML, temporaryFolder.newFolder());
- }
-
- private static TargetLocationLoader getLoader() {
- if (loader == null) {
- Provider provider = LOCATION_LOADER.stream().sorted().findFirst().orElseThrow(
- () -> new IllegalStateException("No TargetLocationLoader found on classpath of test!"));
- loader = provider.get();
- }
- return loader;
- }
-
- protected static void assertStatusOk(IStatus status) {
- if (!status.isOK()) {
- throw new AssertionError(status.toString(), status.getException());
- }
- }
-
- // --- common assertion utilities ---
-
- private static Map assertTargetContent(List expectedUnits, T[] allUnit,
- BiPredicate matcher, Function getLocation, Predicate isSourceUnit,
- Function getSourceTarget, Function toString) {
-
- Map units = new HashMap<>();
- List allElements = new ArrayList<>(Arrays.asList(allUnit));
- for (U expectedUnit : expectedUnits) {
- List matchingUnits = allElements.stream().filter(u -> matcher.test(expectedUnit, u)).toList();
-
- if (matchingUnits.isEmpty()) {
- fail("Expected unit is missing: " + expectedUnit);
- } else if (matchingUnits.size() == 1) {
- T targetUnit = matchingUnits.get(0);
- allElements.remove(targetUnit);
-
- assertEquals("Unexpected 'original' state of " + targetUnit, expectedUnit.isOriginal(),
- isOriginalArtifact(expectedUnit, targetUnit, getLocation));
- assertEquals("Unexpected 'isSource' state of " + targetUnit, expectedUnit.isSourceBundle(),
- isSourceUnit.test(targetUnit));
- if (expectedUnit.isSourceBundle()) {
- String expectedSourceTarget = expectedUnit.id().substring(0,
- expectedUnit.id().length() - SOURCE_BUNDLE_SUFFIX.length());
- assertEquals("Source target id", expectedSourceTarget, getSourceTarget.apply(targetUnit));
- } else {
- assertNull(getSourceTarget.apply(targetUnit));
- }
- units.put(expectedUnit, targetUnit);
- } else {
- fail("Expected bundle contaiend multiple times:" + expectedUnit);
- }
- }
- if (!allElements.isEmpty()) {
- String unepxectedBundlesList = allElements.stream().map(u -> " " + toString.apply(u))
- .collect(Collectors.joining("\n"));
- fail("Encoutnered the following unexpected bundles:" + unepxectedBundlesList);
- }
- return units;
- }
-
- private static boolean isOriginalArtifact(ExpectedUnit expectedUnit, T unit, Function getLocation) {
- ArtifactKey key = expectedUnit.key();
- if (key == null) {
- return false;
- }
- URI location = getLocation.apply(unit);
- String expectedPathSuffix = "/" + String.join("/", ".m2", "repository", key.groupId().replace('.', '/'),
- key.artifactId(), key.version(), key.artifactId() + "-" + key.version() + ".jar");
- return location.toASCIIString().endsWith(expectedPathSuffix);
- }
-
- // --- assertion utilities for Bundles in target ---
-
- static ExpectedBundle originalOSGiBundle(String bsn, String version, String groupArtifact) {
- return originalOSGiBundle(bsn, version, groupArtifact, version);
- }
-
- static ExpectedBundle originalOSGiBundle(String bsn, String version, String groupArtifact, String mavenVersion) {
- return new ExpectedBundle(bsn, version, false, true,
- ArtifactKey.fromPortableString(groupArtifact + ":" + mavenVersion + "::"));
- }
-
- static ExpectedBundle generatedBundle(String bsn, String version, String groupArtifact) {
- return new ExpectedBundle(bsn, version, false, false,
- ArtifactKey.fromPortableString(groupArtifact + ":" + version + "::"));
- }
-
- static List withSourceBundles(List mainBundles) {
- return mainBundles.stream().mapMulti((unit, downStream) -> {
- downStream.accept(unit);
- String sourceId = unit.bsn() + SOURCE_BUNDLE_SUFFIX;
- ExpectedBundle sourceUnit = new ExpectedBundle(sourceId, unit.version(), true, false, unit.key());
- downStream.accept(sourceUnit);
- }).toList();
- }
-
- static Attributes getManifestMainAttributes(TargetBundle targetBundle) throws IOException {
- BundleInfo bundleInfo = targetBundle.getBundleInfo();
- File file = URIUtil.toFile(bundleInfo.getLocation());
- try (var jar = new JarFile(file)) {
- return jar.getManifest().getMainAttributes();
- }
- }
-
- static void assertTargetBundles(ITargetLocation target, List expectedUnits) {
- assertTargetContent(expectedUnits, target.getBundles(), //
- (expectedBundle, bundle) -> {
- BundleInfo info = bundle.getBundleInfo();
- return expectedBundle.bsn().equals(info.getSymbolicName())
- && expectedBundle.version().equals(info.getVersion());
- }, //
- tb -> tb.getBundleInfo().getLocation(), //
- tb -> tb.isSourceBundle(),
- tb -> tb.getSourceTarget() != null ? tb.getSourceTarget().getSymbolicName() : null,
- tb -> tb.getBundleInfo().getSymbolicName() + ":" + tb.getBundleInfo().getVersion());
- }
-
- // --- assertion utilities for Features in a target ---
-
- static ExpectedFeature originalFeature(String id, String version, String groupArtifact,
- List containedPlugins) {
- ArtifactKey key = ArtifactKey.fromPortableString(groupArtifact + ":" + version + "::");
- return new ExpectedFeature(id, version, false, true, key, containedPlugins);
- }
-
- static ExpectedFeature generatedFeature(String id, String version, List containedPlugins) {
- return new ExpectedFeature(id, version, false, false, null, containedPlugins);
- }
-
- static NameVersionDescriptor featurePlugin(String bsn, String version) {
- return new NameVersionDescriptor(bsn, version);
- }
-
- static List withSourceFeatures(List mainFeatures) {
- return mainFeatures.stream().mapMulti((feature, downStream) -> {
- downStream.accept(feature);
- String sourceId = feature.id() + SOURCE_BUNDLE_SUFFIX;
- List sourcePlugins = feature.containedPlugins().stream()
- .map(d -> featurePlugin(d.getId() + SOURCE_BUNDLE_SUFFIX, d.getVersion())).toList();
- ExpectedFeature sourceUnit = new ExpectedFeature(sourceId, feature.version(), true, false, feature.key(),
- sourcePlugins);
- downStream.accept(sourceUnit);
- }).toList();
- }
-
- static Map assertTargetFeatures(ITargetLocation target,
- List expectedFeatures) {
- var encounteredFeatures = assertTargetContent(expectedFeatures, target.getFeatures(), //
- (expectedFeature, feature) -> expectedFeature.id().equals(feature.getId())
- && expectedFeature.version().equals(feature.getVersion()), //
- f -> Path.of(f.getLocation()).toUri(), //
- f -> isSourceFeature(f), //
- f -> isSourceFeature(f) ? f.getId().substring(0, f.getId().length() - SOURCE_BUNDLE_SUFFIX.length())
- : null, //
- f -> f.getId() + ":" + f.getVersion());
- encounteredFeatures.forEach((expectedFeature, feature) -> {
- assertEquals(Set.copyOf(expectedFeature.containedPlugins()), Set.of(feature.getPlugins()));
- });
- return encounteredFeatures;
- }
-
- private static boolean isSourceFeature(TargetFeature f) {
- return f.getId().endsWith(SOURCE_BUNDLE_SUFFIX)
- && Arrays.stream(f.getPlugins()).allMatch(d -> d.getId().endsWith(SOURCE_BUNDLE_SUFFIX));
- }
+ static final String SOURCE_BUNDLE_SUFFIX = ".source";
+ static final TargetBundle[] EMPTY = {};
+
+ @Rule
+ public TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+ private static ServiceLoader LOCATION_LOADER = ServiceLoader.load(TargetLocationLoader.class,
+ AbstractMavenTargetTest.class.getClassLoader());
+ private static TargetLocationLoader loader;
+
+ ITargetLocation resolveMavenTarget(String targetXML) throws Exception {
+ return getLoader().resolveMavenTarget(targetXML, temporaryFolder.newFolder());
+ }
+
+ private static TargetLocationLoader getLoader() {
+ if (loader == null) {
+ Provider provider = LOCATION_LOADER.stream().sorted().findFirst().orElseThrow(
+ () -> new IllegalStateException("No TargetLocationLoader found on classpath of test!"));
+ loader = provider.get();
+ }
+ return loader;
+ }
+
+ protected static void assertStatusOk(IStatus status) {
+ if (!status.isOK()) {
+ throw new AssertionError(status.toString(), status.getException());
+ }
+ }
+
+ // --- common assertion utilities ---
+
+ private static Map assertTargetContent(List expectedUnits, T[] allUnit,
+ BiPredicate matcher, Function getLocation, Predicate isSourceUnit,
+ Function getSourceTarget, Function toString) {
+
+ Map units = new HashMap<>();
+ List allElements = new ArrayList<>(Arrays.asList(allUnit));
+ for (U expectedUnit : expectedUnits) {
+ List matchingUnits = allElements.stream().filter(u -> matcher.test(expectedUnit, u)).toList();
+
+ if (matchingUnits.isEmpty()) {
+ fail("Expected unit is missing: " + expectedUnit);
+ } else if (matchingUnits.size() == 1) {
+ T targetUnit = matchingUnits.get(0);
+ allElements.remove(targetUnit);
+
+ assertEquals("Unexpected 'original' state of " + targetUnit, expectedUnit.isOriginal(),
+ isOriginalArtifact(expectedUnit, targetUnit, getLocation));
+ assertEquals("Unexpected 'isSource' state of " + targetUnit, expectedUnit.isSourceBundle(),
+ isSourceUnit.test(targetUnit));
+ if (expectedUnit.isSourceBundle()) {
+ String expectedSourceTarget = expectedUnit.id().substring(0,
+ expectedUnit.id().length() - SOURCE_BUNDLE_SUFFIX.length());
+ assertEquals("Source target id", expectedSourceTarget, getSourceTarget.apply(targetUnit));
+ } else {
+ assertNull(getSourceTarget.apply(targetUnit));
+ }
+ units.put(expectedUnit, targetUnit);
+ } else {
+ fail("Expected bundle contaiend multiple times:" + expectedUnit);
+ }
+ }
+ if (!allElements.isEmpty()) {
+ String unepxectedBundlesList = allElements.stream().map(u -> " " + toString.apply(u))
+ .collect(Collectors.joining("\n"));
+ fail("Encoutnered the following unexpected bundles:" + unepxectedBundlesList);
+ }
+ return units;
+ }
+
+ private static boolean isOriginalArtifact(ExpectedUnit expectedUnit, T unit, Function getLocation) {
+ ArtifactKey key = expectedUnit.key();
+ if (key == null) {
+ return false;
+ }
+ URI location = getLocation.apply(unit);
+ String expectedPathSuffix = "/" + String.join("/", ".m2", "repository", key.groupId().replace('.', '/'),
+ key.artifactId(), key.version(), key.artifactId() + "-" + key.version() + ".jar");
+ return location.toASCIIString().endsWith(expectedPathSuffix);
+ }
+
+ // --- assertion utilities for Bundles in target ---
+
+ static ExpectedBundle originalOSGiBundle(String bsn, String version, String groupArtifact) {
+ return originalOSGiBundle(bsn, version, groupArtifact, version);
+ }
+
+ static ExpectedBundle originalOSGiBundle(String bsn, String version, String groupArtifact, String mavenVersion) {
+ return new ExpectedBundle(bsn, version, false, true,
+ ArtifactKey.fromPortableString(groupArtifact + ":" + mavenVersion + "::"));
+ }
+
+ static ExpectedBundle generatedBundle(String bsn, String version, String groupArtifact) {
+ return new ExpectedBundle(bsn, version, false, false,
+ ArtifactKey.fromPortableString(groupArtifact + ":" + version + "::"));
+ }
+
+ static List withSourceBundles(List mainBundles) {
+ return mainBundles.stream(). mapMulti((unit, downStream) -> {
+ downStream.accept(unit);
+ String sourceId = unit.bsn() + SOURCE_BUNDLE_SUFFIX;
+ ExpectedBundle sourceUnit = new ExpectedBundle(sourceId, unit.version(), true, false, unit.key());
+ downStream.accept(sourceUnit);
+ }).toList();
+ }
+
+ static Attributes getManifestMainAttributes(TargetBundle targetBundle) throws IOException {
+ BundleInfo bundleInfo = targetBundle.getBundleInfo();
+ File file = URIUtil.toFile(bundleInfo.getLocation());
+ try (var jar = new JarFile(file)) {
+ return jar.getManifest().getMainAttributes();
+ }
+ }
+
+ static void assertTargetBundles(ITargetLocation target, List expectedUnits) {
+ assertTargetContent(expectedUnits, target.getBundles(), //
+ (expectedBundle, bundle) -> {
+ BundleInfo info = bundle.getBundleInfo();
+ return expectedBundle.bsn().equals(info.getSymbolicName())
+ && expectedBundle.version().equals(info.getVersion());
+ }, //
+ tb -> tb.getBundleInfo().getLocation(), //
+ tb -> tb.isSourceBundle(),
+ tb -> tb.getSourceTarget() != null ? tb.getSourceTarget().getSymbolicName() : null,
+ tb -> tb.getBundleInfo().getSymbolicName() + ":" + tb.getBundleInfo().getVersion());
+ }
+
+ // --- assertion utilities for Features in a target ---
+
+ static ExpectedFeature originalFeature(String id, String version, String groupArtifact,
+ List containedPlugins) {
+ ArtifactKey key = ArtifactKey.fromPortableString(groupArtifact + ":" + version + "::");
+ return new ExpectedFeature(id, version, false, true, key, containedPlugins);
+ }
+
+ static ExpectedFeature generatedFeature(String id, String version, List containedPlugins) {
+ return new ExpectedFeature(id, version, false, false, null, containedPlugins);
+ }
+
+ static NameVersionDescriptor featurePlugin(String bsn, String version) {
+ return new NameVersionDescriptor(bsn, version);
+ }
+
+ static List withSourceFeatures(List mainFeatures) {
+ return mainFeatures.stream(). mapMulti((feature, downStream) -> {
+ downStream.accept(feature);
+ String sourceId = feature.id() + SOURCE_BUNDLE_SUFFIX;
+ List sourcePlugins = feature.containedPlugins().stream()
+ .map(d -> featurePlugin(d.getId() + SOURCE_BUNDLE_SUFFIX, d.getVersion())).toList();
+ ExpectedFeature sourceUnit = new ExpectedFeature(sourceId, feature.version(), true, false, feature.key(),
+ sourcePlugins);
+ downStream.accept(sourceUnit);
+ }).toList();
+ }
+
+ static Map assertTargetFeatures(ITargetLocation target,
+ List expectedFeatures) {
+ var encounteredFeatures = assertTargetContent(expectedFeatures, target.getFeatures(), //
+ (expectedFeature, feature) -> expectedFeature.id().equals(feature.getId())
+ && expectedFeature.version().equals(feature.getVersion()), //
+ f -> Path.of(f.getLocation()).toUri(), //
+ f -> isSourceFeature(f), //
+ f -> isSourceFeature(f) ? f.getId().substring(0, f.getId().length() - SOURCE_BUNDLE_SUFFIX.length())
+ : null, //
+ f -> f.getId() + ":" + f.getVersion());
+ encounteredFeatures.forEach((expectedFeature, feature) -> {
+ assertEquals(Set.copyOf(expectedFeature.containedPlugins()), Set.of(feature.getPlugins()));
+ });
+ return encounteredFeatures;
+ }
+
+ static boolean isSourceFeature(TargetFeature f) {
+ return f.getId().endsWith(SOURCE_BUNDLE_SUFFIX)
+ && Arrays.stream(f.getPlugins()).allMatch(d -> d.getId().endsWith(SOURCE_BUNDLE_SUFFIX));
+ }
+
+ static IStatus getTargetStatus(ITargetLocation target) {
+ IStatus status = target.getStatus();
+ if (status != null && !status.isOK()) {
+ return status;
+ }
+ MultiStatus result = new MultiStatus("org.eclipse.pde.core", 0,
+ "Problems occurred getting the plug-ins in this container", null);
+ for (TargetBundle targetBundle : target.getBundles()) {
+ IStatus bundleStatus = targetBundle.getStatus();
+ if (!bundleStatus.isOK()) {
+ result.add(bundleStatus);
+ }
+ }
+ if (result.isOK()) {
+ return Status.OK_STATUS;
+ }
+ return result;
+ }
}
diff --git a/tycho-core/src/test/java/org/eclipse/m2e/pde/target/tests/OSGiMetadataGenerationTest.java b/tycho-core/src/test/java/org/eclipse/m2e/pde/target/tests/OSGiMetadataGenerationTest.java
index 5377f074c9..44cda6e77a 100644
--- a/tycho-core/src/test/java/org/eclipse/m2e/pde/target/tests/OSGiMetadataGenerationTest.java
+++ b/tycho-core/src/test/java/org/eclipse/m2e/pde/target/tests/OSGiMetadataGenerationTest.java
@@ -39,6 +39,59 @@
public class OSGiMetadataGenerationTest extends AbstractMavenTargetTest {
+ @Test
+ public void testBadDependencyInChain() throws Exception {
+ ITargetLocation target = resolveMavenTarget("""
+
+
+
+ edu.ucar
+ cdm
+ 4.5.5
+ jar
+
+
+
+ """);
+ assertStatusOk(getTargetStatus(target));
+ }
+
+ @Test
+ public void testBadDependencyDirect() throws Exception {
+ ITargetLocation target = resolveMavenTarget("""
+
+
+
+ com.ibm.icu
+ icu4j
+ 2.6.1
+ jar
+
+
+
+ """);
+ IStatus targetStatus = getTargetStatus(target);
+ assertEquals(String.valueOf(targetStatus), IStatus.ERROR, targetStatus.getSeverity());
+ }
+
+ @Test
+ public void testMissingOptionalDependency() throws Exception {
+ ITargetLocation target = resolveMavenTarget(
+ """
+
+
+
+ net.sf.saxon
+ Saxon-HE
+ 10.9
+ jar
+
+
+
+ """);
+ assertStatusOk(getTargetStatus(target));
+ }
+
@Test
@Ignore("FIXME")
public void testNonOSGiArtifact_missingArtifactError() throws Exception {