blob: ee9b49517fe90564104bb16da325ff77005b7769 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2013 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
* Matthew Conway - Bug 175186
*******************************************************************************/
package org.eclipse.core.externaltools.internal.model;
import java.util.Map;
import org.eclipse.core.externaltools.internal.ExternalToolsCore;
import org.eclipse.core.externaltools.internal.IExternalToolConstants;
import org.eclipse.core.externaltools.internal.launchConfigurations.ExternalToolsCoreUtil;
import org.eclipse.core.externaltools.internal.registry.ExternalToolMigration;
import org.eclipse.core.resources.ICommand;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.Bundle;
/**
* This project builder implementation will run an external tool during the
* build process.
*/
public final class ExternalToolBuilder extends IncrementalProjectBuilder {
private static final class IgnoreTeamPrivateChanges implements IResourceDeltaVisitor {
private boolean[] fTrueChange;
private IgnoreTeamPrivateChanges(boolean[] trueChange) {
super();
fTrueChange= trueChange;
}
@Override
public boolean visit(IResourceDelta visitDelta) throws CoreException {
IResource resource= visitDelta.getResource();
if (resource instanceof IFile) {
fTrueChange[0]= true;
return false;
}
return true;
}
}
public static final String ID = "org.eclipse.ui.externaltools.ExternalToolBuilder"; //$NON-NLS-1$;
private static String buildType = IExternalToolConstants.BUILD_TYPE_NONE;
private static IProject buildProject= null;
private static IResourceDelta buildDelta= null;
@Override
protected IProject[] build(int kind, Map<String, String> args, IProgressMonitor monitor) throws CoreException {
if (ExternalToolsCore.getDefault().getBundle().getState() != Bundle.ACTIVE) {
return null;
}
ILaunchConfiguration config= BuilderCoreUtils.configFromBuildCommandArgs(getProject(), args, new String[1]);
if (config == null) {
throw ExternalToolsCore.newError(ExternalToolsModelMessages.ExternalToolBuilder_0, null);
}
IProject[] projectsWithinScope= null;
IResource[] resources = ExternalToolsCoreUtil.getResourcesForBuildScope(config);
if (resources != null) {
projectsWithinScope= new IProject[resources.length];
for (int i = 0; i < resources.length; i++) {
projectsWithinScope[i]= resources[i].getProject();
}
}
boolean kindCompatible= commandConfiguredForKind(config, kind);
if (kindCompatible && configEnabled(config)) {
doBuildBasedOnScope(resources, kind, config, args, monitor);
}
return projectsWithinScope;
}
private boolean commandConfiguredForKind(ILaunchConfiguration config, int kind) {
try {
if (!(config.getAttribute(IExternalToolConstants.ATTR_TRIGGERS_CONFIGURED, false))) {
ICommand command= getCommand();
//adapt the builder command to make use of the 3.1 support for setting command build kinds
//this will only happen once for builder/command defined before the support existed
BuilderCoreUtils.configureTriggers(config, command);
IProjectDescription desc= getProject().getDescription();
ICommand[] commands= desc.getBuildSpec();
int index= getBuilderCommandIndex(commands, command);
if (index != -1) {
commands[index]= command;
desc.setBuildSpec(commands);
getProject().setDescription(desc, null);
ILaunchConfigurationWorkingCopy copy= config.getWorkingCopy();
copy.setAttribute(IExternalToolConstants.ATTR_TRIGGERS_CONFIGURED, true);
copy.doSave();
}
return command.isBuilding(kind);
}
} catch (CoreException e) {
ExternalToolsCore.log(e);
return true;
}
return true;
}
private int getBuilderCommandIndex(ICommand[] buildSpec, ICommand command) {
Map<String, String> commandArgs = command.getArguments();
if (commandArgs == null) {
return -1;
}
String handle= commandArgs.get(BuilderCoreUtils.LAUNCH_CONFIG_HANDLE);
if (handle == null) {
return -1;
}
for (int i = 0; i < buildSpec.length; ++i) {
ICommand buildSpecCommand= buildSpec[i];
if (ID.equals(buildSpecCommand.getBuilderName())) {
Map<String, String> buildSpecArgs = buildSpecCommand.getArguments();
if (buildSpecArgs != null) {
String buildSpecHandle= buildSpecArgs.get(BuilderCoreUtils.LAUNCH_CONFIG_HANDLE);
if (handle.equals(buildSpecHandle)) {
return i;
}
}
}
}
return -1;
}
/**
* Returns whether the given builder config is enabled or not.
*
* @param config the config to examine
* @return whether the config is enabled
*/
private boolean configEnabled(ILaunchConfiguration config) {
try {
return ExternalToolsCoreUtil.isBuilderEnabled(config);
} catch (CoreException e) {
ExternalToolsCore.log(e);
}
return true;
}
private void doBuildBasedOnScope(IResource[] resources, int kind, ILaunchConfiguration config, Map<String, String> args, IProgressMonitor monitor) throws CoreException {
boolean buildForChange = true;
if (kind != FULL_BUILD) { //scope not applied for full builds
if (resources != null && resources.length > 0) {
buildForChange = buildScopeIndicatesBuild(resources);
}
}
if (buildForChange) {
launchBuild(kind, config, args, monitor);
}
}
private void launchBuild(int kind, ILaunchConfiguration config, Map<String, String> args, IProgressMonitor monitor) throws CoreException {
monitor.subTask(NLS.bind(ExternalToolsModelMessages.ExternalToolBuilder_Running__0_____1, new String[] { config.getName()}));
buildStarted(kind, args);
// The default value for "launch in background" is true in debug core. If
// the user doesn't go through the UI, the new attribute won't be set. This means
// that existing Ant builders will try to run in the background (and likely conflict with
// each other) without migration.
ILaunchConfiguration newconfig= ExternalToolMigration.migrateRunInBackground(config);
newconfig.launch(ILaunchManager.RUN_MODE, monitor);
buildEnded();
}
/**
* Returns the build type being performed if the
* external tool is being run as a project builder.
*
* @return one of the <code>IExternalToolConstants.BUILD_TYPE_*</code> constants.
*/
public static String getBuildType() {
return buildType;
}
/**
* Returns the project that is being built and has triggered the current external
* tool builder. <code>null</code> is returned if no build is currently occurring.
*
* @return project being built or <code>null</code>.
*/
public static IProject getBuildProject() {
return buildProject;
}
/**
* Returns the <code>IResourceDelta</code> that is being built and has triggered the current external
* tool builder. <code>null</code> is returned if no build is currently occurring.
*
* @return resource delta for the build or <code>null</code>
*/
public static IResourceDelta getBuildDelta() {
return buildDelta;
}
/**
* Stores the currently active build kind and build project when a build begins
* @param buildKind
* @param args the arguments passed into the builder
*/
private void buildStarted(int buildKind, Map<String, String> args) {
switch (buildKind) {
case IncrementalProjectBuilder.INCREMENTAL_BUILD :
buildType = IExternalToolConstants.BUILD_TYPE_INCREMENTAL;
buildDelta = getDelta(getProject());
break;
case IncrementalProjectBuilder.FULL_BUILD :
if(args != null && args.containsKey(BuilderCoreUtils.INC_CLEAN)) {
buildType = IExternalToolConstants.BUILD_TYPE_INCREMENTAL;
buildDelta = getDelta(getProject());
}
else {
buildType = IExternalToolConstants.BUILD_TYPE_FULL;
}
break;
case IncrementalProjectBuilder.AUTO_BUILD :
buildType = IExternalToolConstants.BUILD_TYPE_AUTO;
buildDelta = getDelta(getProject());
break;
case IncrementalProjectBuilder.CLEAN_BUILD :
buildType = IExternalToolConstants.BUILD_TYPE_CLEAN;
break;
default :
buildType = IExternalToolConstants.BUILD_TYPE_NONE;
break;
}
buildProject= getProject();
}
/**
* Clears the current build kind, build project and build delta when a build finishes.
*/
private void buildEnded() {
buildType= IExternalToolConstants.BUILD_TYPE_NONE;
buildProject= null;
buildDelta= null;
}
private boolean buildScopeIndicatesBuild(IResource[] resources) {
for (IResource resource : resources) {
IResourceDelta delta = getDelta(resource.getProject());
if (delta == null) {
//project just added to the workspace..no previous build tree
return true;
}
IPath path = resource.getProjectRelativePath();
IResourceDelta change= delta.findMember(path);
if (change != null) {
final boolean[] trueChange= new boolean[1];
trueChange[0]= false;
try {
change.accept(new IgnoreTeamPrivateChanges(trueChange));
} catch (CoreException e) {
ExternalToolsCore.log("Internal error resolving changed resources during build", e); //$NON-NLS-1$
}
return trueChange[0]; //filtered out team private changes
}
}
return false;
}
@Override
protected void clean(IProgressMonitor monitor) throws CoreException {
ICommand command= getCommand();
ILaunchConfiguration config= BuilderCoreUtils.configFromBuildCommandArgs(getProject(), command.getArguments(), new String[1]);
if (!configEnabled(config)) {
return;
}
if ((!config.getAttribute(IExternalToolConstants.ATTR_TRIGGERS_CONFIGURED, false))) {
//old behavior
super.clean(monitor);
return;
}
launchBuild(IncrementalProjectBuilder.CLEAN_BUILD, config, null, monitor);
}
}