Make sure the generated extension project can be actually be built, support creating an extension project in an empty dir with a single command, e.g. mvn io.quarkus:quarkus-maven-plugin:999-SNAPSHOT:create-extension -DgroupId=org.acme -DartifactId=my-ext -Dversion=1.0-SNAPSHOT

This commit is contained in:
Alexey Loubyansky
2020-01-22 19:11:44 +01:00
parent 66a9a5ffd3
commit 4d99b2e523
26 changed files with 1028 additions and 186 deletions

View File

@@ -1,5 +1,6 @@
package io.quarkus.maven;
import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
@@ -19,15 +20,21 @@ import java.util.stream.Collectors;
import javax.lang.model.SourceVersion;
import org.apache.maven.model.Model;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
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.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.graph.Dependency;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.resolution.ArtifactDescriptorResult;
import org.jboss.logging.Logger;
import freemarker.cache.ClassTemplateLoader;
@@ -38,6 +45,11 @@ import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateExceptionHandler;
import io.quarkus.bootstrap.resolver.maven.MavenArtifactResolver;
import io.quarkus.bootstrap.resolver.maven.MavenRepoInitializer;
import io.quarkus.bootstrap.resolver.maven.workspace.LocalProject;
import io.quarkus.bootstrap.resolver.maven.workspace.LocalWorkspace;
import io.quarkus.maven.utilities.MojoUtils;
import io.quarkus.maven.utilities.PomTransformer;
import io.quarkus.maven.utilities.PomTransformer.Gavtcs;
import io.quarkus.maven.utilities.PomTransformer.Transformation;
@@ -66,20 +78,46 @@ public class CreateExtensionMojo extends AbstractMojo {
private static final String CLASSPATH_PREFIX = "classpath:";
private static final String FILE_PREFIX = "file:";
private static final String QUARKUS_VERSION_PROP = "quarkus.version";
static final String DEFAULT_ENCODING = "utf-8";
static final String DEFAULT_QUARKUS_VERSION = "@{quarkus.version}";
static final String DEFAULT_QUARKUS_VERSION = "@{" + QUARKUS_VERSION_PROP + "}";
static final String QUARKUS_VERSION_POM_EXPR = "${" + QUARKUS_VERSION_PROP + "}";
static final String DEFAULT_BOM_ENTRY_VERSION = "@{project.version}";
static final String DEFAULT_TEMPLATES_URI_BASE = "classpath:/create-extension-templates";
static final String DEFAULT_NAME_SEGMENT_DELIMITER = " - ";
static final Pattern PLACEHOLDER_PATTERN = Pattern.compile("@\\{([^\\}]+)\\}");
static final String PLATFORM_DEFAULT_GROUP_ID = "io.quarkus";
static final String PLATFORM_DEFAULT_ARTIFACT_ID = "quarkus-bom-deployment";
static final String COMPILER_PLUGIN_VERSION_PROP = "compiler-plugin.version";
static final String COMPILER_PLUGIN_VERSION_POM_EXPR = "${" + COMPILER_PLUGIN_VERSION_PROP + "}";
static final String COMPILER_PLUGIN_DEFAULT_VERSION = "3.8.1";
private static final String COMPILER_PLUGIN_GROUP_ID = "org.apache.maven.plugins";
private static final String COMPILER_PLUGIN_ARTIFACT_ID = "maven-compiler-plugin";
private static final String COMPILER_PLUGIN_KEY = COMPILER_PLUGIN_GROUP_ID + ":"
+ COMPILER_PLUGIN_ARTIFACT_ID;
/**
* Directory where the changes should be performed. Default is the current directory of the current Java process.
*
* @since 0.20.0
*/
@Parameter(property = "quarkus.basedir")
Path basedir;
File basedir;
/**
* The {@code groupId} of the Quarkus platform's BOM containing deployment dependencies.
*/
@Parameter(property = "platformGroupId", defaultValue = PLATFORM_DEFAULT_GROUP_ID)
String platformGroupId;
/**
* The {@code artifactId} of the Quarkus platform's BOM containing deployment dependencies.
*/
@Parameter(property = "platformArtifactId", defaultValue = PLATFORM_DEFAULT_ARTIFACT_ID)
String platformArtifactId;
/**
* The {@code groupId} for the newly created Maven modules. If {@code groupId} is left unset, the {@code groupId}
@@ -87,9 +125,19 @@ public class CreateExtensionMojo extends AbstractMojo {
*
* @since 0.20.0
*/
@Parameter(property = "quarkus.groupId")
@Parameter(property = "groupId")
String groupId;
/**
* This parameter was introduced to change the property of {@code groupId} parameter from {@code quarkus.groupId} to
* {@code groupId}
*
* @since 1.3.0
*/
@Deprecated
@Parameter(property = "quarkus.groupId")
String deprecatedGroupId;
/**
* {@code artifactId} of the runtime module. The {@code artifactId}s of the extension parent
* (<code>${artifactId}-parent</code>) and deployment (<code>${artifactId}-deployment</code>) modules will be based
@@ -104,21 +152,31 @@ public class CreateExtensionMojo extends AbstractMojo {
*
* @since 0.20.0
*/
@Parameter(property = "quarkus.artifactId")
@Parameter(property = "artifactId")
String artifactId;
/**
* This parameter was introduced to change the property of {@code artifactId} parameter from {@code quarkus.artifactId} to
* {@code artifactId}
*
* @since 1.3.0
*/
@Deprecated
@Parameter(property = "quarkus.artifactId")
String deprecatedArtifactId;
/**
* A prefix common to all extension artifactIds in the current source tree. If you set {@link #artifactIdPrefix},
* set also {@link #artifactIdBase}, but do not set {@link #artifactId}.
*
* @since 0.20.0
*/
@Parameter(property = "quarkus.artifactIdPrefix")
@Parameter(property = "quarkus.artifactIdPrefix", defaultValue = "")
String artifactIdPrefix;
/**
* The unique part of the {@link #artifactId}. If you set {@link #artifactIdBase}, set also
* {@link #artifactIdPrefix}, but do not set {@link #artifactId}.
* The unique part of the {@link #artifactId}. If you set {@link #artifactIdBase}, {@link #artifactIdPrefix}
* may also be set, but not {@link #artifactId}.
*
* @since 0.20.0
*/
@@ -131,9 +189,19 @@ public class CreateExtensionMojo extends AbstractMojo {
*
* @since 0.20.0
*/
@Parameter(property = "quarkus.artifactVersion")
@Parameter(property = "version")
String version;
/**
* This parameter was introduced to change the property of {@code version} parameter from {@code quarkus.artifactVersion} to
* {@code version}
*
* @since 1.3.0
*/
@Deprecated
@Parameter(property = "quarkus.artifactVersion")
String deprecatedVersion;
/**
* The {@code name} of the runtime module. The {@code name}s of the extension parent and deployment modules will be
* based on this {@code name} too.
@@ -253,17 +321,25 @@ public class CreateExtensionMojo extends AbstractMojo {
*
* @since 0.20.0
*/
@Parameter(defaultValue = DEFAULT_QUARKUS_VERSION, required = true, property = "quarkus.quarkusVersion")
@Parameter(defaultValue = DEFAULT_QUARKUS_VERSION, required = true, property = "quarkus.version")
String quarkusVersion;
/**
* This parameter was introduced to change the property of {@code artifactId} parameter from {@code quarkus.artifactId} to
* {@code artifactId}
*
* @since 1.3.0
*/
@Deprecated
@Parameter(property = "quarkus.quarkusVersion")
String deprecatedQuarkusVersion;
/**
* If {@code true} the Maven dependencies in Runtime and Deployment modules will not have their versions set and the
* {@code quarkus-bootstrap-maven-plugin} in the Runtime module will not have its version set and it will have no
* executions configured. If {@code false} the version set in {@link #quarkusVersion} will be used where applicable
* and {@code quarkus-bootstrap-maven-plugin} in the Runtime module will be configured explicitly. If the value is
* {@code null} the mojo attempts to autodetect the value inspecting the POM hierarchy of the current project: The
* value is {@code true} if {@code quarkus-bootstrap-maven-plugin} is defined in the {@code pluginManagement}
* section of the effective model of the current Maven module; otherwise the value is {@code false}.
* {@code null} the parameter will be treated as it was initialized to {@code false}.
*
* @since 0.20.0
*/
@@ -307,7 +383,7 @@ public class CreateExtensionMojo extends AbstractMojo {
/**
* Path relative to {@link #basedir} pointing at a {@code pom.xml} file containing the BOM (Bill of Materials) that
* manages runtime extension artifacts. If set, the newly created Runtime module will be added to
* {@code <deploymentManagement>} section of this bom; otherwise the newly created Runtime module will not be added
* {@code <dependencyManagement>} section of this bom; otherwise the newly created Runtime module will not be added
* to any BOM.
*
* @since 0.21.0
@@ -318,7 +394,7 @@ public class CreateExtensionMojo extends AbstractMojo {
/**
* Path relative to {@link #basedir} pointing at a {@code pom.xml} file containing the BOM (Bill of Materials) that
* manages deployment time extension artifacts. If set, the newly created Deployment module will be added to
* {@code <deploymentManagement>} section of this bom; otherwise the newly created Deployment module will not be
* {@code <dependencyManagement>} section of this bom; otherwise the newly created Deployment module will not be
* added to any BOM.
*
* @since 0.21.0
@@ -370,18 +446,89 @@ public class CreateExtensionMojo extends AbstractMojo {
@Parameter(defaultValue = "${project}", readonly = true)
MavenProject project;
/**
* The entry point to Aether, i.e. the component doing all the work.
*
* @component
*/
@Component
private RepositorySystem repoSystem;
/**
* The current repository/network configuration of Maven.
*
* @parameter default-value="${repositorySystemSession}"
* @readonly
*/
@Parameter(defaultValue = "${repositorySystemSession}", readonly = true)
private RepositorySystemSession repoSession;
/**
* The project's remote repositories to use for the resolution of artifacts and their dependencies.
*
* @parameter default-value="${project.remoteProjectRepositories}"
* @readonly
*/
@Parameter(defaultValue = "${project.remoteProjectRepositories}", readonly = true, required = true)
private List<RemoteRepository> repos;
/**
* The version of {@code org.apache.maven.plugins:maven-compiler-plugin} that should be used for
* the extension project.
*/
@Parameter(defaultValue = COMPILER_PLUGIN_DEFAULT_VERSION, required = true, property = "quarkus.mavenCompilerPluginVersion")
String compilerPluginVersion;
boolean currentProjectIsBaseDir;
Charset charset;
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
if (this.basedir == null) {
this.basedir = Paths.get(".").toAbsolutePath().normalize();
currentProjectIsBaseDir = true;
this.basedir = new File(".").getAbsoluteFile();
}
if (deprecatedGroupId != null) {
if (groupId != null) {
throw new MojoExecutionException("Either groupId or deprecatedGroupId can be set at a time but got groupId="
+ groupId + " and deprecatedGroupId=" + deprecatedGroupId);
}
groupId = deprecatedGroupId;
}
if (deprecatedArtifactId != null) {
if (artifactId != null) {
throw new MojoExecutionException(
"Either artifactId or deprecatedArtifactId can be set at a time but got artifactId=" + artifactId
+ " and deprecatedArtifactId=" + deprecatedArtifactId);
}
artifactId = deprecatedArtifactId;
}
if (deprecatedVersion != null) {
if (version != null) {
throw new MojoExecutionException("Either version or deprecatedVersion can be set at a time but got version="
+ version + " and deprecatedVersion=" + deprecatedVersion);
}
version = deprecatedVersion;
}
if (deprecatedQuarkusVersion != null) {
if (quarkusVersion != null && !DEFAULT_QUARKUS_VERSION.equals(quarkusVersion)) {
throw new MojoExecutionException(
"Either quarkusVersion or deprecatedQuarkusVersion can be set at a time but got quarkusVersion="
+ quarkusVersion + " and deprecatedQuarkusVersion=" + deprecatedQuarkusVersion);
}
quarkusVersion = deprecatedQuarkusVersion;
}
if (artifactId != null) {
artifactIdBase = artifactIdBase(artifactId);
artifactIdPrefix = artifactId.substring(0, artifactId.length() - artifactIdBase.length());
artifactId = BRACKETS_PATTERN.matcher(artifactId).replaceAll("");
} else if (artifactIdPrefix != null && artifactIdBase != null) {
artifactId = artifactIdPrefix + artifactIdBase;
} else if (artifactIdBase != null) {
artifactId = artifactIdPrefix == null || artifactIdPrefix.isEmpty() ? artifactIdBase
: artifactIdPrefix + artifactIdBase;
} else {
throw new MojoFailureException(String.format(
"Either artifactId or both artifactIdPrefix and artifactIdBase must be specified; found: artifactId=[%s], artifactIdPrefix=[%s], artifactIdBase[%s]",
@@ -411,123 +558,318 @@ public class CreateExtensionMojo extends AbstractMojo {
}
if (runtimeBomPath != null) {
runtimeBomPath = basedir.resolve(runtimeBomPath);
runtimeBomPath = basedir.toPath().resolve(runtimeBomPath);
if (!Files.exists(runtimeBomPath)) {
throw new MojoFailureException("runtimeBomPath does not exist: " + runtimeBomPath);
}
}
if (deploymentBomPath != null) {
deploymentBomPath = basedir.resolve(deploymentBomPath);
deploymentBomPath = basedir.toPath().resolve(deploymentBomPath);
if (!Files.exists(deploymentBomPath)) {
throw new MojoFailureException("deploymentBomPath does not exist: " + deploymentBomPath);
}
}
final Charset charset = Charset.forName(encoding);
charset = Charset.forName(encoding);
final Path basePomXml = basedir.resolve("pom.xml");
if (Files.exists(basePomXml)) {
try (Reader r = Files.newBufferedReader(basePomXml, charset)) {
Model basePom = new MavenXpp3Reader().read(r);
if (!"pom".equals(basePom.getPackaging())) {
try {
File rootPom = null;
Model rootModel = null;
boolean importDeploymentBom = true;
boolean setCompilerPluginVersion = true;
boolean setQuarkusVersionProp = true;
if (isCurrentProjectExists()) {
rootPom = getCurrentProjectPom();
rootModel = MojoUtils.readPom(rootPom);
if (!"pom".equals(rootModel.getPackaging())) {
throw new MojoFailureException(
"Can add extension modules only under a project with packaging 'pom'; found: "
+ basePom.getPackaging() + "");
+ rootModel.getPackaging() + "");
}
if (rootPom.equals(project.getFile())) {
importDeploymentBom = !hasQuarkusDeploymentBom();
setCompilerPluginVersion = !project.getPluginManagement().getPluginsAsMap()
.containsKey(COMPILER_PLUGIN_KEY);
setQuarkusVersionProp = !project.getProperties().containsKey(QUARKUS_VERSION_PROP);
} else {
// aloubyansky: not sure we should support this case and not sure it ever worked properly
// this is about creating an extension project not in the context of the current project from the Maven's plugin perspective
// kind of a pathological use-case from the Maven's perspective, imo
final DefaultArtifact rootArtifact = new DefaultArtifact(getGroupId(rootModel),
rootModel.getArtifactId(), null, rootModel.getPackaging(), getVersion(rootModel));
try {
final LocalWorkspace ws = LocalProject.loadWorkspace(rootPom.getParentFile().toPath()).getWorkspace();
final MavenArtifactResolver mvn = MavenArtifactResolver.builder()
.setRepositorySystem(MavenRepoInitializer.getRepositorySystem(repoSession.isOffline(), ws))
.setRepositorySystemSession(repoSession)
.setRemoteRepositories(repos)
.setWorkspace(LocalProject.loadWorkspace(rootPom.getParentFile().toPath()).getWorkspace())
.build();
final ArtifactDescriptorResult rootDescr = mvn.resolveDescriptor(rootArtifact);
importDeploymentBom = !hasQuarkusDeploymentBom(rootDescr.getManagedDependencies());
// TODO determine whether the compiler plugin is configured for the project
setQuarkusVersionProp = !rootDescr.getProperties().containsKey(QUARKUS_VERSION_PROP);
} catch (Exception e) {
throw new MojoExecutionException("Failed to resolve " + rootArtifact + " descriptor", e);
}
}
} else if (this.grandParentRelativePath != null) {
// aloubyansky: not sure we should support this case, same as above
final File gpPom = getExtensionProjectBaseDir().resolve(this.grandParentRelativePath).normalize()
.toAbsolutePath().toFile();
if (gpPom.exists()) {
rootPom = gpPom;
rootModel = MojoUtils.readPom(gpPom);
final DefaultArtifact rootArtifact = new DefaultArtifact(getGroupId(rootModel),
rootModel.getArtifactId(), null, rootModel.getPackaging(), getVersion(rootModel));
try {
final LocalWorkspace ws = LocalProject.loadWorkspace(rootPom.getParentFile().toPath()).getWorkspace();
final MavenArtifactResolver mvn = MavenArtifactResolver.builder()
.setRepositorySystem(MavenRepoInitializer.getRepositorySystem(repoSession.isOffline(), ws))
.setRepositorySystemSession(repoSession)
.setRemoteRepositories(repos)
.setWorkspace(ws)
.build();
final ArtifactDescriptorResult rootDescr = mvn.resolveDescriptor(rootArtifact);
importDeploymentBom = !hasQuarkusDeploymentBom(rootDescr.getManagedDependencies());
// TODO determine whether the compiler plugin is configured for the project
setQuarkusVersionProp = !rootDescr.getProperties().containsKey(QUARKUS_VERSION_PROP);
} catch (Exception e) {
throw new MojoExecutionException("Failed to resolve " + rootArtifact + " descriptor", e);
}
}
addModules(basePomXml, basePom, charset);
} catch (IOException e) {
throw new MojoExecutionException(String.format("Could not read %s", basePomXml), e);
} catch (XmlPullParserException e) {
throw new MojoExecutionException(String.format("Could not parse %s", basePomXml), e);
} catch (TemplateException e) {
throw new MojoExecutionException(String.format("Could not process a FreeMarker template"), e);
}
} else {
newParent(basedir);
final TemplateParams templateParams = getTemplateParams(rootModel);
final Configuration cfg = getTemplateConfig();
generateExtensionProjects(cfg, templateParams);
if (setQuarkusVersionProp) {
setQuarkusVersionProp(getExtensionProjectBaseDir().resolve("pom.xml").toFile());
}
if (importDeploymentBom) {
addQuarkusDeploymentBom(getExtensionProjectBaseDir().resolve("pom.xml").toFile());
}
if (setCompilerPluginVersion) {
setCompilerPluginVersion(getExtensionProjectBaseDir().resolve("pom.xml").toFile());
}
if (rootModel != null) {
addModules(rootPom.toPath(), templateParams, rootModel);
}
if (runtimeBomPath != null) {
getLog().info(
String.format("Adding [%s] to dependencyManagement in [%s]", templateParams.artifactId,
runtimeBomPath));
List<PomTransformer.Transformation> transformations = new ArrayList<PomTransformer.Transformation>();
transformations
.add(Transformation.addManagedDependency(templateParams.groupId, templateParams.artifactId,
templateParams.bomEntryVersion));
for (Gavtcs gavtcs : templateParams.additionalRuntimeDependencies) {
getLog().info(String.format("Adding [%s] to dependencyManagement in [%s]", gavtcs, runtimeBomPath));
transformations.add(Transformation.addManagedDependency(gavtcs));
}
pomTransformer(runtimeBomPath).transform(transformations);
}
if (deploymentBomPath != null) {
final String aId = templateParams.artifactId + "-deployment";
getLog().info(String.format("Adding [%s] to dependencyManagement in [%s]", aId, deploymentBomPath));
pomTransformer(deploymentBomPath)
.transform(Transformation.addManagedDependency(templateParams.groupId, aId,
templateParams.bomEntryVersion));
}
if (itestParentPath != null) {
generateItest(cfg, templateParams);
}
} catch (IOException e) {
throw new MojoExecutionException(String.format("Could not read %s", project.getFile()), e);
} catch (TemplateException e) {
throw new MojoExecutionException(String.format("Could not process a FreeMarker template"), e);
}
}
void addModules(Path basePomXml, Model basePom, Charset charset)
private void setQuarkusVersionProp(File pom) throws IOException, MojoExecutionException {
pomTransformer(pom.toPath()).transform(Transformation.addProperty(QUARKUS_VERSION_PROP,
quarkusVersion.equals(DEFAULT_QUARKUS_VERSION) ? getPluginVersion() : quarkusVersion));
}
private void setCompilerPluginVersion(File pom) throws IOException {
pomTransformer(pom.toPath()).transform(Transformation.addProperty(COMPILER_PLUGIN_VERSION_PROP, compilerPluginVersion));
pomTransformer(pom.toPath())
.transform(Transformation.addManagedPlugin(
MojoUtils.plugin(COMPILER_PLUGIN_GROUP_ID, COMPILER_PLUGIN_ARTIFACT_ID,
COMPILER_PLUGIN_VERSION_POM_EXPR)));
}
private void addQuarkusDeploymentBom(File pom) throws IOException, MojoExecutionException {
addQuarkusDeploymentBom(MojoUtils.readPom(pom), pom);
}
private void addQuarkusDeploymentBom(Model model, File file) throws IOException, MojoExecutionException {
pomTransformer(file.toPath())
.transform(Transformation.addManagedDependency(
new Gavtcs(platformGroupId, platformArtifactId, QUARKUS_VERSION_POM_EXPR, "pom", null, "import")));
}
private String getPluginVersion() throws MojoExecutionException {
return CreateUtils.resolvePluginInfo(CreateExtensionMojo.class).getVersion();
}
private boolean hasQuarkusDeploymentBom() {
if (project.getDependencyManagement() == null) {
return false;
}
for (org.apache.maven.model.Dependency dep : project.getDependencyManagement().getDependencies()) {
if (dep.getArtifactId().equals("quarkus-core-deployment")
&& dep.getGroupId().equals("io.quarkus")) {
// this is not a 100% accurate check but practically valid
return true;
}
}
return false;
}
private boolean hasQuarkusDeploymentBom(List<Dependency> deps) {
if (deps == null) {
return false;
}
for (Dependency dep : deps) {
if (dep.getArtifact().getArtifactId().equals("quarkus-core-deployment")
&& dep.getArtifact().getGroupId().equals("io.quarkus")) {
// this is not a 100% accurate check but practically valid
return true;
}
}
return false;
}
/**
* @return true if the goal is executed in an existing project
*/
private boolean isCurrentProjectExists() {
return currentProjectIsBaseDir ? project.getFile() != null
: Files.exists(basedir.toPath().resolve("pom.xml"));
}
private File getCurrentProjectPom() {
if (currentProjectIsBaseDir) {
return project.getFile() == null ? new File(project.getBasedir(), "pom.xml") : project.getFile();
}
return new File(basedir, "pom.xml");
}
private Path getExtensionProjectBaseDir() {
if (currentProjectIsBaseDir) {
return project.getBasedir() == null ? basedir.toPath().resolve(artifactIdBase)
: project.getBasedir().toPath().resolve(artifactIdBase);
}
return new File(basedir, artifactIdBase).toPath();
}
private Path getExtensionRuntimeBaseDir() {
return getExtensionProjectBaseDir().resolve("runtime");
}
private Path getExtensionDeploymentBaseDir() {
return getExtensionProjectBaseDir().resolve("deployment");
}
void addModules(Path basePomXml, TemplateParams templateParams, Model basePom)
throws IOException, TemplateException, MojoFailureException, MojoExecutionException {
final Configuration cfg = new Configuration(Configuration.VERSION_2_3_28);
cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
cfg.setTemplateLoader(createTemplateLoader(basedir, templatesUriBase));
cfg.setDefaultEncoding(charset.name());
cfg.setInterpolationSyntax(Configuration.SQUARE_BRACKET_INTERPOLATION_SYNTAX);
cfg.setTagSyntax(Configuration.SQUARE_BRACKET_TAG_SYNTAX);
TemplateParams model = new TemplateParams();
model.artifactId = artifactId;
model.artifactIdPrefix = artifactIdPrefix;
model.artifactIdBase = artifactIdBase;
model.artifactIdBaseCamelCase = toCapCamelCase(model.artifactIdBase);
model.groupId = this.groupId != null ? this.groupId : getGroupId(basePom);
model.version = this.version != null ? this.version : getVersion(basePom);
model.namePrefix = namePrefix;
model.nameBase = nameBase;
model.nameSegmentDelimiter = nameSegmentDelimiter;
model.assumeManaged = detectAssumeManaged();
model.quarkusVersion = quarkusVersion.replace('@', '$');
model.bomEntryVersion = bomEntryVersion.replace('@', '$');
model.grandParentGroupId = grandParentGroupId != null ? grandParentGroupId : getGroupId(basePom);
model.grandParentArtifactId = grandParentArtifactId != null ? grandParentArtifactId : basePom.getArtifactId();
model.grandParentVersion = grandParentVersion != null ? grandParentVersion : getVersion(basePom);
model.grandParentRelativePath = grandParentRelativePath != null ? grandParentRelativePath : "../pom.xml";
model.javaPackageBase = javaPackageBase != null ? javaPackageBase
: getJavaPackage(model.groupId, javaPackageInfix, artifactId);
model.additionalRuntimeDependencies = getAdditionalRuntimeDependencies();
model.runtimeBomPathSet = runtimeBomPath != null;
evalTemplate(cfg, "parent-pom.xml", basedir.resolve(model.artifactIdBase + "/pom.xml"), charset, model);
Files.createDirectories(basedir
.resolve(model.artifactIdBase + "/runtime/src/main/java/" + model.javaPackageBase.replace('.', '/')));
evalTemplate(cfg, "runtime-pom.xml", basedir.resolve(model.artifactIdBase + "/runtime/pom.xml"), charset,
model);
evalTemplate(cfg, "deployment-pom.xml", basedir.resolve(model.artifactIdBase + "/deployment/pom.xml"), charset,
model);
final Path processorPath = basedir
.resolve(model.artifactIdBase + "/deployment/src/main/java/" + model.javaPackageBase.replace('.', '/')
+ "/deployment/" + model.artifactIdBaseCamelCase + "Processor.java");
evalTemplate(cfg, "Processor.java", processorPath, charset, model);
if (!basePom.getModules().contains(model.artifactIdBase)) {
getLog().info(String.format("Adding module [%s] to [%s]", model.artifactIdBase, basePomXml));
new PomTransformer(basePomXml, charset).transform(Transformation.addModule(model.artifactIdBase));
if (!basePom.getModules().contains(templateParams.artifactIdBase)) {
getLog().info(String.format("Adding module [%s] to [%s]", templateParams.artifactIdBase, basePomXml));
pomTransformer(basePomXml).transform(Transformation.addModule(templateParams.artifactIdBase));
}
if (runtimeBomPath != null) {
getLog().info(
String.format("Adding [%s] to dependencyManagement in [%s]", model.artifactId, runtimeBomPath));
List<PomTransformer.Transformation> transformations = new ArrayList<PomTransformer.Transformation>();
transformations
.add(Transformation.addManagedDependency(model.groupId, model.artifactId, model.bomEntryVersion));
for (Gavtcs gavtcs : model.additionalRuntimeDependencies) {
getLog().info(String.format("Adding [%s] to dependencyManagement in [%s]", gavtcs, runtimeBomPath));
transformations.add(Transformation.addManagedDependency(gavtcs));
}
new PomTransformer(runtimeBomPath, charset).transform(transformations);
}
if (deploymentBomPath != null) {
final String aId = model.artifactId + "-deployment";
getLog().info(String.format("Adding [%s] to dependencyManagement in [%s]", aId, deploymentBomPath));
new PomTransformer(deploymentBomPath, charset)
.transform(Transformation.addManagedDependency(model.groupId, aId, model.bomEntryVersion));
}
if (itestParentPath != null) {
generateItest(cfg, charset, model);
}
}
void generateItest(Configuration cfg, Charset charset, TemplateParams model)
throws MojoFailureException, MojoExecutionException, TemplateException {
final Path itestParentAbsPath = basedir.resolve(itestParentPath).toAbsolutePath();
private void generateExtensionProjects(Configuration cfg, TemplateParams templateParams)
throws IOException, TemplateException, MojoExecutionException {
evalTemplate(cfg, "parent-pom.xml", getExtensionProjectBaseDir().resolve("pom.xml"), templateParams);
Files.createDirectories(
getExtensionRuntimeBaseDir().resolve("src/main/java")
.resolve(templateParams.javaPackageBase.replace('.', '/')));
evalTemplate(cfg, "runtime-pom.xml", getExtensionRuntimeBaseDir().resolve("pom.xml"),
templateParams);
evalTemplate(cfg, "deployment-pom.xml", getExtensionDeploymentBaseDir().resolve("pom.xml"),
templateParams);
final Path processorPath = getExtensionDeploymentBaseDir()
.resolve("src/main/java")
.resolve(templateParams.javaPackageBase.replace('.', '/'))
.resolve("deployment")
.resolve(templateParams.artifactIdBaseCamelCase + "Processor.java");
evalTemplate(cfg, "Processor.java", processorPath, templateParams);
}
private PomTransformer pomTransformer(Path basePomXml) {
return new PomTransformer(basePomXml, charset);
}
private TemplateParams getTemplateParams(Model basePom) throws MojoExecutionException {
final TemplateParams templateParams = new TemplateParams();
templateParams.artifactId = artifactId;
templateParams.artifactIdPrefix = artifactIdPrefix;
templateParams.artifactIdBase = artifactIdBase;
templateParams.artifactIdBaseCamelCase = toCapCamelCase(templateParams.artifactIdBase);
if (groupId == null) {
if (basePom == null) {
throw new MojoExecutionException(
"Please provide the desired groupId for the project by setting groupId parameter");
}
templateParams.groupId = getGroupId(basePom);
} else {
templateParams.groupId = groupId;
}
if (version == null) {
if (basePom == null) {
throw new MojoExecutionException(
"Please provide the desired version for the project by setting version parameter");
}
templateParams.version = getVersion(basePom);
} else {
templateParams.version = version;
}
templateParams.namePrefix = namePrefix;
templateParams.nameBase = nameBase;
templateParams.nameSegmentDelimiter = nameSegmentDelimiter;
templateParams.assumeManaged = detectAssumeManaged();
templateParams.quarkusVersion = QUARKUS_VERSION_POM_EXPR;
templateParams.bomEntryVersion = bomEntryVersion.replace('@', '$');
if (basePom != null) {
templateParams.grandParentGroupId = grandParentGroupId != null ? grandParentGroupId : getGroupId(basePom);
templateParams.grandParentArtifactId = grandParentArtifactId != null ? grandParentArtifactId
: basePom.getArtifactId();
templateParams.grandParentVersion = grandParentVersion != null ? grandParentVersion : getVersion(basePom);
templateParams.grandParentRelativePath = grandParentRelativePath != null ? grandParentRelativePath : "../pom.xml";
}
templateParams.javaPackageBase = javaPackageBase != null ? javaPackageBase
: getJavaPackage(templateParams.groupId, javaPackageInfix, artifactId);
templateParams.additionalRuntimeDependencies = getAdditionalRuntimeDependencies();
templateParams.runtimeBomPathSet = runtimeBomPath != null;
return templateParams;
}
private Configuration getTemplateConfig() throws IOException {
final Configuration templateCfg = new Configuration(Configuration.VERSION_2_3_28);
templateCfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
templateCfg.setTemplateLoader(createTemplateLoader(basedir, templatesUriBase));
templateCfg.setDefaultEncoding(encoding);
templateCfg.setInterpolationSyntax(Configuration.SQUARE_BRACKET_INTERPOLATION_SYNTAX);
templateCfg.setTagSyntax(Configuration.SQUARE_BRACKET_TAG_SYNTAX);
return templateCfg;
}
void generateItest(Configuration cfg, TemplateParams model)
throws MojoFailureException, MojoExecutionException, TemplateException, IOException {
final Path itestParentAbsPath = basedir.toPath().resolve(itestParentPath);
try (Reader r = Files.newBufferedReader(itestParentAbsPath, charset)) {
final Model itestParent = new MavenXpp3Reader().read(r);
if (!"pom".equals(itestParent.getPackaging())) {
@@ -541,20 +883,20 @@ public class CreateExtensionMojo extends AbstractMojo {
model.itestParentRelativePath = "../pom.xml";
final Path itestDir = itestParentAbsPath.getParent().resolve(model.artifactIdBase);
evalTemplate(cfg, "integration-test-pom.xml", itestDir.resolve("pom.xml"), charset, model);
evalTemplate(cfg, "integration-test-pom.xml", itestDir.resolve("pom.xml"), model);
final Path testResourcePath = itestDir.resolve("src/main/java/" + model.javaPackageBase.replace('.', '/')
+ "/it/" + model.artifactIdBaseCamelCase + "Resource.java");
evalTemplate(cfg, "TestResource.java", testResourcePath, charset, model);
evalTemplate(cfg, "TestResource.java", testResourcePath, model);
final Path testClassDir = itestDir
.resolve("src/test/java/" + model.javaPackageBase.replace('.', '/') + "/it");
evalTemplate(cfg, "Test.java", testClassDir.resolve(model.artifactIdBaseCamelCase + "Test.java"), charset,
evalTemplate(cfg, "Test.java", testClassDir.resolve(model.artifactIdBaseCamelCase + "Test.java"),
model);
evalTemplate(cfg, "IT.java", testClassDir.resolve(model.artifactIdBaseCamelCase + "IT.java"), charset,
evalTemplate(cfg, "IT.java", testClassDir.resolve(model.artifactIdBaseCamelCase + "IT.java"),
model);
getLog().info(String.format("Adding module [%s] to [%s]", model.artifactIdBase, itestParentAbsPath));
new PomTransformer(itestParentAbsPath, charset).transform(Transformation.addModule(model.artifactIdBase));
pomTransformer(itestParentAbsPath).transform(Transformation.addModule(model.artifactIdBase));
} catch (IOException e) {
throw new MojoExecutionException(String.format("Could not read %s", itestParentAbsPath), e);
@@ -605,20 +947,7 @@ public class CreateExtensionMojo extends AbstractMojo {
}
boolean detectAssumeManaged() {
if (assumeManaged != null) {
return assumeManaged.booleanValue();
} else {
if (project != null && project.getPluginManagement() != null
&& project.getPluginManagement().getPlugins() != null) {
for (Plugin plugin : project.getPluginManagement().getPlugins()) {
if ("io.quarkus".equals(plugin.getGroupId())
&& "quarkus-bootstrap-maven-plugin".equals(plugin.getArtifactId())) {
return true;
}
}
}
return false;
}
return assumeManaged == null ? false : assumeManaged;
}
static String getGroupId(Model basePom) {
@@ -683,12 +1012,7 @@ public class CreateExtensionMojo extends AbstractMojo {
.collect(Collectors.joining("."));
}
void newParent(Path path) {
throw new UnsupportedOperationException(
"Creating standalone extension projects is not supported yet. Only adding modules under and existing pom.xml file is supported.");
}
static TemplateLoader createTemplateLoader(Path basedir, String templatesUriBase) throws IOException {
static TemplateLoader createTemplateLoader(File basedir, String templatesUriBase) throws IOException {
final TemplateLoader defaultLoader = new ClassTemplateLoader(CreateExtensionMojo.class,
DEFAULT_TEMPLATES_URI_BASE.substring(CLASSPATH_PREFIX.length()));
if (DEFAULT_TEMPLATES_URI_BASE.equals(templatesUriBase)) {
@@ -704,7 +1028,7 @@ public class CreateExtensionMojo extends AbstractMojo {
return new MultiTemplateLoader( //
new TemplateLoader[] { //
new FileTemplateLoader(
basedir.resolve(templatesUriBase.substring(FILE_PREFIX.length())).toFile()), //
new File(basedir, templatesUriBase.substring(FILE_PREFIX.length()))), //
defaultLoader //
});
} else {
@@ -714,7 +1038,7 @@ public class CreateExtensionMojo extends AbstractMojo {
}
}
static void evalTemplate(Configuration cfg, String templateUri, Path dest, Charset charset, TemplateParams model)
static void evalTemplate(Configuration cfg, String templateUri, Path dest, TemplateParams model)
throws IOException, TemplateException {
log.infof("Adding '%s'", dest);
final Template template = cfg.getTemplate(templateUri);
@@ -746,6 +1070,12 @@ public class CreateExtensionMojo extends AbstractMojo {
this.itestParentPath = Paths.get(itestParentPath);
}
private void debug(String format, Object... args) {
if (getLog().isDebugEnabled()) {
getLog().debug(String.format(format, args));
}
}
public static class TemplateParams {
String grandParentRelativePath;
String grandParentVersion;

View File

@@ -94,7 +94,7 @@ public final class CreateUtils {
try {
final Path classOrigin = MojoUtils.getClassOrigin(cls);
if (Files.isDirectory(classOrigin)) {
return resolvePluginInfo(classOrigin);
return resolvePluginInfo(classOrigin.resolve("META-INF").resolve("maven").resolve("plugin.xml"));
}
try (FileSystem fs = ZipUtils.newFileSystem(classOrigin)) {
return resolvePluginInfo(fs.getPath("META-INF", "maven", "plugin.xml"));

View File

@@ -18,8 +18,6 @@
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-core-deployment</artifactId>
[#if !assumeManaged ] <version>[=quarkusVersion]</version>
[/#if]
</dependency>
<dependency>
<groupId>[=groupId]</groupId>

View File

@@ -11,10 +11,10 @@
</parent>
[/#if]
[#if groupId?? && groupId != grandParentGroupId ] <groupId>[=groupId]</groupId>
[#if grandParentGroupId?? ][#if groupId != grandParentGroupId ] <groupId>[=groupId]</groupId>[/#if][#else ] <groupId>[=groupId]</groupId>
[/#if]
<artifactId>[=artifactId]-parent</artifactId>
[#if groupId?? && groupId != grandParentGroupId && version?? && version != grandParentVersion ] <version>[=version]</version>
[#if groupId?? && (!grandParentGroupId?? || groupId != grandParentGroupId) && version?? && (!grandParentVersion?? || version != grandParentVersion) ] <version>[=version]</version>
[/#if]
[#if nameBase?? ] <name>[=namePrefix][=nameBase][=nameSegmentDelimiter]Parent</name>
[/#if]

View File

@@ -13,9 +13,13 @@
<artifactId>[=artifactId]</artifactId>
[#if nameBase?? ] <name>[=namePrefix][=nameBase][=nameSegmentDelimiter]Runtime</name>
[/#if]
[#if additionalRuntimeDependencies?size > 0 ]
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-core</artifactId>
</dependency>
[#if additionalRuntimeDependencies?size > 0 ]
[#list additionalRuntimeDependencies as dep]
<dependency>
<groupId>[=dep.groupId]</groupId>
@@ -30,8 +34,8 @@
[/#if]
</dependency>
[/#list]
</dependencies>
[/#if]
</dependencies>
<build>
<plugins>