View Javadoc
1   package io.micronaut.build.services;
2   
3   import org.apache.maven.execution.MavenSession;
4   import org.apache.maven.plugin.MojoExecutionException;
5   import org.apache.maven.plugin.logging.Log;
6   import org.apache.maven.plugin.logging.SystemStreamLog;
7   import org.apache.maven.project.MavenProject;
8   
9   import javax.inject.Inject;
10  import javax.inject.Singleton;
11  import java.io.File;
12  import java.nio.file.Path;
13  import java.util.Arrays;
14  import java.util.List;
15  import java.util.Map;
16  import java.util.Optional;
17  import java.util.concurrent.atomic.AtomicReference;
18  import java.util.function.Function;
19  import java.util.stream.Collectors;
20  import java.util.stream.Stream;
21  
22  /**
23   * Provides methods to compile a Maven project
24   *
25   * @author Álvaro Sánchez-Mariscal
26   * @since 1.1
27   */
28  @Singleton
29  public class CompilerService {
30  
31      private static final String JAVA = "java";
32      private static final String GROOVY = "groovy";
33      private static final String KOTLIN = "kotlin";
34  
35      public static final String MAVEN_COMPILER_PLUGIN = "org.apache.maven.plugins:maven-compiler-plugin";
36      public static final String MAVEN_RESOURCES_PLUGIN = "org.apache.maven.plugins:maven-resources-plugin";
37      public static final String GMAVEN_PLUS_PLUGIN = "org.codehaus.gmavenplus:gmavenplus-plugin";
38      public static final String KOTLIN_MAVEN_PLUGIN = "org.jetbrains.kotlin:kotlin-maven-plugin";
39  
40  
41      /**
42       * @see <a href="https://maven.apache.org/ref/3.6.3/maven-core/lifecycles.html#default_Lifecycle">default Lifecycle</a>
43       */
44      private static final List<String> PHASES_AFTER_COMPILE = Arrays.asList(
45              "compile",
46              "process-classes",
47              "generate-test-sources",
48              "process-test-sources",
49              "generate-test-resources",
50              "process-test-resources",
51              "test-compile",
52              "process-test-classes",
53              "test",
54              "prepare-package",
55              "package",
56              "pre-integration-test",
57              "integration-test",
58              "post-integration-test",
59              "verify",
60              "install",
61              "deploy");
62  
63      private final Log log;
64      private final Map<String, Path> sourceDirectories;
65      private final MavenProject mavenProject;
66      private final MavenSession mavenSession;
67      private final ExecutorService executorService;
68  
69      @SuppressWarnings("CdiInjectionPointsInspection")
70      @Inject
71      public CompilerService(MavenProject mavenProject, MavenSession mavenSession, ExecutorService executorService) {
72          this.log = new SystemStreamLog();
73          this.mavenProject = mavenProject;
74          this.mavenSession = mavenSession;
75          this.executorService = executorService;
76          this.sourceDirectories = resolveSourceDirectories();
77      }
78  
79      public boolean needsCompilation() {
80          return mavenSession.getGoals().stream().noneMatch(PHASES_AFTER_COMPILE::contains);
81      }
82  
83      public Optional<Long> compileProject(boolean copyResources) {
84          Long lastCompilation = null;
85          if (log.isDebugEnabled()) {
86              log.debug("Compiling the project");
87          }
88          try {
89              if (sourceDirectories.containsKey(GROOVY)) {
90                  executorService.executeGoal(GMAVEN_PLUS_PLUGIN, "addSources");
91                  executorService.executeGoal(GMAVEN_PLUS_PLUGIN, "generateStubs");
92                  if (copyResources) {
93                      executorService.executeGoal(MAVEN_RESOURCES_PLUGIN, "resources");
94                  }
95                  executorService.executeGoal(MAVEN_COMPILER_PLUGIN, "compile");
96                  executorService.executeGoal(GMAVEN_PLUS_PLUGIN, "compile");
97                  executorService.executeGoal(GMAVEN_PLUS_PLUGIN, "removeStubs");
98                  lastCompilation = System.currentTimeMillis();
99              }
100             if (sourceDirectories.containsKey(KOTLIN)) {
101                 executorService.executeGoal(KOTLIN_MAVEN_PLUGIN, "kapt");
102                 if (copyResources) {
103                     executorService.executeGoal(MAVEN_RESOURCES_PLUGIN, "resources");
104                 }
105                 executorService.executeGoal(KOTLIN_MAVEN_PLUGIN, "compile");
106                 executorService.executeGoal(MAVEN_COMPILER_PLUGIN, "compile#java-compile");
107                 lastCompilation = System.currentTimeMillis();
108             }
109             if (sourceDirectories.containsKey(JAVA)) {
110                 if (copyResources) {
111                     executorService.executeGoal(MAVEN_RESOURCES_PLUGIN, "resources");
112                 }
113                 executorService.executeGoal(MAVEN_COMPILER_PLUGIN, "compile");
114                 lastCompilation = System.currentTimeMillis();
115             }
116         } catch (MojoExecutionException e) {
117             if (log.isErrorEnabled()) {
118                 log.error("Error while compiling the project: ", e);
119             }
120         }
121         return Optional.ofNullable(lastCompilation);
122     }
123 
124     public Map<String, Path> resolveSourceDirectories() {
125         if (log.isDebugEnabled()) {
126             log.debug("Resolving source directories...");
127         }
128         AtomicReference<String> lang = new AtomicReference<>();
129         Map<String, Path> sourceDirectories = Stream.of(JAVA, GROOVY, KOTLIN)
130                 .peek(lang::set)
131                 .map(l -> new File(mavenProject.getBasedir(), "src/main/" + l))
132                 .filter(File::exists)
133                 .peek(f -> {
134                     if (log.isDebugEnabled()) {
135                         log.debug("Found source: " + f.getPath());
136                     }
137                 })
138                 .map(File::toPath)
139                 .collect(Collectors.toMap(path -> lang.get(), Function.identity()));
140         if (sourceDirectories.isEmpty()) {
141             throw new IllegalStateException("Source folders not found for neither Java/Groovy/Kotlin");
142         }
143         return sourceDirectories;
144     }
145 
146 }