diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index 81b5270142..bc0fe6a86c 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -4,6 +4,30 @@ This page describes the noteworthy improvements provided by each release of Ecli
## 3.0.0 (under development)
+### Support for PDE Declarative Component Annotation progressing
+
+One can enable either global or per project the generation of component xmls in PDE. Until now it was required for Tycho to still import the annotation package even though `classpath=true` was set, beside that one needs to check in the generated xmls.
+
+Tycho now has improved support for this with the following:
+
+1. if there is a `.settings/org.eclipse.pde.ds.annotations.prefs` in the project, tycho adapts the settings there and if `classpath=true` is set no more imports are required.
+2. one can enable a new `tycho-ds-plugin` where global default settings can be configured if project settings are not present, the below shows an example with default values:
+```
+
+ org.eclipse.tycho
+ tycho-ds-plugin
+ ${tycho-version}
+
+ true
+ 1.3
+ false
+ OSGI-INF
+ false
+
+
+```
+If the `tycho-ds-plugin` is enabled for a project it generated the necessary xml files if not already present in the project.
+
### Improved P2 transport for more efficiently http-cache handling and improved offline mode
P2 default transport is more designed as a weak cache that assumes the user is always online.
diff --git a/pom.xml b/pom.xml
index 3b6f3c7d6f..2ce4ab37c4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -476,6 +476,7 @@
tycho-source-plugin
target-platform-configuration
tycho-maven-plugin
+ tycho-ds-plugin
tycho-surefire
diff --git a/tycho-bundles/org.eclipse.tycho.p2.resolver.impl.test/src/test/java/org/eclipse/tycho/p2/target/TargetPlatformTest.java b/tycho-bundles/org.eclipse.tycho.p2.resolver.impl.test/src/test/java/org/eclipse/tycho/p2/target/TargetPlatformTest.java
index e2a13d3f7c..b6b34d7dd8 100644
--- a/tycho-bundles/org.eclipse.tycho.p2.resolver.impl.test/src/test/java/org/eclipse/tycho/p2/target/TargetPlatformTest.java
+++ b/tycho-bundles/org.eclipse.tycho.p2.resolver.impl.test/src/test/java/org/eclipse/tycho/p2/target/TargetPlatformTest.java
@@ -27,6 +27,7 @@
import org.eclipse.equinox.p2.metadata.IInstallableUnit;
import org.eclipse.tycho.ArtifactKey;
import org.eclipse.tycho.ArtifactType;
+import org.eclipse.tycho.artifacts.DependencyResolutionException;
import org.eclipse.tycho.artifacts.IllegalArtifactReferenceException;
import org.eclipse.tycho.artifacts.TargetPlatform;
import org.junit.Before;
@@ -159,9 +160,9 @@ public void testResolveFeature() throws Exception {
@Test
public void testResolveUnknownType() throws Exception {
- IllegalArtifactReferenceException e = assertThrows(IllegalArtifactReferenceException.class,
+ DependencyResolutionException e = assertThrows(DependencyResolutionException.class,
() -> subject.resolveArtifact("invalid-type", "unit", ANY_VERSION));
- assertTrue(e.getMessage().contains("Unknown artifact type"));
+ assertTrue(e.getMessage().contains("invalid-type"));
}
private FinalTargetPlatformImpl createTP() {
diff --git a/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/resolver/P2ResolverImpl.java b/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/resolver/P2ResolverImpl.java
index ccc7edde6f..109a46b721 100644
--- a/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/resolver/P2ResolverImpl.java
+++ b/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/resolver/P2ResolverImpl.java
@@ -45,6 +45,7 @@
import org.eclipse.equinox.p2.metadata.VersionRange;
import org.eclipse.equinox.p2.metadata.expression.IMatchExpression;
import org.eclipse.equinox.p2.publisher.eclipse.BundlesAction;
+import org.eclipse.equinox.p2.query.IQuery;
import org.eclipse.equinox.p2.query.IQueryResult;
import org.eclipse.equinox.p2.query.QueryUtil;
import org.eclipse.equinox.spi.p2.publisher.PublisherHelper;
@@ -148,11 +149,9 @@ public Map resolveArtifactDependencies(Ta
for (ArtifactKey artifactKey : artifacts) {
QueryableCollection queriable = new QueryableCollection(targetPlatform.getInstallableUnits());
VersionRange range = new VersionRange(artifactKey.getVersion());
- IRequirement requirement = MetadataFactory.createRequirement(IInstallableUnit.NAMESPACE_IU_ID,
- artifactKey.getId(), range, null, 1 /* min */, Integer.MAX_VALUE /* max */,
- false /* greedy */);
- IQueryResult result = queriable
- .query(QueryUtil.createLatestQuery(QueryUtil.createMatchQuery(requirement.getMatches())), monitor);
+ IQuery query = ArtifactTypeHelper.createQueryFor(artifactKey.getType(),
+ artifactKey.getId(), range);
+ IQueryResult result = queriable.query(QueryUtil.createLatestQuery(query), monitor);
roots.addAll(result.toUnmodifiableSet());
}
Map results = new LinkedHashMap<>();
diff --git a/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/target/ArtifactTypeHelper.java b/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/target/ArtifactTypeHelper.java
index 94979130c9..4843ed6727 100644
--- a/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/target/ArtifactTypeHelper.java
+++ b/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/target/ArtifactTypeHelper.java
@@ -42,11 +42,8 @@ public class ArtifactTypeHelper {
*
* @param type
* Eclipse artifact type as defined in Tycho's {@link ArtifactType}
- * @throws IllegalArtifactReferenceException
- * if the given artifact type is unknown
*/
- public static IQuery createQueryFor(String type, String id, VersionRange versionRange)
- throws IllegalArtifactReferenceException {
+ public static IQuery createQueryFor(String type, String id, VersionRange versionRange) {
if (ArtifactType.TYPE_ECLIPSE_PLUGIN.equals(type)) {
return QueryUtil.createMatchQuery(createBundleRequirement(id, versionRange).getMatches());
@@ -63,7 +60,10 @@ public static IQuery createQueryFor(String type, String id, Ve
return QueryUtil.createIUQuery(id, versionRange);
} else {
- throw new IllegalArtifactReferenceException("Unknown artifact type \"" + type + "\"");
+
+ IRequirement requirement = MetadataFactory.createRequirement(type, id, versionRange, null,
+ 1 /* min */, Integer.MAX_VALUE /* max */, false /* greedy */);
+ return QueryUtil.createMatchQuery(requirement.getMatches());
}
}
diff --git a/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/target/TargetPlatformBaseImpl.java b/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/target/TargetPlatformBaseImpl.java
index 8f882ed8b2..e284019a4d 100644
--- a/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/target/TargetPlatformBaseImpl.java
+++ b/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/target/TargetPlatformBaseImpl.java
@@ -24,6 +24,7 @@
import org.eclipse.equinox.p2.metadata.IInstallableUnit;
import org.eclipse.equinox.p2.metadata.Version;
import org.eclipse.equinox.p2.metadata.VersionRange;
+import org.eclipse.tycho.ArtifactType;
import org.eclipse.tycho.DefaultArtifactKey;
import org.eclipse.tycho.ReactorProjectIdentities;
import org.eclipse.tycho.artifacts.DependencyResolutionException;
@@ -101,7 +102,10 @@ public final org.eclipse.tycho.ArtifactKey resolveArtifact(String type, String i
} else {
resolvedUnit = resolveUnit(type, id, ArtifactMatcher.parseAsOSGiVersion(version));
}
- return new DefaultArtifactKey(type, id, resolvedUnit.getVersion().toString());
+ if (ArtifactType.TYPE_ECLIPSE_FEATURE.equals(type)) {
+ return new DefaultArtifactKey(type, id, resolvedUnit.getVersion().toString());
+ }
+ return new DefaultArtifactKey(type, resolvedUnit.getId(), resolvedUnit.getVersion().toString());
}
@Override
diff --git a/tycho-core/src/main/java/org/eclipse/tycho/artifacts/configuration/DeclarativeServiceConfigurationReader.java b/tycho-core/src/main/java/org/eclipse/tycho/artifacts/configuration/DeclarativeServiceConfigurationReader.java
new file mode 100644
index 0000000000..6faa5aec18
--- /dev/null
+++ b/tycho-core/src/main/java/org/eclipse/tycho/artifacts/configuration/DeclarativeServiceConfigurationReader.java
@@ -0,0 +1,103 @@
+/*******************************************************************************
+ * Copyright (c) 2022 Christoph Läubrich and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christoph Läubrich - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tycho.artifacts.configuration;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Properties;
+
+import org.apache.maven.model.Plugin;
+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.codehaus.plexus.util.xml.Xpp3Dom;
+import org.eclipse.tycho.core.DeclarativeServicesConfiguration;
+import org.osgi.framework.Version;
+
+@Component(role = DeclarativeServiceConfigurationReader.class)
+public class DeclarativeServiceConfigurationReader {
+
+ public static final String DEFAULT_ADD_TO_CLASSPATH = "true";
+ public static final String DEFAULT_DS_VERSION = "1.3";
+ private static final String PROPERTY_CLASSPATH = "classpath";
+ private static final String PROPERTY_DS_VERSION = "dsVersion";
+ private static final String PROPERTY_ENABLED = "enabled";
+
+ private static final String PDE_DS_ANNOTATIONS_PREFS = ".settings/org.eclipse.pde.ds.annotations.prefs";
+
+ @Requirement
+ private Logger logger;
+
+ public DeclarativeServicesConfiguration getConfiguration(MavenProject mavenProject) throws IOException {
+ Properties settings = getProjectSettings(mavenProject.getBasedir(), getMojoSettings(mavenProject, logger),
+ mavenProject, logger);
+ if (Boolean.parseBoolean(settings.getProperty(PROPERTY_ENABLED))) {
+ return new DeclarativeServicesConfiguration() {
+
+ @Override
+ public boolean isAddToClasspath() {
+ return Boolean.parseBoolean(settings.getProperty(PROPERTY_CLASSPATH, DEFAULT_ADD_TO_CLASSPATH));
+ }
+
+ @Override
+ public Version getSpecificationVersion() {
+ String property = settings.getProperty(PROPERTY_DS_VERSION, DEFAULT_DS_VERSION);
+ if (property.startsWith("V")) {
+ property = property.substring(1).replace('_', '.');
+ }
+ return Version.parseVersion(property);
+ }
+ };
+ }
+ return null;
+ }
+
+ private static Properties getProjectSettings(File basedir, Properties mojoProperties, MavenProject mavenProject,
+ Logger logger) throws FileNotFoundException, IOException {
+ Properties properties = new Properties(mojoProperties);
+ File prefs = new File(basedir, PDE_DS_ANNOTATIONS_PREFS);
+ if (prefs.exists()) {
+ try (FileInputStream stream = new FileInputStream(prefs)) {
+ properties.load(stream);
+ logger.debug("declarative-services project configuration for " + mavenProject.toString() + ":"
+ + System.lineSeparator() + properties);
+ }
+ }
+ return properties;
+ }
+
+ private static Properties getMojoSettings(MavenProject project, Logger logger) {
+ Properties properties = new Properties();
+ Plugin plugin = project.getPlugin("org.eclipse.tycho:tycho-ds-plugin");
+ if (plugin != null) {
+ Xpp3Dom configuration = (Xpp3Dom) plugin.getConfiguration();
+ if (configuration != null) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("declarative-services mojo configuration for " + project.toString() + ":"
+ + System.lineSeparator() + configuration.toString());
+ }
+ setProperty(properties, PROPERTY_CLASSPATH, configuration.getAttribute(PROPERTY_CLASSPATH));
+ setProperty(properties, PROPERTY_DS_VERSION, configuration.getAttribute(PROPERTY_DS_VERSION));
+ setProperty(properties, PROPERTY_ENABLED, configuration.getAttribute(PROPERTY_ENABLED));
+ }
+ }
+ return properties;
+ }
+
+ private static void setProperty(Properties properties, String key, String attribute) {
+ if (attribute != null && !attribute.isEmpty()) {
+ properties.setProperty(key, attribute);
+ }
+ }
+}
diff --git a/tycho-core/src/main/java/org/eclipse/tycho/core/DeclarativeServicesConfiguration.java b/tycho-core/src/main/java/org/eclipse/tycho/core/DeclarativeServicesConfiguration.java
new file mode 100644
index 0000000000..8b551ae9aa
--- /dev/null
+++ b/tycho-core/src/main/java/org/eclipse/tycho/core/DeclarativeServicesConfiguration.java
@@ -0,0 +1,28 @@
+/*******************************************************************************
+ * Copyright (c) 2022 Christoph Läubrich and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christoph Läubrich - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tycho.core;
+
+import org.osgi.framework.Version;
+
+public interface DeclarativeServicesConfiguration {
+
+ /**
+ * Controls if the DS components annotations are made available on the compile-classpath, this
+ * means no explicit import is required.
+ */
+ boolean isAddToClasspath();
+
+ /**
+ * Controls the declarative services specification version to use.
+ */
+ Version getSpecificationVersion();
+
+}
diff --git a/tycho-core/src/main/java/org/eclipse/tycho/core/osgitools/OsgiBundleProject.java b/tycho-core/src/main/java/org/eclipse/tycho/core/osgitools/OsgiBundleProject.java
index 73bc78bc4a..ea3280cc4f 100644
--- a/tycho-core/src/main/java/org/eclipse/tycho/core/osgitools/OsgiBundleProject.java
+++ b/tycho-core/src/main/java/org/eclipse/tycho/core/osgitools/OsgiBundleProject.java
@@ -38,6 +38,7 @@
import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.component.annotations.Requirement;
import org.codehaus.plexus.logging.Logger;
+import org.eclipse.equinox.spi.p2.publisher.PublisherHelper;
import org.eclipse.osgi.container.Module;
import org.eclipse.osgi.container.ModuleContainer;
import org.eclipse.osgi.container.ModuleRevision;
@@ -47,16 +48,21 @@
import org.eclipse.tycho.ArtifactDescriptor;
import org.eclipse.tycho.ArtifactKey;
import org.eclipse.tycho.ArtifactType;
+import org.eclipse.tycho.DefaultArtifactKey;
import org.eclipse.tycho.PackagingType;
import org.eclipse.tycho.ReactorProject;
import org.eclipse.tycho.TychoConstants;
import org.eclipse.tycho.artifacts.DependencyArtifacts;
+import org.eclipse.tycho.artifacts.DependencyResolutionException;
+import org.eclipse.tycho.artifacts.IllegalArtifactReferenceException;
import org.eclipse.tycho.artifacts.TargetPlatform;
+import org.eclipse.tycho.artifacts.configuration.DeclarativeServiceConfigurationReader;
import org.eclipse.tycho.classpath.ClasspathEntry;
import org.eclipse.tycho.classpath.ClasspathEntry.AccessRule;
import org.eclipse.tycho.core.ArtifactDependencyVisitor;
import org.eclipse.tycho.core.ArtifactDependencyWalker;
import org.eclipse.tycho.core.BundleProject;
+import org.eclipse.tycho.core.DeclarativeServicesConfiguration;
import org.eclipse.tycho.core.PluginDescription;
import org.eclipse.tycho.core.TargetPlatformConfiguration;
import org.eclipse.tycho.core.TychoProject;
@@ -118,6 +124,9 @@ public class OsgiBundleProject extends AbstractTychoProject implements BundlePro
@Requirement
private EquinoxServiceFactory equinox;
+ @Requirement
+ private DeclarativeServiceConfigurationReader dsConfigReader;
+
@Override
public ArtifactDependencyWalker getDependencyWalker(ReactorProject project, TargetEnvironment environment) {
return getDependencyWalker(project);
@@ -508,6 +517,27 @@ private void addExtraClasspathEntries(List classpath, ReactorPro
.add(new DefaultClasspathEntry(project, projectKey, Collections.singletonList(location), null));
}
}
+ try {
+ DeclarativeServicesConfiguration configuration = dsConfigReader.getConfiguration(getMavenProject(project));
+ if (configuration != null && configuration.isAddToClasspath()) {
+ TargetPlatform tp = TychoProjectUtils.getTargetPlatform(project);
+ org.osgi.framework.Version specificationVersion = configuration.getSpecificationVersion();
+ ArtifactKey dsJar = tp.resolveArtifact(PublisherHelper.CAPABILITY_NS_JAVA_PACKAGE,
+ "org.osgi.service.component.annotations",
+ "[" + specificationVersion + "," + (specificationVersion.getMajor() + 1) + ".0.0)");
+ File location = tp.getArtifactLocation(
+ new DefaultArtifactKey(ArtifactType.TYPE_ECLIPSE_PLUGIN, dsJar.getId(), dsJar.getVersion()));
+ logger.debug("Resolved declarative service specification " + specificationVersion + " to "
+ + dsJar.getId() + " " + dsJar.getVersion() + " " + location);
+ DefaultAccessRule rule = new DefaultAccessRule("org/osgi/service/component/annotations/*", false);
+ classpath.add(new DefaultClasspathEntry(project, getArtifactKey(project),
+ Collections.singletonList(location), List.of(rule)));
+ }
+ } catch (IOException e) {
+ logger.warn("Can't read Declarative Services Configuration: " + e.getMessage(), e);
+ } catch (IllegalArtifactReferenceException | DependencyResolutionException e) {
+ logger.warn("Can't find declarative service specification in target platform: " + e.getMessage(), e);
+ }
}
protected DefaultClasspathEntry addBundleToClasspath(ArtifactDescriptor matchingBundle, String path) {
diff --git a/tycho-ds-plugin/pom.xml b/tycho-ds-plugin/pom.xml
new file mode 100644
index 0000000000..fce62c7a6b
--- /dev/null
+++ b/tycho-ds-plugin/pom.xml
@@ -0,0 +1,42 @@
+
+ 4.0.0
+
+ org.eclipse.tycho
+ tycho
+ 3.0.0-SNAPSHOT
+
+ tycho-ds-plugin
+ maven-plugin
+
+ Tycho OSGi Declarative Services Plugin
+ A plugin for handling OSGi Declarative Services
+
+
+
+ org.apache.maven
+ maven-compat
+
+
+ org.apache.maven
+ maven-plugin-api
+
+
+ org.apache.maven.plugin-tools
+ maven-plugin-annotations
+
+
+ org.codehaus.plexus
+ plexus-utils
+
+
+ org.eclipse.tycho
+ tycho-core
+ ${project.version}
+
+
+ biz.aQute.bnd
+ biz.aQute.bndlib
+ 6.2.0
+
+
+
\ No newline at end of file
diff --git a/tycho-ds-plugin/src/main/java/org/eclipse/tycho/ds/DeclarativeServicesMojo.java b/tycho-ds-plugin/src/main/java/org/eclipse/tycho/ds/DeclarativeServicesMojo.java
new file mode 100644
index 0000000000..4d86a30b6a
--- /dev/null
+++ b/tycho-ds-plugin/src/main/java/org/eclipse/tycho/ds/DeclarativeServicesMojo.java
@@ -0,0 +1,141 @@
+/*******************************************************************************
+ * Copyright (c) 2021 Christoph Läubrich and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christoph Läubrich - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tycho.ds;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.io.FilenameUtils;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.plugins.annotations.Component;
+import org.apache.maven.plugins.annotations.LifecyclePhase;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.plugins.annotations.ResolutionScope;
+import org.apache.maven.project.MavenProject;
+import org.eclipse.tycho.artifacts.configuration.DeclarativeServiceConfigurationReader;
+import org.eclipse.tycho.classpath.ClasspathEntry;
+import org.eclipse.tycho.core.DeclarativeServicesConfiguration;
+import org.eclipse.tycho.core.TychoProject;
+import org.eclipse.tycho.core.osgitools.DefaultReactorProject;
+import org.eclipse.tycho.core.osgitools.OsgiBundleProject;
+
+import aQute.bnd.component.DSAnnotations;
+import aQute.bnd.osgi.Analyzer;
+import aQute.bnd.osgi.Jar;
+import aQute.bnd.osgi.Resource;
+
+/**
+ * This mojo could be added to a build if validation of the classpath is desired
+ * before the compile-phase.
+ */
+@Mojo(name = "declarative-services", defaultPhase = LifecyclePhase.PROCESS_CLASSES, requiresDependencyResolution = ResolutionScope.COMPILE, threadSafe = true)
+public class DeclarativeServicesMojo extends AbstractMojo {
+
+ /**
+ * Controls if the DS components annotations are made available on the
+ * compile-classpath, this means no explicit import is required.
+ */
+ @Parameter(property = "tycho.ds.classpath", defaultValue = DeclarativeServiceConfigurationReader.DEFAULT_ADD_TO_CLASSPATH)
+ private boolean classpath = Boolean.parseBoolean(DeclarativeServiceConfigurationReader.DEFAULT_ADD_TO_CLASSPATH);
+ /**
+ * Controls the declarative services specification version to use.
+ */
+ @Parameter(property = "tycho.ds.version", defaultValue = DeclarativeServiceConfigurationReader.DEFAULT_DS_VERSION)
+ private String dsVersion = DeclarativeServiceConfigurationReader.DEFAULT_DS_VERSION;
+
+ /**
+ * Enables the processing of declarative services by Tycho, this could be
+ * overridden by project specific configuration
+ */
+ @Parameter(property = "tycho.ds.enabled", defaultValue = "false")
+ private boolean enabled = false;
+
+ /**
+ * Skips the generation of any DS processing regardless of project configuration
+ */
+ @Parameter(property = "tycho.ds.skip", defaultValue = "false")
+ private boolean skip = false;
+
+ /**
+ * The desired path where to place component definitions
+ */
+ @Parameter(property = "tycho.ds.path", defaultValue = "OSGI-INF")
+ private String path = "OSGI-INF";
+
+ @Parameter(property = "project", readonly = true)
+ protected MavenProject project;
+
+ @Component(role = TychoProject.class)
+ private Map projectTypes;
+
+ @Component
+ private DeclarativeServiceConfigurationReader configurationReader;
+
+ @Override
+ public void execute() throws MojoExecutionException, MojoFailureException {
+ if (skip) {
+ return;
+ }
+ TychoProject projectType = projectTypes.get(project.getPackaging());
+ if (projectType instanceof OsgiBundleProject) {
+ try {
+ DeclarativeServicesConfiguration configuration = configurationReader.getConfiguration(project);
+ if (configuration == null) {
+ // nothing to do
+ return;
+ }
+ File outputDirectory = new File(project.getBuild().getOutputDirectory());
+ File targetDirectory = new File(outputDirectory, path);
+ File projectBaseDir = new File(project.getBasedir(), path);
+ try (Jar mavenProjectJar = new Jar(project.getName(), outputDirectory, null);
+ Analyzer analyzer = new Analyzer(mavenProjectJar)) {
+ Map directory = analyzer.getJar().getDirectory("OSGI-INF");
+ if (directory != null) {
+ // clear any existing entries
+ directory.clear();
+ }
+ OsgiBundleProject bundleProject = (OsgiBundleProject) projectType;
+ List classpath = bundleProject.getClasspath(DefaultReactorProject.adapt(project));
+ for (ClasspathEntry entry : classpath) {
+ analyzer.addClasspath(entry.getLocations());
+ }
+ analyzer.addBasicPlugin(new DSAnnotations());
+ analyzer.analyze();
+ String components = analyzer.getProperty("Service-Component");
+ if (components == null || components.isBlank()) {
+ // nothing to do...
+ return;
+ }
+ for (String component : components.split(",\\s*")) {
+ String name = FilenameUtils.getName(component);
+ if (new File(projectBaseDir, name).isFile()) {
+ // this is an exiting component definition, we should not mess with that...
+ continue;
+ }
+ Resource resource = analyzer.getJar().getResource(component);
+ if (resource != null) {
+ File file = new File(targetDirectory, name);
+ file.getParentFile().mkdirs();
+ resource.write(file);
+ }
+ }
+ }
+ } catch (Exception e) {
+ throw new MojoFailureException("generation of ds components failed: " + e.getMessage(), e);
+ }
+ }
+ }
+
+}
diff --git a/tycho-its/projects/tycho-ds/.settings/org.eclipse.pde.ds.annotations.prefs b/tycho-its/projects/tycho-ds/.settings/org.eclipse.pde.ds.annotations.prefs
new file mode 100644
index 0000000000..47c495fc68
--- /dev/null
+++ b/tycho-its/projects/tycho-ds/.settings/org.eclipse.pde.ds.annotations.prefs
@@ -0,0 +1,8 @@
+#classpath=true
+#dsVersion=V1_3
+#eclipse.preferences.version=1
+enabled=true
+#generateBundleActivationPolicyLazy=true
+#path=OSGI-INF
+#validationErrorLevel=error
+#validationErrorLevel.missingImplicitUnbindMethod=error
diff --git a/tycho-its/projects/tycho-ds/META-INF/MANIFEST.MF b/tycho-its/projects/tycho-ds/META-INF/MANIFEST.MF
new file mode 100644
index 0000000000..d6767c0406
--- /dev/null
+++ b/tycho-its/projects/tycho-ds/META-INF/MANIFEST.MF
@@ -0,0 +1,11 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Tycho DS
+Bundle-SymbolicName: tycho.ds
+Bundle-Version: 1.0.0.qualifier
+Automatic-Module-Name: tycho.ds
+Bundle-RequiredExecutionEnvironment: JavaSE-11
+Service-Component: OSGI-INF/tycho.ds.TestComponent.xml,
+ OSGI-INF/tycho.ds.TestComponent2.xml
+Bundle-ActivationPolicy: lazy
+Import-Package: org.osgi.framework
diff --git a/tycho-its/projects/tycho-ds/OSGI-INF/tycho.ds.TestComponent2.xml b/tycho-its/projects/tycho-ds/OSGI-INF/tycho.ds.TestComponent2.xml
new file mode 100644
index 0000000000..35f1e153fb
--- /dev/null
+++ b/tycho-its/projects/tycho-ds/OSGI-INF/tycho.ds.TestComponent2.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/tycho-its/projects/tycho-ds/build.properties b/tycho-its/projects/tycho-ds/build.properties
new file mode 100644
index 0000000000..cb80418aa6
--- /dev/null
+++ b/tycho-its/projects/tycho-ds/build.properties
@@ -0,0 +1,6 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ OSGI-INF/tycho.ds.TestComponent.xml,\
+ OSGI-INF/tycho.ds.TestComponent2.xml
diff --git a/tycho-its/projects/tycho-ds/pom.xml b/tycho-its/projects/tycho-ds/pom.xml
new file mode 100644
index 0000000000..f25a4c624e
--- /dev/null
+++ b/tycho-its/projects/tycho-ds/pom.xml
@@ -0,0 +1,41 @@
+
+
+ 4.0.0
+ org.tycho.its
+ tycho.ds
+ 1.0.0-SNAPSHOT
+ eclipse-plugin
+
+ 3.0.0-SNAPSHOT
+ https://download.eclipse.org/releases/2022-03/
+
+
+
+
+ featureRepo
+ p2
+ ${repo-url}
+
+
+
+
+
+ org.eclipse.tycho
+ tycho-maven-plugin
+ ${tycho-version}
+ true
+
+
+ org.eclipse.tycho
+ tycho-ds-plugin
+ ${tycho-version}
+
+ true
+ 1.3
+ false
+ OSGI-INF
+
+
+
+
+
diff --git a/tycho-its/projects/tycho-ds/src/tycho/ds/TestComponent.java b/tycho-its/projects/tycho-ds/src/tycho/ds/TestComponent.java
new file mode 100644
index 0000000000..f2773f6103
--- /dev/null
+++ b/tycho-its/projects/tycho-ds/src/tycho/ds/TestComponent.java
@@ -0,0 +1,19 @@
+package tycho.ds;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+
+@Component
+public class TestComponent {
+
+ @Reference
+ private Runnable service;
+
+ @Activate
+ public void activate(BundleContext bundleContext) {
+
+ }
+
+}
diff --git a/tycho-its/projects/tycho-ds/src/tycho/ds/TestComponent2.java b/tycho-its/projects/tycho-ds/src/tycho/ds/TestComponent2.java
new file mode 100644
index 0000000000..08a590c3da
--- /dev/null
+++ b/tycho-its/projects/tycho-ds/src/tycho/ds/TestComponent2.java
@@ -0,0 +1,19 @@
+package tycho.ds;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+
+@Component
+public class TestComponent2 {
+
+ @Reference
+ private Runnable service;
+
+ @Activate
+ public void activate(BundleContext bundleContext) {
+
+ }
+
+}
diff --git a/tycho-its/src/test/java/org/eclipse/tycho/test/compiler/CompilerClasspathEntryTest.java b/tycho-its/src/test/java/org/eclipse/tycho/test/compiler/CompilerClasspathEntryTest.java
index eb4100c6ab..91bbf6886a 100644
--- a/tycho-its/src/test/java/org/eclipse/tycho/test/compiler/CompilerClasspathEntryTest.java
+++ b/tycho-its/src/test/java/org/eclipse/tycho/test/compiler/CompilerClasspathEntryTest.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2021 Christoph Läubrich and others.
+ * Copyright (c) 2021, 2022 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
@@ -12,29 +12,48 @@
*******************************************************************************/
package org.eclipse.tycho.test.compiler;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+
import org.apache.maven.it.Verifier;
import org.eclipse.tycho.test.AbstractTychoIntegrationTest;
+import org.eclipse.tycho.test.util.EnvironmentUtil;
import org.junit.Test;
public class CompilerClasspathEntryTest extends AbstractTychoIntegrationTest {
- @Test
- public void testJUnit4Container() throws Exception {
- Verifier verifier = getVerifier("compiler.junitcontainer/junit4-in-bundle", true);
- verifier.executeGoal("test");
- verifier.verifyErrorFreeLog();
- }
-
- @Test
- public void testJUnit4ContainerWithDependencies() throws Exception {
- Verifier verifier = getVerifier("compiler.junitcontainer/junit4-in-bundle-with-dependencies", true);
- verifier.executeGoal("test");
- verifier.verifyErrorFreeLog();
- }
-
- public void testLibEntry() throws Exception {
- Verifier verifier = getVerifier("compiler.libentry/my.bundle", false);
- verifier.executeGoal("compile");
- verifier.verifyErrorFreeLog();
- }
+ @Test
+ public void testJUnit4Container() throws Exception {
+ Verifier verifier = getVerifier("compiler.junitcontainer/junit4-in-bundle", true);
+ verifier.executeGoal("test");
+ verifier.verifyErrorFreeLog();
+ }
+
+ @Test
+ public void testJUnit4ContainerWithDependencies() throws Exception {
+ Verifier verifier = getVerifier("compiler.junitcontainer/junit4-in-bundle-with-dependencies", true);
+ verifier.executeGoal("test");
+ verifier.verifyErrorFreeLog();
+ }
+
+ @Test
+ public void testLibEntry() throws Exception {
+ Verifier verifier = getVerifier("compiler.libentry/my.bundle", false);
+ verifier.executeGoal("compile");
+ verifier.verifyErrorFreeLog();
+ }
+
+ @Test
+ public void testDSComponents() throws Exception {
+ Verifier verifier = getVerifier("tycho-ds", false, true);
+ verifier.setSystemProperty("repo-url", EnvironmentUtil.ECLIPSE_LATEST);
+ verifier.executeGoal("package");
+ verifier.verifyErrorFreeLog();
+ File generated = new File(verifier.getBasedir(), "target/classes/OSGI-INF");
+ assertTrue(new File(generated, "tycho.ds.TestComponent.xml").isFile());
+ assertFalse(new File(generated, "tycho.ds.TestComponent2.xml").isFile());
+ }
+
}
diff --git a/tycho-maven-plugin/src/main/resources/META-INF/plexus/components.xml b/tycho-maven-plugin/src/main/resources/META-INF/plexus/components.xml
index a8df33b5a6..7bae5ba7c8 100644
--- a/tycho-maven-plugin/src/main/resources/META-INF/plexus/components.xml
+++ b/tycho-maven-plugin/src/main/resources/META-INF/plexus/components.xml
@@ -27,6 +27,9 @@
org.eclipse.tycho:tycho-compiler-plugin:${project.version}:compile
+
+ org.eclipse.tycho:tycho-ds-plugin:${project.version}:declarative-services
+
org.apache.maven.plugins:maven-resources-plugin:${resources-plugin.version}:testResources
diff --git a/tycho-packaging-plugin/src/main/java/org/eclipse/tycho/packaging/IncludeValidationHelper.java b/tycho-packaging-plugin/src/main/java/org/eclipse/tycho/packaging/IncludeValidationHelper.java
index 292c69a319..cf70389c3d 100644
--- a/tycho-packaging-plugin/src/main/java/org/eclipse/tycho/packaging/IncludeValidationHelper.java
+++ b/tycho-packaging-plugin/src/main/java/org/eclipse/tycho/packaging/IncludeValidationHelper.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2012 SAP AG and others.
+ * Copyright (c) 2012, 2022 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
@@ -9,6 +9,7 @@
*
* Contributors:
* SAP AG - initial API and implementation
+ * Christoph Läubrich - also check for files in the classes output directory
*******************************************************************************/
package org.eclipse.tycho.packaging;
@@ -18,6 +19,7 @@
import java.util.Arrays;
import java.util.List;
+import org.apache.maven.model.Build;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.component.annotations.Component;
@@ -29,65 +31,79 @@
@Component(role = IncludeValidationHelper.class)
public class IncludeValidationHelper {
- @Requirement
- private Logger log;
+ @Requirement
+ private Logger log;
- public IncludeValidationHelper() {
- }
+ public IncludeValidationHelper() {
+ }
- public IncludeValidationHelper(Logger log) {
- this.log = log;
- }
+ public IncludeValidationHelper(Logger log) {
+ this.log = log;
+ }
- public void checkBinIncludesExist(MavenProject project, BuildProperties buildProperties, boolean strict,
- String... ignoredIncludes) throws MojoExecutionException {
- checkIncludesExist("bin.includes", buildProperties.getBinIncludes(), project, strict, ignoredIncludes);
- }
+ public void checkBinIncludesExist(MavenProject project, BuildProperties buildProperties, boolean strict,
+ String... ignoredIncludes) throws MojoExecutionException {
+ checkIncludesExist("bin.includes", buildProperties.getBinIncludes(), project, strict, ignoredIncludes);
+ }
- public void checkSourceIncludesExist(MavenProject project, BuildProperties buildProperties, boolean strict)
- throws MojoExecutionException {
- checkIncludesExist("src.includes", buildProperties.getSourceIncludes(), project, strict);
- }
+ public void checkSourceIncludesExist(MavenProject project, BuildProperties buildProperties, boolean strict)
+ throws MojoExecutionException {
+ checkIncludesExist("src.includes", buildProperties.getSourceIncludes(), project, strict);
+ }
- private void checkIncludesExist(String buildPropertiesKey, List includePatterns, MavenProject project,
- boolean strict, String... ignoredIncludes) throws MojoExecutionException {
- File baseDir = project.getBasedir();
- List nonMatchingIncludes = new ArrayList<>();
- List ignoreList = Arrays.asList(ignoredIncludes);
- if (includePatterns == null || includePatterns.isEmpty()) {
- String message = new File(baseDir, "build.properties").getAbsolutePath() + ": " + buildPropertiesKey
- + " value(s) must be specified.";
- if (strict) {
- throw new MojoExecutionException(message);
- } else {
- log.warn(message);
- }
- }
- for (String includePattern : includePatterns) {
- if (ignoreList.contains(includePattern)) {
- continue;
- }
- if (new File(baseDir, includePattern).exists()) {
- continue;
- }
- // it does not exist as a file nor dir. Try if it matches any files as ant pattern
- DirectoryScanner scanner = new DirectoryScanner();
- scanner.setIncludes(new String[] { includePattern });
- scanner.setBasedir(baseDir);
- scanner.scan();
- if (scanner.getIncludedFiles().length == 0) {
- nonMatchingIncludes.add(includePattern);
- }
- }
- if (nonMatchingIncludes.size() > 0) {
- String message = new File(baseDir, "build.properties").getAbsolutePath() + ": " + buildPropertiesKey
- + " value(s) " + nonMatchingIncludes + " do not match any files.";
- if (strict) {
- throw new MojoExecutionException(message);
- } else {
- log.warn(message);
- }
- }
- }
+ private void checkIncludesExist(String buildPropertiesKey, List includePatterns, MavenProject project,
+ boolean strict, String... ignoredIncludes) throws MojoExecutionException {
+ File baseDir = project.getBasedir();
+ List nonMatchingIncludes = new ArrayList<>();
+ List ignoreList = Arrays.asList(ignoredIncludes);
+ if (includePatterns == null || includePatterns.isEmpty()) {
+ String message = new File(baseDir, "build.properties").getAbsolutePath() + ": " + buildPropertiesKey
+ + " value(s) must be specified.";
+ if (strict) {
+ throw new MojoExecutionException(message);
+ } else {
+ log.warn(message);
+ }
+ }
+ for (String includePattern : includePatterns) {
+ if (ignoreList.contains(includePattern)) {
+ continue;
+ }
+ if (checkDir(baseDir, nonMatchingIncludes, includePattern)) {
+ continue;
+ }
+ Build build = project.getBuild();
+ if (build != null) {
+ String outputDirectory = build.getOutputDirectory();
+ if (outputDirectory != null
+ && checkDir(new File(outputDirectory), nonMatchingIncludes, includePattern)) {
+ continue;
+ }
+ }
+ nonMatchingIncludes.add(includePattern);
+ }
+ if (nonMatchingIncludes.size() > 0) {
+ String message = new File(baseDir, "build.properties").getAbsolutePath() + ": " + buildPropertiesKey
+ + " value(s) " + nonMatchingIncludes + " do not match any files.";
+ if (strict) {
+ throw new MojoExecutionException(message);
+ } else {
+ log.warn(message);
+ }
+ }
+ }
+
+ private boolean checkDir(File baseDir, List nonMatchingIncludes, String includePattern) {
+ if (new File(baseDir, includePattern).exists()) {
+ return true;
+ }
+ // it does not exist as a file nor dir. Try if it matches any files as ant
+ // pattern
+ DirectoryScanner scanner = new DirectoryScanner();
+ scanner.setIncludes(new String[] { includePattern });
+ scanner.setBasedir(baseDir);
+ scanner.scan();
+ return scanner.getIncludedFiles().length > 0;
+ }
}