From c5e6f221143b57f83d6afb430439f9c2b89aab94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20L=C3=A4ubrich?= Date: Sat, 11 Nov 2023 20:02:48 +0100 Subject: [PATCH] Add a javadoc dependency injector When using the javadoc goal to aggregate documentation using configured source directories there is the problem that also a reference to projects that are mentioned in the source directories is required. This adds a new (configurable) injector that adds the projects mentioned as the source as dependencies of the project. --- .../core/maven/MavenDependencyInjector.java | 35 +++++++ .../maven/TychoMavenLifecycleParticipant.java | 32 +------ .../tycho/extras/docbundle/ConfigureMojo.java | 41 ++++++++ .../docbundle/JavadocBuildListener.java | 93 +++++++++++++++++++ .../helper/PluginConfigurationHelper.java | 28 +++++- .../eclipse/tycho/helper/ProjectHelper.java | 5 +- .../versions/VersionBumpBuildListener.java | 10 +- 7 files changed, 208 insertions(+), 36 deletions(-) create mode 100644 tycho-extras/tycho-document-bundle-plugin/src/main/java/org/eclipse/tycho/extras/docbundle/ConfigureMojo.java create mode 100644 tycho-extras/tycho-document-bundle-plugin/src/main/java/org/eclipse/tycho/extras/docbundle/JavadocBuildListener.java diff --git a/tycho-core/src/main/java/org/eclipse/tycho/core/maven/MavenDependencyInjector.java b/tycho-core/src/main/java/org/eclipse/tycho/core/maven/MavenDependencyInjector.java index e7573381da..7a977ece07 100644 --- a/tycho-core/src/main/java/org/eclipse/tycho/core/maven/MavenDependencyInjector.java +++ b/tycho-core/src/main/java/org/eclipse/tycho/core/maven/MavenDependencyInjector.java @@ -62,6 +62,41 @@ public final class MavenDependencyInjector { + /** + * Injects a set of additional project dependencies into an existing maven project. + * + * @param project + * @param dependencyProjects + */ + public static void injectMavenProjectDependencies(MavenProject project, Iterable dependencyProjects) { + Model model = project.getModel(); + Set existingDependencies = model.getDependencies().stream().map(MavenDependencyInjector::getProjectKey) + .collect(Collectors.toCollection(HashSet::new)); + for (MavenProject dependencyProject : dependencyProjects) { + if (dependencyProject == project) { + continue; + } + Dependency dependency = new Dependency(); + dependency.setArtifactId(dependencyProject.getArtifactId()); + dependency.setGroupId(dependencyProject.getGroupId()); + dependency.setVersion(dependencyProject.getVersion()); + String packaging = dependencyProject.getPackaging(); + dependency.setType(packaging); + dependency.setScope(Artifact.SCOPE_COMPILE); + dependency.setOptional(false); + if (existingDependencies.add(getProjectKey(dependency))) { + model.addDependency(dependency); + } + } + } + + private static String getProjectKey(Dependency dependency) { + + return dependency.getGroupId() + ":" + dependency.getArtifactId() + ":" + + Objects.requireNonNullElse(dependency.getType(), "jar") + ":" + dependency.getVersion() + ":" + + Objects.requireNonNullElse(dependency.getClassifier(), ""); + } + /** * Injects the dependencies of a project (as determined by the p2 dependency resolver) back into * the Maven model. diff --git a/tycho-core/src/main/java/org/eclipse/tycho/core/maven/TychoMavenLifecycleParticipant.java b/tycho-core/src/main/java/org/eclipse/tycho/core/maven/TychoMavenLifecycleParticipant.java index c1691e20f5..2c0377b850 100644 --- a/tycho-core/src/main/java/org/eclipse/tycho/core/maven/TychoMavenLifecycleParticipant.java +++ b/tycho-core/src/main/java/org/eclipse/tycho/core/maven/TychoMavenLifecycleParticipant.java @@ -29,7 +29,6 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; @@ -41,11 +40,8 @@ import org.apache.maven.AbstractMavenLifecycleParticipant; import org.apache.maven.MavenExecutionException; -import org.apache.maven.artifact.Artifact; import org.apache.maven.execution.MavenExecutionRequest; import org.apache.maven.execution.MavenSession; -import org.apache.maven.model.Dependency; -import org.apache.maven.model.Model; import org.apache.maven.model.Plugin; import org.apache.maven.model.io.ModelWriter; import org.apache.maven.project.MavenProject; @@ -158,28 +154,13 @@ public void afterProjectsRead(MavenSession session) throws MavenExecutionExcepti //do not inject additional dependencies for non Tycho managed projects! continue; } - Model model = project.getModel(); - Set existingDependencies = model.getDependencies().stream() - .map(TychoMavenLifecycleParticipant::getKey) - .collect(Collectors.toCollection(HashSet::new)); Collection dependencyProjects = closure.getDependencyProjects(project); - for (MavenProject dependencyProject : dependencyProjects) { - Dependency dependency = new Dependency(); - dependency.setArtifactId(dependencyProject.getArtifactId()); - dependency.setGroupId(dependencyProject.getGroupId()); - dependency.setVersion(dependencyProject.getVersion()); - String packaging = dependencyProject.getPackaging(); - dependency.setType(packaging); - dependency.setScope(Artifact.SCOPE_COMPILE); - dependency.setOptional(false); - if (existingDependencies.add(getKey(dependency))) { - model.addDependency(dependency); - } - } + MavenDependencyInjector.injectMavenProjectDependencies(project, dependencyProjects); if (DUMP_DATA) { try { Set visited = new HashSet<>(); - modelWriter.write(new File(project.getBasedir(), "pom-model.xml"), Map.of(), model); + modelWriter.write(new File(project.getBasedir(), "pom-model.xml"), Map.of(), + project.getModel()); try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter( new FileOutputStream(new File(project.getBasedir(), "requirements.txt"))))) { writer.write(project.getId() + ":\r\n"); @@ -223,13 +204,6 @@ private void dumpProjectRequirements(MavenProject project, BufferedWriter writer } } - private static String getKey(Dependency dependency) { - - return dependency.getGroupId() + ":" + dependency.getArtifactId() + ":" - + Objects.requireNonNullElse(dependency.getType(), "jar") + ":" + dependency.getVersion() + ":" - + Objects.requireNonNullElse(dependency.getClassifier(), ""); - } - @Override public void afterSessionEnd(MavenSession session) throws MavenExecutionException { buildListeners.notifyBuildEnd(session); diff --git a/tycho-extras/tycho-document-bundle-plugin/src/main/java/org/eclipse/tycho/extras/docbundle/ConfigureMojo.java b/tycho-extras/tycho-document-bundle-plugin/src/main/java/org/eclipse/tycho/extras/docbundle/ConfigureMojo.java new file mode 100644 index 0000000000..bafcda25cc --- /dev/null +++ b/tycho-extras/tycho-document-bundle-plugin/src/main/java/org/eclipse/tycho/extras/docbundle/ConfigureMojo.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2023 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.extras.docbundle; + +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +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; + +/** + * This mojo is only there + */ +@Mojo(name = "configure-document-bundle-plugin", defaultPhase = LifecyclePhase.INITIALIZE, requiresDependencyResolution = ResolutionScope.NONE, threadSafe = true) +public class ConfigureMojo extends AbstractMojo { + + /** + * name of the parameter to inject javadoc source dependencies + */ + public static final String PARAM_INJECT_JAVADOC_DEPENDENCIES = "injectJavadocDependencies"; + @Parameter(name = PARAM_INJECT_JAVADOC_DEPENDENCIES) + private boolean dummyBoolean; + + @Override + public void execute() throws MojoExecutionException, MojoFailureException { + throw new MojoFailureException("This mojo is not intended to be ever called"); + } + +} diff --git a/tycho-extras/tycho-document-bundle-plugin/src/main/java/org/eclipse/tycho/extras/docbundle/JavadocBuildListener.java b/tycho-extras/tycho-document-bundle-plugin/src/main/java/org/eclipse/tycho/extras/docbundle/JavadocBuildListener.java new file mode 100644 index 0000000000..e8f5de830e --- /dev/null +++ b/tycho-extras/tycho-document-bundle-plugin/src/main/java/org/eclipse/tycho/extras/docbundle/JavadocBuildListener.java @@ -0,0 +1,93 @@ +/******************************************************************************* + * Copyright (c) 2023 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.extras.docbundle; + +import java.io.File; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +import org.apache.maven.execution.MavenSession; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.eclipse.tycho.build.BuildListener; +import org.eclipse.tycho.core.maven.MavenDependencyInjector; +import org.eclipse.tycho.helper.PluginConfigurationHelper; +import org.eclipse.tycho.helper.PluginConfigurationHelper.Configuration; +import org.eclipse.tycho.helper.ProjectHelper; + +@Component(role = BuildListener.class, hint = "javadoc") +public class JavadocBuildListener implements BuildListener { + + private static final String JAVADOC_GOAL = "javadoc"; + private static final String ARTIFACT_ID = "maven-javadoc-plugin"; + private static final String GROUP_ID = "org.apache.maven.plugins"; + + @Requirement + private ProjectHelper projectHelper; + + @Requirement + private PluginConfigurationHelper configurationHelper; + + @Override + public void buildStarted(MavenSession session) { + List projects = session.getProjects(); + for (MavenProject project : projects) { + if (isJavadocProject(project, session)) { + Configuration configuration = configurationHelper.getConfiguration(GROUP_ID, ARTIFACT_ID, JAVADOC_GOAL, + project, session); + List additionalProjects = configuration.getString("sourcepath").stream() + .flatMap(sourcepath -> { + return Arrays.stream(sourcepath.split(";|:")); + }).map(s -> s.strip()).map(s -> new File(project.getBasedir(), s).toPath().normalize()) + .map(sourcePath -> getProject(sourcePath, projects)).filter(Objects::nonNull).distinct() + .toList(); + MavenDependencyInjector.injectMavenProjectDependencies(project, additionalProjects); + } + } + + } + + private MavenProject getProject(Path sourcePath, List projects) { + MavenProject match = null; + int matchNameCount = -1; + for (MavenProject mavenProject : projects) { + Path basePath = mavenProject.getBasedir().toPath(); + if (sourcePath.startsWith(basePath)) { + int nameCount = basePath.getNameCount(); + if (match == null || nameCount > matchNameCount) { + match = mavenProject; + matchNameCount = nameCount; + } + } + } + return match; + } + + private boolean isJavadocProject(MavenProject project, MavenSession mavenSession) { + if (projectHelper.hasPluginExecution(GROUP_ID, ARTIFACT_ID, JAVADOC_GOAL, project, mavenSession)) { + Configuration configuration = configurationHelper.getConfiguration(ConfigureMojo.class, project, + mavenSession); + return configuration.getBoolean(ConfigureMojo.PARAM_INJECT_JAVADOC_DEPENDENCIES).orElse(false); + } + return false; + } + + @Override + public void buildEnded(MavenSession session) { + // nothing to do... + } + +} diff --git a/tycho-spi/src/main/java/org/eclipse/tycho/helper/PluginConfigurationHelper.java b/tycho-spi/src/main/java/org/eclipse/tycho/helper/PluginConfigurationHelper.java index 8509e842ea..e8dce66a84 100644 --- a/tycho-spi/src/main/java/org/eclipse/tycho/helper/PluginConfigurationHelper.java +++ b/tycho-spi/src/main/java/org/eclipse/tycho/helper/PluginConfigurationHelper.java @@ -20,12 +20,14 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; +import org.apache.maven.execution.MavenSession; import org.apache.maven.plugin.LegacySupport; import org.apache.maven.plugin.Mojo; import org.apache.maven.plugin.MojoExecution; import org.apache.maven.plugin.descriptor.MojoDescriptor; import org.apache.maven.plugin.descriptor.PluginDescriptor; import org.apache.maven.plugin.descriptor.PluginDescriptorBuilder; +import org.apache.maven.project.MavenProject; import org.codehaus.plexus.component.annotations.Component; import org.codehaus.plexus.component.annotations.Requirement; import org.codehaus.plexus.util.InterpolationFilterReader; @@ -58,7 +60,26 @@ public Configuration getConfiguration(Xpp3Dom configuration) { return new Configuration(configuration); } + public Configuration getConfiguration(String pluginGroupId, String pluginArtifactId, String goal, + MavenProject project, MavenSession mavenSession) { + return new Configuration( + projectHelper.getPluginConfiguration(pluginGroupId, pluginArtifactId, goal, project, mavenSession)); + } + public Configuration getConfiguration(Class mojo) { + MavenSession currentSession = legacySupport.getSession(); + if (currentSession == null) { + return getConfiguration((Xpp3Dom) null); + } + MavenProject currentProject = currentSession.getCurrentProject(); + if (currentProject == null) { + return getConfiguration((Xpp3Dom) null); + } + return getConfiguration(mojo, currentProject, currentSession); + } + + public Configuration getConfiguration(Class mojo, MavenProject project, + MavenSession mavenSession) { URL resource = mojo.getResource("/META-INF/maven/plugin.xml"); if (resource == null) { throw new IllegalStateException("can't find plugin descriptor of mojo " + mojo.getName()); @@ -77,7 +98,7 @@ public Configuration getConfiguration(Class mojo) { for (MojoDescriptor mojoDescriptor : pluginDescriptor.getMojos()) { if (mojo.getName().equals(mojoDescriptor.getImplementation())) { Xpp3Dom configuration = projectHelper.getPluginConfiguration(pluginDescriptor.getGroupId(), - pluginDescriptor.getArtifactId(), mojoDescriptor.getGoal()); + pluginDescriptor.getArtifactId(), mojoDescriptor.getGoal(), project, mavenSession); return getConfiguration(configuration); } @@ -132,6 +153,11 @@ public > Optional getEnum(String name, Class type) { }); } + @Override + public String toString() { + return configuration == null ? "-empty configuration-" : String.valueOf(configuration); + } + } } diff --git a/tycho-spi/src/main/java/org/eclipse/tycho/helper/ProjectHelper.java b/tycho-spi/src/main/java/org/eclipse/tycho/helper/ProjectHelper.java index a5c0fe9869..452416868b 100644 --- a/tycho-spi/src/main/java/org/eclipse/tycho/helper/ProjectHelper.java +++ b/tycho-spi/src/main/java/org/eclipse/tycho/helper/ProjectHelper.java @@ -86,7 +86,7 @@ public List getPlugins(MavenProject project, MavenSession mavenSession) * @param goal * @param project * @param mavenSession - * @return true if an execution was found or false otherwhise. + * @return true if an execution was found or false otherwise. */ public boolean hasPluginExecution(String pluginGroupId, String pluginArtifactId, String goal, MavenProject project, MavenSession mavenSession) { @@ -127,11 +127,14 @@ public Xpp3Dom getPluginConfiguration(String pluginGroupId, String pluginArtifac if (goal == null) { return getDom(plugin.getConfiguration()); } + //first check for goal specific configuration for (PluginExecution execution : plugin.getExecutions()) { if (execution.getGoals().contains(goal)) { return getDom(execution.getConfiguration()); } } + //get plugin config + return getDom(plugin.getConfiguration()); } } return null; diff --git a/tycho-versions-plugin/src/main/java/org/eclipse/tycho/versions/VersionBumpBuildListener.java b/tycho-versions-plugin/src/main/java/org/eclipse/tycho/versions/VersionBumpBuildListener.java index 54a0d17d57..9f5a6514c4 100644 --- a/tycho-versions-plugin/src/main/java/org/eclipse/tycho/versions/VersionBumpBuildListener.java +++ b/tycho-versions-plugin/src/main/java/org/eclipse/tycho/versions/VersionBumpBuildListener.java @@ -32,7 +32,7 @@ import org.eclipse.tycho.versions.pom.PomFile; import org.osgi.framework.Version; -@Component(role = BuildListener.class) +@Component(role = BuildListener.class, hint = "version-bump") public class VersionBumpBuildListener implements BuildListener { @Requirement @@ -79,10 +79,10 @@ public void buildEnded(MavenSession session) { String newVersion = suggestedVersion.map(String::valueOf) .orElseGet(() -> Versions.incrementVersion(currentVersion, VersionBumpMojo.getIncrement(session, project, projectHelper))); - boolean isSnapshot = currentVersion.endsWith(TychoConstants.SUFFIX_SNAPSHOT); - if (isSnapshot) { - newVersion += TychoConstants.SUFFIX_SNAPSHOT; - } + boolean isSnapshot = currentVersion.endsWith(TychoConstants.SUFFIX_SNAPSHOT); + if (isSnapshot) { + newVersion += TychoConstants.SUFFIX_SNAPSHOT; + } logger.info(project.getId() + " requires a version bump from " + currentVersion + " => " + newVersion); engine.setProjects(metadataReader.getProjects());