From 485b58c423459b3e9e0e9474741dfc08be6b7ace Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20L=C3=A4ubrich?= Date: Sat, 14 Dec 2024 18:20:22 +0100 Subject: [PATCH] Add support for TargetDefinition#implicitDependencies PDE has support for implicitDependencies defined in the target that Tycho currently ignores. This adds the required support to Tycho to understand dependencies defined in this way. --- RELEASE_NOTES.md | 7 + .../core/TargetPlatformConfiguration.java | 7 +- .../TargetPlatformClasspathContributor.java | 113 +++ .../TargetDefinitionResolverTest.java | 6 + .../InstallableUnitLocationUpdater.java | 6 + .../compiler.annotations/annotations.target | 11 +- .../projects/compiler.annotations/pom.xml | 10 +- .../src/test/jdt/annotations/TestJDT.java | 10 + .../targetplatform/TargetDefinition.java | 13 + .../targetplatform/TargetDefinitionFile.java | 931 +++++++++--------- 10 files changed, 650 insertions(+), 464 deletions(-) create mode 100644 tycho-core/src/main/java/org/eclipse/tycho/core/osgitools/targetplatform/TargetPlatformClasspathContributor.java create mode 100644 tycho-its/projects/compiler.annotations/src/test/jdt/annotations/TestJDT.java diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 60ea2abf49..fb7007aef6 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -6,11 +6,18 @@ If you are reading this in the browser, then you can quickly jump to specific ve ## 5.0.0 (under development) +## Support for implicit dependencies in target definitions + +In target definitions Tycho now supports to use the ``, +see [Eclipse Help](https://help.eclipse.org/latest/topic/org.eclipse.pde.doc.user/guide/tools/editors/target_editor/environment_page.htm) +for more details. + ## 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: + ``` diff --git a/tycho-core/src/main/java/org/eclipse/tycho/core/TargetPlatformConfiguration.java b/tycho-core/src/main/java/org/eclipse/tycho/core/TargetPlatformConfiguration.java index 472e14437d..5a19a313e3 100644 --- a/tycho-core/src/main/java/org/eclipse/tycho/core/TargetPlatformConfiguration.java +++ b/tycho-core/src/main/java/org/eclipse/tycho/core/TargetPlatformConfiguration.java @@ -29,10 +29,13 @@ import java.util.Map; import java.util.Set; import java.util.function.Supplier; +import java.util.stream.Stream; import org.codehaus.plexus.util.xml.Xpp3Dom; import org.eclipse.equinox.p2.metadata.IRequirement; import org.eclipse.tycho.ArtifactKey; +import org.eclipse.tycho.ArtifactType; +import org.eclipse.tycho.DefaultArtifactKey; import org.eclipse.tycho.OptionalResolutionAction; import org.eclipse.tycho.TargetEnvironment; import org.eclipse.tycho.core.resolver.shared.IncludeSourceMode; @@ -267,7 +270,9 @@ public DependencyResolverConfiguration getDependencyResolverConfiguration() { @Override public List getAdditionalArtifacts() { - return extraRequirements; + Stream targetFiles = getTargets().stream().flatMap(tdf -> tdf.implicitDependencies()) + .map(id -> new DefaultArtifactKey(ArtifactType.TYPE_ECLIPSE_PLUGIN, id.getId())); + return Stream.concat(extraRequirements.stream(), targetFiles).distinct().toList(); } @Override diff --git a/tycho-core/src/main/java/org/eclipse/tycho/core/osgitools/targetplatform/TargetPlatformClasspathContributor.java b/tycho-core/src/main/java/org/eclipse/tycho/core/osgitools/targetplatform/TargetPlatformClasspathContributor.java new file mode 100644 index 0000000000..64150a1eb1 --- /dev/null +++ b/tycho-core/src/main/java/org/eclipse/tycho/core/osgitools/targetplatform/TargetPlatformClasspathContributor.java @@ -0,0 +1,113 @@ +/******************************************************************************* + * 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.tycho.core.osgitools.targetplatform; + +import java.io.File; +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.logging.Logger; +import org.eclipse.tycho.ArtifactKey; +import org.eclipse.tycho.ArtifactType; +import org.eclipse.tycho.ClasspathEntry; +import org.eclipse.tycho.ReactorProject; +import org.eclipse.tycho.TargetPlatform; +import org.eclipse.tycho.classpath.ClasspathContributor; +import org.eclipse.tycho.core.TargetPlatformConfiguration; +import org.eclipse.tycho.core.TychoProjectManager; +import org.eclipse.tycho.targetplatform.TargetDefinition.ImplicitDependency; + +@Component(role = ClasspathContributor.class, hint = "target-platform") +public class TargetPlatformClasspathContributor implements ClasspathContributor { + + @Requirement + private Logger logger; + + @Requirement + private TychoProjectManager projectManager; + + @Override + public List getAdditionalClasspathEntries(MavenProject project, String scope) { + + TargetPlatform platform = projectManager.getTargetPlatform(project).orElse(null); + if (platform == null) { + return List.of(); + } + TargetPlatformConfiguration configuration = projectManager.getTargetPlatformConfiguration(project); + List dependencies = configuration.getTargets().stream() + .flatMap(tdf -> tdf.implicitDependencies()).distinct().toList(); + return dependencies.stream().map(dependency -> getClasspathEntry(platform, dependency)).filter(Objects::nonNull) + .toList(); + } + + private ClasspathEntry getClasspathEntry(TargetPlatform targetPlatform, ImplicitDependency dependency) { + try { + ArtifactKey key = targetPlatform.resolveArtifact(ArtifactType.TYPE_ECLIPSE_PLUGIN, dependency.getId(), + null); + + return new TargetPlatformClasspathEntry(targetPlatform, key); + } catch (Exception e) { + logger.warn("Can't resolve ImplicitDependency with id " + dependency.getId(), e); + return null; + } + } + + private static final class TargetPlatformClasspathEntry implements ClasspathEntry { + private final TargetPlatform targetPlatform; + private final ArtifactKey key; + private List files; + + private TargetPlatformClasspathEntry(TargetPlatform targetPlatform, ArtifactKey key) { + this.targetPlatform = targetPlatform; + this.key = key; + } + + @Override + public ReactorProject getMavenProject() { + return null; + } + + @Override + public synchronized List getLocations() { + if (files == null) { + File file = targetPlatform.getArtifactLocation(getArtifactKey()); + if (file == null) { + files = List.of(); + } else { + files = List.of(file); + } + } + return files; + } + + @Override + public ArtifactKey getArtifactKey() { + return key; + } + + @Override + public Collection getAccessRules() { + return null; + } + + @Override + public String toString() { + return "TargetPlatformClasspathEntry[" + key + "]"; + } + } + +} diff --git a/tycho-core/src/test/java/org/eclipse/tycho/p2resolver/TargetDefinitionResolverTest.java b/tycho-core/src/test/java/org/eclipse/tycho/p2resolver/TargetDefinitionResolverTest.java index 81717fa5c9..b72ded2553 100644 --- a/tycho-core/src/test/java/org/eclipse/tycho/p2resolver/TargetDefinitionResolverTest.java +++ b/tycho-core/src/test/java/org/eclipse/tycho/p2resolver/TargetDefinitionResolverTest.java @@ -26,6 +26,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.stream.Stream; import org.codehaus.plexus.component.repository.exception.ComponentLookupException; import org.eclipse.equinox.p2.core.IProvisioningAgent; @@ -303,6 +304,11 @@ public String getTargetEE() { return null; } + @Override + public Stream implicitDependencies() { + return Stream.empty(); + } + } enum TestRepositories { diff --git a/tycho-extras/tycho-version-bump-plugin/src/main/java/org/eclipse/tycho/versionbump/InstallableUnitLocationUpdater.java b/tycho-extras/tycho-version-bump-plugin/src/main/java/org/eclipse/tycho/versionbump/InstallableUnitLocationUpdater.java index d172ff1baf..d04d56f349 100644 --- a/tycho-extras/tycho-version-bump-plugin/src/main/java/org/eclipse/tycho/versionbump/InstallableUnitLocationUpdater.java +++ b/tycho-extras/tycho-version-bump-plugin/src/main/java/org/eclipse/tycho/versionbump/InstallableUnitLocationUpdater.java @@ -21,6 +21,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.stream.Stream; import javax.inject.Inject; import javax.inject.Named; @@ -215,6 +216,11 @@ public String getTargetEE() { return delegate.getTargetEE(); } + @Override + public Stream implicitDependencies() { + return delegate.implicitDependencies(); + } + } private static final class LatestVersionLocation implements InstallableUnitLocation { diff --git a/tycho-its/projects/compiler.annotations/annotations.target b/tycho-its/projects/compiler.annotations/annotations.target index a3d5e3c194..94bb468dba 100644 --- a/tycho-its/projects/compiler.annotations/annotations.target +++ b/tycho-its/projects/compiler.annotations/annotations.target @@ -2,8 +2,14 @@ - + + + org.eclipse.jdt + org.eclipse.jdt.annotation + 2.3.100 + jar + org.osgi org.osgi.annotation.bundle @@ -19,4 +25,7 @@ + + + \ No newline at end of file diff --git a/tycho-its/projects/compiler.annotations/pom.xml b/tycho-its/projects/compiler.annotations/pom.xml index 152b3756a9..c9fa90fcd8 100644 --- a/tycho-its/projects/compiler.annotations/pom.xml +++ b/tycho-its/projects/compiler.annotations/pom.xml @@ -6,16 +6,8 @@ 1.0.0-SNAPSHOT eclipse-plugin - 3.0.0-SNAPSHOT - https://download.eclipse.org/releases/2022-03/ + 5.0.0-SNAPSHOT - - - featureRepo - p2 - ${repo-url} - - diff --git a/tycho-its/projects/compiler.annotations/src/test/jdt/annotations/TestJDT.java b/tycho-its/projects/compiler.annotations/src/test/jdt/annotations/TestJDT.java new file mode 100644 index 0000000000..f47ffd9bbe --- /dev/null +++ b/tycho-its/projects/compiler.annotations/src/test/jdt/annotations/TestJDT.java @@ -0,0 +1,10 @@ +package test.jdt.annotations; + +import org.eclipse.jdt.annotation.Nullable; + +public class TestJDT { + + public void callMeWithNull(@Nullable Object obj) { + + } +} diff --git a/tycho-targetplatform/src/main/java/org/eclipse/tycho/targetplatform/TargetDefinition.java b/tycho-targetplatform/src/main/java/org/eclipse/tycho/targetplatform/TargetDefinition.java index 8947038180..7577cec011 100644 --- a/tycho-targetplatform/src/main/java/org/eclipse/tycho/targetplatform/TargetDefinition.java +++ b/tycho-targetplatform/src/main/java/org/eclipse/tycho/targetplatform/TargetDefinition.java @@ -21,6 +21,7 @@ import java.util.Collection; import java.util.List; import java.util.Properties; +import java.util.stream.Stream; import org.eclipse.tycho.IArtifactFacade; import org.eclipse.tycho.MavenArtifactRepositoryReference; @@ -54,9 +55,21 @@ public interface TargetDefinition { @Override public boolean equals(Object obj); + /** + * + * @return a stream of implicit dependencies defined in the target to add as an + * additional dependency to every project + */ + Stream implicitDependencies(); + @Override public int hashCode(); + public interface ImplicitDependency { + + String getId(); + } + public interface Location { /** 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 5864add332..a026bbb9d2 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 @@ -41,6 +41,7 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.IntStream; +import java.util.stream.Stream; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -70,93 +71,94 @@ public final class TargetDefinitionFile implements TargetDefinition { public static final String ELEMENT_LOCATIONS = "locations"; private static final Map FILE_CACHE = new ConcurrentHashMap<>(); - //just for information purpose - private final String origin; + // just for information purpose + private final String origin; - private List locations; + private List locations; - private boolean hasIncludeBundles; + private boolean hasIncludeBundles; - private String targetEE; + private String targetEE; + private final List implicitDependencies; public static final String FILE_EXTENSION = ".target"; public static final String APPLICATION_TARGET = "application/target"; - private abstract static class AbstractPathLocation implements TargetDefinition.PathLocation { - private String path; + private abstract static class AbstractPathLocation implements TargetDefinition.PathLocation { + private String path; - public AbstractPathLocation(String path) { - this.path = path; - } + public AbstractPathLocation(String path) { + this.path = path; + } - @Override - public String getPath() { - return path; - } - } + @Override + public String getPath() { + return path; + } + } - private static class DirectoryTargetLocation extends AbstractPathLocation - implements TargetDefinition.DirectoryLocation { + private static class DirectoryTargetLocation extends AbstractPathLocation + implements TargetDefinition.DirectoryLocation { - public DirectoryTargetLocation(String path) { - super(path); - } + public DirectoryTargetLocation(String path) { + super(path); + } - @Override - public String getTypeDescription() { - return "Directory"; - } + @Override + public String getTypeDescription() { + return "Directory"; + } - } + } - private static class ProfileTargetPlatformLocation extends AbstractPathLocation - implements TargetDefinition.ProfileLocation { + private static class ProfileTargetPlatformLocation extends AbstractPathLocation + implements TargetDefinition.ProfileLocation { - public ProfileTargetPlatformLocation(String path) { - super(path); - } + public ProfileTargetPlatformLocation(String path) { + super(path); + } - @Override - public String getTypeDescription() { - return "Profile"; - } + @Override + public String getTypeDescription() { + return "Profile"; + } - } + } - private static class FeatureTargetPlatformLocation extends AbstractPathLocation - implements TargetDefinition.FeaturesLocation { + private static class FeatureTargetPlatformLocation extends AbstractPathLocation + implements TargetDefinition.FeaturesLocation { - private final String feature; - private final String version; + private final String feature; + private final String version; - public FeatureTargetPlatformLocation(String path, String feature, String version) { - super(path); - this.feature = feature; - this.version = version; - } + public FeatureTargetPlatformLocation(String path, String feature, String version) { + super(path); + this.feature = feature; + this.version = version; + } - @Override - public String getTypeDescription() { - return "Feature"; - } + @Override + public String getTypeDescription() { + return "Feature"; + } - @Override - public String getId() { - return feature; - } + @Override + public String getId() { + return feature; + } - @Override - public String getVersion() { - return version; - } + @Override + public String getVersion() { + return version; + } - } + } private record TargetReference(String getUri) implements TargetDefinition.TargetReferenceLocation { - @Override - public String getTypeDescription() { - return "Target"; - } - } + @Override + public String getTypeDescription() { + return "Target"; + } + } private record OSGIRepositoryLocation(String getUri, Collection getRequirements) implements TargetDefinition.RepositoryLocation { @@ -172,18 +174,18 @@ private record MavenLocation(Collection getRoots, Collection getChildren(Element element, String tagName) { - NodeList list = element.getChildNodes(); - - int length = list.getLength(); - List nodes = IntStream.range(0, length).mapToObj(list::item).toList(); - return nodes.stream().filter(Element.class::isInstance).map(Element.class::cast) - .filter(e -> e.getNodeName().equals(tagName)).toList(); - } - - private static Element getChild(Element element, String tagName) { - List list = getChildren(element, tagName); - if (list.isEmpty()) { - return null; - } - return list.get(0); - } + private static String getKey(IArtifactFacade artifact) { + if (artifact == null) { + return ""; + } + String key = artifact.getGroupId() + ":" + artifact.getArtifactId(); + String classifier = artifact.getClassifier(); + if (classifier != null && !classifier.isBlank()) { + key += ":" + classifier; + } + key += ":" + artifact.getVersion(); + return key; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("GroupId = "); + builder.append(getGroupId()); + builder.append(", ArtifactId = "); + builder.append(getArtifactId()); + builder.append(", Version = "); + builder.append(getVersion()); + builder.append(", ArtifactType = "); + builder.append(getArtifactType()); + builder.append(", IncludeDependencyScope = "); + return builder.toString(); + } + + @Override + public boolean isIgnored(IArtifactFacade artifact) { + return globalExcludes.contains(getKey(artifact)); + } + + } + + private static String getTextFromChild(Element dom, String childName, String defaultValue) { + for (Element element : getChildren(dom, childName)) { + return element.getTextContent(); + } + if (defaultValue != null) { + return defaultValue; + } + throw new TargetDefinitionSyntaxException("Missing child element '" + childName + "'"); + } + + private static List getChildren(Element element, String tagName) { + NodeList list = element.getChildNodes(); + + int length = list.getLength(); + List nodes = IntStream.range(0, length).mapToObj(list::item).toList(); + return nodes.stream().filter(Element.class::isInstance).map(Element.class::cast) + .filter(e -> e.getNodeName().equals(tagName)).toList(); + } + + private static Element getChild(Element element, String tagName) { + List list = getChildren(element, tagName); + if (list.isEmpty()) { + return null; + } + return list.get(0); + } private record IULocation(List getUnits, List getRepositories, IncludeMode getIncludeMode, boolean includeAllEnvironments, boolean includeSource, boolean includeConfigurePhase, @@ -297,76 +299,97 @@ public String toString() { } } - private TargetDefinitionFile(Document document, String origin) throws TargetDefinitionSyntaxException { - this.origin = origin; - Element dom = document.getDocumentElement(); - locations = parseLocations(dom); - hasIncludeBundles = getChild(dom, "includeBundles") != null; - targetEE = parseTargetEE(dom); - } - - public static Document parseDocument(InputStream input) - throws ParserConfigurationException, SAXException, IOException { - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - DocumentBuilder builder = factory.newDocumentBuilder(); - return builder.parse(input); - } - - public static void writeDocument(Document document, OutputStream outputStream) throws IOException { - try (OutputStream os = new BufferedOutputStream(outputStream)) { - TransformerFactory transformerFactory = TransformerFactory.newInstance(); - try { - Transformer transformer = transformerFactory.newTransformer(); - DOMSource source = new DOMSource(document); - StreamResult result = new StreamResult(os); - transformer.transform(source, result); - } catch (TransformerException e) { - throw new IOException(e); - } - } - } - - @Override - public List getLocations() { - return locations; - } - - @Override - public boolean hasIncludedBundles() { - return hasIncludeBundles; - } - - @Override - public String getOrigin() { - return origin; - } - - public static TargetDefinitionFile read(File file) { - return read(file.toURI()); - } - - public static TargetDefinitionFile read(URI uri) { - try { + private TargetDefinitionFile(Document document, String origin) throws TargetDefinitionSyntaxException { + this.origin = origin; + Element dom = document.getDocumentElement(); + locations = parseLocations(dom); + hasIncludeBundles = getChild(dom, "includeBundles") != null; + targetEE = parseTargetEE(dom); + implicitDependencies = parseImplicitDependencies(dom); + } + + private static List parseImplicitDependencies(Element dom) { + List list = new ArrayList<>(); + Element implicitDependencies = getChild(dom, "implicitDependencies"); + if (implicitDependencies != null) { + for (Element element : getChildren(implicitDependencies, "plugin")) { + String id = element.getAttribute("id"); + if (id != null && !id.isEmpty()) { + list.add(new ImplicitDependency() { + + @Override + public String getId() { + return id; + } + }); + } + } + } + return list; + } + + public static Document parseDocument(InputStream input) + throws ParserConfigurationException, SAXException, IOException { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = factory.newDocumentBuilder(); + return builder.parse(input); + } + + public static void writeDocument(Document document, OutputStream outputStream) throws IOException { + try (OutputStream os = new BufferedOutputStream(outputStream)) { + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + try { + Transformer transformer = transformerFactory.newTransformer(); + DOMSource source = new DOMSource(document); + StreamResult result = new StreamResult(os); + transformer.transform(source, result); + } catch (TransformerException e) { + throw new IOException(e); + } + } + } + + @Override + public List getLocations() { + return locations; + } + + @Override + public boolean hasIncludedBundles() { + return hasIncludeBundles; + } + + @Override + public String getOrigin() { + return origin; + } + + public static TargetDefinitionFile read(File file) { + return read(file.toURI()); + } + + public static TargetDefinitionFile read(URI uri) { + try { return FILE_CACHE.computeIfAbsent(uri.normalize(), key -> { - try { + try { try (InputStream input = openTargetStream(uri)) { return parse(parseDocument(input), getOrigin(uri)); - } catch (ParserConfigurationException e) { - throw new TargetDefinitionSyntaxException("No valid XML parser: " + e.getMessage(), e); - } catch (SAXException e) { - throw new TargetDefinitionSyntaxException( - "Target definition is not well-formed XML: " + e.getMessage(), e); - } - } catch (IOException e) { - throw new TargetDefinitionSyntaxException( - "I/O error while reading target definition file: " + e.getMessage(), e); - } - - }); - } catch (TargetDefinitionSyntaxException e) { - throw new RuntimeException("Invalid syntax in target definition " + uri + ": " + e.getMessage(), e); - } - } + } catch (ParserConfigurationException e) { + throw new TargetDefinitionSyntaxException("No valid XML parser: " + e.getMessage(), e); + } catch (SAXException e) { + throw new TargetDefinitionSyntaxException( + "Target definition is not well-formed XML: " + e.getMessage(), e); + } + } catch (IOException e) { + throw new TargetDefinitionSyntaxException( + "I/O error while reading target definition file: " + e.getMessage(), e); + } + + }); + } catch (TargetDefinitionSyntaxException e) { + throw new RuntimeException("Invalid syntax in target definition " + uri + ": " + e.getMessage(), e); + } + } private static String getOrigin(URI uri) { if (isDataUrl(uri)) { @@ -393,85 +416,89 @@ private static boolean isDataUrl(URI uri) { return "data".equals(uri.getScheme()); } - public static TargetDefinitionFile parse(Document document, String origin) { - return new TargetDefinitionFile(document, origin); - } + public static TargetDefinitionFile parse(Document document, String origin) { + return new TargetDefinitionFile(document, origin); + } - @Override - public String getTargetEE() { - return targetEE; - } + @Override + public String getTargetEE() { + return targetEE; + } + + @Override + public String toString() { + return "TargetDefinitionFile[" + origin + "]"; + } - @Override - public String toString() { - return "TargetDefinitionFile[" + origin + "]"; - } + @Override + public Stream implicitDependencies() { + return implicitDependencies.stream(); + } - /** + /** * List all target files in the given folder * * @param folder - * @return the found target files or empty array if nothing was found, folder is not a directory - * or the directory could not be read + * @return the found target files or empty array if nothing was found, folder is + * not a directory or the directory could not be read */ public static File[] listTargetFiles(File folder) { - if (folder.isDirectory()) { - File[] targetFiles = folder.listFiles(TargetDefinitionFile::isTargetFile); - if (targetFiles != null) { - return targetFiles; - } - } - return new File[0]; + if (folder.isDirectory()) { + File[] targetFiles = folder.listFiles(TargetDefinitionFile::isTargetFile); + if (targetFiles != null) { + return targetFiles; + } + } + return new File[0]; } /** * * @param file - * @return true if the given files likely denotes are targetfile based on file - * naming, false otherwise + * @return true if the given files likely denotes are targetfile + * based on file naming, false otherwise */ public static boolean isTargetFile(File file) { return file != null && file.isFile() && file.getName().toLowerCase().endsWith(TargetDefinitionFile.FILE_EXTENSION) - && !file.getName().startsWith(".polyglot."); + && !file.getName().startsWith(".polyglot."); } - + private static List parseLocations(Element dom) { - ArrayList locations = new ArrayList<>(); + ArrayList locations = new ArrayList<>(); Element locationsDom = getChild(dom, ELEMENT_LOCATIONS); - if (locationsDom != null) { - for (Element locationDom : getChildren(locationsDom, "location")) { - String type = locationDom.getAttribute("type"); - if (InstallableUnitLocation.TYPE.equals(type)) { - locations.add(parseIULocation(locationDom)); - } else if ("Directory".equals(type)) { - locations.add(new DirectoryTargetLocation(locationDom.getAttribute("path"))); - } else if ("Profile".equals(type)) { - locations.add(new ProfileTargetPlatformLocation(locationDom.getAttribute("path"))); - } else if ("Feature".equals(type)) { - locations.add(new FeatureTargetPlatformLocation(locationDom.getAttribute("path"), - locationDom.getAttribute("id"), locationDom.getAttribute("version"))); - } else if (MavenGAVLocation.TYPE.equals(type)) { - locations.add(parseMavenLocation(locationDom)); - } else if ("Target".equals(type)) { - locations.add(new TargetReference(locationDom.getAttribute("uri"))); + if (locationsDom != null) { + for (Element locationDom : getChildren(locationsDom, "location")) { + String type = locationDom.getAttribute("type"); + if (InstallableUnitLocation.TYPE.equals(type)) { + locations.add(parseIULocation(locationDom)); + } else if ("Directory".equals(type)) { + locations.add(new DirectoryTargetLocation(locationDom.getAttribute("path"))); + } else if ("Profile".equals(type)) { + locations.add(new ProfileTargetPlatformLocation(locationDom.getAttribute("path"))); + } else if ("Feature".equals(type)) { + locations.add(new FeatureTargetPlatformLocation(locationDom.getAttribute("path"), + locationDom.getAttribute("id"), locationDom.getAttribute("version"))); + } else if (MavenGAVLocation.TYPE.equals(type)) { + locations.add(parseMavenLocation(locationDom)); + } else if ("Target".equals(type)) { + locations.add(new TargetReference(locationDom.getAttribute("uri"))); } else if (TargetDefinition.RepositoryLocation.TYPE.equals(type)) { locations.add(parseRepositoryLocation(locationDom)); - } else { - locations.add(new OtherLocation(type)); - } - } - } - return Collections.unmodifiableList(locations); - } + } else { + locations.add(new OtherLocation(type)); + } + } + } + return Collections.unmodifiableList(locations); + } private static TargetDefinition.RepositoryLocation parseRepositoryLocation(Element dom) { String uri = dom.getAttribute("uri"); NodeList childNodes = dom.getChildNodes(); List requirements = IntStream.range(0, childNodes.getLength()).mapToObj(childNodes::item) .filter(Element.class::isInstance).map(Element.class::cast) - .filter(element -> element.getNodeName().equalsIgnoreCase("require")) - .flatMap(element -> { + .filter(element -> element.getNodeName().equalsIgnoreCase("require")).flatMap(element -> { String textContent = element.getTextContent(); Parameters parameters = new Parameters(textContent); return CapReqBuilder.getRequirementsFrom(parameters).stream(); @@ -479,210 +506,208 @@ private static TargetDefinition.RepositoryLocation parseRepositoryLocation(Eleme return new OSGIRepositoryLocation(uri, requirements); } - private static MavenLocation parseMavenLocation(Element dom) { - Set globalExcludes = new LinkedHashSet<>(); - for (Element element : getChildren(dom, "exclude")) { - globalExcludes.add(element.getTextContent()); - } - Collection scopes = new ArrayList<>(); - String scope = dom.getAttribute("includeDependencyScope"); - if (dom.hasAttribute("includeDependencyScopes")) { - String scopesAttribute = dom.getAttribute("includeDependencyScopes"); - for (String s : scopesAttribute.split(",")) { - scopes.add(s.strip()); - } - } else { - //backward compat ... - String SCOPE_COMPILE = "compile"; - String SCOPE_TEST = "test"; - String SCOPE_RUNTIME = "runtime"; - String SCOPE_PROVIDED = "provided"; - String SCOPE_SYSTEM = "system"; - if (scope == null || scope.isBlank() || SCOPE_COMPILE.equalsIgnoreCase(scope)) { - scopes.add(SCOPE_COMPILE); - } else if (SCOPE_PROVIDED.equalsIgnoreCase(scope)) { - scopes.add(SCOPE_PROVIDED); - scopes.add(SCOPE_COMPILE); - scopes.add(SCOPE_SYSTEM); - scopes.add(SCOPE_RUNTIME); - } else if (SCOPE_TEST.equalsIgnoreCase(scope)) { - scopes.add(SCOPE_TEST); - scopes.add(SCOPE_COMPILE); - scopes.add(SCOPE_PROVIDED); - scopes.add(SCOPE_SYSTEM); - scopes.add(SCOPE_RUNTIME); - } - } - Element featureTemplate = getChild(dom, "feature"); - return new MavenLocation(parseRoots(dom, globalExcludes), scopes, parseManifestStrategy(dom), - Boolean.parseBoolean(dom.getAttribute("includeSource")), parseInstructions(dom), + private static MavenLocation parseMavenLocation(Element dom) { + Set globalExcludes = new LinkedHashSet<>(); + for (Element element : getChildren(dom, "exclude")) { + globalExcludes.add(element.getTextContent()); + } + Collection scopes = new ArrayList<>(); + String scope = dom.getAttribute("includeDependencyScope"); + if (dom.hasAttribute("includeDependencyScopes")) { + String scopesAttribute = dom.getAttribute("includeDependencyScopes"); + for (String s : scopesAttribute.split(",")) { + scopes.add(s.strip()); + } + } else { + // backward compat ... + String SCOPE_COMPILE = "compile"; + String SCOPE_TEST = "test"; + String SCOPE_RUNTIME = "runtime"; + String SCOPE_PROVIDED = "provided"; + String SCOPE_SYSTEM = "system"; + if (scope == null || scope.isBlank() || SCOPE_COMPILE.equalsIgnoreCase(scope)) { + scopes.add(SCOPE_COMPILE); + } else if (SCOPE_PROVIDED.equalsIgnoreCase(scope)) { + scopes.add(SCOPE_PROVIDED); + scopes.add(SCOPE_COMPILE); + scopes.add(SCOPE_SYSTEM); + scopes.add(SCOPE_RUNTIME); + } else if (SCOPE_TEST.equalsIgnoreCase(scope)) { + scopes.add(SCOPE_TEST); + scopes.add(SCOPE_COMPILE); + scopes.add(SCOPE_PROVIDED); + scopes.add(SCOPE_SYSTEM); + scopes.add(SCOPE_RUNTIME); + } + } + Element featureTemplate = getChild(dom, "feature"); + return new MavenLocation(parseRoots(dom, globalExcludes), scopes, parseManifestStrategy(dom), + Boolean.parseBoolean(dom.getAttribute("includeSource")), parseInstructions(dom), parseDependencyDepth(dom, scope), parseRepositoryReferences(dom), featureTemplate, dom.getAttribute("label")); - } + } - private static IULocation parseIULocation(Element dom) { - List units = new ArrayList<>(); - for (Element unitDom : getChildren(dom, "unit")) { - String id = unitDom.getAttribute("id"); - String version = unitDom.getAttribute("version"); + private static IULocation parseIULocation(Element dom) { + List units = new ArrayList<>(); + 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<>(); - for (Element node : getChildren(dom, "repository")) { - String id = node.getAttribute("id"); - String uri = node.getAttribute("location"); - repositories.add(new Repository(id, uri)); - } - - String rawFollowRepositoryReferences = dom.getAttribute("followRepositoryReferences"); - final FollowRepositoryReferences followRepositoryReferences; - if (rawFollowRepositoryReferences == null || rawFollowRepositoryReferences.isEmpty()) { - followRepositoryReferences = FollowRepositoryReferences.DEFAULT; - } else if (Boolean.parseBoolean(rawFollowRepositoryReferences)) { - followRepositoryReferences = FollowRepositoryReferences.ENABLED; - } else { - followRepositoryReferences = FollowRepositoryReferences.DISABLED; - } - - return new IULocation(Collections.unmodifiableList(units), Collections.unmodifiableList(repositories), - parseIncludeMode(dom), Boolean.parseBoolean(dom.getAttribute("includeAllPlatforms")), - Boolean.parseBoolean(dom.getAttribute("includeSource")), - Boolean.parseBoolean(dom.getAttribute("includeConfigurePhase")), - followRepositoryReferences - ); - } - - private static String parseTargetEE(Element dom) { - Element targetJRE = getChild(dom, "targetJRE"); - if (targetJRE != null) { - Attr path = targetJRE.getAttributeNode("path"); - if (path != null) { - String pathValue = path.getValue(); - return pathValue.substring(pathValue.lastIndexOf('/') + 1); - } - } - return null; - } - - private static IncludeMode parseIncludeMode(Element dom) { - Attr attributeValue = dom.getAttributeNode("includeMode"); - if (attributeValue == null || "planner".equals(attributeValue.getTextContent())) { - return IncludeMode.PLANNER; - } else if ("slicer".equals(attributeValue.getTextContent())) { - return IncludeMode.SLICER; - } - throw new TargetDefinitionSyntaxException("Invalid value for attribute 'includeMode': " + attributeValue + ""); - } - - private static MissingManifestStrategy parseManifestStrategy(Element dom) { - String attributeValue = dom.getAttribute("missingManifest"); - if ("generate".equalsIgnoreCase(attributeValue)) { - return MissingManifestStrategy.GENERATE; - } else if ("ignore".equals(attributeValue)) { - return MissingManifestStrategy.IGNORE; - } - return MissingManifestStrategy.ERROR; - } - - private static Collection parseInstructions(Element dom) { - List list = new ArrayList<>(); - for (Element element : getChildren(dom, "instructions")) { - String reference = element.getAttribute("reference"); - String text = element.getTextContent(); - 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 Collections.unmodifiableCollection(list); - } - - private static Collection parseRoots(Element dom, Set globalExcludes) { - for (Element dependencies : getChildren(dom, "dependencies")) { - List roots = new ArrayList<>(); - for (Element dependency : getChildren(dependencies, "dependency")) { - roots.add(parseDependecyRoot(dependency, globalExcludes)); - } - return Collections.unmodifiableCollection(roots); - } - //backward compatibility for old format... - return Collections.singleton(parseDependecyRoot(dom, globalExcludes)); - } - - private static MavenDependencyRoot parseDependecyRoot(Element dom, Set globalExcludes) { - return new MavenDependencyRoot(// - getTextFromChild(dom, "groupId", null), // - getTextFromChild(dom, "artifactId", null), // - getTextFromChild(dom, "version", null), // - getTextFromChild(dom, "classifier", ""), // - getTextFromChild(dom, "type", "jar"), // - globalExcludes); - } - - private static DependencyDepth parseDependencyDepth(Element dom, String scope) { - if (dom.getAttributeNode("includeDependencyDepth") == null) { - //backward compat - if (scope == null || scope.isBlank()) { - return DependencyDepth.NONE; - } else { - return DependencyDepth.INFINITE; - } - } - String attribute = dom.getAttribute("includeDependencyDepth"); - if ("NONE".equalsIgnoreCase(attribute)) { - return DependencyDepth.NONE; - } else if ("DIRECT".equalsIgnoreCase(attribute)) { - return DependencyDepth.DIRECT; - } else if ("INFINITE".equalsIgnoreCase(attribute)) { - return DependencyDepth.INFINITE; - } - //safe default - return DependencyDepth.NONE; - } - - private static Collection parseRepositoryReferences(Element dom) { - for (Element dependencies : getChildren(dom, "repositories")) { - List list = new ArrayList<>(); - for (Element repository : getChildren(dependencies, "repository")) { - String id = getTextFromChild(repository, "id", String.valueOf(System.identityHashCode(repository))); - String url = getTextFromChild(repository, "url", null); - list.add(new MavenArtifactRepositoryReference() { - - @Override - public String getId() { - return id; - } - - @Override - public String getUrl() { - return url; - } - - }); - } - return Collections.unmodifiableCollection(list); - } - return Collections.emptyList(); - } + units.add(new Unit(id, version)); + } + final List repositories = new ArrayList<>(); + for (Element node : getChildren(dom, "repository")) { + String id = node.getAttribute("id"); + String uri = node.getAttribute("location"); + repositories.add(new Repository(id, uri)); + } + + String rawFollowRepositoryReferences = dom.getAttribute("followRepositoryReferences"); + final FollowRepositoryReferences followRepositoryReferences; + if (rawFollowRepositoryReferences == null || rawFollowRepositoryReferences.isEmpty()) { + followRepositoryReferences = FollowRepositoryReferences.DEFAULT; + } else if (Boolean.parseBoolean(rawFollowRepositoryReferences)) { + followRepositoryReferences = FollowRepositoryReferences.ENABLED; + } else { + followRepositoryReferences = FollowRepositoryReferences.DISABLED; + } + + return new IULocation(Collections.unmodifiableList(units), Collections.unmodifiableList(repositories), + parseIncludeMode(dom), Boolean.parseBoolean(dom.getAttribute("includeAllPlatforms")), + Boolean.parseBoolean(dom.getAttribute("includeSource")), + Boolean.parseBoolean(dom.getAttribute("includeConfigurePhase")), followRepositoryReferences); + } + + private static String parseTargetEE(Element dom) { + Element targetJRE = getChild(dom, "targetJRE"); + if (targetJRE != null) { + Attr path = targetJRE.getAttributeNode("path"); + if (path != null) { + String pathValue = path.getValue(); + return pathValue.substring(pathValue.lastIndexOf('/') + 1); + } + } + return null; + } + + private static IncludeMode parseIncludeMode(Element dom) { + Attr attributeValue = dom.getAttributeNode("includeMode"); + if (attributeValue == null || "planner".equals(attributeValue.getTextContent())) { + return IncludeMode.PLANNER; + } else if ("slicer".equals(attributeValue.getTextContent())) { + return IncludeMode.SLICER; + } + throw new TargetDefinitionSyntaxException("Invalid value for attribute 'includeMode': " + attributeValue + ""); + } + + private static MissingManifestStrategy parseManifestStrategy(Element dom) { + String attributeValue = dom.getAttribute("missingManifest"); + if ("generate".equalsIgnoreCase(attributeValue)) { + return MissingManifestStrategy.GENERATE; + } else if ("ignore".equals(attributeValue)) { + return MissingManifestStrategy.IGNORE; + } + return MissingManifestStrategy.ERROR; + } + + private static Collection parseInstructions(Element dom) { + List list = new ArrayList<>(); + for (Element element : getChildren(dom, "instructions")) { + String reference = element.getAttribute("reference"); + String text = element.getTextContent(); + 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 Collections.unmodifiableCollection(list); + } + + private static Collection parseRoots(Element dom, Set globalExcludes) { + for (Element dependencies : getChildren(dom, "dependencies")) { + List roots = new ArrayList<>(); + for (Element dependency : getChildren(dependencies, "dependency")) { + roots.add(parseDependecyRoot(dependency, globalExcludes)); + } + return Collections.unmodifiableCollection(roots); + } + // backward compatibility for old format... + return Collections.singleton(parseDependecyRoot(dom, globalExcludes)); + } + + private static MavenDependencyRoot parseDependecyRoot(Element dom, Set globalExcludes) { + return new MavenDependencyRoot(// + getTextFromChild(dom, "groupId", null), // + getTextFromChild(dom, "artifactId", null), // + getTextFromChild(dom, "version", null), // + getTextFromChild(dom, "classifier", ""), // + getTextFromChild(dom, "type", "jar"), // + globalExcludes); + } + + private static DependencyDepth parseDependencyDepth(Element dom, String scope) { + if (dom.getAttributeNode("includeDependencyDepth") == null) { + // backward compat + if (scope == null || scope.isBlank()) { + return DependencyDepth.NONE; + } else { + return DependencyDepth.INFINITE; + } + } + String attribute = dom.getAttribute("includeDependencyDepth"); + if ("NONE".equalsIgnoreCase(attribute)) { + return DependencyDepth.NONE; + } else if ("DIRECT".equalsIgnoreCase(attribute)) { + return DependencyDepth.DIRECT; + } else if ("INFINITE".equalsIgnoreCase(attribute)) { + return DependencyDepth.INFINITE; + } + // safe default + return DependencyDepth.NONE; + } + + private static Collection parseRepositoryReferences(Element dom) { + for (Element dependencies : getChildren(dom, "repositories")) { + List list = new ArrayList<>(); + for (Element repository : getChildren(dependencies, "repository")) { + String id = getTextFromChild(repository, "id", String.valueOf(System.identityHashCode(repository))); + String url = getTextFromChild(repository, "url", null); + list.add(new MavenArtifactRepositoryReference() { + + @Override + public String getId() { + return id; + } + + @Override + public String getUrl() { + return url; + } + + }); + } + return Collections.unmodifiableCollection(list); + } + return Collections.emptyList(); + } }