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; + } }