| package org.eclipse.cdt.managedbuilder.internal.core; |
| |
| /********************************************************************** |
| * Copyright (c) 2002,2003 Rational Software Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Common Public License v0.5 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/cpl-v05.html |
| * |
| * Contributors: |
| * IBM Rational Software - Initial API and implementation |
| * **********************************************************************/ |
| |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Enumeration; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Properties; |
| |
| import org.eclipse.cdt.core.CCorePlugin; |
| import org.eclipse.cdt.core.CommandLauncher; |
| import org.eclipse.cdt.core.ConsoleOutputStream; |
| import org.eclipse.cdt.core.ErrorParserManager; |
| import org.eclipse.cdt.core.model.ICModelMarker; |
| import org.eclipse.cdt.core.resources.ACBuilder; |
| import org.eclipse.cdt.core.resources.IConsole; |
| import org.eclipse.cdt.managedbuilder.core.IManagedBuildInfo; |
| import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager; |
| import org.eclipse.cdt.managedbuilder.core.ManagedBuilderCorePlugin; |
| import org.eclipse.core.resources.IMarker; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IResourceDelta; |
| import org.eclipse.core.resources.IResourceDeltaVisitor; |
| import org.eclipse.core.resources.IWorkspace; |
| import org.eclipse.core.resources.IWorkspaceRoot; |
| 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.core.runtime.NullProgressMonitor; |
| import org.eclipse.core.runtime.OperationCanceledException; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.core.runtime.SubProgressMonitor; |
| |
| public class GeneratedMakefileBuilder extends ACBuilder { |
| // String constants |
| private static final String MESSAGE = "ManagedMakeBuilder.message"; //$NON-NLS-1$ |
| private static final String BUILD_ERROR = MESSAGE + ".error"; //$NON-NLS-1$ |
| private static final String REFRESH_ERROR = BUILD_ERROR + ".refresh"; //$NON-NLS-1$ |
| private static final String BUILD_FINISHED = MESSAGE + ".finished"; //$NON-NLS-1$ |
| private static final String INCREMENTAL = MESSAGE + ".incremental"; //$NON-NLS-1$ |
| private static final String MAKE = MESSAGE + ".make"; //$NON-NLS-1$ |
| private static final String REBUILD = MESSAGE + ".rebuild"; //$NON-NLS-1$ |
| private static final String START = MESSAGE + ".starting"; //$NON-NLS-1$ |
| private static final String REFRESH = MESSAGE + ".updating"; //$NON-NLS-1$ |
| private static final String MARKERS = MESSAGE + ".creating.markers"; //$NON-NLS-1$ |
| |
| // Local variables |
| protected List resourcesToBuild; |
| protected List ruleList; |
| |
| |
| public class ResourceDeltaVisitor implements IResourceDeltaVisitor { |
| private boolean buildNeeded = false; |
| |
| public boolean visit(IResourceDelta delta) throws CoreException { |
| IResource resource = delta.getResource(); |
| // If the project has changed, then a build is needed and we can stop |
| if (resource != null && resource.getProject() == getProject()) { |
| buildNeeded = true; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| public boolean shouldBuild() { |
| return buildNeeded; |
| } |
| } |
| |
| /** |
| * |
| */ |
| public GeneratedMakefileBuilder() { |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.core.internal.events.InternalBuilder#build(int, java.util.Map, org.eclipse.core.runtime.IProgressMonitor) |
| */ |
| protected IProject[] build(int kind, Map args, IProgressMonitor monitor) throws CoreException { |
| String statusMsg = ManagedBuilderCorePlugin.getFormattedString(START, getProject().getName()); |
| if (statusMsg != null) { |
| monitor.subTask(statusMsg); |
| } |
| |
| // Get the build information |
| IManagedBuildInfo info = ManagedBuildManager.getBuildInfo(getProject()); |
| |
| if (kind == IncrementalProjectBuilder.FULL_BUILD || info.isDirty()) { |
| fullBuild(monitor, info); |
| } |
| else if (kind == IncrementalProjectBuilder.AUTO_BUILD && info.isDirty()) { |
| fullBuild(monitor, info); |
| } |
| else { |
| // Create a delta visitor to make sure we should be rebuilding |
| ResourceDeltaVisitor visitor = new ResourceDeltaVisitor(); |
| IResourceDelta delta = getDelta(getProject()); |
| if (delta == null) { |
| fullBuild(monitor, info); |
| } |
| else { |
| delta.accept(visitor); |
| if (visitor.shouldBuild()) { |
| incrementalBuild(delta, info, monitor); |
| } |
| } |
| } |
| |
| // Scrub the build info of all the projects participating in the build |
| info.setDirty(false); |
| IProject[] deps = getProject().getReferencedProjects(); |
| for (int i = 0; i < deps.length; i++) { |
| IProject project = deps[i]; |
| IManagedBuildInfo depInfo = ManagedBuildManager.getBuildInfo(project); |
| // May not be a managed project |
| if (depInfo != null) { |
| depInfo.setDirty(false); |
| } |
| } |
| |
| // Ask build mechanism to compute deltas for project dependencies next time |
| return deps; |
| } |
| |
| /** |
| * Check whether the build has been canceled. Cancellation requests |
| * propagated to the caller by throwing <code>OperationCanceledException</code>. |
| * |
| * @see org.eclipse.core.runtime.OperationCanceledException#OperationCanceledException() |
| */ |
| public void checkCancel(IProgressMonitor monitor) { |
| if (monitor != null && monitor.isCanceled()) { |
| throw new OperationCanceledException(); |
| } |
| } |
| |
| /** |
| * @param monitor |
| */ |
| protected void fullBuild(IProgressMonitor monitor, IManagedBuildInfo info) throws CoreException { |
| // Always need one of these bad boys |
| if (monitor == null) { |
| monitor = new NullProgressMonitor(); |
| } |
| |
| // Regenerate the makefiles for any managed projects this project depends on |
| IProject[] deps = getProject().getReferencedProjects(); |
| for (int i = 0; i < deps.length; i++) { |
| IProject depProject = deps[i]; |
| if (ManagedBuildManager.manages(depProject)) { |
| IManagedBuildInfo depInfo = ManagedBuildManager.getBuildInfo(depProject); |
| MakefileGenerator generator = new MakefileGenerator(depProject, depInfo, monitor); |
| try { |
| generator.regenerateMakefiles(); |
| } catch (CoreException e) { |
| // Throw the exception back to the builder |
| throw e; |
| } |
| } |
| } |
| |
| // Need to report status to the user |
| String statusMsg = ManagedBuilderCorePlugin.getFormattedString(REBUILD, getProject().getName()); |
| monitor.subTask(statusMsg); |
| |
| // Regenerate the makefiles for this project |
| MakefileGenerator generator = new MakefileGenerator(getProject(), info, monitor); |
| try { |
| generator.regenerateMakefiles(); |
| } catch (CoreException e) { |
| // Throw the exception back to the builder |
| throw e; |
| } |
| |
| // Now call make |
| IPath topBuildDir = generator.getTopBuildDir(); |
| if (topBuildDir != null) { |
| invokeMake(true, topBuildDir.removeFirstSegments(1), info, monitor); |
| } else { |
| monitor.done(); |
| } |
| } |
| |
| /* (non-javadoc) |
| * Answers an array of strings with the proper make targets |
| * |
| * @param fullBuild |
| * @return |
| */ |
| protected String[] getMakeTargets(boolean fullBuild) { |
| List args = new ArrayList(); |
| if (fullBuild) { |
| args.add("clean"); |
| } |
| args.add("all"); |
| return (String[])args.toArray(new String[args.size()]); |
| } |
| |
| /** |
| * @return |
| */ |
| protected List getResourcesToBuild() { |
| if (resourcesToBuild == null) { |
| resourcesToBuild = new ArrayList(); |
| } |
| return resourcesToBuild; |
| } |
| |
| /* (non-javadoc) |
| * Answers the list of build rules that have been assembled. If there are none, |
| * answers an empty list, never <code>null</code> |
| * |
| * @return |
| */ |
| protected List getRuleList() { |
| if (ruleList == null) { |
| ruleList = new ArrayList(); |
| } |
| return ruleList; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.core.resources.ACBuilder#getWorkingDirectory() |
| */ |
| public IPath getWorkingDirectory() { |
| IProject currProject = getProject(); |
| IPath workingDirectory = currProject.getLocation(); |
| return workingDirectory; |
| } |
| |
| /** |
| * @param delta |
| * @param monitor |
| */ |
| protected void incrementalBuild(IResourceDelta delta, IManagedBuildInfo info, IProgressMonitor monitor) throws CoreException { |
| // Rebuild the resource tree in the delta |
| String statusMsg = null; |
| |
| // Need to report status to the user |
| if (monitor == null) { |
| monitor = new NullProgressMonitor(); |
| } |
| statusMsg = ManagedBuilderCorePlugin.getFormattedString(INCREMENTAL, getProject().getName()); |
| monitor.subTask(statusMsg); |
| |
| // Regenerate the makefiles for any managed projects this project depends on |
| IProject[] deps = getProject().getReferencedProjects(); |
| for (int i = 0; i < deps.length; i++) { |
| IProject depProject = deps[i]; |
| if (ManagedBuildManager.manages(depProject)) { |
| IManagedBuildInfo depInfo = ManagedBuildManager.getBuildInfo(depProject); |
| MakefileGenerator generator = new MakefileGenerator(depProject, depInfo, monitor); |
| try { |
| generator.regenerateMakefiles(); |
| } catch (CoreException e) { |
| // Throw the exception back to the builder |
| throw e; |
| } |
| } |
| } |
| |
| // Ask the makefile generator to generate any makefiles needed to build delta |
| MakefileGenerator generator = new MakefileGenerator(getProject(), info, monitor); |
| try { |
| generator.generateMakefiles(delta); |
| } catch (CoreException e) { |
| // Throw the exception back to the builder |
| throw e; |
| } |
| |
| // Run the build |
| IPath buildDir = new Path(info.getConfigurationName()); |
| invokeMake(false, buildDir, info, monitor); |
| } |
| |
| protected void invokeMake(boolean fullBuild, IPath buildDir, IManagedBuildInfo info, IProgressMonitor monitor) { |
| // Get the project and make sure there's a monitor to cancel the build |
| IProject currentProject = getProject(); |
| if (monitor == null) { |
| monitor = new NullProgressMonitor(); |
| } |
| |
| try { |
| // Figure out the working directory for the build and make sure there is a makefile there |
| IPath workingDirectory = getWorkingDirectory().append(buildDir); |
| IWorkspace workspace = currentProject.getWorkspace(); |
| if (workspace == null) { |
| return; |
| } |
| IWorkspaceRoot root = workspace.getRoot(); |
| if (root == null) { |
| return; |
| } |
| IPath makefile = workingDirectory.addTrailingSeparator().append(MakefileGenerator.MAKEFILE_NAME); |
| if (root.getFileForLocation(makefile) == null) { |
| return; |
| } |
| |
| // Flag to the user that make is about to be called |
| IPath makeCommand = new Path(info.getMakeCommand()); |
| if (makeCommand != null) { |
| String[] msgs = new String[2]; |
| msgs[0] = makeCommand.toString(); |
| msgs[1] = currentProject.getName(); |
| monitor.beginTask(ManagedBuilderCorePlugin.getFormattedString(MAKE, msgs), IProgressMonitor.UNKNOWN); |
| |
| // Get a build console for the project |
| IConsole console = CCorePlugin.getDefault().getConsole(); |
| console.start(currentProject); |
| ConsoleOutputStream consoleOutStream = console.getOutputStream(); |
| |
| // Remove all markers for this project |
| removeAllMarkers(currentProject); |
| IProject[] deps = currentProject.getReferencedProjects(); |
| for (int i = 0; i < deps.length; i++) { |
| IProject project = deps[i]; |
| removeAllMarkers(project); |
| } |
| |
| // Get the arguments to be passed to make from build model |
| ArrayList makeArgs = new ArrayList(); |
| String args = info.getMakeArguments(); |
| if (args.length() > 0) { |
| makeArgs.add(args); |
| } |
| makeArgs.addAll(Arrays.asList(getMakeTargets(fullBuild))); |
| String[] makeTargets = (String[]) makeArgs.toArray(new String[makeArgs.size()]); |
| |
| // Get a launcher for the make command |
| String errMsg = null; |
| CommandLauncher launcher = new CommandLauncher(); |
| launcher.showCommand(true); |
| |
| // Set the environmennt, some scripts may need the CWD var to be set. |
| Properties props = launcher.getEnvironment(); |
| props.put("CWD", workingDirectory.toOSString()); //$NON-NLS-1$ |
| props.put("PWD", workingDirectory.toOSString()); //$NON-NLS-1$ |
| String[] env = null; |
| ArrayList envList = new ArrayList(); |
| Enumeration names = props.propertyNames(); |
| if (names != null) { |
| while (names.hasMoreElements()) { |
| String key = (String) names.nextElement(); |
| envList.add(key + "=" + props.getProperty(key)); //$NON-NLS-1$ |
| } |
| env = (String[]) envList.toArray(new String[envList.size()]); |
| } |
| |
| // Hook up an error parser |
| ErrorParserManager epm = new ErrorParserManager(this); |
| epm.setOutputStream(consoleOutStream); |
| OutputStream stdout = epm.getOutputStream(); |
| OutputStream stderr = epm.getOutputStream(); |
| |
| // Launch make |
| Process proc = launcher.execute(makeCommand, makeTargets, env, workingDirectory); |
| if (proc != null) { |
| try { |
| // Close the input of the process since we will never write to it |
| proc.getOutputStream().close(); |
| } catch (IOException e) { |
| } |
| |
| if (launcher.waitAndRead(stdout, stderr, new SubProgressMonitor(monitor, IProgressMonitor.UNKNOWN)) != CommandLauncher.OK) { |
| errMsg = launcher.getErrorMessage(); |
| } |
| |
| // Force a resync of the projects without allowing the user to cancel. |
| // This is probably unkind, but short of this there is no way to insure |
| // the UI is up-to-date with the build results |
| monitor.subTask(ManagedBuilderCorePlugin.getResourceString(REFRESH)); |
| try { |
| currentProject.refreshLocal(IResource.DEPTH_INFINITE, null); |
| for (int j = 0; j < deps.length; ++j) { |
| IProject project = deps[j]; |
| project.refreshLocal(IResource.DEPTH_INFINITE, null); |
| } |
| } catch (CoreException e) { |
| monitor.subTask(ManagedBuilderCorePlugin.getResourceString(REFRESH_ERROR)); |
| } |
| } else { |
| errMsg = launcher.getErrorMessage(); |
| } |
| |
| // Report either the success or failure of our mission |
| StringBuffer buf = new StringBuffer(); |
| if (errMsg != null && errMsg.length() > 0) { |
| String errorDesc = ManagedBuilderCorePlugin.getResourceString(BUILD_ERROR); |
| buf.append(errorDesc); |
| buf.append(System.getProperty("line.separator", "\n")); |
| buf.append("(").append(errMsg).append(")"); |
| } else { |
| // Report a successful build |
| String successMsg = ManagedBuilderCorePlugin.getFormattedString(BUILD_FINISHED, currentProject.getName()); |
| buf.append(successMsg); |
| buf.append(System.getProperty("line.separator", "\n")); |
| } |
| |
| // Write message on the console |
| consoleOutStream.write(buf.toString().getBytes()); |
| consoleOutStream.flush(); |
| stdout.close(); |
| stderr.close(); |
| |
| monitor.subTask(ManagedBuilderCorePlugin.getResourceString(MARKERS)); //$NON-NLS-1$ |
| epm.reportProblems(); |
| } |
| } catch (Exception e) { |
| CCorePlugin.log(e); |
| forgetLastBuiltState(); |
| } finally { |
| monitor.done(); |
| } |
| } |
| |
| private void removeAllMarkers(IProject project) throws CoreException { |
| IWorkspace workspace = project.getWorkspace(); |
| |
| // remove all markers |
| IMarker[] markers = project.findMarkers(ICModelMarker.C_MODEL_PROBLEM_MARKER, true, IResource.DEPTH_INFINITE); |
| if (markers != null) { |
| workspace.deleteMarkers(markers); |
| } |
| } |
| |
| } |