1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  package io.micronaut.build.services;
17  
18  import org.apache.maven.execution.MavenSession;
19  import org.apache.maven.plugin.MojoExecutionException;
20  import org.apache.maven.plugin.logging.Log;
21  import org.apache.maven.plugin.logging.SystemStreamLog;
22  import org.apache.maven.project.*;
23  import org.apache.maven.shared.invoker.*;
24  import org.eclipse.aether.RepositorySystemSession;
25  import org.eclipse.aether.graph.Dependency;
26  import org.eclipse.aether.graph.DependencyFilter;
27  import org.eclipse.aether.util.filter.DependencyFilterUtils;
28  
29  import javax.inject.Inject;
30  import javax.inject.Singleton;
31  import java.io.File;
32  import java.nio.file.Path;
33  import java.util.*;
34  import java.util.concurrent.atomic.AtomicReference;
35  import java.util.function.Function;
36  import java.util.stream.Collectors;
37  import java.util.stream.Stream;
38  
39  
40  
41  
42  
43  
44  
45  @Singleton
46  public class CompilerService {
47  
48      public static final String MAVEN_COMPILER_PLUGIN = "org.apache.maven.plugins:maven-compiler-plugin";
49      public static final String MAVEN_JAR_PLUGIN = "org.apache.maven.plugins:maven-jar-plugin";
50      public static final String MAVEN_RESOURCES_PLUGIN = "org.apache.maven.plugins:maven-resources-plugin";
51      public static final String GMAVEN_PLUS_PLUGIN = "org.codehaus.gmavenplus:gmavenplus-plugin";
52      public static final String KOTLIN_MAVEN_PLUGIN = "org.jetbrains.kotlin:kotlin-maven-plugin";
53  
54      private static final String JAVA = "java";
55      private static final String GROOVY = "groovy";
56      private static final String KOTLIN = "kotlin";
57      private static final String COMPILE_GOAL = "compile";
58      private static final String RESOURCES_GOAL = "resources";
59  
60      private final Log log;
61      private final Map<String, Path> sourceDirectories;
62      private final MavenProject mavenProject;
63      private final MavenSession mavenSession;
64      private final ExecutorService executorService;
65      private final ProjectDependenciesResolver resolver;
66      private final Invoker invoker;
67  
68      @SuppressWarnings("CdiInjectionPointsInspection")
69      @Inject
70      public CompilerService(MavenProject mavenProject, MavenSession mavenSession, ExecutorService executorService,
71                             ProjectDependenciesResolver resolver) {
72          this.resolver = resolver;
73          this.log = new SystemStreamLog();
74          this.mavenProject = mavenProject;
75          this.mavenSession = mavenSession;
76          this.executorService = executorService;
77          this.sourceDirectories = resolveSourceDirectories();
78          this.invoker = new DefaultInvoker();
79      }
80  
81      
82  
83  
84  
85  
86      public Optional<Long> compileProject(boolean copyResources) {
87          Long lastCompilation = null;
88          if (log.isDebugEnabled()) {
89              log.debug("Compiling the project");
90          }
91          try {
92              if (sourceDirectories.containsKey(GROOVY)) {
93                  executorService.executeGoal(GMAVEN_PLUS_PLUGIN, "addSources");
94                  executorService.executeGoal(GMAVEN_PLUS_PLUGIN, "generateStubs");
95                  if (copyResources) {
96                      executorService.executeGoal(MAVEN_RESOURCES_PLUGIN, RESOURCES_GOAL);
97                  }
98                  executorService.executeGoal(MAVEN_COMPILER_PLUGIN, COMPILE_GOAL);
99                  executorService.executeGoal(GMAVEN_PLUS_PLUGIN, COMPILE_GOAL);
100                 executorService.executeGoal(GMAVEN_PLUS_PLUGIN, "removeStubs");
101                 lastCompilation = System.currentTimeMillis();
102             }
103             if (sourceDirectories.containsKey(KOTLIN)) {
104                 executorService.executeGoal(KOTLIN_MAVEN_PLUGIN, "kapt");
105                 if (copyResources) {
106                     executorService.executeGoal(MAVEN_RESOURCES_PLUGIN, RESOURCES_GOAL);
107                 }
108                 executorService.executeGoal(KOTLIN_MAVEN_PLUGIN, COMPILE_GOAL);
109                 executorService.executeGoal(MAVEN_COMPILER_PLUGIN, "compile#java-compile");
110                 lastCompilation = System.currentTimeMillis();
111             }
112             if (sourceDirectories.containsKey(JAVA)) {
113                 if (copyResources) {
114                     executorService.executeGoal(MAVEN_RESOURCES_PLUGIN, RESOURCES_GOAL);
115                 }
116                 executorService.executeGoal(MAVEN_COMPILER_PLUGIN, COMPILE_GOAL);
117                 lastCompilation = System.currentTimeMillis();
118             }
119         } catch (MojoExecutionException e) {
120             if (log.isErrorEnabled()) {
121                 log.error("Error while compiling the project: ", e);
122             }
123         }
124         return Optional.ofNullable(lastCompilation);
125     }
126 
127     
128 
129 
130     public Map<String, Path> resolveSourceDirectories() {
131         if (log.isDebugEnabled()) {
132             log.debug("Resolving source directories...");
133         }
134         AtomicReference<String> lang = new AtomicReference<>();
135         Map<String, Path> sourceDirectoriesToResolve = Stream.of(JAVA, GROOVY, KOTLIN)
136                 .peek(lang::set)
137                 .map(l -> new File(mavenProject.getBasedir(), "src/main/" + l))
138                 .filter(File::exists)
139                 .peek(f -> {
140                     if (log.isDebugEnabled()) {
141                         log.debug("Found source: " + f.getPath());
142                     }
143                 })
144                 .map(File::toPath)
145                 .collect(Collectors.toMap(path -> lang.get(), Function.identity()));
146         if (sourceDirectoriesToResolve.isEmpty()) {
147             throw new IllegalStateException("Source folders not found for neither Java/Groovy/Kotlin");
148         }
149         return sourceDirectoriesToResolve;
150     }
151 
152     
153 
154 
155     public List<Dependency> resolveDependencies(String... scopes) {
156         try {
157             DependencyFilter filter = DependencyFilterUtils.classpathFilter(scopes);
158             RepositorySystemSession session = mavenSession.getRepositorySession();
159             DependencyResolutionRequest dependencyResolutionRequest = new DefaultDependencyResolutionRequest(mavenProject, session);
160             dependencyResolutionRequest.setResolutionFilter(filter);
161             DependencyResolutionResult result = resolver.resolve(dependencyResolutionRequest);
162             return result.getDependencies();
163         } catch (org.apache.maven.project.DependencyResolutionException e) {
164             if (log.isWarnEnabled()) {
165                 log.warn("Error while trying to resolve dependencies for the current project", e);
166             }
167             return Collections.emptyList();
168         }
169     }
170 
171     
172 
173 
174     public String buildClasspath(List<Dependency> dependencies) {
175         Comparator<Dependency> byGroupId = Comparator.comparing(d -> d.getArtifact().getGroupId());
176         Comparator<Dependency> byArtifactId = Comparator.comparing(d -> d.getArtifact().getArtifactId());
177         return dependencies.stream()
178                 .sorted(byGroupId.thenComparing(byArtifactId))
179                 .map(dependency -> dependency.getArtifact().getFile().getAbsolutePath())
180                 .collect(Collectors.joining(File.pathSeparator));
181     }
182 
183     
184 
185 
186     public InvocationResult packageProject() throws MavenInvocationException {
187         InvocationRequest request = new DefaultInvocationRequest();
188         request.setPomFile(mavenProject.getFile());
189         request.setGoals(Collections.singletonList(MAVEN_JAR_PLUGIN + ":jar"));
190         request.setBatchMode(true);
191         request.setQuiet(true);
192         return invoker.execute(request);
193     }
194 }