Skip to content

Commit 51609ad

Browse files
committed
Record the build timestamp and assume every file changed that is newer
Currently we only analyze the current delta but this can have several subtile problems: - when a file is modified outside of eclipse it might not be recognized by eclipse immediately - when files are modified outside the "interesting folders" the delta maybe not includes this - if a build fails the next delta will not necessarily include all files of the previous delta because the error might be in a java source file and a resource file was changed This adds a project scoped timestamp to record when the last successful build happens and assumes every file changed that has a timestamp larger than the last build, this still does not handle deletes properly but improves the situation already for existing files. It also leads to probably more files queried/assumed changed in case of failing builds or when the build timestamp is unkown but ensures that we only ever process more files than required but never less ones.
1 parent 40d3b44 commit 51609ad

File tree

1 file changed

+79
-6
lines changed

1 file changed

+79
-6
lines changed

org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/builder/MavenBuilderImpl.java

Lines changed: 79 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,18 @@
1616
import static org.eclipse.core.resources.IncrementalProjectBuilder.FULL_BUILD;
1717

1818
import java.io.File;
19+
import java.text.DateFormat;
1920
import java.util.ArrayList;
2021
import java.util.Collection;
2122
import java.util.Collections;
23+
import java.util.Date;
2224
import java.util.HashSet;
2325
import java.util.LinkedHashMap;
2426
import java.util.LinkedHashSet;
2527
import java.util.List;
2628
import java.util.Map;
2729
import java.util.Set;
30+
import java.util.concurrent.ConcurrentHashMap;
2831
import java.util.concurrent.atomic.AtomicBoolean;
2932

3033
import org.slf4j.Logger;
@@ -37,7 +40,9 @@
3740
import org.eclipse.core.resources.IResourceDelta;
3841
import org.eclipse.core.resources.IncrementalProjectBuilder;
3942
import org.eclipse.core.resources.ResourcesPlugin;
43+
import org.eclipse.core.runtime.Adapters;
4044
import org.eclipse.core.runtime.CoreException;
45+
import org.eclipse.core.runtime.IAdaptable;
4146
import org.eclipse.core.runtime.IPath;
4247
import org.eclipse.core.runtime.IProgressMonitor;
4348
import org.eclipse.core.runtime.QualifiedName;
@@ -78,6 +83,8 @@ public class MavenBuilderImpl {
7883

7984
private final List<IIncrementalBuildFramework> incrementalBuildFrameworks;
8085

86+
private final Map<IProject, ProjectBuildState> deltaState = new ConcurrentHashMap<>();
87+
8188
public MavenBuilderImpl(DeltaProvider deltaProvider) {
8289
this.deltaProvider = deltaProvider;
8390
this.incrementalBuildFrameworks = loadIncrementalBuildFrameworks();
@@ -113,9 +120,10 @@ public Set<IProject> build(MavenSession session, IMavenProjectFacade projectFaca
113120
if(!hasRelevantDelta(projectFacade, delta)) {
114121
return Set.of(project);
115122
}
116-
123+
ProjectBuildState buildState = deltaState.computeIfAbsent(project, ProjectBuildState::new);
117124
final BuildResultCollector participantResults = new BuildResultCollector();
118-
List<BuildContext> incrementalContexts = setupProjectBuildContext(project, kind, delta, participantResults);
125+
List<BuildContext> incrementalContexts = setupProjectBuildContext(project, kind, delta, participantResults,
126+
buildState);
119127

120128
debugBuildStart(debugHooks, projectFacade, kind, args, participants, delta, monitor);
121129

@@ -184,7 +192,10 @@ public Set<IProject> build(MavenSession session, IMavenProjectFacade projectFaca
184192
// Process errors and warnings
185193
MavenExecutionResult result = session.getResult();
186194
processBuildResults(project, mavenProject, result, participantResults, buildErrors);
187-
195+
if(buildErrors.isEmpty()) {
196+
//we only commit this when there are no errors so just in case a failure is cased by a changed file it is again queried afterwards
197+
buildState.commit();
198+
}
188199
return dependencies;
189200
}
190201

@@ -227,10 +238,12 @@ private boolean hasRelevantDelta(IMavenProjectFacade projectFacade, IResourceDel
227238
}
228239

229240
private List<IIncrementalBuildFramework.BuildContext> setupProjectBuildContext(IProject project, int kind,
230-
IResourceDelta delta, IIncrementalBuildFramework.BuildResultCollector results) throws CoreException {
241+
IResourceDelta delta, IIncrementalBuildFramework.BuildResultCollector results, ProjectBuildState buildState)
242+
throws CoreException {
231243
List<IIncrementalBuildFramework.BuildContext> contexts = new ArrayList<>();
232244

233-
BuildDelta buildDelta = delta != null ? new EclipseResourceBuildDelta(delta) : null;
245+
BuildDelta buildDelta = delta != null ? new ProjectBuildStateDelta(buildState, new EclipseResourceBuildDelta(delta))
246+
: null;
234247
for(IIncrementalBuildFramework framework : incrementalBuildFrameworks) {
235248
contexts.add(framework.setupProjectBuildContext(project, kind, buildDelta, results));
236249
}
@@ -408,7 +421,7 @@ public void clean(MavenSession session, IMavenProjectFacade projectFacade,
408421

409422
final BuildResultCollector participantResults = new BuildResultCollector();
410423
List<BuildContext> incrementalContexts = setupProjectBuildContext(project, IncrementalProjectBuilder.CLEAN_BUILD,
411-
null, participantResults);
424+
null, participantResults, null);
412425

413426
Map<Throwable, MojoExecutionKey> buildErrors = new LinkedHashMap<>();
414427
try {
@@ -452,4 +465,64 @@ public void clean(MavenSession session, IMavenProjectFacade projectFacade,
452465
DeltaProvider getDeltaProvider() {
453466
return deltaProvider;
454467
}
468+
469+
private static final class ProjectBuildState {
470+
471+
private long lastBuild;
472+
473+
private IProject project;
474+
475+
public ProjectBuildState(IProject project) {
476+
this.project = project;
477+
}
478+
479+
public void commit() {
480+
this.lastBuild = System.currentTimeMillis();
481+
}
482+
483+
@Override
484+
public String toString() {
485+
return "BuildState for " + project + " lat reccorded timestamp "
486+
+ DateFormat.getDateTimeInstance().format(new Date(lastBuild));
487+
}
488+
}
489+
490+
private static final class ProjectBuildStateDelta implements BuildDelta, IAdaptable {
491+
492+
private ProjectBuildState buildState;
493+
494+
private BuildDelta delegate;
495+
496+
/**
497+
* @param buildState
498+
* @param eclipseResourceBuildDelta
499+
*/
500+
public ProjectBuildStateDelta(ProjectBuildState buildState, BuildDelta delegate) {
501+
this.buildState = buildState;
502+
this.delegate = delegate;
503+
}
504+
505+
@Override
506+
public boolean hasDelta(File file) {
507+
//first check the delegate...
508+
if(delegate != null && delegate.hasDelta(file)) {
509+
return true;
510+
}
511+
//... now perform additional checks
512+
if(file.isFile()) {
513+
long lastModified = file.lastModified();
514+
if(lastModified > buildState.lastBuild) {
515+
//if the file is modified after the last build timestamp we assume it was modified even though not part of the current delta!
516+
return true;
517+
}
518+
}
519+
return false;
520+
}
521+
522+
@Override
523+
public <T> T getAdapter(Class<T> adapter) {
524+
return Adapters.adapt(delegate, adapter);
525+
}
526+
527+
}
455528
}

0 commit comments

Comments
 (0)