View Javadoc
1   /*
2    * Copyright 2017-2022 original authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package io.micronaut.build.aot;
17  
18  import io.micronaut.build.services.CompilerService;
19  import io.micronaut.build.services.DependencyResolutionService;
20  import io.micronaut.build.services.ExecutorService;
21  import org.apache.maven.execution.MavenSession;
22  import org.apache.maven.plugin.MojoExecutionException;
23  import org.apache.maven.plugins.annotations.Parameter;
24  import org.apache.maven.project.MavenProject;
25  import org.apache.maven.shared.invoker.InvocationResult;
26  import org.apache.maven.shared.invoker.MavenInvocationException;
27  import org.codehaus.plexus.util.StringUtils;
28  import org.codehaus.plexus.util.xml.Xpp3Dom;
29  import org.eclipse.aether.RepositorySystem;
30  import org.eclipse.aether.artifact.Artifact;
31  import org.eclipse.aether.artifact.DefaultArtifact;
32  import org.eclipse.aether.resolution.DependencyResolutionException;
33  import org.eclipse.aether.util.artifact.JavaScopes;
34  import org.twdata.maven.mojoexecutor.MojoExecutor;
35  
36  import javax.inject.Inject;
37  import java.io.File;
38  import java.util.ArrayList;
39  import java.util.Arrays;
40  import java.util.Collections;
41  import java.util.List;
42  import java.util.stream.Stream;
43  
44  import static io.micronaut.build.aot.Constants.*;
45  import static io.micronaut.build.services.DependencyResolutionService.toClasspath;
46  import static org.twdata.maven.mojoexecutor.MojoExecutor.configuration;
47  import static org.twdata.maven.mojoexecutor.MojoExecutor.element;
48  
49  /**
50   * Base class for Micronaut AOT mojos.
51   */
52  public abstract class AbstractMicronautAotCliMojo extends AbstractMicronautAotMojo {
53  
54      public static final String EXEC_MAVEN_PLUGIN_GROUP = "org.codehaus.mojo";
55      public static final String EXEC_MAVEN_PLUGIN_ARTIFACT = "exec-maven-plugin";
56      public static final String EXEC_MAVEN_PLUGIN_VERSION_PROPERTY = "exec-maven-plugin.version";
57      public static final String DEFAULT_EXEC_MAVEN_PLUGIN_VERSION = "3.0.0";
58  
59      private static final String[] AOT_MODULES = new String[]{
60              "api",
61              "cli",
62              "std-optimizers"
63      };
64  
65      /**
66       * Package name to use for generated sources.
67       */
68      @Parameter(property = MICRONAUT_AOT_PACKAGE_NAME)
69      protected String packageName;
70  
71      private final ExecutorService executorService;
72  
73      private final DependencyResolutionService dependencyResolutionService;
74  
75      @Parameter
76      private List<org.apache.maven.model.Dependency> aotDependencies;
77  
78      @Inject
79      public AbstractMicronautAotCliMojo(CompilerService compilerService, ExecutorService executorService,
80                                         MavenProject mavenProject, MavenSession mavenSession,
81                                         RepositorySystem repositorySystem, DependencyResolutionService dependencyResolutionService) {
82          super(compilerService, mavenProject, mavenSession, repositorySystem);
83          this.executorService = executorService;
84          this.dependencyResolutionService = dependencyResolutionService;
85      }
86  
87      protected abstract List<String> getExtraArgs() throws MojoExecutionException;
88  
89      @Override
90      protected void doExecute() throws MojoExecutionException, DependencyResolutionException {
91          if (StringUtils.isEmpty(packageName)) {
92              throw new MojoExecutionException(MICRONAUT_AOT_PACKAGE_NAME + " is not set, and is required if AOT is enabled");
93          }
94          try {
95              getLog().info("Packaging project");
96              compilerService.compileProject(true);
97              InvocationResult packagingResult = compilerService.packageProject();
98              if (packagingResult.getExitCode() != 0) {
99                  getLog().error("Error when packaging the project: ", packagingResult.getExecutionException());
100             } else {
101                 executeAot();
102             }
103         } catch (MavenInvocationException e) {
104             getLog().error("Error when packaging project", e);
105         }
106     }
107 
108     private void executeAot() throws DependencyResolutionException, MojoExecutionException {
109         getLog().info("Executing Micronaut AOT analysis");
110         Xpp3Dom config = createExecPluginConfig();
111 
112         executorService.executeGoal(
113                 EXEC_MAVEN_PLUGIN_GROUP,
114                 EXEC_MAVEN_PLUGIN_ARTIFACT,
115                 mavenProject.getProperties().getProperty(EXEC_MAVEN_PLUGIN_VERSION_PROPERTY, DEFAULT_EXEC_MAVEN_PLUGIN_VERSION),
116                 "exec",
117                 config
118         );
119     }
120 
121     private Xpp3Dom createExecPluginConfig() throws DependencyResolutionException, MojoExecutionException {
122         List<String> aotClasspath = resolveAotClasspath();
123         List<String> aotPluginsClasspath = resolveAotPluginsClasspath();
124         List<String> applicationClasspath = resolveApplicationClasspath();
125 
126         List<String> classpath = new ArrayList<>(aotPluginsClasspath.size() + applicationClasspath.size());
127         classpath.addAll(aotClasspath);
128         classpath.addAll(aotPluginsClasspath);
129         classpath.addAll(applicationClasspath);
130         MojoExecutor.Element[] runnerArgs = Stream.concat(Stream.of(
131                         "-classpath",
132                         String.join(File.pathSeparator, aotClasspath),
133                         MICRONAUT_AOT_MAIN_CLASS,
134 
135                         // CLI args
136                         "--classpath=" + String.join(File.pathSeparator, classpath),
137                         "--package=" + packageName,
138                         "--runtime=" + runtime
139                 ), getExtraArgs().stream())
140                 .map(arg -> element("argument", arg))
141                 .toArray(MojoExecutor.Element[]::new);
142         return configuration(
143                 element("executable", "java"),
144                 element("arguments", runnerArgs)
145         );
146     }
147 
148     private List<String> resolveApplicationClasspath() {
149         String projectJar = new File(mavenProject.getBuild().getDirectory(), mavenProject.getBuild().getFinalName() + ".jar").getAbsolutePath();
150         List<String> result = new ArrayList<>();
151         result.add(projectJar);
152         String classpath = compilerService.buildClasspath(compilerService.resolveDependencies(JavaScopes.RUNTIME));
153         result.addAll(Arrays.asList(classpath.split(File.pathSeparator)));
154         return result;
155     }
156 
157     private List<String> resolveAotClasspath() throws DependencyResolutionException {
158         Stream<Artifact> aotArtifacts = Arrays.stream(AOT_MODULES)
159                 .map(m -> new DefaultArtifact(MICRONAUT_AOT_GROUP_ID + ":" + MICRONAUT_AOT_ARTIFACT_ID_PREFIX + m + ":" + micronautAotVersion));
160         return toClasspath(dependencyResolutionService.artifactResultsFor(aotArtifacts, false));
161     }
162 
163     private List<String> resolveAotPluginsClasspath() throws DependencyResolutionException {
164         if (aotDependencies != null && !aotDependencies.isEmpty()) {
165             Stream<Artifact> aotPlugins = aotDependencies.stream().map(d -> new DefaultArtifact(d.getGroupId(), d.getArtifactId(), d.getType(), d.getVersion()));
166             return toClasspath(dependencyResolutionService.artifactResultsFor(aotPlugins, false));
167         } else {
168             return Collections.emptyList();
169         }
170     }
171 
172 }