package org.eclipse.core.internal.events;
import java.util.*;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.eclipse.core.internal.dtree.DeltaDataTree;
import org.eclipse.core.internal.resources.*;
import org.eclipse.core.internal.resources.ComputeProjectOrder.Digraph;
import org.eclipse.core.internal.utils.Messages;
import org.eclipse.core.internal.utils.Policy;
import org.eclipse.core.internal.watson.ElementTree;
import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.jobs.*;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.Bundle;
public class BuildManager implements ICoreConstants, IManager, ILifecycleListener {
private static final String BUILDER_INIT = "BuilderInitInfo";
static class DeltaCache<E> {
private final Map<IPath, E> deltas = new HashMap<>();
private ElementTree newTree;
private ElementTree oldTree;
public void flush() {
deltas.clear();
this.oldTree = null;
this.newTree = null;
}
public E computeIfAbsent(IPath project, ElementTree anOldTree, ElementTree aNewTree, Supplier<E> calculator) {
if (!(areEqual(this.oldTree, anOldTree) && areEqual(this.newTree, aNewTree))) {
this.oldTree = anOldTree;
this.newTree = aNewTree;
deltas.clear();
}
return deltas.computeIfAbsent(project, p -> calculator.get());
}
private static boolean areEqual(ElementTree cached, ElementTree requested) {
return !ElementTree.hasChanges(requested, cached, ResourceComparator.getBuildComparator(), true);
}
}
class MissingBuilder extends IncrementalProjectBuilder {
private boolean hasBeenBuilt = false;
private String name;
MissingBuilder(String name) {
this.name = name;
}
@Override
protected IProject[] build(int kind, Map<String, String> args, IProgressMonitor monitor) {
if (!hasBeenBuilt && Policy.DEBUG_BUILD_FAILURE) {
hasBeenBuilt = true;
String msg = NLS.bind(Messages.events_skippingBuilder, name, getProject().getName());
Policy.log(IStatus.WARNING, msg, null);
}
return null;
}
String getName() {
return name;
}
@Override
public ISchedulingRule getRule(int kind, Map<String, String> args) {
return null;
}
}
private static final int TOTAL_BUILD_WORK = Policy.totalWork * 1000;
final AutoBuildJob autoBuildJob;
private final Set<IProject> builtProjects = Collections.synchronizedSet(new HashSet<>());
protected final Set<InternalBuilder> currentBuilders;
private DeltaDataTree currentDelta;
private ElementTree currentLastBuiltTree;
private ElementTree currentTree;
final private DeltaCache<IResourceDelta> deltaCache = new DeltaCache<>();
final private DeltaCache<DeltaDataTree> deltaTreeCache = new DeltaCache<>();
private ILock lock;
private boolean rebuildRequested = false;
private final Bundle systemBundle = Platform.getBundle("org.eclipse.osgi");
private Object builderInitializationLock = new Object();
private long timeStamp = -1;
private long overallTimeStamp = -1;
private Workspace workspace;
public BuildManager(Workspace workspace, ILock workspaceLock) {
this.workspace = workspace;
this.currentBuilders = Collections.synchronizedSet(new HashSet<>());
this.autoBuildJob = new AutoBuildJob(workspace);
this.lock = workspaceLock;
InternalBuilder.buildManager = this;
}
private void basicBuild(int trigger, IncrementalProjectBuilder builder, Map<String, String> args, MultiStatus status, IProgressMonitor monitor) {
InternalBuilder currentBuilder = builder;
try {
currentBuilders.add(currentBuilder);
currentBuilder.clearLastBuiltStateRequests();
boolean clean = trigger == IncrementalProjectBuilder.CLEAN_BUILD;
currentLastBuiltTree = currentBuilder.getLastBuiltTree();
boolean isBuilding = builder.getCommand().isBuilding(trigger);
if (!clean && currentLastBuiltTree == null) {
if (trigger == IncrementalProjectBuilder.AUTO_BUILD && !isBuilding)
return;
trigger = IncrementalProjectBuilder.FULL_BUILD;
isBuilding = isBuilding || builder.getCommand().isBuilding(trigger);
}
if (!isBuilding) {
if (clean)
currentBuilder.setLastBuiltTree(null);
return;
}
currentTree = ((trigger == IncrementalProjectBuilder.FULL_BUILD) || clean) ? null : workspace.getElementTree();
int depth = -1;
ISchedulingRule rule = null;
try {
if (!needsBuild(currentBuilder, trigger)) {
monitor.beginTask("", 1);
monitor.done();
return;
}
rule = builder.getRule(trigger, args);
String name = currentBuilder.getLabel();
String message;
if (name != null) {
message = NLS.bind(Messages.events_invoking_2, name, builder.getProject().getFullPath());
} else {
message = NLS.bind(Messages.events_invoking_1, builder.getProject().getFullPath());
}
monitor.subTask(message);
hookStartBuild(builder, trigger);
if (rule != null && currentTree != null) {
workspace.newWorkingTree();
}
depth = getWorkManager().beginUnprotected();
if (rule != null) {
Job.getJobManager().beginRule(rule, monitor);
if (currentTree != null)
currentTree = workspace.getElementTree();
}
SafeRunner.run(getSafeRunnable(currentBuilder, trigger, args, status, monitor));
} finally {
if (depth >= 0) {
getWorkManager().endUnprotected(depth);
}
if (rule != null) {
Job.getJobManager().endRule(rule);
}
if (clean || currentBuilder.wasForgetStateRequested()) {
currentBuilder.setLastBuiltTree(null);
} else if (currentBuilder.wasRememberStateRequested()) {
if (trigger == IncrementalProjectBuilder.FULL_BUILD) {
currentBuilder.setLastBuiltTree(null);
}
} else {
ElementTree lastTree = workspace.getElementTree();
lastTree.immutable();
currentBuilder.setLastBuiltTree(lastTree);
}
hookEndBuild(builder);
}
} finally {
currentBuilders.remove(currentBuilder);
currentTree = null;
currentLastBuiltTree = null;
currentDelta = null;
}
}
protected void basicBuild(IBuildConfiguration buildConfiguration, int trigger, IBuildContext context, ICommand[] commands, MultiStatus status, IProgressMonitor monitor) {
try {
for (int i = 0; i < commands.length; i++) {
checkCanceled(trigger, monitor);
BuildCommand command = (BuildCommand) commands[i];
IProgressMonitor sub = Policy.subMonitorFor(monitor, 1);
IncrementalProjectBuilder builder = getBuilder(buildConfiguration, command, i, status, context);
if (builder != null)
basicBuild(trigger, builder, command.getArguments(false), status, sub);
}
} catch (CoreException e) {
status.add(e.getStatus());
}
}
private IStatus basicBuild(IBuildConfiguration buildConfiguration, int trigger, IBuildContext context, IProgressMonitor monitor) {
try {
hookStartBuild(new IBuildConfiguration[] {buildConfiguration}, trigger);
MultiStatus status = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.INTERNAL_ERROR, Messages.events_errors, null);
basicBuild(buildConfiguration, trigger, context, status, monitor);
return status;
} finally {
hookEndBuild(trigger);
}
}
private void basicBuild(final IBuildConfiguration buildConfiguration, final int trigger, final IBuildContext context, final MultiStatus status, final IProgressMonitor monitor) {
try {
final IProject project = buildConfiguration.getProject();
final ICommand[] commands;
if (project.isAccessible())
commands = ((Project) project).internalGetDescription().getBuildSpec(false);
else
commands = null;
int work = commands == null ? 0 : commands.length;
monitor.beginTask(NLS.bind(Messages.events_building_1, project.getFullPath()), work);
if (work == 0)
return;
ISafeRunnable code = new ISafeRunnable() {
@Override
public void handleException(Throwable e) {
if (e instanceof OperationCanceledException) {
if (Policy.DEBUG_BUILD_INVOKING)
Policy.debug("Build canceled");
throw (OperationCanceledException) e;
}
String errorText = e.getMessage();
if (errorText == null)
errorText = NLS.bind(Messages.events_unknown, e.getClass().getName(), project.getName());
status.add(new Status(IStatus.WARNING, ResourcesPlugin.PI_RESOURCES, IResourceStatus.INTERNAL_ERROR, errorText, e));
}
@Override
public void run() throws Exception {
basicBuild(buildConfiguration, trigger, context, commands, status, monitor);
}
};
SafeRunner.run(code);
} finally {
monitor.done();
}
}
private IStatus basicBuild(IBuildConfiguration buildConfiguration, int trigger, String builderName, Map<String, String> args, IProgressMonitor monitor) {
final IProject project = buildConfiguration.getProject();
monitor = Policy.monitorFor(monitor);
try {
String message = NLS.bind(Messages.events_building_1, project.getFullPath());
monitor.beginTask(message, 1);
try {
hookStartBuild(new IBuildConfiguration[] {buildConfiguration}, trigger);
MultiStatus status = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.INTERNAL_ERROR, Messages.events_errors, null);
ICommand command = getCommand(project, builderName, args);
try {
IBuildContext context = new BuildContext(buildConfiguration);
IncrementalProjectBuilder builder = getBuilder(buildConfiguration, command, -1, status, context);
if (builder != null)
basicBuild(trigger, builder, args, status, Policy.subMonitorFor(monitor, 1));
} catch (CoreException e) {
status.add(e.getStatus());
}
return status;
} finally {
hookEndBuild(trigger);
}
} finally {
monitor.done();
}
}
private void basicBuildLoop(IBuildConfiguration[] configs, IBuildConfiguration[] requestedConfigs, int trigger, MultiStatus status, IProgressMonitor monitor) {
int projectWork = configs.length > 0 ? TOTAL_BUILD_WORK / configs.length : 0;
int maxIterations = workspace.getDescription().getMaxBuildIterations();
if (maxIterations <= 0)
maxIterations = 1;
rebuildRequested = true;
for (int iter = 0; rebuildRequested && iter < maxIterations; iter++) {
rebuildRequested = false;
builtProjects.clear();
for (IBuildConfiguration config : configs) {
if (config.getProject().isAccessible()) {
IBuildContext context = new BuildContext(config, requestedConfigs, configs);
basicBuild(config, trigger, context, status, Policy.subMonitorFor(monitor, projectWork));
builtProjects.add(config.getProject());
}
}
trigger = IncrementalProjectBuilder.INCREMENTAL_BUILD;
}
}
public IStatus build(IBuildConfiguration[] configs, IBuildConfiguration[] requestedConfigs, int trigger, IProgressMonitor monitor) {
monitor = Policy.monitorFor(monitor);
try {
monitor.beginTask(Messages.events_building_0, TOTAL_BUILD_WORK);
try {
hookStartBuild(configs, trigger);
MultiStatus status = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.BUILD_FAILED, Messages.events_errors, null);
basicBuildLoop(configs, requestedConfigs, trigger, status, monitor);
return status;
} finally {
hookEndBuild(trigger);
}
} finally {
endBuild(trigger, monitor);
}
}
public IStatus buildParallel(Digraph<IBuildConfiguration> configs, IBuildConfiguration[] requestedConfigs, int trigger, JobGroup buildJobGroup, IProgressMonitor monitor) {
monitor = Policy.monitorFor(monitor);
try {
monitor.beginTask(Messages.events_building_0, TOTAL_BUILD_WORK);
try {
builtProjects.clear();
hookStartBuild(configs.vertexList.stream().map(vertex -> vertex.id).toArray(IBuildConfiguration[]::new), trigger);
MultiStatus status = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.BUILD_FAILED, Messages.events_errors, null);
parallelBuildLoop(configs, requestedConfigs, trigger, buildJobGroup, status, monitor);
return status;
} finally {
hookEndBuild(trigger);
}
} finally {
endBuild(trigger, monitor);
}
}
private void endBuild(int trigger, IProgressMonitor monitor) {
boolean cancelledBuild = monitor.isCanceled();
monitor.done();
if (trigger == IncrementalProjectBuilder.INCREMENTAL_BUILD || trigger == IncrementalProjectBuilder.FULL_BUILD) {
autoBuildJob.avoidBuild();
} else if (cancelledBuild) {
autoBuildJob.avoidBuildIfNotInterrupted();
}
}
private void parallelBuildLoop(final Digraph<IBuildConfiguration> configs, IBuildConfiguration[] requestedConfigs, int trigger, JobGroup buildJobGroup, MultiStatus status, IProgressMonitor monitor) {
final int projectWork = configs.vertexList.size() > 0 ? TOTAL_BUILD_WORK / configs.vertexList.size() : 0;
builtProjects.clear();
final GraphProcessor<IBuildConfiguration> graphProcessor = new GraphProcessor<>(configs, IBuildConfiguration.class, (config, graphCrawler) -> {
IBuildContext context = new BuildContext(config, requestedConfigs, graphCrawler.getSequentialOrder());
try {
workspace.prepareOperation(null, monitor);
workspace.beginOperation(false);
basicBuild(config, trigger, context, status, Policy.subMonitorFor(monitor, projectWork));
workspace.endOperation(null, false);
builtProjects.add(config.getProject());
} catch (CoreException ex) {
status.add(new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, ex.getMessage(), ex));
}
}, config -> getRule(config, trigger, null, Collections.emptyMap()), buildJobGroup);
graphProcessor.processGraphWithParallelJobs();
try {
Job.getJobManager().join(graphProcessor, monitor);
} catch (OperationCanceledException | InterruptedException e) {
status.add(new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, e.getMessage(), e));
}
}
public IStatus build(IBuildConfiguration buildConfiguration, int trigger, String builderName, Map<String, String> args, IProgressMonitor monitor) {
monitor = Policy.monitorFor(monitor);
if (builderName == null) {
IBuildContext context = new BuildContext(buildConfiguration);
return basicBuild(buildConfiguration, trigger, context, monitor);
}
return basicBuild(buildConfiguration, trigger, builderName, args, monitor);
}
private void checkCanceled(int trigger, IProgressMonitor monitor) {
if (systemBundle.getState() == Bundle.STOPPING)
throw new OperationCanceledException();
Policy.checkCanceled(monitor);
if (trigger != IncrementalProjectBuilder.AUTO_BUILD)
return;
if (autoBuildJob.isInterrupted())
throw new OperationCanceledException();
}
public ArrayList<BuilderPersistentInfo> createBuildersPersistentInfo(IProject project) throws CoreException {
ArrayList<BuilderPersistentInfo> oldInfos = getBuildersPersistentInfo(project);
ProjectDescription desc = ((Project) project).internalGetDescription();
ICommand[] commands = desc.getBuildSpec(false);
if (commands.length == 0)
return null;
IBuildConfiguration[] configs = project.getBuildConfigs();
ArrayList<BuilderPersistentInfo> newInfos = new ArrayList<>(commands.length * configs.length);
for (int i = 0; i < commands.length; i++) {
BuildCommand command = (BuildCommand) commands[i];
String builderName = command.getBuilderName();
boolean supportsConfigs = command.supportsConfigs();
int numberConfigs = supportsConfigs ? configs.length : 1;
for (int j = 0; j < numberConfigs; j++) {
IBuildConfiguration config = configs[j];
BuilderPersistentInfo info = null;
IncrementalProjectBuilder builder = ((BuildCommand) commands[i]).getBuilder(config);
if (builder == null) {
if (oldInfos != null)
info = getBuilderInfo(oldInfos, builderName, supportsConfigs ? config.getName() : null, i);
} else if (!(builder instanceof MissingBuilder)) {
ElementTree oldTree = ((InternalBuilder) builder).getLastBuiltTree();
if (oldTree != null) {
info = new BuilderPersistentInfo(project.getName(), supportsConfigs ? config.getName() : null, builderName, i);
info.setLastBuildTree(oldTree);
info.setInterestingProjects(((InternalBuilder) builder).getInterestingProjects());
}
}
if (info != null)
newInfos.add(info);
}
}
return newInfos;
}
private String debugBuilder() {
return currentBuilders == null ? "<no builder>" : currentBuilders.getClass().getName();
}
private String debugProject() {
if (currentBuilders == null)
return "<no project>";
return "[" + currentBuilders.stream().map(builder -> builder.getProject().getFullPath().toString()).collect(Collectors.joining(",")) + "]";
}
private String debugTrigger(int trigger) {
switch (trigger) {
case IncrementalProjectBuilder.FULL_BUILD :
return "FULL_BUILD";
case IncrementalProjectBuilder.CLEAN_BUILD :
return "CLEAN_BUILD";
case IncrementalProjectBuilder.AUTO_BUILD :
case IncrementalProjectBuilder.INCREMENTAL_BUILD :
default :
return "INCREMENTAL_BUILD";
}
}
public void endTopLevel(boolean needsBuild) {
autoBuildJob.build(needsBuild);
}
private boolean getBooleanAttribute(IConfigurationElement element, String name) {
String valueString = element.getAttribute(name);
return valueString != null && valueString.equalsIgnoreCase(Boolean.TRUE.toString());
}
private IncrementalProjectBuilder getBuilder(IBuildConfiguration buildConfiguration, ICommand command, int buildSpecIndex, MultiStatus status) throws CoreException {
BuildCommand buildCommand = (BuildCommand) command;
InternalBuilder result = buildCommand.getBuilder(buildConfiguration);
String builderName = command.getBuilderName();
IProject project = buildConfiguration.getProject();
if (result == null) {
BuilderPersistentInfo info;
synchronized (builderInitializationLock) {
BuilderPersistentInfo builderInitInProgress = getBuilderInitInfo(project, builderName);
if (builderInitInProgress != null) {
info = builderInitInProgress;
} else {
info = removePersistentBuilderInfo(builderName, buildConfiguration, buildSpecIndex);
setBuilderInitInfo(project, builderName, info);
}
}
result = buildCommand.getBuilder(buildConfiguration);
if (result == null) {
result = initializeBuilder(command, builderName, buildConfiguration, info, status);
}
synchronized (builderInitializationLock) {
InternalBuilder other = buildCommand.getBuilder(buildConfiguration);
if (other == null) {
buildCommand.addBuilder(buildConfiguration, (IncrementalProjectBuilder) result);
} else {
result = other;
}
setBuilderInitInfo(project, builderName, null);
}
}
result.setBuildConfig(buildConfiguration);
if (!validateNature(result, builderName)) {
result.setLastBuiltTree(null);
return null;
}
return (IncrementalProjectBuilder) result;
}
private IncrementalProjectBuilder getBuilder(IBuildConfiguration buildConfiguration, ICommand command, int buildSpecIndex, MultiStatus status, IBuildContext context) throws CoreException {
InternalBuilder builder = getBuilder(buildConfiguration, command, buildSpecIndex, status);
if (builder != null)
builder.setContext(context);
return (IncrementalProjectBuilder) builder;
}
private BuilderPersistentInfo getBuilderInfo(ArrayList<BuilderPersistentInfo> infos, String builderName, String configName, int buildSpecIndex) {
BuilderPersistentInfo nameMatch = null;
for (BuilderPersistentInfo info : infos) {
if (info.getBuilderName().equals(builderName) && (info.getConfigName() == null || info.getConfigName().equals(configName))) {
if (nameMatch == null)
nameMatch = info;
if (buildSpecIndex == -1 || info.getBuildSpecIndex() == -1 || buildSpecIndex == info.getBuildSpecIndex())
return info;
}
}
return nameMatch;
}
@SuppressWarnings({"unchecked"})
public ArrayList<BuilderPersistentInfo> getBuildersPersistentInfo(IProject project) throws CoreException {
return (ArrayList<BuilderPersistentInfo>) project.getSessionProperty(K_BUILD_LIST);
}
private ICommand getCommand(IProject project, String builderName, Map<String, String> args) {
ICommand[] buildSpec = ((Project) project).internalGetDescription().getBuildSpec(false);
for (ICommand element : buildSpec)
if (element.getBuilderName().equals(builderName))
return element;
BuildCommand result = new BuildCommand();
result.setBuilderName(builderName);
result.setArguments(args);
return result;
}
IResourceDelta getDelta(IProject project) {
try {
lock.acquire();
if (currentTree == null) {
if (Policy.DEBUG_BUILD_FAILURE)
Policy.debug("Build: no tree for delta " + debugBuilder() + " [" + debugProject() + "]");
return null;
}
Set<InternalBuilder> interestedBuilders = getInterestedBuilders(project);
if (interestedBuilders.isEmpty()) {
if (Policy.DEBUG_BUILD_FAILURE)
Policy.debug("Build: project not interesting for current builders " + debugBuilder() + " [" + debugProject() + "] " + project.getFullPath());
return null;
}
if (currentDelta != null && currentDelta.findNodeAt(project.getFullPath()) == null) {
if (!project.exists())
return null;
return ResourceDeltaFactory.newEmptyDelta(project);
}
IResourceDelta resultDelta = deltaCache.computeIfAbsent(project.getFullPath(), currentLastBuiltTree, currentTree, () -> {
long startTime = 0L;
if (Policy.DEBUG_BUILD_DELTA) {
startTime = System.currentTimeMillis();
Policy.debug("Computing delta for project: " + project.getName());
}
IResourceDelta result = ResourceDeltaFactory.computeDelta(workspace, currentLastBuiltTree, currentTree, project.getFullPath(), -1);
if (Policy.DEBUG_BUILD_FAILURE && result == null)
Policy.debug("Build: no delta " + debugBuilder() + " [" + debugProject() + "] " + project.getFullPath());
if (Policy.DEBUG_BUILD_DELTA)
Policy.debug("Finished computing delta, time: " + (System.currentTimeMillis() - startTime) + "ms" + ((ResourceDelta) result).toDeepDebugString());
return result;
});
return resultDelta;
} finally {
lock.release();
}
}
private ISafeRunnable getSafeRunnable(final InternalBuilder currentBuilder, final int trigger, final Map<String, String> args, final MultiStatus status, final IProgressMonitor monitor) {
return new ISafeRunnable() {
@Override
public void handleException(Throwable e) {
if (e instanceof OperationCanceledException) {
if (Policy.DEBUG_BUILD_INVOKING)
Policy.debug("Build canceled");
currentBuilder.forgetLastBuiltState();
throw (OperationCanceledException) e;
}
String builderName = currentBuilder.getLabel();
if (builderName == null || builderName.length() == 0)
builderName = currentBuilder.getClass().getName();
String pluginId = currentBuilder.getPluginId();
String message = NLS.bind(Messages.events_builderError, builderName, currentBuilder.getProject().getName());
status.add(new Status(IStatus.ERROR, pluginId, IResourceStatus.BUILD_FAILED, message, e));
if (e instanceof CoreException)
status.add(((CoreException) e).getStatus());
}
@Override
public void run() throws Exception {
IProject[] prereqs = null;
if (trigger != IncrementalProjectBuilder.CLEAN_BUILD)
prereqs = currentBuilder.build(trigger, args, monitor);
else
currentBuilder.clean(monitor);
if (prereqs == null)
prereqs = new IProject[0];
currentBuilder.setInterestingProjects(prereqs.clone());
}
};
}
private WorkManager getWorkManager() {
try {
return workspace.getWorkManager();
} catch (CoreException e) {
}
return null;
}
@Override
public void handleEvent(LifecycleEvent event) {
IProject project = null;
switch (event.kind) {
case LifecycleEvent.PRE_PROJECT_DELETE :
case LifecycleEvent.PRE_PROJECT_MOVE :
project = (IProject) event.resource;
if (project.isAccessible())
setBuildersPersistentInfo(project, null);
}
}
boolean hasBeenBuilt(IProject project) {
return builtProjects.contains(project);
}
private void hookEndBuild(IncrementalProjectBuilder builder) {
if (ResourceStats.TRACE_BUILDERS)
ResourceStats.endBuild();
if (!Policy.DEBUG_BUILD_INVOKING || timeStamp == -1)
return;
Policy.debug("Builder finished: " + toString(builder) + " time: " + (System.currentTimeMillis() - timeStamp) + "ms");
timeStamp = -1;
}
private void hookEndBuild(int trigger) {
builtProjects.clear();
deltaCache.flush();
deltaTreeCache.flush();
if (trigger == IncrementalProjectBuilder.CLEAN_BUILD)
autoBuildJob.forceBuild();
if (Policy.DEBUG_BUILD_INVOKING) {
Policy.debug("Top-level build-end time: " + (System.currentTimeMillis() - overallTimeStamp));
overallTimeStamp = -1;
}
}
private void hookStartBuild(IncrementalProjectBuilder builder, int trigger) {
if (ResourceStats.TRACE_BUILDERS)
ResourceStats.startBuild(builder);
if (Policy.DEBUG_BUILD_INVOKING) {
timeStamp = System.currentTimeMillis();
Policy.debug("Invoking (" + debugTrigger(trigger) + ") on builder: " + toString(builder));
}
}
private void hookStartBuild(IBuildConfiguration[] configs, int trigger) {
if (Policy.DEBUG_BUILD_STACK)
Policy.debug(new RuntimeException("Starting build: " + debugTrigger(trigger)));
if (Policy.DEBUG_BUILD_INVOKING) {
overallTimeStamp = System.currentTimeMillis();
StringBuilder sb = new StringBuilder("Top-level build-start of: ");
for (IBuildConfiguration config : configs)
sb.append(config).append(", ");
sb.append(debugTrigger(trigger));
Policy.debug(sb.toString());
}
}
private InternalBuilder initializeBuilder(ICommand command, String builderName, IBuildConfiguration buildConfiguration, BuilderPersistentInfo info, MultiStatus status) {
IProject project = buildConfiguration.getProject();
InternalBuilder builder = null;
try {
builder = instantiateBuilder(builderName);
} catch (CoreException e) {
status.add(new ResourceStatus(IResourceStatus.BUILD_FAILED, project.getFullPath(), NLS.bind(Messages.events_instantiate_1, builderName), e));
status.add(e.getStatus());
}
if (builder == null) {
builder = new MissingBuilder(builderName);
}
if (info != null) {
ElementTree tree = info.getLastBuiltTree();
if (tree != null) {
builder.setLastBuiltTree(tree);
}
builder.setInterestingProjects(info.getInterestingProjects());
}
builder.setCommand(command);
builder.setBuildConfig(buildConfiguration);
builder.startupOnInitialize();
return builder;
}
private BuilderPersistentInfo removePersistentBuilderInfo(String builderName, IBuildConfiguration buildConfiguration, int buildSpecIndex) throws CoreException {
IProject project = buildConfiguration.getProject();
ArrayList<BuilderPersistentInfo> infos = getBuildersPersistentInfo(project);
if (infos != null) {
BuilderPersistentInfo info = getBuilderInfo(infos, builderName, buildConfiguration.getName(), buildSpecIndex);
if (info != null) {
infos.remove(info);
if (infos.isEmpty()) {
setBuildersPersistentInfo(project, null);
}
return info;
}
}
return null;
}
private IncrementalProjectBuilder instantiateBuilder(String builderName) throws CoreException {
IExtension extension = Platform.getExtensionRegistry().getExtension(ResourcesPlugin.PI_RESOURCES, ResourcesPlugin.PT_BUILDERS, builderName);
if (extension == null)
return null;
IConfigurationElement[] configs = extension.getConfigurationElements();
if (configs.length == 0)
return null;
String natureId = null;
if (getBooleanAttribute(configs[0], "hasNature")) {
String builderId = extension.getUniqueIdentifier();
natureId = workspace.getNatureManager().findNatureForBuilder(builderId);
if (natureId == null)
return null;
}
InternalBuilder builder = (InternalBuilder) configs[0].createExecutableExtension("run");
builder.setPluginId(extension.getContributor().getName());
builder.setLabel(extension.getLabel());
builder.setNatureId(natureId);
builder.setCallOnEmptyDelta(getBooleanAttribute(configs[0], "callOnEmptyDelta"));
return (IncrementalProjectBuilder) builder;
}
public void interrupt() {
autoBuildJob.interrupt();
}
public boolean isAutobuildBuildPending() {
return autoBuildJob.getState() != Job.NONE;
}
private boolean isInterestingProject(InternalBuilder currentBuilder, IProject project) {
if (project.equals(currentBuilder.getProject()))
return true;
IProject[] interestingProjects = currentBuilder.getInterestingProjects();
for (IProject interestingProject : interestingProjects) {
if (interestingProject.equals(project)) {
return true;
}
}
return false;
}
private Set<InternalBuilder> getInterestedBuilders(final IProject project) {
final Set<InternalBuilder> res = new HashSet<>();
for (final InternalBuilder builder : this.currentBuilders) {
if (isInterestingProject(builder, project)) {
res.add(builder);
}
}
return res;
}
private boolean needsBuild(InternalBuilder builder, int trigger) {
switch (trigger) {
case IncrementalProjectBuilder.CLEAN_BUILD :
return true;
case IncrementalProjectBuilder.FULL_BUILD :
return true;
case IncrementalProjectBuilder.INCREMENTAL_BUILD :
for (InternalBuilder currentBuilder : this.currentBuilders) {
if (currentBuilder.callOnEmptyDelta()) {
return true;
}
}
}
ElementTree oldTree = builder.getLastBuiltTree();
ElementTree newTree = workspace.getElementTree();
long start = System.currentTimeMillis();
currentDelta = deltaTreeCache.computeIfAbsent(null, oldTree, newTree, () -> {
if (Policy.DEBUG_BUILD_NEEDED) {
String message = "Checking if need to build. Starting delta computation between: " + oldTree + " and "
+ newTree;
Policy.debug(message);
}
DeltaDataTree computed = newTree.getDataTree().forwardDeltaWith(oldTree.getDataTree(), ResourceComparator.getBuildComparator());
if (Policy.DEBUG_BUILD_NEEDED)
Policy.debug("End delta computation. (" + (System.currentTimeMillis() - start) + "ms).");
return computed;
});
if (currentDelta.findNodeAt(builder.getProject().getFullPath()) != null) {
if (Policy.DEBUG_BUILD_NEEDED)
Policy.debug(toString(builder) + " needs building because of changes in: " + builder.getProject().getName());
return true;
}
IProject[] projects = builder.getInterestingProjects();
for (IProject project : projects) {
if (currentDelta.findNodeAt(project.getFullPath()) != null) {
if (Policy.DEBUG_BUILD_NEEDED)
Policy.debug(toString(builder) + " needs building because of changes in: " + project.getName());
return true;
}
}
return false;
}
private void removeBuilders(IProject project, String builderId) throws CoreException {
IProjectDescription desc = project.getDescription();
ICommand[] oldSpec = desc.getBuildSpec();
int oldLength = oldSpec.length;
if (oldLength == 0)
return;
int remaining = 0;
for (int i = 0; i < oldSpec.length; i++) {
if (oldSpec[i].getBuilderName().equals(builderId))
oldSpec[i] = null;
else
remaining++;
}
if (remaining == oldSpec.length)
return;
ICommand[] newSpec = new ICommand[remaining];
for (int i = 0, newIndex = 0; i < oldLength; i++) {
if (oldSpec[i] != null)
newSpec[newIndex++] = oldSpec[i];
}
desc.setBuildSpec(newSpec);
project.setDescription(desc, IResource.NONE, null);
}
void requestRebuild() {
rebuildRequested = true;
}
public void setBuildersPersistentInfo(IProject project, List<BuilderPersistentInfo> list) {
try {
project.setSessionProperty(K_BUILD_LIST, list);
} catch (CoreException e) {
logProjectAccessError(project, e, "Project missing in setBuildersPersistentInfo");
}
}
private void setBuilderInitInfo(IProject project, String builderName, BuilderPersistentInfo info) {
try {
project.setSessionProperty(keyForBuilderInfo(builderName), info);
} catch (CoreException e) {
logProjectAccessError(project, e, "Project missing in setBuilderInitInfo");
}
}
private BuilderPersistentInfo getBuilderInitInfo(IProject project, String builderName) {
try {
return (BuilderPersistentInfo) project.getSessionProperty(keyForBuilderInfo(builderName));
} catch (CoreException e) {
logProjectAccessError(project, e, "Project missing in getBuilderInitInfo");
}
return null;
}
private void logProjectAccessError(IProject project, CoreException e, String message) {
Policy.log(new ResourceStatus(IStatus.ERROR, 1, project.getFullPath(), message, e));
}
private static QualifiedName keyForBuilderInfo(String builderName) {
return new QualifiedName(ResourcesPlugin.PI_RESOURCES, BUILDER_INIT + builderName);
}
@Override
public void shutdown(IProgressMonitor monitor) {
autoBuildJob.cancel();
}
@Override
public void startup(IProgressMonitor monitor) {
workspace.addLifecycleListener(this);
}
private String toString(InternalBuilder builder) {
String name = builder.getClass().getName();
name = name.substring(name.lastIndexOf('.') + 1);
if (builder instanceof MissingBuilder)
name = name + ": '" + ((MissingBuilder) builder).getName() + "'";
return name + "(" + builder.getBuildConfig() + ")";
}
private boolean validateNature(InternalBuilder builder, String builderId) throws CoreException {
String nature = builder.getNatureId();
if (nature == null)
return true;
IProject project = builder.getProject();
if (!project.hasNature(nature)) {
removeBuilders(project, builderId);
return false;
}
return project.isNatureEnabled(nature);
}
public ISchedulingRule getRule(IBuildConfiguration buildConfiguration, int trigger, String builderName, Map<String, String> buildArgs) {
IProject project = buildConfiguration.getProject();
MultiStatus status = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.INTERNAL_ERROR, Messages.events_errors, null);
if (builderName == null) {
final ICommand[] commands;
if (project.isAccessible()) {
Set<ISchedulingRule> rules = new HashSet<>();
commands = ((Project) project).internalGetDescription().getBuildSpec(false);
boolean hasNullBuildRule = false;
BuildContext context = new BuildContext(buildConfiguration);
for (int i = 0; i < commands.length; i++) {
BuildCommand command = (BuildCommand) commands[i];
Map<String, String> allArgs = command.getArguments(true);
if (allArgs == null) {
allArgs = buildArgs;
} else if (buildArgs != null) {
allArgs.putAll(buildArgs);
}
try {
IncrementalProjectBuilder builder = getBuilder(buildConfiguration, command, i, status, context);
if (builder != null) {
ISchedulingRule builderRule = builder.getRule(trigger, allArgs);
if (builderRule != null)
rules.add(builderRule);
else
hasNullBuildRule = true;
}
} catch (CoreException e) {
status.add(e.getStatus());
}
}
if (rules.isEmpty())
return null;
if (!hasNullBuildRule)
return new MultiRule(rules.toArray(new ISchedulingRule[rules.size()]));
}
} else {
ICommand command = getCommand(project, builderName, buildArgs);
Map<String, String> allArgs = new HashMap<>();
if (command.getArguments() != null) {
allArgs.putAll(command.getArguments());
}
if (buildArgs != null) {
allArgs.putAll(buildArgs);
}
try {
IncrementalProjectBuilder builder = getBuilder(buildConfiguration, command, -1, status);
if (builder != null)
return builder.getRule(trigger, allArgs);
} catch (CoreException e) {
status.add(e.getStatus());
}
}
if (!status.isOK())
Policy.log(status);
return workspace.getRoot();
}
}