001/* 002 * Copyright 2017-2022 original authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package io.micronaut.maven.aot; 017 018import io.micronaut.maven.services.CompilerService; 019import io.micronaut.maven.services.DependencyResolutionService; 020import io.micronaut.maven.services.ExecutorService; 021import org.apache.maven.plugin.MojoExecutionException; 022import org.apache.maven.plugins.annotations.Parameter; 023import org.apache.maven.project.MavenProject; 024import org.apache.maven.shared.invoker.InvocationResult; 025import org.apache.maven.shared.invoker.MavenInvocationException; 026import org.codehaus.plexus.util.StringUtils; 027import org.codehaus.plexus.util.xml.Xpp3Dom; 028import org.eclipse.aether.artifact.Artifact; 029import org.eclipse.aether.artifact.DefaultArtifact; 030import org.eclipse.aether.resolution.DependencyResolutionException; 031import org.eclipse.aether.util.artifact.JavaScopes; 032import org.twdata.maven.mojoexecutor.MojoExecutor; 033 034import javax.inject.Inject; 035import java.io.File; 036import java.util.ArrayList; 037import java.util.Arrays; 038import java.util.Collections; 039import java.util.List; 040import java.util.Optional; 041import java.util.stream.Collectors; 042import java.util.stream.Stream; 043 044import static io.micronaut.maven.aot.Constants.MICRONAUT_AOT_ARTIFACT_ID_PREFIX; 045import static io.micronaut.maven.aot.Constants.MICRONAUT_AOT_GROUP_ID; 046import static io.micronaut.maven.aot.Constants.MICRONAUT_AOT_MAIN_CLASS; 047import static io.micronaut.maven.aot.Constants.MICRONAUT_AOT_PACKAGE_NAME; 048import static io.micronaut.maven.services.DependencyResolutionService.toClasspath; 049import static org.twdata.maven.mojoexecutor.MojoExecutor.configuration; 050import static org.twdata.maven.mojoexecutor.MojoExecutor.element; 051 052/** 053 * Base class for Micronaut AOT mojos. 054 */ 055public abstract class AbstractMicronautAotCliMojo extends AbstractMicronautAotMojo { 056 057 public static final String EXEC_MAVEN_PLUGIN_GROUP = "org.codehaus.mojo"; 058 public static final String EXEC_MAVEN_PLUGIN_ARTIFACT = "exec-maven-plugin"; 059 public static final String EXEC_MAVEN_PLUGIN_VERSION_PROPERTY = "exec-maven-plugin.version"; 060 public static final String DEFAULT_EXEC_MAVEN_PLUGIN_VERSION = "3.1.0"; 061 062 private static final String[] AOT_MODULES = { 063 "api", 064 "cli", 065 "std-optimizers" 066 }; 067 068 /** 069 * Package name to use for generated sources. 070 */ 071 @Parameter(property = MICRONAUT_AOT_PACKAGE_NAME) 072 protected String packageName; 073 074 private final ExecutorService executorService; 075 076 private final DependencyResolutionService dependencyResolutionService; 077 078 @Parameter 079 private List<org.apache.maven.model.Dependency> aotDependencies; 080 081 /** 082 * Additional JVM arguments to pass to the AOT compiler (eg: <code>--enable-preview</code>). 083 * 084 * @since 4.0.2 085 */ 086 @Parameter(property = "micronaut.aot.jvmArgs") 087 private List<String> aotJvmArgs; 088 089 @Inject 090 public AbstractMicronautAotCliMojo(CompilerService compilerService, ExecutorService executorService, 091 MavenProject mavenProject, DependencyResolutionService dependencyResolutionService) { 092 super(compilerService, mavenProject); 093 this.executorService = executorService; 094 this.dependencyResolutionService = dependencyResolutionService; 095 } 096 097 protected abstract List<String> getExtraArgs() throws MojoExecutionException; 098 099 @Override 100 protected void doExecute() throws MojoExecutionException, DependencyResolutionException { 101 if (StringUtils.isEmpty(packageName)) { 102 throw new MojoExecutionException(MICRONAUT_AOT_PACKAGE_NAME + " is not set, and is required if AOT is enabled"); 103 } 104 try { 105 getLog().info("Packaging project"); 106 compilerService.compileProject(); 107 InvocationResult packagingResult = compilerService.packageProject(); 108 if (packagingResult.getExitCode() != 0) { 109 getLog().error("Error when packaging the project: ", packagingResult.getExecutionException()); 110 } else { 111 executeAot(); 112 } 113 } catch (MavenInvocationException e) { 114 getLog().error("Error when packaging project", e); 115 } 116 } 117 118 private void executeAot() throws DependencyResolutionException, MojoExecutionException { 119 getLog().info("Executing Micronaut AOT analysis"); 120 Xpp3Dom config = createExecPluginConfig(); 121 122 try { 123 executorService.executeGoal( 124 EXEC_MAVEN_PLUGIN_GROUP, 125 EXEC_MAVEN_PLUGIN_ARTIFACT, 126 mavenProject.getProperties().getProperty(EXEC_MAVEN_PLUGIN_VERSION_PROPERTY, DEFAULT_EXEC_MAVEN_PLUGIN_VERSION), 127 "exec", 128 config 129 ); 130 } catch (MojoExecutionException e) { 131 getLog().error("Error when executing Micronaut AOT: " + e.getMessage()); 132 String commandLine = Arrays.stream(config.getChild("arguments").getChildren()) 133 .map(Xpp3Dom::getValue) 134 .collect(Collectors.joining(" ")); 135 getLog().error("Command line was: java " + commandLine); 136 throw e; 137 } 138 139 } 140 141 private Xpp3Dom createExecPluginConfig() throws DependencyResolutionException, MojoExecutionException { 142 List<String> aotClasspath = resolveAotClasspath(); 143 List<String> aotPluginsClasspath = resolveAotPluginsClasspath(); 144 List<String> applicationClasspath = resolveApplicationClasspath(); 145 146 var classpath = new ArrayList<String>(aotPluginsClasspath.size() + applicationClasspath.size()); 147 classpath.addAll(aotClasspath); 148 classpath.addAll(aotPluginsClasspath); 149 classpath.addAll(applicationClasspath); 150 Stream<String> jvmArgs = Optional.ofNullable(aotJvmArgs).orElse(List.of()).stream(); 151 Stream<String> mainArgs = Stream.of( 152 "-classpath", 153 String.join(File.pathSeparator, aotClasspath), 154 MICRONAUT_AOT_MAIN_CLASS, 155 156 // CLI args 157 "--classpath=" + String.join(File.pathSeparator, classpath), 158 "--package=" + packageName, 159 "--runtime=" + runtime 160 ); 161 MojoExecutor.Element[] runnerArgs = Stream.concat(Stream.concat(jvmArgs, mainArgs), getExtraArgs().stream()) 162 .map(arg -> element("argument", arg)) 163 .toArray(MojoExecutor.Element[]::new); 164 return configuration( 165 element("executable", "java"), 166 element("arguments", runnerArgs) 167 ); 168 } 169 170 private List<String> resolveApplicationClasspath() { 171 String projectJar = new File(mavenProject.getBuild().getDirectory(), mavenProject.getBuild().getFinalName() + ".jar") 172 .getAbsolutePath(); 173 var result = new ArrayList<String>(); 174 result.add(projectJar); 175 String classpath = compilerService.buildClasspath(compilerService.resolveDependencies(mavenProject, JavaScopes.RUNTIME)); 176 result.addAll(Arrays.asList(classpath.split(File.pathSeparator))); 177 return result; 178 } 179 180 private List<String> resolveAotClasspath() throws DependencyResolutionException { 181 Stream<Artifact> aotArtifacts = Arrays.stream(AOT_MODULES) 182 .map(m -> new DefaultArtifact(MICRONAUT_AOT_GROUP_ID + ":" + MICRONAUT_AOT_ARTIFACT_ID_PREFIX + m + ":" + micronautAotVersion)); 183 return toClasspath(dependencyResolutionService.artifactResultsFor(aotArtifacts, false)); 184 } 185 186 private List<String> resolveAotPluginsClasspath() throws DependencyResolutionException { 187 if (aotDependencies != null && !aotDependencies.isEmpty()) { 188 Stream<Artifact> aotPlugins = aotDependencies.stream() 189 .map(d -> new DefaultArtifact(d.getGroupId(), d.getArtifactId(), d.getType(), d.getVersion())); 190 return toClasspath(dependencyResolutionService.artifactResultsFor(aotPlugins, false)); 191 } else { 192 return Collections.emptyList(); 193 } 194 } 195 196}