/*******************************************************************************
 * Copyright (c) 2002, 2007 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 Rational Software - Initial API and implementation
 *******************************************************************************/
package org.eclipse.cdt.managedbuilder.internal.core;

import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

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.IMarkerGenerator;
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.buildmodel.BuildDescriptionManager;
import org.eclipse.cdt.managedbuilder.buildmodel.IBuildDescription;
import org.eclipse.cdt.managedbuilder.buildmodel.IBuildIOType;
import org.eclipse.cdt.managedbuilder.buildmodel.IBuildResource;
import org.eclipse.cdt.managedbuilder.buildmodel.IBuildStep;
import org.eclipse.cdt.managedbuilder.core.IConfiguration;
import org.eclipse.cdt.managedbuilder.core.IManagedBuildInfo;
import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager;
import org.eclipse.cdt.managedbuilder.envvar.IBuildEnvironmentVariable;
import org.eclipse.cdt.managedbuilder.internal.buildmodel.DescriptionBuilder;
import org.eclipse.cdt.managedbuilder.internal.buildmodel.IBuildModelBuilder;
import org.eclipse.cdt.managedbuilder.internal.buildmodel.ParallelBuilder;
import org.eclipse.cdt.managedbuilder.internal.buildmodel.StepBuilder;
import org.eclipse.cdt.managedbuilder.macros.BuildMacroException;
import org.eclipse.cdt.managedbuilder.macros.IBuildMacroProvider;
import org.eclipse.cdt.managedbuilder.makegen.IManagedBuilderMakefileGenerator;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
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.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.SubProgressMonitor;

/**
 * This is the incremental builder associated with a managed build project. It dynamically 
 * decides the makefile generator it wants to use for a specific target.
 * 
 * @since 1.2 
 */
public class GeneratedMakefileBuilder extends ACBuilder {
	/**
	 * @since 1.2
	 */
	public class ResourceDeltaVisitor implements IResourceDeltaVisitor {
		private String buildGoalName;
		private IManagedBuildInfo buildInfo;
		private boolean incrBuildNeeded = false;
		private boolean fullBuildNeeded = false;
		private List reservedNames;
		
		/**
		 * 
		 */
		public ResourceDeltaVisitor(IManagedBuildInfo info) {
			buildInfo = info;
			String ext = buildInfo.getBuildArtifactExtension();
			//try to resolve build macros in the build artifact extension
			try{
				ext = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(
						ext,
						"", //$NON-NLS-1$
						" ", //$NON-NLS-1$
						IBuildMacroProvider.CONTEXT_CONFIGURATION,
						info.getDefaultConfiguration());
			} catch (BuildMacroException e){
			}
			
			String name = buildInfo.getBuildArtifactName();
			//try to resolve build macros in the build artifact name
			try{
				String resolved = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(
						name,
						"", //$NON-NLS-1$
						" ", //$NON-NLS-1$
						IBuildMacroProvider.CONTEXT_CONFIGURATION,
						info.getDefaultConfiguration());
				if((resolved = resolved.trim()).length() > 0)
					name = resolved;
			} catch (BuildMacroException e){
			}

			if (ext.length() > 0) {
				buildGoalName = buildInfo.getOutputPrefix(ext) + name + IManagedBuilderMakefileGenerator.DOT + ext;
			} else {
				buildGoalName = name;
			}
			reservedNames = Arrays.asList(new String[]{".cdtbuild", ".cdtproject", ".project"});	//$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
		}

		/**
		 * @param changedResource
		 * @return
		 */
		private boolean isGeneratedResource(IResource resource) {
			// Is this a generated directory ...
			IPath path = resource.getProjectRelativePath();
			String[] configNames = buildInfo.getConfigurationNames();
			for (int i = 0; i < configNames.length; i++) {
				String name = configNames[i];
				IPath root = new Path(name);
				// It is if it is a root of the resource pathname
				if (root.isPrefixOf(path)) return true;
			}
			return false;
		}

		/**
		 * @param resource
		 * @return
		 */
		private boolean isProjectFile(IResource resource) {
			return reservedNames.contains(resource.getName()); 
		}

		public boolean shouldBuildIncr() {
			return incrBuildNeeded;
		}
		
		public boolean shouldBuildFull() {
			return fullBuildNeeded;
		}

		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()) {
				switch(resource.getType()){
				case IResource.FILE:
					String name = resource.getName();						
					if ((!name.equals(buildGoalName) &&
						// TODO:  Also need to check for secondary outputs
						(resource.isDerived() || 
						(isProjectFile(resource)) ||
						(isGeneratedResource(resource))))) {
					     // The resource that changed has attributes which make it uninteresting, 
						 // so don't do anything
					    ;							
				    } 
					else {
						//  TODO:  Should we do extra checks here to determine if a build is really needed,
						//         or do you just do exclusion checks like above?
						//         We could check for:
						//         o  The build goal name
						//         o  A secondary output
						//         o  An input file to a tool:
						//            o  Has an extension of a source file used by a tool
						//            o  Has an extension of a header file used by a tool
						//            o  Has the name of an input file specified in an InputType via:
						//               o  An Option
						//               o  An AdditionalInput
						//
						//if (resourceName.equals(buildGoalName) || 
						//	(buildInfo.buildsFileType(ext) || buildInfo.isHeaderFile(ext))) {
						
							// We need to do an incremental build, at least
							incrBuildNeeded = true;
							if (delta.getKind() == IResourceDelta.REMOVED) {
								// If a meaningful resource was removed, then force a full build
								// This is required because an incremental build will trigger make to
								// do nothing for a missing source, since the state after the file 
								// removal is uptodate, as far as make is concerned
								// A full build will clean, and ultimately trigger a relink without
								// the object generated from the deleted source, which is what we want
								fullBuildNeeded = true;
								// There is no point in checking anything else since we have
								// decided to do a full build anyway
								break;
							}
							
						//}
					} 

					return false;
				}
			}
			return true;
		}
	}

	private static class OtherConfigVerifier implements IResourceDeltaVisitor {
		IConfiguration config;
		IConfiguration configs[];
		Configuration otherConfigs[];
		int resourceChangeState;
		
		private static final IPath[] ignoreList = {
			new Path(".cdtproject"), //$NON-NLS-1$
			new Path(".cproject"), //$NON-NLS-1$
			new Path(".cdtbuild"), //$NON-NLS-1$
			new Path(".settings"), //$NON-NLS-1$
		};
		
		OtherConfigVerifier(IConfiguration cfg){
			config = cfg;
			configs = cfg.getManagedProject().getConfigurations();
			otherConfigs = new Configuration[configs.length - 1];
			int counter = 0;
			for(int i = 0; i < configs.length; i++){
				if(configs[i] != config)
					otherConfigs[counter++] = (Configuration)configs[i];
			}
		}
		
		public boolean visit(IResourceDelta delta) throws CoreException {
			
			IResource rc = delta.getResource();
			if(rc.getType() == IResource.FILE){
				if(isResourceValuable(rc))
					resourceChangeState |= delta.getKind();
				return false;
			}
			return !isGeneratedForConfig(rc, config) && isResourceValuable(rc);
		}
		
		public void updateOtherConfigs(IResourceDelta delta){
			if(delta == null)
				resourceChangeState = ~0;
			else {
				try {
					delta.accept(this);
				} catch (CoreException e) {
					resourceChangeState = ~0;
				}
			}
				
			setResourceChangeStateForOtherConfigs();
		}
		
		private void setResourceChangeStateForOtherConfigs(){
			for(int i = 0; i < otherConfigs.length; i++){
				otherConfigs[i].addResourceChangeState(resourceChangeState);
			}
		}
	
		private boolean isGeneratedForConfig(IResource resource, IConfiguration cfg) {
			// Is this a generated directory ...
			IPath path = resource.getProjectRelativePath();
			IPath root = new Path(cfg.getName());
			// It is if it is a root of the resource pathname
			if (root.isPrefixOf(path))
				return true;
			return false;
		}

		private boolean isResourceValuable(IResource rc){
			IPath path = rc.getProjectRelativePath();
			for(int i = 0; i < ignoreList.length; i++){
				if(ignoreList[i].equals(path))
					return false;
			}
			return true;
		}
	}

	// String constants
	private static final String BUILD_ERROR = "ManagedMakeBuilder.message.error";	//$NON-NLS-1$
	private static final String BUILD_FINISHED = "ManagedMakeBuilder.message.finished";	//$NON-NLS-1$
	private static final String CONSOLE_HEADER = "ManagedMakeBuilder.message.console.header";	//$NON-NLS-1$
	private static final String ERROR_HEADER = "GeneratedmakefileBuilder error [";	//$NON-NLS-1$
	private static final String MAKE = "ManagedMakeBuilder.message.make";	//$NON-NLS-1$
	private static final String MARKERS = "ManagedMakeBuilder.message.creating.markers";	//$NON-NLS-1$
	private static final String NEWLINE = System.getProperty("line.separator");	//$NON-NLS-1$
	private static final String NOTHING_BUILT = "ManagedMakeBuilder.message.no.build";	//$NON-NLS-1$
	private static final String REFRESH = "ManagedMakeBuilder.message.updating";	//$NON-NLS-1$
	private static final String REFRESH_ERROR = BUILD_ERROR + ".refresh";	//$NON-NLS-1$
	private static final String TRACE_FOOTER = "]: ";	//$NON-NLS-1$
	private static final String TRACE_HEADER = "GeneratedmakefileBuilder trace [";	//$NON-NLS-1$
	private static final String TYPE_CLEAN = "ManagedMakeBuilder.type.clean";	//$NON-NLS-1$
	private static final String TYPE_INC = "ManagedMakeBuider.type.incremental";	//$NON-NLS-1$
	private static final String WARNING_UNSUPPORTED_CONFIGURATION = "ManagedMakeBuilder.warning.unsupported.configuration";	//$NON-NLS-1$
	private static final String BUILD_CANCELLED = "ManagedMakeBuilder.message.cancelled";	//$NON-NLS-1$
	private static final String BUILD_FINISHED_WITH_ERRS = "ManagedMakeBuilder.message.finished.with.errs";	//$NON-NLS-1$
	private static final String BUILD_FAILED_ERR = "ManagedMakeBuilder.message.internal.builder.error";	//$NON-NLS-1$
	private static final String BUILD_STOPPED_ERR = "ManagedMakeBuilder.message.stopped.error";	//$NON-NLS-1$
	private static final String INTERNAL_BUILDER_HEADER_NOTE = "ManagedMakeBuilder.message.internal.builder.header.note";	//$NON-NLS-1$
	private static final String TYPE_REBUILD = "ManagedMakeBuider.type.rebuild";	//$NON-NLS-1$
	private static final String INTERNAL_BUILDER = "ManagedMakeBuilder.message.internal.builder";	//$NON-NLS-1$
	public static boolean VERBOSE = false;
	
	// Local variables
	protected Vector generationProblems;
	protected IProject[] referencedProjects;
	protected List resourcesToBuild;
	private IConsole console;
	private ConsoleOutputStream consoleOutStream;
	public static void outputTrace(String resourceName, String message) {
		if (VERBOSE) {
			System.out.println(TRACE_HEADER + resourceName + TRACE_FOOTER + message + NEWLINE);
		}
	}

	public static void outputError(String resourceName, String message) {
		if (VERBOSE) {
			System.err.println(ERROR_HEADER + resourceName + TRACE_FOOTER + message + NEWLINE);
		}
	}

	/**
	 * Zero-argument constructor needed to fulfill the contract of an
	 * incremental builder.
	 */
	public GeneratedMakefileBuilder() {
	}

	/**
	 * @param epm
	 */
	private void addBuilderMarkers(ErrorParserManager epm) {
		IWorkspaceRoot root = CCorePlugin.getWorkspace().getRoot();
		Iterator iter = getGenerationProblems().iterator();
		while (iter.hasNext()) {
			IStatus stat = (IStatus)iter.next();
			IResource location = root.findMember(stat.getMessage());
			if (stat.getCode() == IManagedBuilderMakefileGenerator.SPACES_IN_PATH) {
				epm.generateMarker(location, -1, ManagedMakeMessages.getResourceString("MakefileGenerator.error.spaces"), IMarkerGenerator.SEVERITY_WARNING, null);	//$NON-NLS-1$
			}
		}
	}

	/* (non-javadoc)
	 * Emits a message to the console indicating that there were no source files to build
	 * @param buildType
	 * @param status
	 * @param configName
	 */
	private void emitNoSourceMessage(int buildType, IStatus status, String configName) throws CoreException {
		try {
			StringBuffer buf = new StringBuffer();
			IConsole console = CCorePlugin.getDefault().getConsole();
			console.start(getProject());
			ConsoleOutputStream consoleOutStream = console.getOutputStream();
			// Report a successful clean
			String[] consoleHeader = new String[3];
			if (buildType == FULL_BUILD || buildType == INCREMENTAL_BUILD) {
				consoleHeader[0] = ManagedMakeMessages.getResourceString(TYPE_INC);
			} else {
				consoleHeader[0] = new String();
				outputError(getProject().getName(), "The given build type is not supported in this context");	//$NON-NLS-1$
			}			
			consoleHeader[1] = configName;
			consoleHeader[2] = getProject().getName();
			buf.append(System.getProperty("line.separator", "\n"));	//$NON-NLS-1$	//$NON-NLS-2$
			buf.append(ManagedMakeMessages.getFormattedString(CONSOLE_HEADER, consoleHeader));
			buf.append(System.getProperty("line.separator", "\n"));	//$NON-NLS-1$	//$NON-NLS-2$
			buf.append(System.getProperty("line.separator", "\n"));	//$NON-NLS-1$	//$NON-NLS-2$
			buf.append(status.getMessage());
			buf.append(System.getProperty("line.separator", "\n"));  //$NON-NLS-1$//$NON-NLS-2$
			consoleOutStream.write(buf.toString().getBytes());
			consoleOutStream.flush();
			consoleOutStream.close();
		} catch (CoreException e) {
			// Throw the exception back to the builder
			throw e;
		} catch (IOException io) {	//  Ignore console failures...
		}		
	}

	/**
	 * 
	 * This method has been created so that subclasses can override how the builder obtains its
	 * build info.  The default implementation retrieves the info from the build manager.
	 * 
	 * @return An IManagedBuildInfo object representing the build info.
	 */
	protected IManagedBuildInfo getBuildInfo() {
		return ManagedBuildManager.getBuildInfo(getProject());
	}
	
	/* (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 {
		// We should always tell the build system what projects we reference
		referencedProjects = getProject().getReferencedProjects();

		// Get the build information
		IManagedBuildInfo info = getBuildInfo();
		if (info == null) {
			outputError(getProject().getName(), "Build information was not found");	//$NON-NLS-1$
			return referencedProjects;
		}
		if (!info.isValid()) {
			outputError(getProject().getName(), "Build information is not valid");	//$NON-NLS-1$
			return referencedProjects;
		}

		IConfiguration cfg = info.getDefaultConfiguration();

		updateOtherConfigs(cfg, kind);

		if(((Configuration)cfg).isInternalBuilderEnabled()){
			invokeInternalBuilder(cfg, kind != FULL_BUILD, ((Configuration)cfg).getInternalBuilderIgnoreErr(), monitor);

			// Scrub the build info the project
			info.setRebuildState(false);
			return referencedProjects;
		}

		// Create a makefile generator for the build
		IManagedBuilderMakefileGenerator generator = ManagedBuildManager.getBuildfileGenerator(info.getDefaultConfiguration());
		generator.initialize(getProject(), info, monitor);

		//perform necessary cleaning and build type calculation
		if(cfg.needsFullRebuild()){
			//configuration rebuild state is set to true,
			//full rebuild is needed in any case
			//clean first, then make a full build
			outputTrace(getProject().getName(), "config rebuild state is set to true, making a full rebuild");	//$NON-NLS-1$
			clean(new SubProgressMonitor(monitor, IProgressMonitor.UNKNOWN));
			fullBuild(info, generator, monitor);
		} else {
			boolean fullBuildNeeded = info.needsRebuild();
			IBuildDescription des = null;
			
			IResourceDelta delta = kind == FULL_BUILD ? null : getDelta(getProject());
			if(delta == null)
				fullBuildNeeded = true;
			if(cfg.needsRebuild() || delta != null){
				//use a build desacription model to calculate the resources to be cleaned 
				//only in case there are some changes to the project sources or build information
				try{
					int flags = BuildDescriptionManager.REBUILD | BuildDescriptionManager.DEPFILES | BuildDescriptionManager.DEPS;
					if(delta != null)
						flags |= BuildDescriptionManager.REMOVED;

					outputTrace(getProject().getName(), "using a build description..");	//$NON-NLS-1$

					des = BuildDescriptionManager.createBuildDescription(info.getDefaultConfiguration(), getDelta(getProject()), flags);
	
					BuildDescriptionManager.cleanGeneratedRebuildResources(des);
				} catch (Throwable e){
					//TODO: log error
					outputError(getProject().getName(), "error occured while build description calculation: " + e.getLocalizedMessage());	//$NON-NLS-1$
					//in case an error occured, make it behave in the old stile:
					if(info.needsRebuild()){
						//make a full clean if an info needs a rebuild
						clean(new SubProgressMonitor(monitor, IProgressMonitor.UNKNOWN));
						fullBuildNeeded = true;
					}
					else if(delta != null && !fullBuildNeeded){
						// Create a delta visitor to detect the build type
						ResourceDeltaVisitor visitor = new ResourceDeltaVisitor(info);
						delta.accept(visitor);
						if (visitor.shouldBuildFull()) {
							clean(new SubProgressMonitor(monitor, IProgressMonitor.UNKNOWN));
							fullBuildNeeded = true;
						}
					}
				}
			}

			if(fullBuildNeeded){
				outputTrace(getProject().getName(), "performing a full build");	//$NON-NLS-1$
				fullBuild(info, generator, monitor);
			} else {
				outputTrace(getProject().getName(), "performing an incremental build");	//$NON-NLS-1$
				incrementalBuild(delta, info, generator, monitor);
			}
		}
/*
		// So let's figure out why we got called
		if (kind == FULL_BUILD) {
			outputTrace(getProject().getName(), "Full build needed/requested");	//$NON-NLS-1$
			fullBuild(info, generator, monitor);
		}
		else if (kind == AUTO_BUILD && info.needsRebuild()) {
			outputTrace(getProject().getName(), "Autobuild requested, full build needed");	//$NON-NLS-1$
			fullBuild(info, generator, monitor);
		}
		else {
			// Create a delta visitor to make sure we should be rebuilding
			ResourceDeltaVisitor visitor = new ResourceDeltaVisitor(info);
			IResourceDelta delta = getDelta(getProject());
			if (delta == null) {
				outputTrace(getProject().getName(), "Incremental build requested, full build needed");	//$NON-NLS-1$
				fullBuild(info, generator, monitor);
			}
			else {
				delta.accept(visitor);
				if (visitor.shouldBuildFull()) {
					outputTrace(getProject().getName(), "Incremental build requested, full build needed");	//$NON-NLS-1$
					fullBuild(info, generator, monitor);
				} else if (visitor.shouldBuildIncr()) {
					outputTrace(getProject().getName(), "Incremental build requested");	//$NON-NLS-1$
					incrementalBuild(delta, info, generator, monitor);
				}
				else if (referencedProjects != null) {
					//  Also check to see is any of the dependent projects changed
					for (int i=0; i<referencedProjects.length; i++) {
						IProject ref = referencedProjects[i];
						IResourceDelta refDelta = getDelta(ref);
						if (refDelta == null) {
							outputTrace(getProject().getName(), "Incremental build because of changed referenced project");	//$NON-NLS-1$
							incrementalBuild(delta, info, generator, monitor);
							//  Should only build this project once, for this delta
							break;
						} else {
							int refKind = refDelta.getKind(); 
							if (refKind != IResourceDelta.NO_CHANGE) {
								int refFlags = refDelta.getFlags();
								if (!(refKind == IResourceDelta.CHANGED &&
									  refFlags == IResourceDelta.OPEN)) {
									outputTrace(getProject().getName(), "Incremental build because of changed referenced project");	//$NON-NLS-1$
									incrementalBuild(delta, info, generator, monitor);
									//  Should only build this project once, for this delta
									break;
								}
							}
						}						
					}
				}
			}
		}
*/
		// Scrub the build info the project
		info.setRebuildState(false);
		// Ask build mechanism to compute deltas for project dependencies next time
		return referencedProjects;
	}

	private void updateOtherConfigs(IConfiguration cfg, int buildKind){
		new OtherConfigVerifier(cfg).updateOtherConfigs(buildKind == FULL_BUILD ? null : getDelta(getProject()));
	}
	
	/**
	 * 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()) {
			outputTrace(getProject().getName(), "Build cancelled");	//$NON-NLS-1$
			forgetLastBuiltState();
			throw new OperationCanceledException();
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.core.resources.IncrementalProjectBuilder#clean(org.eclipse.core.runtime.IProgressMonitor)
	 */
	protected void clean(IProgressMonitor monitor) throws CoreException {
		referencedProjects = getProject().getReferencedProjects();
		outputTrace(getProject().getName(), "Clean build requested");	//$NON-NLS-1$
		IManagedBuildInfo info = getBuildInfo();
		if (info == null) {
			outputError(getProject().getName(), "Build information was not found");	//$NON-NLS-1$
			return;
		}
		if (!info.isValid()) {
			outputError(getProject().getName(), "Build information is not valid");	//$NON-NLS-1$
			return;
		}
		IPath buildDirPath = getProject().getLocation().append(info.getConfigurationName());
		IWorkspace workspace = CCorePlugin.getWorkspace();
		IContainer buildDir = workspace.getRoot().getContainerForLocation(buildDirPath);
		if (buildDir == null || !buildDir.isAccessible()){
			outputError(buildDir.getName(), "Could not delete the build directory");	//$NON-NLS-1$
			return;
		}
		String status;		
		try {
			// try the brute force approach first
			status = ManagedMakeMessages.getFormattedString("ManagedMakeBuilder.message.clean.deleting.output", buildDir.getName());	//$NON-NLS-1$
			monitor.subTask(status);
			workspace.delete(new IResource[]{buildDir}, true, monitor);
			StringBuffer buf = new StringBuffer();
			// write to the console
			IConsole console = CCorePlugin.getDefault().getConsole();
			console.start(getProject());
			ConsoleOutputStream consoleOutStream = console.getOutputStream();
			String[] consoleHeader = new String[3];
			consoleHeader[0] = ManagedMakeMessages.getResourceString(TYPE_CLEAN);
			consoleHeader[1] = info.getConfigurationName();
			consoleHeader[2] = getProject().getName();
			buf.append(System.getProperty("line.separator", "\n"));	//$NON-NLS-1$	//$NON-NLS-2$
			buf.append(ManagedMakeMessages.getFormattedString(CONSOLE_HEADER, consoleHeader));
			buf.append(System.getProperty("line.separator", "\n"));	//$NON-NLS-1$	//$NON-NLS-2$
			consoleOutStream.write(buf.toString().getBytes());
			consoleOutStream.flush();
			buf = new StringBuffer();
			// Report a successful clean
			String successMsg = ManagedMakeMessages.getFormattedString(BUILD_FINISHED, getProject().getName());
			buf.append(successMsg);
			buf.append(System.getProperty("line.separator", "\n"));  //$NON-NLS-1$//$NON-NLS-2$
			consoleOutStream.write(buf.toString().getBytes());
			consoleOutStream.flush();
			consoleOutStream.close();
		} catch (CoreException e) {
			// Create a makefile generator for the build
			status = ManagedMakeMessages.getFormattedString("ManagedMakeBuilder.message.clean.build.clean", buildDir.getName());	//$NON-NLS-1$
			monitor.subTask(status);
			IManagedBuilderMakefileGenerator generator = ManagedBuildManager.getBuildfileGenerator(info.getDefaultConfiguration());
			generator.initialize(getProject(), info, monitor);
			cleanBuild(info, generator, monitor);
		}  catch (IOException io) {}	//  Ignore console failures...		
	}
	
	/* (non-Javadoc)
	 * @param info
	 * @param generator
	 * @param monitor
	 */
	protected void cleanBuild(IManagedBuildInfo info, IManagedBuilderMakefileGenerator generator, IProgressMonitor monitor) {
		// Make sure that there is a top level directory and a set of makefiles
		IPath buildDir = generator.getBuildWorkingDir();
		if (buildDir == null) {
			buildDir = new Path(info.getConfigurationName());
		}
		IPath makefilePath = getProject().getLocation().append(buildDir.append(generator.getMakefileName()));		
		IWorkspaceRoot root = CCorePlugin.getWorkspace().getRoot();
		IFile makefile = root.getFileForLocation(makefilePath);
		
		if (buildDir != null && makefile != null && makefile.isAccessible()) {		
			// invoke make with the clean argument
			String statusMsg = ManagedMakeMessages.getFormattedString("ManagedMakeBuilder.message.starting", getProject().getName());	//$NON-NLS-1$
			monitor.subTask(statusMsg);
			checkCancel(monitor);
			invokeMake(CLEAN_BUILD, buildDir, info, generator, monitor);
		}
	}

	/* (non-Javadoc)
	 * @param info
	 * @param generator
	 * @param monitor
	 */
	protected void fullBuild(IManagedBuildInfo info, IManagedBuilderMakefileGenerator generator, IProgressMonitor monitor) throws CoreException {
		// Always need one of these bad boys
		if (monitor == null) {
			monitor = new NullProgressMonitor();
		}
		
		checkCancel(monitor);
		//If the previous builder invocation was cancelled, generated files might be corrupted
		//in case one or more of the generated makefiles (e.g. dep files) are corrupted, 
		//the builder invocation might fail because of the possible syntax errors, so e.g. "make clean" will not work 
		//we need to explicitly clean the generated directories
//		clean(new SubProgressMonitor(monitor, IProgressMonitor.UNKNOWN));
		
		// Regenerate the makefiles for this project
		checkCancel(monitor);
		String statusMsg = ManagedMakeMessages.getFormattedString("ManagedMakeBuilder.message.rebuild.makefiles", getProject().getName());	//$NON-NLS-1$
		monitor.subTask(statusMsg);
		//generator = ManagedBuildManager.getBuildfileGenerator(info.getDefaultConfiguration());
		generator.initialize(getProject(), info, monitor);
		MultiStatus result = generator.regenerateMakefiles();
		if (result.getCode() == IStatus.WARNING || result.getCode() == IStatus.INFO) {
			IStatus[] kids = result.getChildren();
			for (int index = 0; index < kids.length; ++index) {
				// One possibility is that there is nothing to build
				IStatus status = kids[index]; 
				if (status.getCode() == IManagedBuilderMakefileGenerator.NO_SOURCE_FOLDERS) {
					// Inform the user, via the console, that there is nothing to build
					// either because there are no buildable sources files or all potentially
					// buildable files have been excluded from build
					try { 
						emitNoSourceMessage(FULL_BUILD, status, info.getConfigurationName());
					} catch (CoreException e) {
						// Throw the exception back to the builder
					    throw e;
					}					
					// Dude, we're done
					return;
				} else {
					// Stick this in the list of stuff to warn the user about
					getGenerationProblems().add(status);		
				}				
			}
		}
		
		// Now call make
		checkCancel(monitor);
		statusMsg = ManagedMakeMessages.getFormattedString("ManagedMakeBuilder.message.starting", getProject().getName());	//$NON-NLS-1$
		monitor.subTask(statusMsg);
		IPath topBuildDir = generator.getBuildWorkingDir();
		if (topBuildDir != null) {
			invokeMake(FULL_BUILD, topBuildDir, info, generator, monitor);
		} else {
			statusMsg = ManagedMakeMessages.getFormattedString(NOTHING_BUILT, getProject().getName());	//$NON-NLS-1$
			monitor.subTask(statusMsg);
			return;
		}
		
		// Now regenerate the dependencies
		checkCancel(monitor);
		statusMsg = ManagedMakeMessages.getFormattedString("ManagedMakeBuilder.message.regen.deps", getProject().getName());	//$NON-NLS-1$
		monitor.subTask(statusMsg);
		try {
			generator.regenerateDependencies(false);
		} catch (CoreException e) {
			// Throw the exception back to the builder
			throw e;
		}

		//  Build finished message
		statusMsg = ManagedMakeMessages.getFormattedString(BUILD_FINISHED, getProject().getName());	//$NON-NLS-1$
		monitor.subTask(statusMsg);
	}
	
	/* (non-Javadoc)
	 * 
	 * @return
	 */
	private Vector getGenerationProblems() {
		if (generationProblems == null) {
			generationProblems = new Vector();
		}
		return generationProblems;
	}
	
	/* (non-javadoc)
	 * Answers an array of strings with the proper make targets
        * for a build with no custom prebuild/postbuild steps 
	 * 
	 * @param fullBuild
	 * @return
	 */
	protected String[] getMakeTargets(int buildType) {
		List args = new ArrayList();
		switch (buildType) {
			case CLEAN_BUILD:
				args.add("clean");	//$NON-NLS-1$
				break;
			case FULL_BUILD:
			case INCREMENTAL_BUILD:
				args.add("all");	//$NON-NLS-1$
				break;
		}
		return (String[])args.toArray(new String[args.size()]);
	}
	
	/**
	 * @return
	 */
	protected List getResourcesToBuild() {
		if (resourcesToBuild == null) {
			resourcesToBuild = new ArrayList();
		}
		return resourcesToBuild;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.core.resources.ACBuilder#getWorkingDirectory()
	 */
	public IPath getWorkingDirectory() {
		return getProject().getLocation();
	}

	/* (non-Javadoc)
	 * @param delta
	 * @param info
	 * @param monitor
	 * @throws CoreException
	 */
	protected void incrementalBuild(IResourceDelta delta, IManagedBuildInfo info, IManagedBuilderMakefileGenerator generator, IProgressMonitor monitor) throws CoreException {
		// Need to report status to the user
		if (monitor == null) {
			monitor = new NullProgressMonitor();
		}
		
		// Ask the makefile generator to generate any makefiles needed to build delta
		checkCancel(monitor);
		String statusMsg = ManagedMakeMessages.getFormattedString("ManagedMakeBuilder.message.update.makefiles", getProject().getName());	//$NON-NLS-1$
		monitor.subTask(statusMsg);
		MultiStatus result = generator.generateMakefiles(delta);
		if (result.getCode() == IStatus.WARNING || result.getCode() == IStatus.INFO) {
			IStatus[] kids = result.getChildren();
			for (int index = 0; index < kids.length; ++index) {
				// One possibility is that there is nothing to build
				IStatus status = kids[index]; 
				if (status.getCode() == IManagedBuilderMakefileGenerator.NO_SOURCE_FOLDERS) {
					// Inform the user, via the console, that there is nothing to build
					// either because there are no buildable sources files or all potentially
					// buildable files have been excluded from build
					try { 
						emitNoSourceMessage(INCREMENTAL_BUILD, status, info.getConfigurationName());
					} catch (CoreException e) {
						// Throw the exception back to the builder
					    throw e;
					}					
					// Dude, we're done
					return;
				} else {
					// Stick this in the list of stuff to warn the user about
					getGenerationProblems().add(status);		
				}				
			}
		}

		// Run the build
		checkCancel(monitor);
		statusMsg = ManagedMakeMessages.getFormattedString("ManagedMakeBuilder.message.starting", getProject().getName());	//$NON-NLS-1$
		monitor.subTask(statusMsg);
		IPath buildDir = generator.getBuildWorkingDir();
		if (buildDir != null) {
			invokeMake(INCREMENTAL_BUILD, buildDir, info, generator, monitor);
		} else {
			statusMsg = ManagedMakeMessages.getFormattedString(NOTHING_BUILT, getProject().getName());	//$NON-NLS-1$
			monitor.subTask(statusMsg);
			return;
		}
		
		// Generate the dependencies for all changes
		checkCancel(monitor);
		statusMsg = ManagedMakeMessages.getFormattedString("ManagedMakeBuilder.message.updating.deps", getProject().getName());	//$NON-NLS-1$
		monitor.subTask(statusMsg);
		try {
			generator.generateDependencies();
		} catch (CoreException e) {
			throw e;
		}
		
		// Build finished message
		statusMsg = ManagedMakeMessages.getFormattedString(BUILD_FINISHED, getProject().getName());	//$NON-NLS-1$
		monitor.subTask(statusMsg);
}

	/* (non-Javadoc)
     * @param buildType 
	 * @param buildDir
	 * @param info
     * @param generator 
	 * @param monitor
	 */
	protected void invokeMake(int buildType, IPath buildDir, IManagedBuildInfo info, IManagedBuilderMakefileGenerator generator, 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.append(generator.getMakefileName());
			if (root.getFileForLocation(makefile) == null) {
				return; 
			}

			// Flag to the user that make is about to be called
			String makeCmd = info.getBuildCommand();
			//try to resolve the build macros in the builder command
			try{
				String resolved = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(
						makeCmd,
						"", //$NON-NLS-1$
						" ", //$NON-NLS-1$
						IBuildMacroProvider.CONTEXT_CONFIGURATION,
						info.getDefaultConfiguration());
				if((resolved = resolved.trim()).length() > 0)
					makeCmd = resolved;
			} catch (BuildMacroException e){
			}

			IPath makeCommand = new Path(makeCmd); 
			if (makeCommand != null) {
				String[] msgs = new String[2];
				msgs[0] = makeCommand.toString();
				msgs[1] = currentProject.getName();
				monitor.subTask(ManagedMakeMessages.getFormattedString(MAKE, msgs));

				// Get a build console for the project
				StringBuffer buf = new StringBuffer();
				IConsole console = CCorePlugin.getDefault().getConsole();
				console.start(currentProject);
				ConsoleOutputStream consoleOutStream = console.getOutputStream();
				String[] consoleHeader = new String[3];
				switch (buildType) {
					case FULL_BUILD:
					case INCREMENTAL_BUILD:
						consoleHeader[0] = ManagedMakeMessages.getResourceString(TYPE_INC);
						break;
					case CLEAN_BUILD:
						consoleHeader[0] = ManagedMakeMessages.getResourceString(TYPE_CLEAN);
						break;
				}
						
				consoleHeader[1] = info.getConfigurationName();
				consoleHeader[2] = currentProject.getName();
				buf.append(System.getProperty("line.separator", "\n"));	//$NON-NLS-1$	//$NON-NLS-2$
				buf.append(ManagedMakeMessages.getFormattedString(CONSOLE_HEADER, consoleHeader));
				buf.append(System.getProperty("line.separator", "\n"));	//$NON-NLS-1$	//$NON-NLS-2$
				buf.append(System.getProperty("line.separator", "\n"));	//$NON-NLS-1$	//$NON-NLS-2$
				
				IConfiguration cfg = info.getDefaultConfiguration();
				if(!cfg.isSupported()){
					buf.append(ManagedMakeMessages.getFormattedString(WARNING_UNSUPPORTED_CONFIGURATION,new String[] {cfg.getName(),cfg.getToolChain().getName()}));
					buf.append(System.getProperty("line.separator", "\n"));	//$NON-NLS-1$	//$NON-NLS-2$
					buf.append(System.getProperty("line.separator", "\n"));	//$NON-NLS-1$	//$NON-NLS-2$
				}
				consoleOutStream.write(buf.toString().getBytes());
				consoleOutStream.flush();
				
				// Remove all markers for this project
				removeAllMarkers(currentProject);
			
				// Get a launcher for the make command
				String errMsg = null;
				CommandLauncher launcher = new CommandLauncher();
				launcher.showCommand(true);
	
				// Set the environmennt
				IBuildEnvironmentVariable variables[] = ManagedBuildManager.getEnvironmentVariableProvider().getVariables(cfg,true,true);
				String[] env = null;
				ArrayList envList = new ArrayList();
				if (variables != null) {
					for(int i = 0; i < variables.length; i++){
						envList.add(variables[i].getName() + "=" + variables[i].getValue());	//$NON-NLS-1$
					}
					env = (String[]) envList.toArray(new String[envList.size()]);
				}
			
				// Hook up an error parser manager
				String[] errorParsers = info.getDefaultConfiguration().getErrorParserList(); 
				ErrorParserManager epm = new ErrorParserManager(getProject(), workingDirectory, this, errorParsers);
				epm.setOutputStream(consoleOutStream);
				// This variable is necessary to ensure that the EPM stream stay open
				// until we explicitly close it. See bug#123302.
				OutputStream epmOutputStream = epm.getOutputStream();
			
                // Get the arguments to be passed to make from build model 
				ArrayList makeArgs = new ArrayList();
				String arg = info.getBuildArguments();
				if (arg.length() > 0) {
					String[] args = arg.split("\\s"); //$NON-NLS-1$ 
					for (int i = 0; i < args.length; ++i) {
						makeArgs.add(args[i]);
					}
				}

				String[] makeTargets;
				String prebuildStep = info.getPrebuildStep();
				//try to resolve the build macros in the prebuildStep
				try{
					prebuildStep = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(
							prebuildStep,
							"", //$NON-NLS-1$
							" ", //$NON-NLS-1$
							IBuildMacroProvider.CONTEXT_CONFIGURATION,
							cfg);
				} catch (BuildMacroException e){
				}
				boolean prebuildStepPresent = (prebuildStep.length() > 0);
				Process proc = null;
				boolean isuptodate = false;

				if (prebuildStepPresent) {
					ArrayList premakeArgs = (ArrayList) makeArgs.clone();
					String[] premakeTargets;
					switch (buildType) {
					case INCREMENTAL_BUILD: {
						// For an incremental build with a prebuild step: 
						// Check the status of the main build with "make -q main-build" 
						// If up to date: 
						// then: don't invoke the prebuild step, which should be run only if 
						//       something needs to be built in the main build 
						// else: invoke the prebuild step and the main build step 
						premakeArgs.add("-q"); //$NON-NLS-1$ 
						premakeArgs.add("main-build"); //$NON-NLS-1$ 
						premakeTargets = (String[]) premakeArgs.toArray(new String[premakeArgs.size()]);
						proc = launcher.execute(makeCommand, premakeTargets, 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(epm.getOutputStream(), epm.getOutputStream(),
									new SubProgressMonitor(monitor,
											IProgressMonitor.UNKNOWN)) != CommandLauncher.OK) {
								errMsg = launcher.getErrorMessage();
							}
						} else {
							errMsg = launcher.getErrorMessage();
						}

						if ((errMsg != null && errMsg.length() > 0) || proc == null) {
							// Can't tell if the build is needed, so assume it is, and let any errors be triggered
							// when the "real" build is invoked below 
							makeArgs.add("pre-build"); //$NON-NLS-1$ 
							makeArgs.add("main-build"); //$NON-NLS-1$ 
						} else {
							// The "make -q" command launch was successful 
							if (proc.exitValue() == 0) {
								// If the status value returned from "make -q" is 0, then the build state is up-to-date
								isuptodate = true;
								// Report that the build was up to date, and thus nothing needs to be built
								String uptodateMsg = ManagedMakeMessages
										.getFormattedString(NOTHING_BUILT, currentProject.getName());
								buf = new StringBuffer();
								buf.append(System.getProperty("line.separator", "\n")); //$NON-NLS-1$//$NON-NLS-2$
								buf.append(uptodateMsg);
								buf.append(System.getProperty("line.separator", "\n")); //$NON-NLS-1$//$NON-NLS-2$
								// Write message on the console 
								consoleOutStream.write(buf.toString().getBytes());
								consoleOutStream.flush();
								epmOutputStream.close();
								consoleOutStream.close();
							} else {
								// The status value was other than 0, so press on with the build process
								makeArgs.add("pre-build"); //$NON-NLS-1$                        
								makeArgs.add("main-build"); //$NON-NLS-1$ 
							}
						}
						break;
					}
					case FULL_BUILD: {
//						makeArgs.add("clean"); //$NON-NLS-1$        
						makeArgs.add("pre-build"); //$NON-NLS-1$ 
						makeArgs.add("main-build"); //$NON-NLS-1$                           
						break;
					}
					case CLEAN_BUILD: {
						makeArgs.add("clean"); //$NON-NLS-1$                                                            
						break;
					}
					}

				} else {
					// No prebuild step 
					// 
					makeArgs.addAll(Arrays.asList(getMakeTargets(buildType)));
				}

				makeTargets = (String[]) makeArgs.toArray(new String[makeArgs.size()]);

				// Launch make - main invocation 
				if (!isuptodate) {
					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) {
						}

						int state = launcher.waitAndRead(epm.getOutputStream(), epm.getOutputStream(),
								new SubProgressMonitor(monitor,
										IProgressMonitor.UNKNOWN));
						if(state != CommandLauncher.OK){
							errMsg = launcher.getErrorMessage();
							
							if(state == CommandLauncher.COMMAND_CANCELED){
								//TODO: the better way of handling cancel is needed
								//currently the rebuild state is set to true forcing the full rebuild
								//on the next builder invocation
								info.getDefaultConfiguration().setRebuildState(true);
							}
						}

						// 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(ManagedMakeMessages
								.getResourceString(REFRESH));
						try {
							currentProject.refreshLocal(
									IResource.DEPTH_INFINITE, null);
						} catch (CoreException e) {
							monitor.subTask(ManagedMakeMessages
									.getResourceString(REFRESH_ERROR));
						}
					} else {
						errMsg = launcher.getErrorMessage();
					}

					// Report either the success or failure of our mission 
					buf = new StringBuffer();
					if (errMsg != null && errMsg.length() > 0) {
						String errorDesc = ManagedMakeMessages
								.getResourceString(BUILD_ERROR);
						buf.append(errorDesc);
						buf.append(System.getProperty("line.separator", "\n")); //$NON-NLS-1$//$NON-NLS-2$
						buf.append("(").append(errMsg).append(")"); //$NON-NLS-1$ //$NON-NLS-2$ 
					} else {
						// Report a successful build 
						String successMsg = ManagedMakeMessages
								.getFormattedString(BUILD_FINISHED,
										currentProject.getName());
						buf.append(successMsg);
						buf.append(System.getProperty("line.separator", "\n")); //$NON-NLS-1$//$NON-NLS-2$
					}

					// Write message on the console 
					consoleOutStream.write(buf.toString().getBytes());
					consoleOutStream.flush();
					epmOutputStream.close();

					// Generate any error markers that the build has discovered 
					monitor.subTask(ManagedMakeMessages
							.getResourceString(MARKERS));
					addBuilderMarkers(epm);
					epm.reportProblems();
					consoleOutStream.close();
				}
			}                               
		} catch (Exception e) {
			forgetLastBuiltState();
		} finally {
			getGenerationProblems().clear();
		}
	}

	/* (non-Javadoc)
	 * Removes the IMarkers for the project specified in the argument if the
	 * project exists, and is open. 
	 * 
	 * @param project
	 */
	private void removeAllMarkers(IProject project) {
		if (project == null || !project.isAccessible()) return;

		// Clear out the problem markers
		IWorkspace workspace = project.getWorkspace();
		IMarker[] markers;
		try {
			markers = project.findMarkers(ICModelMarker.C_MODEL_PROBLEM_MARKER, true, IResource.DEPTH_INFINITE);
		} catch (CoreException e) {
			// Handled just about every case in the sanity check
			return;
		}
		if (markers != null) {
			try {
				workspace.deleteMarkers(markers);
			} catch (CoreException e) {
				// The only situation that might cause this is some sort of resource change event
				return;
			}
		}
	}
	
	/**
	 * called to invoke the MBS Internal Builder for building the given configuration
	 *  
	 * @param cfg configuration to be built
	 * @param buildIncrementaly if true, incremental build will be performed,
	 * only files that need rebuild will be built.
	 * If false, full rebuild will be performed
	 * @param resumeOnErr if true, build will continue in case of error while building.
	 * If false the build will stop on the first error 
	 * @param monitor monitor
	 */
	protected void invokeInternalBuilder(IConfiguration cfg, 
			boolean buildIncrementaly,
			boolean resumeOnErr,
			IProgressMonitor monitor) {
		
		boolean isParallel = ((Configuration)cfg).getInternalBuilderParallel();
		
		// Get the project and make sure there's a monitor to cancel the build
		IProject currentProject = cfg.getOwner().getProject();
		if (monitor == null) {
			monitor = new NullProgressMonitor();
		}
		
		String[] msgs = new String[2];
		msgs[0] = ManagedMakeMessages.getResourceString(INTERNAL_BUILDER);
		msgs[1] = currentProject.getName();

		ConsoleOutputStream consoleOutStream = null;
		IConsole console = null;
		OutputStream epmOutputStream = null;
		try {
			int flags = 0;
			IResourceDelta delta = null;
			
			if(buildIncrementaly){
				flags = BuildDescriptionManager.REBUILD | BuildDescriptionManager.REMOVED | BuildDescriptionManager.DEPS;
				delta = getDelta(currentProject);
			}
			
			IBuildDescription des = BuildDescriptionManager.createBuildDescription(cfg, delta, flags);
	
			DescriptionBuilder builder = null;
			if (!isParallel)
				builder = new DescriptionBuilder(des, buildIncrementaly, resumeOnErr, null);

			// Get a build console for the project
			StringBuffer buf = new StringBuffer();
			console = CCorePlugin.getDefault().getConsole();
			console.start(currentProject);
			consoleOutStream = console.getOutputStream();
			String[] consoleHeader = new String[3];
			if(buildIncrementaly)
				consoleHeader[0] = ManagedMakeMessages.getResourceString(TYPE_INC);
			else
				consoleHeader[0] = ManagedMakeMessages.getResourceString(TYPE_REBUILD);
						
			consoleHeader[1] = cfg.getName();
			consoleHeader[2] = currentProject.getName();
			buf.append(System.getProperty("line.separator", "\n"));	//$NON-NLS-1$	//$NON-NLS-2$
			buf.append(ManagedMakeMessages.getFormattedString(CONSOLE_HEADER, consoleHeader));
			buf.append(System.getProperty("line.separator", "\n"));	//$NON-NLS-1$	//$NON-NLS-2$
			buf.append(System.getProperty("line.separator", "\n"));	//$NON-NLS-1$	//$NON-NLS-2$
			
			buf.append(ManagedMakeMessages.getResourceString(INTERNAL_BUILDER_HEADER_NOTE));
			buf.append("\n"); //$NON-NLS-1$
			
			if(!cfg.isSupported()){
				buf.append(ManagedMakeMessages.getFormattedString(WARNING_UNSUPPORTED_CONFIGURATION,new String[] {cfg.getName(),cfg.getToolChain().getName()}));
				buf.append(System.getProperty("line.separator", "\n"));	//$NON-NLS-1$	//$NON-NLS-2$
				buf.append(System.getProperty("line.separator", "\n"));	//$NON-NLS-1$	//$NON-NLS-2$
			}
			consoleOutStream.write(buf.toString().getBytes());
			consoleOutStream.flush();
			
			if(isParallel || builder.getNumCommands() > 0) {
				// Remove all markers for this project
				removeAllMarkers(currentProject);
				
				// Hook up an error parser manager
				String[] errorParsers = cfg.getErrorParserList(); 
				ErrorParserManager epm = new ErrorParserManager(getProject(), des.getDefaultBuildDirLocation(), this, errorParsers);
				epm.setOutputStream(consoleOutStream);
				// This variable is necessary to ensure that the EPM stream stay open
				// until we explicitly close it. See bug#123302.
				epmOutputStream = epm.getOutputStream();
				
				int status = 0;
				
				long t1 = System.currentTimeMillis();
				if (isParallel)
					status = ParallelBuilder.build(des, null, null, epmOutputStream, epmOutputStream, monitor, resumeOnErr, buildIncrementaly);
				else
				    status = builder.build(epmOutputStream, epmOutputStream, monitor);
				long t2 = System.currentTimeMillis();
				
				// Report either the success or failure of our mission 
				buf = new StringBuffer();
				
				switch(status){
				case IBuildModelBuilder.STATUS_OK:
					buf.append(ManagedMakeMessages
							.getFormattedString(BUILD_FINISHED,
									currentProject.getName()));
					break;
				case IBuildModelBuilder.STATUS_CANCELLED:
					buf.append(ManagedMakeMessages
							.getResourceString(BUILD_CANCELLED));
					break;
				case IBuildModelBuilder.STATUS_ERROR_BUILD:
					String msg = resumeOnErr ? 
							ManagedMakeMessages.getResourceString(BUILD_FINISHED_WITH_ERRS) :
								ManagedMakeMessages.getResourceString(BUILD_STOPPED_ERR);
					buf.append(msg);
					break;
				case IBuildModelBuilder.STATUS_ERROR_LAUNCH:
				default:
					buf.append(ManagedMakeMessages
							.getResourceString(BUILD_FAILED_ERR));
					break;
				}
				buf.append(System.getProperty("line.separator", "\n")); //$NON-NLS-1$//$NON-NLS-2$
				
				// Report time and number of threads used
//				buf.append("Time consumed: ");
//				buf.append(t2 - t1);
//				buf.append(" ms.  ");
//				if (isParallel) {
//					buf.append("Parallel threads used: ");
//					buf.append(ParallelBuilder.lastThreadsUsed);
//				}
//				buf.append("\n");
				
				// Report time and number of threads used
				buf.append(ManagedMakeMessages.getFormattedString("CommonBuilder.6", Integer.toString((int)(t2 - t1)))); //$NON-NLS-1$
//				buf.append(t2 - t1);
//				buf.append(" ms.  ");
				if (isParallel) {
					buf.append(ManagedMakeMessages.getFormattedString("CommonBuilder.7", Integer.toString(ParallelBuilder.lastThreadsUsed))); //$NON-NLS-1$
//					buf.append(ParallelBuilder.lastThreadsUsed);
				}
				buf.append(System.getProperty("line.separator", "\n")); //$NON-NLS-1$ //$NON-NLS-2$

				// Write message on the console 
				consoleOutStream.write(buf.toString().getBytes());
				consoleOutStream.flush();
				epmOutputStream.close();
				epmOutputStream = null;
				// Generate any error markers that the build has discovered 
				monitor.subTask(ManagedMakeMessages
						.getResourceString(MARKERS));
				addBuilderMarkers(epm);
				epm.reportProblems();
			} else {
				buf = new StringBuffer();
				buf.append(ManagedMakeMessages.getFormattedString(NOTHING_BUILT, getProject().getName()));
				buf.append(System.getProperty("line.separator", "\n")); //$NON-NLS-1$//$NON-NLS-2$
				consoleOutStream.write(buf.toString().getBytes());
				consoleOutStream.flush();
			}

		} catch (Exception e) {
			if(consoleOutStream != null){
				StringBuffer buf = new StringBuffer();
				String errorDesc = ManagedMakeMessages
							.getResourceString(BUILD_ERROR);
				buf.append(errorDesc);
				buf.append(System.getProperty("line.separator", "\n")); //$NON-NLS-1$//$NON-NLS-2$
				buf.append("(").append(e.getLocalizedMessage()).append(")"); //$NON-NLS-1$ //$NON-NLS-2$
				buf.append(System.getProperty("line.separator", "\n")); //$NON-NLS-1$//$NON-NLS-2$

				try {
					consoleOutStream.write(buf.toString().getBytes());
					consoleOutStream.flush();
				} catch (IOException e1) {
				}
			}
			forgetLastBuiltState();
		} finally {
			if(epmOutputStream != null){
				try {
					epmOutputStream.close();
				} catch (IOException e) {
				} 
			}
			if(consoleOutStream != null){
				try {
					consoleOutStream.close();
				} catch (IOException e) {
				}
			}
			getGenerationProblems().clear();
			monitor.done();
		}
	}
	
	/**
	 * Called to invoke the MBS Internal Builder for building the given resources in
	 * the given configuration
	 * 
	 * This method is considered experimental.  Clients implementing this API should expect
	 * possible changes in the API.
	 *  
	 * @param cfg configuration to be built
	 * @param buildIncrementaly if true, incremental build will be performed,
	 * only files that need rebuild will be built.
	 * If false, full rebuild will be performed
	 * @param resumeOnErr if true, build will continue in case of error while building.
	 * If false the build will stop on the first error 
	 * @param monitor Progress monitor.  For every resource built this monitor will consume one unit of work.
	 */
	public void invokeInternalBuilder(IResource[] resourcesToBuild, IConfiguration cfg, 
			boolean buildIncrementaly,
			boolean resumeOnErr,
			boolean initNewConsole,
			boolean printFinishedMessage,
			IProgressMonitor monitor) {
		// Get the project and make sure there's a monitor to cancel the build
		IProject currentProject = cfg.getOwner().getProject();
		if (monitor == null) {
			monitor = new NullProgressMonitor();
		}
		
		try {
			int flags = 0;
			IResourceDelta delta = null;
			
			if(buildIncrementaly){
				flags = BuildDescriptionManager.REBUILD | BuildDescriptionManager.REMOVED | BuildDescriptionManager.DEPS;
				delta = getDelta(currentProject);
			}
			
			
			String[] msgs = new String[2];
			msgs[0] = ManagedMakeMessages.getResourceString(INTERNAL_BUILDER);
			msgs[1] = currentProject.getName();

			if(initNewConsole)
				initNewBuildConsole(currentProject);
			
			StringBuffer buf = new StringBuffer();
			
			if (initNewConsole) {
				if (buildIncrementaly)
					buf.append(ManagedMakeMessages.getResourceString("GeneratedMakefileBuilder.buildSelectedIncremental")); //$NON-NLS-1$
				else
					buf.append(ManagedMakeMessages.getResourceString("GeneratedMakefileBuilder.buildSelectedRebuild")); //$NON-NLS-1$
				

				buf.append(System.getProperty("line.separator", "\n")); //$NON-NLS-1$	//$NON-NLS-2$
				buf.append(System.getProperty("line.separator", "\n")); //$NON-NLS-1$	//$NON-NLS-2$

				buf.append(ManagedMakeMessages
						.getResourceString(INTERNAL_BUILDER_HEADER_NOTE));
				buf.append("\n"); //$NON-NLS-1$
			}
			
			
			if(!cfg.isSupported()){
				buf.append(ManagedMakeMessages.getFormattedString(WARNING_UNSUPPORTED_CONFIGURATION,new String[] {cfg.getName(),cfg.getToolChain().getName()}));
				buf.append(System.getProperty("line.separator", "\n"));	//$NON-NLS-1$	//$NON-NLS-2$
				buf.append(System.getProperty("line.separator", "\n"));	//$NON-NLS-1$	//$NON-NLS-2$
			}
			consoleOutStream.write(buf.toString().getBytes());
			consoleOutStream.flush();
				
			// Remove all markers for this project
			// TODO remove only necessary markers
			removeAllMarkers(currentProject);
			
			IBuildDescription des = BuildDescriptionManager.createBuildDescription(cfg, delta, flags);
			
			// Hook up an error parser manager
			String[] errorParsers = cfg.getErrorParserList(); 
			ErrorParserManager epm = new ErrorParserManager(currentProject, des.getDefaultBuildDirLocation(), this, errorParsers);
			epm.setOutputStream(consoleOutStream);
			// This variable is necessary to ensure that the EPM stream stay open
			// until we explicitly close it. See bug#123302.
			OutputStream epmOutputStream = epm.getOutputStream();
			
			boolean errorsFound = false;
			
		doneBuild: for (int k = 0; k < resourcesToBuild.length; k++) {
				IBuildResource buildResource = des
						.getBuildResource(resourcesToBuild[k]);

//				step collector 
				Set dependentSteps = new HashSet();

//				get dependent IO types
				IBuildIOType depTypes[] = buildResource.getDependentIOTypes();

//				iterate through each type and add the step the type belongs to to the collector
				for(int j = 0; j < depTypes.length; j++){
				IBuildIOType type = depTypes[j];
				if(type != null && type.getStep() != null)
					dependentSteps.add(type.getStep());
				}

				monitor.subTask(ManagedMakeMessages.getResourceString("GeneratedMakefileBuilder.buildingFile") + resourcesToBuild[k].getProjectRelativePath()); //$NON-NLS-1$
				
				// iterate through all build steps
				Iterator stepIter = dependentSteps.iterator();
				
				while(stepIter.hasNext())
				{
					IBuildStep step = (IBuildStep) stepIter.next();
					
					StepBuilder stepBuilder = new StepBuilder(step, null);
					
					int status = stepBuilder.build(consoleOutStream, epmOutputStream, new SubProgressMonitor(monitor, 1, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK));
					
					// Refresh the output resource without allowing the user to cancel. 
					// This is probably unkind, but short of this there is no way to ensure 
					// the UI is up-to-date with the build results 
					IBuildIOType[] outputIOTypes = step.getOutputIOTypes();
					
					for(int j = 0; j < outputIOTypes.length; j++ )
					{
						IBuildResource[] resources = outputIOTypes[j].getResources();
						
						for(int i = 0; i < resources.length; i++)
						{
							IFile file = currentProject.getFile(resources[i].getLocation());
							file.refreshLocal(IResource.DEPTH_INFINITE, null);
						}
					}
					
					// check status
					
					switch (status) {
					case IBuildModelBuilder.STATUS_OK:
						// don't print anything if the step was successful,
						// since the build might not be done as a whole
						break;
					case IBuildModelBuilder.STATUS_CANCELLED:
						buf.append(ManagedMakeMessages
								.getResourceString(BUILD_CANCELLED));
						break doneBuild;
					case IBuildModelBuilder.STATUS_ERROR_BUILD:
						errorsFound = true;
						if (!resumeOnErr) {
							buf.append(ManagedMakeMessages
									.getResourceString(BUILD_STOPPED_ERR));
							break doneBuild;
						}
						break;
					case IBuildModelBuilder.STATUS_ERROR_LAUNCH:
					default:
						buf.append(ManagedMakeMessages
								.getResourceString(BUILD_FAILED_ERR));
						break doneBuild;
					}
				}

				
			}

			// check status
			// Report either the success or failure of our mission
			buf = new StringBuffer();

			
			buf.append(System.getProperty("line.separator", "\n")); //$NON-NLS-1$//$NON-NLS-2$
			
			if (printFinishedMessage) {
				if (errorsFound) {
					buf.append(ManagedMakeMessages
							.getResourceString(BUILD_FAILED_ERR));
				} else {
					buf
							.append(ManagedMakeMessages
									.getResourceString("GeneratedMakefileBuilder.buildResourcesFinished")); //$NON-NLS-1$
				}
			}
			
			// Write message on the console 
			consoleOutStream.write(buf.toString().getBytes());
			consoleOutStream.flush();
			epmOutputStream.close();

			// Generate any error markers that the build has discovered 
			addBuilderMarkers(epm);
			epm.reportProblems();
			consoleOutStream.close();
		} catch (Exception e) {
			StringBuffer buf = new StringBuffer();
			String errorDesc = ManagedMakeMessages
						.getResourceString(BUILD_ERROR);
			buf.append(errorDesc);
			buf.append(System.getProperty("line.separator", "\n")); //$NON-NLS-1$//$NON-NLS-2$
			buf.append("(").append(e.getLocalizedMessage()).append(")"); //$NON-NLS-1$ //$NON-NLS-2$ 

			forgetLastBuiltState();
		} finally {
			getGenerationProblems().clear();
		}
	}
	
	private void removeAllMarkers(IFile file) {
		IMarker[] markers;
		try {
			markers = file.findMarkers(
					ICModelMarker.C_MODEL_PROBLEM_MARKER, true,
					IResource.DEPTH_INFINITE);
		} catch (CoreException e) {
			// Handled just about every case in the sanity check
			return;
		}
		if (markers != null) {
			try {
				file.getWorkspace().deleteMarkers(markers);
			} catch (CoreException e) {
				// The only situation that might cause this is some sort of
				// resource change event
				return;
			}
		}
	}
	
	public void cleanFile(IFile file, IProgressMonitor monitor) {

		monitor.subTask(ManagedMakeMessages.getResourceString("GeneratedMakefileBuilder.0") //$NON-NLS-1$
				+ file.getProjectRelativePath());

		// remove all markers on the file
		removeAllMarkers(file);

		IProject currentProject = file.getProject();

		IManagedBuildInfo info = ManagedBuildManager
				.getBuildInfo(currentProject);

		// if we have no info then don't do anything
		if (info == null) {
			// monitor.worked(1);
			return;

		}

		IConfiguration cfg = info.getDefaultConfiguration();

		// figure out the output file for this file
		IPath sourcePath = file.getProjectRelativePath();

		int flags = BuildDescriptionManager.REBUILD | BuildDescriptionManager.REMOVED;;
		IResourceDelta delta = getDelta(currentProject);
		
		try {
			IBuildDescription des = BuildDescriptionManager
					.createBuildDescription(cfg, delta, flags);

			IBuildResource buildResource = des.getBuildResource(file);

			// step collector
			Set dependentSteps = new HashSet();

			// get dependent IO types
			IBuildIOType depTypes[] = buildResource.getDependentIOTypes();

			// iterate through each type and add the step the type belongs to to
			// the collector
			for (int j = 0; j < depTypes.length; j++) {
				IBuildIOType type = depTypes[j];
				if (type != null && type.getStep() != null)
					dependentSteps.add(type.getStep());
			}

			// iterate through all build steps
			Iterator stepIter = dependentSteps.iterator();

			while (stepIter.hasNext()) {
				IBuildStep step = (IBuildStep) stepIter.next();

				// Delete the output resources
				IBuildIOType[] outputIOTypes = step.getOutputIOTypes();

				for (int j = 0; j < outputIOTypes.length; j++) {
					IBuildResource[] resources = outputIOTypes[j]
							.getResources();

					for (int i = 0; i < resources.length; i++) {
						IResource outputFile = currentProject
								.findMember(resources[i].getFullPath().removeFirstSegments(1)); // strip project name

						if (outputFile != null)
							outputFile.delete(true, new SubProgressMonitor(
									monitor, 1));
					}
				}

			}

		} catch (CoreException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}


	/**
	 * @param currentProject
	 * @return
	 * @throws CoreException
	 */
	private void initNewBuildConsole(IProject currentProject) throws CoreException {
		// Get a build console for the project
		console = CCorePlugin.getDefault().getConsole();
		console.start(currentProject);
		consoleOutStream = console.getOutputStream();
	}
}
