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 }