diff --git a/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/resolver/MavenTargetDefinitionContent.java b/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/resolver/MavenTargetDefinitionContent.java index 4aef696c43..41f6d71b6d 100644 --- a/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/resolver/MavenTargetDefinitionContent.java +++ b/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/resolver/MavenTargetDefinitionContent.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2020 Christoph Läubrich and others. + * Copyright (c) 2020, 2021 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 @@ -49,6 +49,7 @@ import org.eclipse.tycho.p2.repository.GAV; import org.eclipse.tycho.p2.target.TargetDefinitionContent; import org.eclipse.tycho.p2.target.facade.TargetDefinition.BNDInstructions; +import org.eclipse.tycho.p2.target.facade.TargetDefinition.MavenDependency; import org.eclipse.tycho.p2.target.facade.TargetDefinition.MavenGAVLocation; import org.eclipse.tycho.p2.target.facade.TargetDefinition.MavenGAVLocation.MissingManifestStrategy; import org.eclipse.tycho.p2.target.facade.TargetDefinitionResolutionException; @@ -84,109 +85,114 @@ public MavenTargetDefinitionContent(MavenGAVLocation location, MavenDependencies instructionsMap.put(reference, properties); logger.info((reference.isEmpty() ? "default instructions" : reference) + " = " + properties); } - Collection resolve = mavenDependenciesResolver.resolve(location.getGroupId(), location.getArtifactId(), - location.getVersion(), location.getArtifactType(), location.getClassifier(), - location.getIncludeDependencyScope()); + for (MavenDependency mavenDependency : location.getRoots()) { + Collection resolve = mavenDependenciesResolver.resolve(mavenDependency.getGroupId(), + mavenDependency.getArtifactId(), mavenDependency.getVersion(), + mavenDependency.getArtifactType(), mavenDependency.getClassifier(), + location.getIncludeDependencyScope()); - Iterator resolvedArtifacts = resolve.stream().filter(IArtifactFacade.class::isInstance) - .map(IArtifactFacade.class::cast).iterator(); - Properties defaultProperties = WrappedArtifact.createPropertiesForPrefix("wrapped"); - while (resolvedArtifacts.hasNext()) { - IArtifactFacade mavenArtifact = resolvedArtifacts.next(); - logger.debug("Resolved " + mavenArtifact + "..."); - String symbolicName; - String bundleVersion; - try { - File bundleLocation = mavenArtifact.getLocation(); - BundleDescription bundleDescription = BundlesAction.createBundleDescription(bundleLocation); - if (bundleDescription == null) { - throw new TargetDefinitionResolutionException( - "Artifact " + mavenArtifact + " of location " + location + " is not a valid jar file"); - } else { - symbolicName = bundleDescription.getSymbolicName(); - bundleVersion = bundleDescription.getVersion().toString(); - IInstallableUnit unit; - if (symbolicName == null) { - if (location.getMissingManifestStrategy() == MissingManifestStrategy.IGNORE) { - logger.info("Ignoring " + asDebugString(mavenArtifact) - + " as it is not a bundle and MissingManifestStrategy is set to ignore for this location"); - continue; - } - if (location.getMissingManifestStrategy() == MissingManifestStrategy.ERROR) { - throw new TargetDefinitionResolutionException("Artifact " + asDebugString(mavenArtifact) - + " is not a bundle and MissingManifestStrategy is set to error for this location"); - } - File tempFile = File.createTempFile("tycho_wrapped_bundle", ".jar"); - tempFile.deleteOnExit(); - WrappedArtifact wrappedArtifact; - try { - Properties properties = instructionsMap.getOrDefault(getKey(mavenArtifact), - instructionsMap.getOrDefault("", defaultProperties)); - wrappedArtifact = WrappedArtifact.createWrappedArtifact(mavenArtifact, properties, - tempFile); - } catch (Exception e) { - throw new TargetDefinitionResolutionException( - "Artifact " + asDebugString(mavenArtifact) + " could not be wrapped", e); - } - logger.info( - asDebugString(mavenArtifact) + " is wrapped as a bundle with bundle symbolic name " - + wrappedArtifact.getWrappedBsn()); - logger.info(wrappedArtifact.getReferenceHint()); - if (logger.isDebugEnabled()) { - logger.debug("The follwoing manifest was generated for this artifact:\r\n" - + wrappedArtifact.getGeneratedManifest()); - } - unit = publish(BundlesAction.createBundleDescription(tempFile), tempFile); - symbolicName = wrappedArtifact.getWrappedBsn(); - bundleVersion = wrappedArtifact.getWrappedVersion(); + Iterator resolvedArtifacts = resolve.stream().filter(IArtifactFacade.class::isInstance) + .map(IArtifactFacade.class::cast).iterator(); + Properties defaultProperties = WrappedArtifact.createPropertiesForPrefix("wrapped"); + while (resolvedArtifacts.hasNext()) { + IArtifactFacade mavenArtifact = resolvedArtifacts.next(); + logger.debug("Resolved " + mavenArtifact + "..."); + String symbolicName; + String bundleVersion; + try { + File bundleLocation = mavenArtifact.getLocation(); + BundleDescription bundleDescription = BundlesAction.createBundleDescription(bundleLocation); + if (bundleDescription == null) { + throw new TargetDefinitionResolutionException("Artifact " + mavenArtifact + " of location " + + location + " is not a valid jar file"); } else { - unit = publish(bundleDescription, bundleLocation); - } - if (logger.isDebugEnabled()) { - logger.debug("MavenResolver: artifact " + asDebugString(mavenArtifact) + " at location " - + bundleLocation + " resolves installable unit " - + new VersionedId(unit.getId(), unit.getVersion())); - } - } - } catch (BundleException | IOException e) { - throw new TargetDefinitionResolutionException("Artifact " + asDebugString(mavenArtifact) - + " of location " + location + " could not be read", e); - } - if (sourceMode == IncludeSourceMode.force - || (sourceMode == IncludeSourceMode.honor && location.includeSource())) { - Collection sourceArtifacts = mavenDependenciesResolver.resolve(mavenArtifact.getGroupId(), - mavenArtifact.getArtifactId(), mavenArtifact.getVersion(), mavenArtifact.getPackagingType(), - "sources", null); - Iterator sources = sourceArtifacts.stream() - .filter(IArtifactFacade.class::isInstance).map(IArtifactFacade.class::cast).iterator(); - while (sources.hasNext()) { - IArtifactFacade sourceArtifact = sources.next(); - File sourceFile = sourceArtifact.getLocation(); - try { - Manifest manifest; - try (JarFile jar = new JarFile(sourceFile)) { - manifest = Objects.requireNonNullElseGet(jar.getManifest(), Manifest::new); - } + symbolicName = bundleDescription.getSymbolicName(); + bundleVersion = bundleDescription.getVersion().toString(); IInstallableUnit unit; - if (isValidSourceManifest(manifest)) { - unit = publish(BundlesAction.createBundleDescription(sourceFile), sourceFile); + if (symbolicName == null) { + if (location.getMissingManifestStrategy() == MissingManifestStrategy.IGNORE) { + logger.info("Ignoring " + asDebugString(mavenArtifact) + + " as it is not a bundle and MissingManifestStrategy is set to ignore for this location"); + continue; + } + if (location.getMissingManifestStrategy() == MissingManifestStrategy.ERROR) { + throw new TargetDefinitionResolutionException("Artifact " + + asDebugString(mavenArtifact) + + " is not a bundle and MissingManifestStrategy is set to error for this location"); + } + File tempFile = File.createTempFile("tycho_wrapped_bundle", ".jar"); + tempFile.deleteOnExit(); + WrappedArtifact wrappedArtifact; + try { + Properties properties = instructionsMap.getOrDefault(getKey(mavenArtifact), + instructionsMap.getOrDefault("", defaultProperties)); + wrappedArtifact = WrappedArtifact.createWrappedArtifact(mavenArtifact, properties, + tempFile); + } catch (Exception e) { + throw new TargetDefinitionResolutionException( + "Artifact " + asDebugString(mavenArtifact) + " could not be wrapped", e); + } + logger.info(asDebugString(mavenArtifact) + + " is wrapped as a bundle with bundle symbolic name " + + wrappedArtifact.getWrappedBsn()); + logger.info(wrappedArtifact.getReferenceHint()); + if (logger.isDebugEnabled()) { + logger.debug("The follwoing manifest was generated for this artifact:\r\n" + + wrappedArtifact.getGeneratedManifest()); + } + unit = publish(BundlesAction.createBundleDescription(tempFile), tempFile); + symbolicName = wrappedArtifact.getWrappedBsn(); + bundleVersion = wrappedArtifact.getWrappedVersion(); } else { - unit = generateSourceBundle(symbolicName, bundleVersion, manifest, sourceFile); + unit = publish(bundleDescription, bundleLocation); } - if (unit != null && logger.isDebugEnabled()) { - logger.debug("MavenResolver: source-artifact " + asDebugString(sourceArtifact) - + ":sources at location " + sourceFile + " resolves installable unit " + if (logger.isDebugEnabled()) { + logger.debug("MavenResolver: artifact " + asDebugString(mavenArtifact) + " at location " + + bundleLocation + " resolves installable unit " + new VersionedId(unit.getId(), unit.getVersion())); } - } catch (IOException | BundleException e) { - logger.warn("MavenResolver: source-artifact " + asDebugString(sourceArtifact) - + ":sources at location " + sourceFile + " can't be converted to a source bundle: " - + e); - continue; + } + } catch (BundleException | IOException e) { + throw new TargetDefinitionResolutionException("Artifact " + asDebugString(mavenArtifact) + + " of location " + location + " could not be read", e); + } + if (sourceMode == IncludeSourceMode.force + || (sourceMode == IncludeSourceMode.honor && location.includeSource())) { + Collection sourceArtifacts = mavenDependenciesResolver.resolve(mavenArtifact.getGroupId(), + mavenArtifact.getArtifactId(), mavenArtifact.getVersion(), + mavenArtifact.getPackagingType(), "sources", null); + Iterator sources = sourceArtifacts.stream() + .filter(IArtifactFacade.class::isInstance).map(IArtifactFacade.class::cast).iterator(); + while (sources.hasNext()) { + IArtifactFacade sourceArtifact = sources.next(); + File sourceFile = sourceArtifact.getLocation(); + try { + Manifest manifest; + try (JarFile jar = new JarFile(sourceFile)) { + manifest = Objects.requireNonNullElseGet(jar.getManifest(), Manifest::new); + } + IInstallableUnit unit; + if (isValidSourceManifest(manifest)) { + unit = publish(BundlesAction.createBundleDescription(sourceFile), sourceFile); + } else { + unit = generateSourceBundle(symbolicName, bundleVersion, manifest, sourceFile); + } + if (unit != null && logger.isDebugEnabled()) { + logger.debug("MavenResolver: source-artifact " + asDebugString(sourceArtifact) + + ":sources at location " + sourceFile + " resolves installable unit " + + new VersionedId(unit.getId(), unit.getVersion())); + } + } catch (IOException | BundleException e) { + logger.warn("MavenResolver: source-artifact " + asDebugString(sourceArtifact) + + ":sources at location " + sourceFile + + " can't be converted to a source bundle: " + e); + continue; + } } } } } + } } diff --git a/tycho-bundles/org.eclipse.tycho.p2.resolver.shared/src/main/java/org/eclipse/tycho/p2/target/facade/TargetDefinition.java b/tycho-bundles/org.eclipse.tycho.p2.resolver.shared/src/main/java/org/eclipse/tycho/p2/target/facade/TargetDefinition.java index 4a1a58a1f8..e9d8ce4516 100644 --- a/tycho-bundles/org.eclipse.tycho.p2.resolver.shared/src/main/java/org/eclipse/tycho/p2/target/facade/TargetDefinition.java +++ b/tycho-bundles/org.eclipse.tycho.p2.resolver.shared/src/main/java/org/eclipse/tycho/p2/target/facade/TargetDefinition.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2011, 2020 SAP AG and others. + * Copyright (c) 2011, 2021 SAP AG 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 @@ -12,6 +12,7 @@ * Christoph Läubrich - [Bug 538144] - support other target locations (Directory, Feature, Installations) * [Bug 568729] - Support new "Maven" Target location * [Bug 569481] - Support for maven target location includeSource="true" attribute + * [Issue 189] - Support multiple maven-dependencies for one target location *******************************************************************************/ package org.eclipse.tycho.p2.target.facade; @@ -80,18 +81,10 @@ enum MissingManifestStrategy { MissingManifestStrategy getMissingManifestStrategy(); - String getGroupId(); - - String getArtifactId(); - - String getVersion(); - - String getArtifactType(); - - String getClassifier(); - Collection getInstructions(); + Collection getRoots(); + boolean includeSource(); } @@ -175,4 +168,17 @@ public interface BNDInstructions { public Properties getInstructions(); } + public interface MavenDependency { + + String getGroupId(); + + String getArtifactId(); + + String getVersion(); + + String getArtifactType(); + + String getClassifier(); + } + } diff --git a/tycho-core/src/main/java/org/eclipse/tycho/core/ee/TargetDefinitionFile.java b/tycho-core/src/main/java/org/eclipse/tycho/core/ee/TargetDefinitionFile.java index 39fb527741..71ff3c62ce 100644 --- a/tycho-core/src/main/java/org/eclipse/tycho/core/ee/TargetDefinitionFile.java +++ b/tycho-core/src/main/java/org/eclipse/tycho/core/ee/TargetDefinitionFile.java @@ -12,6 +12,7 @@ * - [Bug 533747] - Target file is read and parsed over and over again * - [Bug 568729] - Support new "Maven" Target location * - [Bug 569481] - Support for maven target location includeSource="true" attribute + * - [Issue 189] - Support multiple maven-dependencies for one target location *******************************************************************************/ package org.eclipse.tycho.core.ee; @@ -164,6 +165,73 @@ public boolean includeSource() { return Boolean.parseBoolean(dom.getAttributeValue("includeSource")); } + @Override + public String toString() { + StringBuilder builder = new StringBuilder("MavenDependencyRoots = "); + builder.append(getRoots()); + builder.append(", IncludeDependencyScope = "); + builder.append(getIncludeDependencyScope()); + builder.append(", MissingManifestStrategy = "); + builder.append(getMissingManifestStrategy()); + builder.append(", IncludeSource = "); + builder.append(includeSource()); + return builder.toString(); + } + + @Override + public Collection getInstructions() { + List list = new ArrayList<>(); + for (Element element : dom.getChildren("instructions")) { + String reference = element.getAttributeValue("reference"); + String text = element.getText(); + Properties properties = new Properties(); + try { + properties.load(new StringReader(text)); + } catch (IOException e) { + throw new TargetDefinitionSyntaxException("parsing instructions into properties failed", e); + } + list.add(new BNDInstructions() { + + @Override + public String getReference() { + if (reference == null) { + return ""; + } + return reference; + } + + @Override + public Properties getInstructions() { + return properties; + } + }); + } + return list; + } + + @Override + public Collection getRoots() { + for (Element dependencies : dom.getChildren("dependencies")) { + List roots = new ArrayList<>(); + for (Element dependency : dependencies.getChildren("dependency")) { + roots.add(new MavenDependencyRoot(dependency)); + } + return roots; + } + //backward compatibility for old format... + return Collections.singleton(new MavenDependencyRoot(dom)); + } + + } + + private static final class MavenDependencyRoot implements MavenDependency { + + private Element dom; + + public MavenDependencyRoot(Element dom) { + this.dom = dom; + } + @Override public String getGroupId() { return getTextFromChild("groupId", null); @@ -211,45 +279,9 @@ public String toString() { builder.append(", ArtifactType = "); builder.append(getArtifactType()); builder.append(", IncludeDependencyScope = "); - builder.append(getIncludeDependencyScope()); - builder.append(", MissingManifestStrategy = "); - builder.append(getMissingManifestStrategy()); - builder.append(", IncludeSource = "); - builder.append(includeSource()); return builder.toString(); } - @Override - public Collection getInstructions() { - List list = new ArrayList<>(); - for (Element element : dom.getChildren("instructions")) { - String reference = element.getAttributeValue("reference"); - String text = element.getText(); - Properties properties = new Properties(); - try { - properties.load(new StringReader(text)); - } catch (IOException e) { - throw new TargetDefinitionSyntaxException("parsing instructions into properties failed", e); - } - list.add(new BNDInstructions() { - - @Override - public String getReference() { - if (reference == null) { - return ""; - } - return reference; - } - - @Override - public Properties getInstructions() { - return properties; - } - }); - } - return list; - } - } public class IULocation implements TargetDefinition.InstallableUnitLocation { diff --git a/tycho-its/projects/target.mavenMulti/pom.xml b/tycho-its/projects/target.mavenMulti/pom.xml new file mode 100644 index 0000000000..94df2c139c --- /dev/null +++ b/tycho-its/projects/target.mavenMulti/pom.xml @@ -0,0 +1,49 @@ + + + + + 4.0.0 + + org.eclipse.tycho.itests + issue-189-parent + 0.0.1-SNAPSHOT + pom + + + 2.5.0-SNAPSHOT + + + + test.bundle + test.feature + + + + + + org.eclipse.tycho + tycho-maven-plugin + ${tycho-version} + true + + + org.eclipse.tycho + target-platform-configuration + ${tycho-version} + + + ../test.target + + + + + + + + + diff --git a/tycho-its/projects/target.mavenMulti/test.bundle/META-INF/MANIFEST.MF b/tycho-its/projects/target.mavenMulti/test.bundle/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..3cd79fda15 --- /dev/null +++ b/tycho-its/projects/target.mavenMulti/test.bundle/META-INF/MANIFEST.MF @@ -0,0 +1,9 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: Bundle +Bundle-SymbolicName: test.bundle +Bundle-Version: 0.0.1.qualifier +Automatic-Module-Name: test.bundle +Bundle-RequiredExecutionEnvironment: JavaSE-11 +Require-Bundle: org.eclipse.jetty.http;bundle-version="11.0.6", + slf4j.api;bundle-version="2.0.0" diff --git a/tycho-its/projects/target.mavenMulti/test.bundle/build.properties b/tycho-its/projects/target.mavenMulti/test.bundle/build.properties new file mode 100644 index 0000000000..34d2e4d2da --- /dev/null +++ b/tycho-its/projects/target.mavenMulti/test.bundle/build.properties @@ -0,0 +1,4 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + . diff --git a/tycho-its/projects/target.mavenMulti/test.bundle/pom.xml b/tycho-its/projects/target.mavenMulti/test.bundle/pom.xml new file mode 100644 index 0000000000..254f32c6fb --- /dev/null +++ b/tycho-its/projects/target.mavenMulti/test.bundle/pom.xml @@ -0,0 +1,20 @@ + + + + 4.0.0 + + test.bundle + eclipse-plugin + + + org.eclipse.tycho.itests + issue-189-parent + 0.0.1-SNAPSHOT + + + \ No newline at end of file diff --git a/tycho-its/projects/target.mavenMulti/test.bundle/src/test/bundle/TestClass.java b/tycho-its/projects/target.mavenMulti/test.bundle/src/test/bundle/TestClass.java new file mode 100644 index 0000000000..0cba01732b --- /dev/null +++ b/tycho-its/projects/target.mavenMulti/test.bundle/src/test/bundle/TestClass.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2021 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 test.bundle; + +import org.eclipse.jetty.http.HttpField; + +public class TestClass { + public static void main(String[] args) { + HttpField field = new HttpField("test", "me"); + } +} diff --git a/tycho-its/projects/target.mavenMulti/test.feature/build.properties b/tycho-its/projects/target.mavenMulti/test.feature/build.properties new file mode 100644 index 0000000000..64f93a9f0b --- /dev/null +++ b/tycho-its/projects/target.mavenMulti/test.feature/build.properties @@ -0,0 +1 @@ +bin.includes = feature.xml diff --git a/tycho-its/projects/target.mavenMulti/test.feature/feature.xml b/tycho-its/projects/target.mavenMulti/test.feature/feature.xml new file mode 100644 index 0000000000..3bcfbbb6eb --- /dev/null +++ b/tycho-its/projects/target.mavenMulti/test.feature/feature.xml @@ -0,0 +1,40 @@ + + + + + [Enter Feature Description here.] + + + + [Enter Copyright Description here.] + + + + [Enter License Description here.] + + + + + + + + + diff --git a/tycho-its/projects/target.mavenMulti/test.feature/pom.xml b/tycho-its/projects/target.mavenMulti/test.feature/pom.xml new file mode 100644 index 0000000000..f05949e2ad --- /dev/null +++ b/tycho-its/projects/target.mavenMulti/test.feature/pom.xml @@ -0,0 +1,20 @@ + + + + 4.0.0 + + test.feature + eclipse-feature + + + org.eclipse.tycho.itests + issue-189-parent + 0.0.1-SNAPSHOT + + + \ No newline at end of file diff --git a/tycho-its/projects/target.mavenMulti/test.target b/tycho-its/projects/target.mavenMulti/test.target new file mode 100644 index 0000000000..ef07f1159e --- /dev/null +++ b/tycho-its/projects/target.mavenMulti/test.target @@ -0,0 +1,22 @@ + + + + + + + + org.eclipse.jetty + jetty-server + 11.0.6 + jar + + + org.eclipse.jetty + jetty-servlet + 11.0.6 + jar + + + + + diff --git a/tycho-its/src/test/java/org/eclipse/tycho/test/target/TargetPlatformLocationsTest.java b/tycho-its/src/test/java/org/eclipse/tycho/test/target/TargetPlatformLocationsTest.java index 9b9e5bb2d8..2ea69c1ce7 100644 --- a/tycho-its/src/test/java/org/eclipse/tycho/test/target/TargetPlatformLocationsTest.java +++ b/tycho-its/src/test/java/org/eclipse/tycho/test/target/TargetPlatformLocationsTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2020 Christoph Läubrich and others. + * Copyright (c) 2020, 2021 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 @@ -25,6 +25,13 @@ public void testMavenLocation() throws Exception { verifier.verifyErrorFreeLog(); } + @Test + public void testMavenLocationMulti() throws Exception { + Verifier verifier = getVerifier("target.mavenMulti", false, true); + verifier.executeGoal("verify"); + verifier.verifyErrorFreeLog(); + } + public void testDirectoryLocation() throws Exception { Verifier verifier = getVerifier("target.directory", false, true); verifier.executeGoal("verify");