/******************************************************************************* * Copyright (c) 2000, 2013 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * 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 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; /* (non-Javadoc) * @see org.eclipse.core.internal.events.InternalBuilder#build(int, java.util.Map, org.eclipse.core.runtime.IProgressMonitor) */ @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 (int i = 0; i < resources.length; i++) { IResourceDelta delta = getDelta(resources[i].getProject()); if (delta == null) { //project just added to the workspace..no previous build tree return true; } IPath path= resources[i].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); } }