in src/main/java/org/apache/maven/plugin/compiler/ToolExecutor.java [491:624]
public boolean compile(final JavaCompiler compiler, final Options configuration, final Writer otherOutput)
throws IOException {
/*
* Announce what the compiler is about to do.
*/
if (sourceFiles.isEmpty()) {
String message = "No sources to compile.";
try {
Files.delete(outputDirectory);
} catch (DirectoryNotEmptyException e) {
message += " However, the output directory is not empty.";
}
logger.info(message);
return true;
}
if (logger.isDebugEnabled()) {
int n = sourceFiles.size();
@SuppressWarnings("checkstyle:MagicNumber")
var sb = new StringBuilder(n * 40).append("The source files to compile are:");
for (SourceFile file : sourceFiles) {
sb.append(System.lineSeparator()).append(" ").append(file);
}
logger.debug(sb);
}
/*
* Create a `JavaFileManager`, configure all paths (dependencies and sources), then run the compiler.
* The Java file manager has a cache, so it needs to be disposed after the compilation is completed.
* The same `JavaFileManager` may be reused for many compilation units (e.g. multi-releases) before
* disposal in order to reuse its cache.
*/
boolean success = true;
try (StandardJavaFileManager fileManager = compiler.getStandardFileManager(listener, LOCALE, encoding)) {
setDependencyPaths(fileManager);
if (!generatedSourceDirectories.isEmpty()) {
fileManager.setLocationFromPaths(StandardLocation.SOURCE_OUTPUT, generatedSourceDirectories);
}
boolean isVersioned = false;
Path latestOutputDirectory = null;
/*
* More than one compilation unit may exist in the case of a multi-releases project.
* Units are compiled in the order of the release version, with base compiled first.
* At the beginning of each new iteration, `latestOutputDirectory` is the path to
* the compiled classes of the previous version.
*/
compile:
for (final SourcesForRelease unit : groupByReleaseAndModule()) {
Path outputForRelease = null;
boolean isClasspathProject = false;
boolean isModularProject = false;
configuration.setRelease(unit.getReleaseString());
for (final Map.Entry<String, Set<Path>> root : unit.roots.entrySet()) {
final String moduleName = inferModuleNameIfMissing(root.getKey());
if (moduleName.isEmpty()) {
isClasspathProject = true;
} else {
isModularProject = true;
}
if (isClasspathProject & isModularProject) {
throw new CompilationFailureException("Mix of modular and non-modular sources.");
}
final Set<Path> sourcePaths = root.getValue();
if (isClasspathProject) {
fileManager.setLocationFromPaths(StandardLocation.SOURCE_PATH, sourcePaths);
} else {
fileManager.setLocationForModule(StandardLocation.MODULE_SOURCE_PATH, moduleName, sourcePaths);
}
outputForRelease = outputDirectory; // Modified below if compiling a non-base release.
if (isVersioned) {
if (isClasspathProject) {
/*
* For a non-modular project, this block is executed at most once par compilation unit.
* Add the paths to the classes compiled for previous versions.
*/
if (classpath == null) {
classpath = new ArrayList<>();
}
classpath.add(0, latestOutputDirectory);
fileManager.setLocationFromPaths(StandardLocation.CLASS_PATH, classpath);
outputForRelease = Files.createDirectories(
SourceDirectory.outputDirectoryForReleases(outputForRelease, unit.release));
} else {
/*
* For a modular project, this block can be executed an arbitrary number of times
* (once per module).
* TODO: need to provide --patch-module. Difficulty is that we can specify only once.
*/
throw new UnsupportedOperationException(
"Multi-versions of a modular project is not yet implemented.");
}
} else {
/*
* This addition is for allowing AbstractCompilerMojo.writeDebugFile(…) to show those paths.
* It has no effect on the compilation performed in this method, because the dependencies
* have already been set by the call to `setDependencyPaths(fileManager)`.
*/
if (!sourcePaths.isEmpty()) {
dependencies.put(SourcePathType.valueOf(moduleName), List.copyOf(sourcePaths));
}
}
}
fileManager.setLocationFromPaths(StandardLocation.CLASS_OUTPUT, Set.of(outputForRelease));
latestOutputDirectory = outputForRelease;
/*
* Compile the source files now. The following loop should be executed exactly once.
* It may be executed twice when compiling test classes overwriting the `module-info`,
* in which case the `module-info` needs to be compiled separately from other classes.
* However, this is a deprecated practice.
*/
JavaCompiler.CompilationTask task;
for (CompilationTaskSources c : toCompilationTasks(unit)) {
Iterable<? extends JavaFileObject> sources = fileManager.getJavaFileObjectsFromPaths(c.files);
task = compiler.getTask(otherOutput, fileManager, listener, configuration.options, null, sources);
success = c.compile(task);
if (!success) {
break compile;
}
}
isVersioned = true; // Any further iteration is for a version after the base version.
}
/*
* Post-compilation.
*/
if (listener instanceof DiagnosticLogger diagnostic) {
diagnostic.logSummary();
}
} catch (UncheckedIOException e) {
throw e.getCause();
}
if (success && incrementalBuild != null) {
incrementalBuild.writeCache();
incrementalBuild = null;
}
return success;
}