fileExtensions;
/**
* to enable/disable incremental compilation feature.
@@ -876,37 +874,33 @@ public void execute() throws MojoExecutionException, CompilationFailureException
incrementalBuildHelperRequest = new IncrementalBuildHelperRequest().inputFiles(sources);
- DirectoryScanResult dsr = computeInputFileTreeChanges(incrementalBuildHelper, sources);
-
- boolean immutableOutputFile = compiler.getCompilerOutputStyle()
- .equals(CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES)
- && !canUpdateTarget;
- boolean dependencyChanged = isDependencyChanged();
- boolean sourceChanged = isSourceChanged(compilerConfiguration, compiler);
- boolean inputFileTreeChanged = hasInputFileTreeChanged(dsr);
- // CHECKSTYLE_OFF: LineLength
- if (immutableOutputFile || dependencyChanged || sourceChanged || inputFileTreeChanged)
- // CHECKSTYLE_ON: LineLength
- {
- String cause = immutableOutputFile
- ? "immutable single output file"
- : (dependencyChanged
- ? "changed dependency"
- : (sourceChanged ? "changed source code" : "added or removed source files"));
- getLog().info("Recompiling the module because of " + cause + ".");
- if (showCompilationChanges) {
- for (String fileAdded : dsr.getFilesAdded()) {
- getLog().info("\t+ " + fileAdded);
- }
- for (String fileRemoved : dsr.getFilesRemoved()) {
- getLog().info("\t- " + fileRemoved);
- }
- }
-
+ // Strategies used to detect modifications.
+ Supplier immutableOutputFile = () -> (compiler.getCompilerOutputStyle()
+ .equals(CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES)
+ && !canUpdateTarget)
+ ? "immutable single output file"
+ : null;
+ Supplier dependencyChanged = () -> isDependencyChanged() ? "changed dependency" : null;
+ Supplier sourceChanged =
+ () -> isSourceChanged(compilerConfiguration, compiler) ? "changed source code" : null;
+ Supplier inputFileTreeChanged =
+ () -> hasInputFileTreeChanged(computeInputFileTreeChanges(incrementalBuildHelper, sources))
+ ? "added or removed source files"
+ : null;
+
+ // Lazy evaluation of the incremental compilation detection.
+ String cause = Stream.of(immutableOutputFile, dependencyChanged, sourceChanged, inputFileTreeChanged)
+ .map(Supplier::get)
+ .filter(Objects::nonNull)
+ .findFirst()
+ .orElse(null);
+
+ if (cause != null) {
+ getLog().info("Recompiling the module because of "
+ + MessageUtils.buffer().strong(cause) + ".");
compilerConfiguration.setSourceFiles(sources);
} else {
getLog().info("Nothing to compile - all classes are up to date.");
-
return;
}
} catch (CompilerException e) {
@@ -937,8 +931,7 @@ public void execute() throws MojoExecutionException, CompilationFailureException
}
if (staleSources.isEmpty()) {
- getLog().info("Nothing to compile - all classes are up to date");
-
+ getLog().info("Nothing to compile - all classes are up to date.");
return;
}
@@ -1145,7 +1138,8 @@ public void execute() throws MojoExecutionException, CompilationFailureException
// ----------------------------------------------------------------------
if (StringUtils.isEmpty(compilerConfiguration.getSourceEncoding())) {
- getLog().warn("File encoding has not been set, using platform encoding " + ReaderFactory.FILE_ENCODING
+ getLog().warn("File encoding has not been set, using platform encoding "
+ + MessageUtils.buffer().strong(Charset.defaultCharset())
+ ", i.e. build is platform dependent!");
}
@@ -1372,20 +1366,21 @@ private Set getCompileSources(Compiler compiler, CompilerConfiguration com
/**
* @param compilerConfiguration
* @param compiler
- * @return true
if at least a single source file is newer than it's class file
+ * @return {@code true} if at least a single source file is newer than it's class file
*/
- private boolean isSourceChanged(CompilerConfiguration compilerConfiguration, Compiler compiler)
- throws CompilerException, MojoExecutionException {
- Set staleSources =
- computeStaleSources(compilerConfiguration, compiler, getSourceInclusionScanner(staleMillis));
+ private boolean isSourceChanged(CompilerConfiguration compilerConfiguration, Compiler compiler) {
+ Set staleSources = Collections.emptySet();
+ try {
+ staleSources = computeStaleSources(compilerConfiguration, compiler, getSourceInclusionScanner(staleMillis));
+ } catch (MojoExecutionException | CompilerException ex) {
+ // we cannot detect Stale Sources, so don't do anything beside logging
+ getLog().warn("Cannot detect stale sources.");
+ return false;
+ }
if (getLog().isDebugEnabled() || showCompilationChanges) {
for (File f : staleSources) {
- if (showCompilationChanges) {
- getLog().info("Stale source detected: " + f.getAbsolutePath());
- } else {
- getLog().debug("Stale source detected: " + f.getAbsolutePath());
- }
+ getLog().info("\tStale source detected: " + f.getAbsolutePath());
}
}
return !staleSources.isEmpty();
@@ -1400,10 +1395,11 @@ protected int getRequestThreadCount() {
return session.getRequest().getDegreeOfConcurrency();
}
- protected Date getBuildStartTime() {
- MavenExecutionRequest request = session.getRequest();
- Date buildStartTime = request == null ? new Date() : request.getStartTime();
- return buildStartTime == null ? new Date() : buildStartTime;
+ private Optional getBuildStartTime() {
+ return Optional.ofNullable(session.getRequest())
+ .map(MavenExecutionRequest::getStartTime)
+ .map(java.util.Date::toInstant)
+ .map(i -> i.truncatedTo(ChronoUnit.MILLIS));
}
private String getMemoryValue(String setting) {
@@ -1539,36 +1535,41 @@ private static List removeEmptyCompileSourceRoots(List compileSo
* generated classes and if we got a file which is >= the build-started timestamp, then we caught a file which
* got changed during this build.
*
- * @return true
if at least one single dependency has changed.
+ * @return {@code true} if at least one single dependency has changed.
*/
- protected boolean isDependencyChanged() {
- if (session == null) {
+ private boolean isDependencyChanged() {
+ final Instant buildStartTime = getBuildStartTime().orElse(null);
+ if (buildStartTime == null) {
// we just cannot determine it, so don't do anything beside logging
- getLog().info("Cannot determine build start date, skipping incremental build detection.");
+ getLog().debug("Cannot determine build start time, skipping incremental build detection.");
return false;
}
if (fileExtensions == null || fileExtensions.isEmpty()) {
- fileExtensions = Collections.unmodifiableList(Arrays.asList("class", "jar"));
+ fileExtensions = new HashSet<>(Arrays.asList("class", "jar"));
}
- Date buildStartTime = getBuildStartTime();
-
List pathElements = new ArrayList<>();
pathElements.addAll(getClasspathElements());
pathElements.addAll(getModulepathElements());
for (String pathElement : pathElements) {
- File artifactPath = new File(pathElement);
- if (artifactPath.isDirectory() || artifactPath.isFile()) {
- if (!artifactPath.equals(getOutputDirectory()) && hasNewFile(artifactPath, buildStartTime)) {
- if (showCompilationChanges) {
- getLog().info("New dependency detected: " + artifactPath.getAbsolutePath());
- } else {
- getLog().debug("New dependency detected: " + artifactPath.getAbsolutePath());
+ Path artifactPath = Paths.get(pathElement);
+
+ // Search files only on dependencies (other modules), not on the current project,
+ if (Files.isDirectory(artifactPath)
+ && !artifactPath.equals(getOutputDirectory().toPath())) {
+ try (Stream walk = Files.walk(artifactPath)) {
+ if (walk.anyMatch(p -> hasNewFile(p, buildStartTime))) {
+ return true;
}
- return true;
+ } catch (IOException ex) {
+ // we just cannot determine it, so don't do anything beside logging
+ getLog().warn("I/O error walking the path: " + ex.getMessage());
+ return false;
}
+ } else if (hasNewFile(artifactPath, buildStartTime)) {
+ return true;
}
}
@@ -1577,31 +1578,35 @@ protected boolean isDependencyChanged() {
}
/**
- * @param classPathEntry entry to check
+ * @param file entry to check
* @param buildStartTime time build start
* @return if any changes occurred
*/
- private boolean hasNewFile(File classPathEntry, Date buildStartTime) {
- if (!classPathEntry.exists()) {
- return false;
- }
-
- if (classPathEntry.isFile()) {
- return classPathEntry.lastModified() >= buildStartTime.getTime()
- && fileExtensions.contains(FileUtils.getExtension(classPathEntry.getName()));
- }
-
- File[] children = classPathEntry.listFiles();
-
- for (File child : children) {
- if (hasNewFile(child, buildStartTime)) {
- return true;
+ private boolean hasNewFile(Path file, Instant buildStartTime) {
+ if (Files.isRegularFile(file) && fileExtensions.contains(getFileExtension(file))) {
+ try {
+ Instant lastModifiedTime =
+ Files.getLastModifiedTime(file).toInstant().truncatedTo(ChronoUnit.MILLIS);
+ boolean isChanged = lastModifiedTime.isAfter(buildStartTime);
+ if (isChanged && (getLog().isDebugEnabled() || showCompilationChanges)) {
+ getLog().info("\tNew dependency detected: " + file.toAbsolutePath());
+ }
+ return isChanged;
+ } catch (IOException ex) {
+ // we just cannot determine it, so don't do anything beside logging
+ getLog().warn("I/O error reading the lastModifiedTime: " + ex.getMessage());
}
}
return false;
}
+ private static String getFileExtension(Path path) {
+ String fileName = path.getFileName().toString();
+ int dotIndex = fileName.lastIndexOf('.');
+ return (dotIndex == -1) ? "" : fileName.substring(dotIndex + 1);
+ }
+
private List resolveProcessorPathEntries() throws MojoExecutionException {
if (annotationProcessorPaths == null || annotationProcessorPaths.isEmpty()) {
return null;
@@ -1753,36 +1758,92 @@ private String getMavenCompilerPluginVersion() {
return pomProperties.getProperty("version");
}
- private DirectoryScanResult computeInputFileTreeChanges(IncrementalBuildHelper ibh, Set inputFiles)
- throws MojoExecutionException {
- File mojoConfigBase = ibh.getMojoStatusDirectory();
- File mojoConfigFile = new File(mojoConfigBase, INPUT_FILES_LST_FILENAME);
-
- String[] oldInputFiles = new String[0];
+ private FilesDelta computeInputFileTreeChanges(IncrementalBuildHelper ibh, Set inputFiles) {
+ Path mojoConfigBase = null;
+ try {
+ mojoConfigBase = ibh.getMojoStatusDirectory().toPath();
+ } catch (MojoExecutionException e) {
+ // we cannot get the mojo status dir, so don't do anything beside logging
+ getLog().warn("Error reading mojo status directory.");
+ return FilesDelta.EMPTY;
+ }
+ Path mojoConfigFile = mojoConfigBase.resolve(INPUT_FILES_LST_FILENAME);
- if (mojoConfigFile.exists()) {
+ List oldInputFiles = Collections.emptyList();
+ if (Files.isRegularFile(mojoConfigFile)) {
try {
- oldInputFiles = FileUtils.fileReadArray(mojoConfigFile);
+ oldInputFiles = Files.readAllLines(mojoConfigFile);
} catch (IOException e) {
- throw new MojoExecutionException("Error reading old mojo status " + mojoConfigFile, e);
+ // we cannot read the mojo config file, so don't do anything beside logging
+ getLog().warn("Error while reading old mojo status: " + mojoConfigFile);
+ return FilesDelta.EMPTY;
}
}
- String[] inputFileNames = inputFiles.stream().map(File::getAbsolutePath).toArray(String[]::new);
-
- DirectoryScanResult dsr = DirectoryScanner.diffFiles(oldInputFiles, inputFileNames);
+ List newInputFiles =
+ inputFiles.stream().sorted().map(File::getAbsolutePath).collect(Collectors.toList());
try {
- FileUtils.fileWriteArray(mojoConfigFile, inputFileNames);
+ Files.write(mojoConfigFile, newInputFiles);
} catch (IOException e) {
- throw new MojoExecutionException("Error while storing the mojo status", e);
+ // we cannot write the mojo config file, so don't do anything beside logging
+ getLog().warn("Error while writing new mojo status: " + mojoConfigFile);
+ return FilesDelta.EMPTY;
}
- return dsr;
+ FilesDelta inputTreeChanges = new FilesDelta(oldInputFiles, newInputFiles);
+ if (getLog().isDebugEnabled() || showCompilationChanges) {
+ for (String fileAdded : inputTreeChanges.getFilesAdded()) {
+ getLog().info("\tInput tree files (+): " + fileAdded);
+ }
+ for (String fileRemoved : inputTreeChanges.getFilesRemoved()) {
+ getLog().info("\tInput tree files (-): " + fileRemoved);
+ }
+ }
+
+ return inputTreeChanges;
}
- private boolean hasInputFileTreeChanged(DirectoryScanResult dsr) {
- return dsr.getFilesAdded().length > 0 || dsr.getFilesRemoved().length > 0;
+ private boolean hasInputFileTreeChanged(FilesDelta filesDelta) {
+ return !filesDelta.getFilesAdded().isEmpty()
+ || !filesDelta.getFilesRemoved().isEmpty();
+ }
+
+ private static class FilesDelta {
+ private final List oldList;
+ private final List newList;
+ private List fileAdded;
+ private List fileRemoved;
+
+ static final FilesDelta EMPTY = new FilesDelta();
+
+ FilesDelta() {
+ this.oldList = Collections.emptyList();
+ this.newList = Collections.emptyList();
+ this.fileAdded = Collections.emptyList();
+ this.fileRemoved = Collections.emptyList();
+ }
+
+ FilesDelta(List oldList, List newList) {
+ this.oldList = oldList;
+ this.newList = newList;
+ }
+
+ List getFilesAdded() {
+ if (fileAdded == null) {
+ fileAdded = new ArrayList<>(newList);
+ fileAdded.removeAll(oldList);
+ }
+ return fileAdded;
+ }
+
+ List getFilesRemoved() {
+ if (fileRemoved == null) {
+ fileRemoved = new ArrayList<>(oldList);
+ fileRemoved.removeAll(newList);
+ }
+ return fileRemoved;
+ }
}
public void setTarget(String target) {