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.aot.std.sourcegen.AbstractStaticServiceLoaderSourceGenerator;
19  import io.micronaut.aot.std.sourcegen.KnownMissingTypesSourceGenerator;
20  import io.micronaut.build.services.CompilerService;
21  import io.micronaut.build.services.DependencyResolutionService;
22  import io.micronaut.build.services.ExecutorService;
23  import org.apache.commons.io.FileUtils;
24  import org.apache.maven.execution.MavenSession;
25  import org.apache.maven.plugin.MojoExecutionException;
26  import org.apache.maven.plugins.annotations.Mojo;
27  import org.apache.maven.plugins.annotations.Parameter;
28  import org.apache.maven.plugins.annotations.ResolutionScope;
29  import org.apache.maven.project.MavenProject;
30  import org.eclipse.aether.RepositorySystem;
31  
32  import javax.inject.Inject;
33  import java.io.File;
34  import java.io.IOException;
35  import java.io.InputStream;
36  import java.io.OutputStream;
37  import java.nio.file.Files;
38  import java.nio.file.NoSuchFileException;
39  import java.nio.file.Path;
40  import java.util.ArrayList;
41  import java.util.List;
42  import java.util.Properties;
43  import java.util.stream.Stream;
44  
45  /**
46   * <p>Invokes the <a href="https://micronaut-projects.github.io/micronaut-aot/latest/guide/">Micronaut AOT</a>
47   * optimizer, generating sources/classes and the effective AOT configuration properties file. Refer to the Micronaut
48   * AOT documentation for more information.</p>
49   *
50   * <p><strong>WARNING</strong>: this goal is not intended to be executed directly. Instead, enable AOT with the
51   * <code>micronaut.aot.enabled</code> property, eg:</p>
52   *
53   * <pre>mvn -Dmicronaut.aot.enabled=true package</pre>
54   * <pre>mvn -Dmicronaut.aot.enabled=true mn:run</pre>
55   */
56  @Mojo(name = AotAnalysisMojo.NAME, requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME)
57  public class AotAnalysisMojo extends AbstractMicronautAotCliMojo {
58  
59      public static final String NAME = "aot-analysis";
60      public static final String AOT_PROPERTIES_FILE_NAME = "aot.properties";
61  
62      /**
63       * The project's target directory.
64       */
65      @Parameter(defaultValue = "${project.build.directory}", required = true)
66      private File baseDirectory;
67  
68      /**
69       * Micronaut AOT configuration file. Run the <a href="aot-sample-config-mojo.html"><code>aot-sample-config</code> goal</a> to
70       * see all the possible options.
71       */
72      @Parameter(property = "micronaut.aot.config", defaultValue = AOT_PROPERTIES_FILE_NAME)
73      private File configFile;
74  
75      @Inject
76      @SuppressWarnings("CdiInjectionPointsInspection")
77      public AotAnalysisMojo(CompilerService compilerService, ExecutorService executorService, MavenProject mavenProject,
78                             MavenSession mavenSession, RepositorySystem repositorySystem,
79                             DependencyResolutionService dependencyResolutionService) {
80          super(compilerService, executorService, mavenProject, mavenSession, repositorySystem, dependencyResolutionService);
81      }
82  
83      @Override
84      protected List<String> getExtraArgs() throws MojoExecutionException {
85          List<String> args = new ArrayList<>();
86          args.add("--output");
87          File generated = outputFile("generated");
88          args.add(generated.getAbsolutePath());
89          File effectiveConfigFile = writeEffectiveConfigFile();
90          args.add("--config");
91          args.add(effectiveConfigFile.getAbsolutePath());
92          return args;
93      }
94  
95      private File writeEffectiveConfigFile() throws MojoExecutionException {
96          File userProvidedFile = this.configFile == null ? new File(baseDirectory, AOT_PROPERTIES_FILE_NAME) : this.configFile;
97          Properties props = new Properties();
98          if (userProvidedFile.exists()) {
99              try (InputStream in = Files.newInputStream(userProvidedFile.toPath())) {
100                 getLog().info("Using AOT configuration file: " + configFile.getAbsolutePath());
101                 props.load(in);
102             } catch (IOException e) {
103                 throw new MojoExecutionException("Unable to parse configuration file", e);
104             }
105         }
106         if (!props.containsKey(KnownMissingTypesSourceGenerator.OPTION.key())) {
107             props.put(KnownMissingTypesSourceGenerator.OPTION.key(), String.join(",", Constants.TYPES_TO_CHECK));
108         }
109         props.computeIfAbsent(AbstractStaticServiceLoaderSourceGenerator.SERVICE_TYPES,
110                 key -> String.join(",", Constants.SERVICE_TYPES));
111         File effectiveConfig = outputFile("effective-" + AOT_PROPERTIES_FILE_NAME);
112         try (OutputStream out = Files.newOutputStream(effectiveConfig.toPath())) {
113             props.store(out, "Effective AOT configuration");
114         } catch (IOException e) {
115             throw new MojoExecutionException("Unable to parse configuration file", e);
116         }
117         return effectiveConfig;
118     }
119 
120     @Override
121     protected void onSuccess(File outputDir) throws MojoExecutionException {
122         Path generated = outputDir.toPath().resolve("generated");
123         Path generatedClasses = generated.resolve("classes");
124         try {
125             FileUtils.copyDirectory(generatedClasses.toFile(), outputDirectory);
126             try (Stream<String> linesStream = Files.lines(generated.resolve("logs").resolve("resource-filter.txt"))) {
127                 linesStream.forEach(toRemove -> {
128                     try {
129                         Files.delete(outputDirectory.toPath().resolve(toRemove));
130                         getLog().debug("Removed " + toRemove);
131                     } catch (IOException e) {
132                         if (!(e instanceof NoSuchFileException)) {
133                             getLog().warn("Error while deleting " + toRemove, e);
134                         }
135                     }
136                 });
137             }
138         } catch (IOException e) {
139             throw new MojoExecutionException("Error when copying the Micronaut AOT generated classes into the target directory", e);
140         }
141     }
142 
143     @Override
144     String getName() {
145         return NAME;
146     }
147 }