Skip to content

Commit

Permalink
* Support configuring quarkus.bootstrap.effective-model-builder as a …
Browse files Browse the repository at this point in the history
…POM property;

* suport resolving JAR artifacts with classifiers from the classes dir;
* support the JAR and Surefire plugin configurations in the pluginManagement when initializing artifact source sets.
  • Loading branch information
Alexey Loubyansky committed Nov 15, 2024
1 parent 19ee291 commit 405455a
Show file tree
Hide file tree
Showing 15 changed files with 304 additions and 79 deletions.
3 changes: 2 additions & 1 deletion devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java
Original file line number Diff line number Diff line change
Expand Up @@ -1375,7 +1375,8 @@ private QuarkusDevModeLauncher newLauncher(String actualDebugPort, String bootst
.setPreferPomsFromWorkspace(true)
// it's important to set the base directory instead of the POM
// which maybe manipulated by a plugin and stored outside the base directory
.setCurrentProject(project.getBasedir().toString());
.setCurrentProject(project.getBasedir().toString())
.setEffectiveModelBuilder(BootstrapMavenContextConfig.getEffectiveModelBuilderProperty(projectProperties));

// There are a couple of reasons we don't want to use the original Maven session:
// 1) a reload could be triggered by a change in a pom.xml, in which case the Maven session might not be in sync any more with the effective POM;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import io.quarkus.bootstrap.resolver.AppModelResolverException;
import io.quarkus.bootstrap.resolver.BootstrapAppModelResolver;
import io.quarkus.bootstrap.resolver.maven.BootstrapMavenContext;
import io.quarkus.bootstrap.resolver.maven.BootstrapMavenContextConfig;
import io.quarkus.bootstrap.resolver.maven.BootstrapMavenException;
import io.quarkus.bootstrap.resolver.maven.EffectiveModelResolver;
import io.quarkus.bootstrap.resolver.maven.IncubatingApplicationModelResolver;
Expand Down Expand Up @@ -195,7 +196,9 @@ private MavenArtifactResolver artifactResolver(QuarkusBootstrapMojo mojo, Launch
.setPreferPomsFromWorkspace(true)
.setProjectModelProvider(getProjectMap(mojo.mavenSession())::get)
// pass the repositories since Maven extensions could manipulate repository configs
.setRemoteRepositories(mojo.remoteRepositories()));
.setRemoteRepositories(mojo.remoteRepositories())
.setEffectiveModelBuilder(BootstrapMavenContextConfig
.getEffectiveModelBuilderProperty(mojo.mavenProject().getProperties())));
}
// PROD packaging mode with workspace discovery disabled
return MavenArtifactResolver.builder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ public class BootstrapMavenContext {
private static final String SETTINGS_XML = "settings.xml";
private static final String SETTINGS_SECURITY = "settings.security";

private static final String EFFECTIVE_MODEL_BUILDER_PROP = "quarkus.bootstrap.effective-model-builder";
static final String EFFECTIVE_MODEL_BUILDER_PROP = "quarkus.bootstrap.effective-model-builder";
private static final String WARN_ON_FAILING_WS_MODULES_PROP = "quarkus.bootstrap.warn-on-failing-workspace-modules";

private static final String MAVEN_RESOLVER_TRANSPORT_KEY = "maven.resolver.transport";
Expand Down Expand Up @@ -1080,8 +1080,7 @@ public boolean isPreferPomsFromWorkspace() {

public boolean isEffectiveModelBuilder() {
if (effectiveModelBuilder == null) {
final String s = PropertyUtils.getProperty(EFFECTIVE_MODEL_BUILDER_PROP);
effectiveModelBuilder = s == null ? false : Boolean.parseBoolean(s);
effectiveModelBuilder = Boolean.getBoolean(EFFECTIVE_MODEL_BUILDER_PROP);
}
return effectiveModelBuilder;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.function.Function;

import org.apache.maven.model.Model;
Expand All @@ -20,6 +21,23 @@

public class BootstrapMavenContextConfig<T extends BootstrapMavenContextConfig<?>> {

/**
* Resolves the effective value of the {@code effective-model-builder} option by looking for the
* {@code quarkus.bootstrap.effective-model-builder} property among the system properties and,
* if not set, in the properties argument.
* <p>
* If the property is found, the method will return the result of {@link java.lang.Boolean#parseBoolean}.
* If the property is not set, the method will return false.
*
* @param props primary source of properties
* @return whether effective model builder should be enabled
*/
public static boolean getEffectiveModelBuilderProperty(Properties props) {
final String value = System.getProperty(BootstrapMavenContext.EFFECTIVE_MODEL_BUILDER_PROP);
return value == null ? Boolean.parseBoolean(props.getProperty(BootstrapMavenContext.EFFECTIVE_MODEL_BUILDER_PROP))
: Boolean.parseBoolean(value);
}

protected String localRepo;
protected String[] localRepoTail;
protected Boolean localRepoTailIgnoreAvailability;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -366,71 +366,19 @@ public WorkspaceModule toWorkspaceModule(BootstrapMavenContext ctx) {
.setBuildDir(getOutputDir());

final Model model = modelBuildingResult == null ? getRawModel() : modelBuildingResult.getEffectiveModel();
if (!ArtifactCoords.TYPE_POM.equals(model.getPackaging())) {
final Build build = model.getBuild();
boolean addDefaultSourceSet = true;
if (build != null && !build.getPlugins().isEmpty()) {
for (Plugin plugin : build.getPlugins()) {
if (plugin.getArtifactId().equals("maven-jar-plugin")) {
if (plugin.getExecutions().isEmpty()) {
final DefaultArtifactSources src = processJarPluginExecutionConfig(plugin.getConfiguration(),
false);
if (src != null) {
addDefaultSourceSet = false;
moduleBuilder.addArtifactSources(src);
}
} else {
for (PluginExecution e : plugin.getExecutions()) {
DefaultArtifactSources src = null;
if (e.getGoals().contains(ArtifactCoords.TYPE_JAR)) {
src = processJarPluginExecutionConfig(e.getConfiguration(), false);
addDefaultSourceSet &= !(src != null && e.getId().equals("default-jar"));
} else if (e.getGoals().contains("test-jar")) {
src = processJarPluginExecutionConfig(e.getConfiguration(), true);
}
if (src != null) {
moduleBuilder.addArtifactSources(src);
}
}
}
} else if (plugin.getArtifactId().equals("maven-surefire-plugin") && plugin.getConfiguration() != null) {
Object config = plugin.getConfiguration();
if (!(config instanceof Xpp3Dom)) {
continue;
}
Xpp3Dom dom = (Xpp3Dom) config;
final Xpp3Dom depExcludes = dom.getChild("classpathDependencyExcludes");
if (depExcludes != null) {
final Xpp3Dom[] excludes = depExcludes.getChildren("classpathDependencyExclude");
if (excludes != null) {
final List<String> list = new ArrayList<>(excludes.length);
for (Xpp3Dom exclude : excludes) {
list.add(exclude.getValue());
}
moduleBuilder.setTestClasspathDependencyExclusions(list);
}
}
final Xpp3Dom additionalElements = dom.getChild("additionalClasspathElements");
if (additionalElements != null) {
final Xpp3Dom[] elements = additionalElements.getChildren("additionalClasspathElement");
if (elements != null) {
final List<String> list = new ArrayList<>(elements.length);
for (Xpp3Dom element : elements) {
for (String s : element.getValue().split(",")) {
list.add(stripProjectBasedirPrefix(s, PROJECT_BASEDIR));
}
}
moduleBuilder.setAdditionalTestClasspathElements(list);
}
}
}
}
}

if (!ArtifactCoords.TYPE_POM.equals(getPackaging())) {
final List<Plugin> plugins = model.getBuild() == null ? List.of() : model.getBuild().getPlugins();
boolean addDefaultSourceSet = addSourceSetsFromPlugins(plugins, moduleBuilder);
if (addDefaultSourceSet) {
moduleBuilder.addArtifactSources(new DefaultArtifactSources(ArtifactSources.MAIN,
List.of(new DefaultSourceDir(getSourcesSourcesDir(), getClassesDir(), getGeneratedSourcesDir())),
collectMainResources(null)));
var pluginManagement = model.getBuild() == null ? null : model.getBuild().getPluginManagement();
if (pluginManagement != null) {
addDefaultSourceSet = addSourceSetsFromPlugins(pluginManagement.getPlugins(), moduleBuilder);
}
if (addDefaultSourceSet) {
moduleBuilder.addArtifactSources(new DefaultArtifactSources(ArtifactSources.MAIN,
List.of(new DefaultSourceDir(getSourcesSourcesDir(), getClassesDir(), getGeneratedSourcesDir())),
collectMainResources(null)));
}
}
if (!moduleBuilder.hasTestSources()) {
// FIXME: do tests have generated sources?
Expand All @@ -454,6 +402,70 @@ public WorkspaceModule toWorkspaceModule(BootstrapMavenContext ctx) {
return this.module = moduleBuilder.build();
}

private boolean addSourceSetsFromPlugins(List<Plugin> plugins, WorkspaceModule.Mutable moduleBuilder) {
boolean addDefaultSourceSet = true;
int processedPlugins = 0;
for (int i = 0; i < plugins.size() && processedPlugins < 2; ++i) {
var plugin = plugins.get(i);
if (plugin.getArtifactId().equals("maven-jar-plugin")) {
++processedPlugins;
if (plugin.getExecutions().isEmpty()) {
final DefaultArtifactSources src = processJarPluginExecutionConfig(plugin.getConfiguration(),
false);
if (src != null) {
addDefaultSourceSet = false;
moduleBuilder.addArtifactSources(src);
}
} else {
for (PluginExecution e : plugin.getExecutions()) {
DefaultArtifactSources src = null;
if (e.getGoals().contains(ArtifactCoords.TYPE_JAR)) {
src = processJarPluginExecutionConfig(e.getConfiguration(), false);
addDefaultSourceSet &= !(src != null && e.getId().equals("default-jar"));
} else if (e.getGoals().contains("test-jar")) {
src = processJarPluginExecutionConfig(e.getConfiguration(), true);
}
if (src != null) {
moduleBuilder.addArtifactSources(src);
}
}
}
} else if (plugin.getArtifactId().equals("maven-surefire-plugin") && plugin.getConfiguration() != null) {
++processedPlugins;
Object config = plugin.getConfiguration();
if (!(config instanceof Xpp3Dom)) {
continue;
}
Xpp3Dom dom = (Xpp3Dom) config;
final Xpp3Dom depExcludes = dom.getChild("classpathDependencyExcludes");
if (depExcludes != null) {
final Xpp3Dom[] excludes = depExcludes.getChildren("classpathDependencyExclude");
if (excludes != null) {
final List<String> list = new ArrayList<>(excludes.length);
for (Xpp3Dom exclude : excludes) {
list.add(exclude.getValue());
}
moduleBuilder.setTestClasspathDependencyExclusions(list);
}
}
final Xpp3Dom additionalElements = dom.getChild("additionalClasspathElements");
if (additionalElements != null) {
final Xpp3Dom[] elements = additionalElements.getChildren("additionalClasspathElement");
if (elements != null) {
final List<String> list = new ArrayList<>(elements.length);
for (Xpp3Dom element : elements) {
for (String s : element.getValue().split(",")) {
list.add(stripProjectBasedirPrefix(s, PROJECT_BASEDIR));
}
}
moduleBuilder.setAdditionalTestClasspathElements(list);
}
}
}
}
return addDefaultSourceSet;
}

private List<io.quarkus.maven.dependency.Dependency> toArtifactDependencies(List<Dependency> rawModelDeps,
BootstrapMavenContext ctx) {
if (rawModelDeps.isEmpty()) {
Expand Down Expand Up @@ -509,7 +521,7 @@ private DefaultArtifactSources processJarPluginExecutionConfig(Object config, bo
new DefaultSourceDir(new DirectoryPathTree(test ? getTestSourcesSourcesDir() : getSourcesSourcesDir()),
new DirectoryPathTree(test ? getTestClassesDir() : getClassesDir(), filter),
// FIXME: wrong for tests
new DirectoryPathTree(test ? getGeneratedSourcesDir() : getGeneratedSourcesDir(), filter),
new DirectoryPathTree(getGeneratedSourcesDir(), filter),
Map.of()));
final Collection<SourceDir> resources = test ? collectTestResources(filter) : collectMainResources(filter);
return new DefaultArtifactSources(classifier, sources, resources);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,16 +124,12 @@ public File findArtifact(Artifact artifact) {
return path.toFile();
}

if (!artifact.getClassifier().isEmpty()) {
if ("tests".equals(artifact.getClassifier())) {
//special classifier used for test jars
path = lp.getTestClassesDir();
if (Files.exists(path)) {
return path.toFile();
}
if ("tests".equals(artifact.getClassifier())) {
//special classifier used for test jars
path = lp.getTestClassesDir();
if (Files.exists(path)) {
return path.toFile();
}
// otherwise, this artifact hasn't been built yet
return null;
}

if (ArtifactCoords.TYPE_JAR.equals(artifact.getExtension())) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1622,4 +1622,12 @@ public void testThatAptInAnnotationProcessorsWorks() throws MavenInvocationExcep
assertThat(entityMetamodelClassFile).exists();
assertThat(entityQueryClassFile).doesNotExist();
}

@Test
void testMultimoduleFilteredClassifier()
throws MavenInvocationException, IOException {
testDir = initProject("projects/multimodule-filtered-classifier");
run(true);
assertThat(devModeClient.getHttpResponse("/")).isEqualTo("Big");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>acme-parent</artifactId>
<groupId>org.acme</groupId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>acme-app</artifactId>
<dependencies>
<dependency>
<groupId>org.acme</groupId>
<version>\${project.version}</version>
<artifactId>acme-lib</artifactId>
<classifier>shared</classifier>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>\${quarkus.platform.group-id}</groupId>
<artifactId>quarkus-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.acme;

import org.acme.shared.BigBean;

import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;

@Path("/")
public class App {

@Inject
BigBean bean;

@GET
public String get() {
return bean.getName();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>acme-parent</artifactId>
<groupId>org.acme</groupId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>acme-lib</artifactId>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.acme;

import jakarta.enterprise.inject.Produces;
import org.acme.shared.BigBean;

/**
* The purpose of this class is to create a conflict with the shared BigBeanProducer
*/
public class BigBeanProducer {

@Produces
public BigBean getName() {
return new BigBean();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.acme.shared;

public class BigBean {
public String getName() {
return "Big";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.acme.shared;

import jakarta.enterprise.inject.Produces;

public class BigBeanProducer {

@Produces
public BigBean getBigBean() {
return new BigBean();
}
}
Loading

0 comments on commit 405455a

Please sign in to comment.