Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
[cdt-patch] Makefile generation patch

OK,
It's late. I have done some work on the makefile generator to properly 
handle generating a project with multiple directories. I am submitting the 
first increment of work in this patch. Tomorrow morning I will post my 
design document explaining the direction I am going with the file 
generation. Later tomorrow I hope to add some JUnit tests to test the new 
builder's output (sorry John, I broke the rule about submitting tests with 
code).

I have smoke-tested the patch on Solaris, but until I work-up a Gnu tool 
specification in the build model, I can't really do much more than insure 
that it doesn't break anything.

Sean Evoy
Rational Software - IBM Software Group
Ottawa, Ontario, Canada

Index: ChangeLog
===================================================================
RCS file: /home/tools/org.eclipse.cdt.core/ChangeLog,v
retrieving revision 1.115
diff -u -r1.115 ChangeLog
--- ChangeLog	25 Jul 2003 01:57:51 -0000	1.115
+++ ChangeLog	25 Jul 2003 02:24:03 -0000
@@ -1,3 +1,35 @@
+2003-07-24 Sean Evoy
+	Changes introduced to make the managed build system work with
+	multi-folder project.
+	
+	* src/org/eclipse/cdt/core/ManagedCProjectNature.java:
+	now removes the cbuilder from a project before it adds its 
+	own builder. This is a temporary fix to stop the managed build 
+	system from building a project twice. When the new StandardBuildNature 
+	is introduced, this code will be removed.
+
+	* src/org/eclipse/cdt/internal/core/CCorePluginResources.properties:
+	New builder messages added.
+	
+	* src/org/eclipse/cdt/internal/core/GeneratedMakefileBuilder.java:
+	Moved the actual directory and file creation to a delegate class.
+	
+	* src/org/eclipse/cdt/internal/core/MakefileGenerator.java:
+	New class that does the grunt work of creating build output directories 
+	and makefiles.
+	
+	* build/org/eclipse/cdt/core/build/managed/ManagedBuildManager.java:
+	Short-term changes to make it possible for build info clients to get the
+	path and symbol information. When a permanent mechanism is implemented
+	for clients to discover this information, these methods (IScannerInfoxxx) 
+	will be removed.
+	
+	* build/org/eclipse/cdt/core/build/managed/IManagedBuildInfo.java
+	* build/org/eclipse/cdt/internal/core/build/managed/ManagedBuildInfo.java:
+	Some minor changes have been made to extract more information from the 
+	build model. Currently, the values are hard-coded to simplify some integration
+	testing. This will be addressed in the next patch.
+	
 2003-07-24 Alain Magloire
 
 	* utils/org/eclipse/cdt/utils/Elf.java:
Index: build/org/eclipse/cdt/core/build/managed/IManagedBuildInfo.java
===================================================================
RCS file: /home/tools/org.eclipse.cdt.core/build/org/eclipse/cdt/core/build/managed/IManagedBuildInfo.java,v
retrieving revision 1.1
diff -u -r1.1 IManagedBuildInfo.java
--- build/org/eclipse/cdt/core/build/managed/IManagedBuildInfo.java	4 Jul 2003 18:36:45 -0000	1.1
+++ build/org/eclipse/cdt/core/build/managed/IManagedBuildInfo.java	25 Jul 2003 02:24:03 -0000
@@ -23,6 +23,15 @@
 	public void addTarget(ITarget target);
 		
 	/**
+	 * Answers <code>true</code> if the build system knows how to 
+	 * build a file with the extension passed in the argument.
+	 *  
+	 * @param srcExt
+	 * @return
+	 */
+	public boolean buildsFileType(String srcExt);
+
+	/**
 	 * Returns the name of the artifact to build for the receiver.
 	 * 
 	 * @return
@@ -30,6 +39,21 @@
 	public String getBuildArtifactName();
 
 	/**
+	 * Answers the command needed to remove files on the build machine
+	 * 
+	 * @return
+	 */
+	public String getCleanCommand();
+
+	/**
+	 * Answers the name of the default configuration, for example <code>Debug</code>  
+	 * or <code>Release</code>.
+	 * 
+	 * @return
+	 */
+	public String getConfigurationName();
+
+	/**
 	 * Get the default configuration associated with the receiver
 	 * 
 	 * @return
@@ -54,6 +78,16 @@
 	public String getOutputExtension(String resourceExtension);
 	
 	/**
+	 * Answers the flag to be passed to the build tool to produce a specific output 
+	 * or an empty <code>String</code> if there is no special flag. For example, the
+	 * GCC tools use the -o flag to produce a named output, for example
+	 * 		gcc -c foo.c -o foo.o
+	 * 
+	 * @return
+	 */
+	public String getOutputFlag();
+
+	/**
 	 * Get the target specified in the argument.
 	 * 
 	 * @param id
@@ -89,6 +123,21 @@
 	public String getFlagsForTarget(String extension);
 
 	/**
+	 * Answers a string array containing the arguments to be passed to
+	 * make. For example, if the user has selected a build that stops
+	 * at the first error, the array would contain {"k"}.
+	 * 
+	 * @return
+	 */
+	public String[] getMakeArguments();
+
+	/**
+	 * Answers a <code>String</code> containing the make command invocation 
+	 * for the default target/configuration.
+	 */
+	public String getMakeCommand();
+
+	/**
 	 * Returns a <code>String</code> containing the command-line invocation 
 	 * for the tool associated with the source extension.
 	 * 
@@ -120,5 +169,7 @@
 	 * @param target
 	 */
 	public void setDefaultTarget(ITarget target);
+
+
 	
 }
Index: build/org/eclipse/cdt/core/build/managed/ManagedBuildManager.java
===================================================================
RCS file: /home/tools/org.eclipse.cdt.core/build/org/eclipse/cdt/core/build/managed/ManagedBuildManager.java,v
retrieving revision 1.11
diff -u -r1.11 ManagedBuildManager.java
--- build/org/eclipse/cdt/core/build/managed/ManagedBuildManager.java	24 Jul 2003 14:15:07 -0000	1.11
+++ build/org/eclipse/cdt/core/build/managed/ManagedBuildManager.java	25 Jul 2003 02:24:03 -0000
@@ -328,6 +328,28 @@
 		getExtensionTargetMap().put(target.getId(), target);
 	}
 		
+	private static ManagedBuildInfo loadBuildInfo(IProject project) {
+		ManagedBuildInfo buildInfo = null;
+		IFile file = project.getFile(FILE_NAME);
+		if (!file.exists())
+			return null;
+	
+		try {
+			InputStream stream = file.getContents();
+			DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
+			Document document = parser.parse(stream);
+			Node rootElement = document.getFirstChild();
+			if (rootElement.getNodeName().equals(ROOT_ELEM_NAME)) {
+				buildInfo = new ManagedBuildInfo(project, (Element)rootElement);
+				project.setSessionProperty(buildInfoProperty, buildInfo);
+			}
+		} catch (Exception e) {
+			buildInfo = null;
+		}
+
+		return buildInfo;
+	}
+
 	private static void loadExtensions() {
 		if (extensionTargetsLoaded)
 			return;
@@ -347,26 +369,37 @@
 		}
 	}
 
-	private static ManagedBuildInfo loadBuildInfo(IProject project) {
-		ManagedBuildInfo buildInfo = null;
+	/**
+	 * @param project
+	 * @return
+	 */
+	public static boolean manages(IResource resource) {
+		// The managed build manager manages build information for the 
+		// resource IFF it it is a project and has a build file with the proper
+		// root element
+		IProject project = null;
+		if (resource instanceof IProject){
+			project = (IProject)resource;
+		} else if (resource instanceof IFile) {
+			project = ((IFile)resource).getProject();
+		} else {
+			return false;
+		}
 		IFile file = project.getFile(FILE_NAME);
-		if (!file.exists())
-			return null;
-	
-		try {
-			InputStream stream = file.getContents();
-			DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
-			Document document = parser.parse(stream);
-			Node rootElement = document.getFirstChild();
-			if (rootElement.getNodeName().equals(ROOT_ELEM_NAME)) {
-				buildInfo = new ManagedBuildInfo(project, (Element)rootElement);
-				project.setSessionProperty(buildInfoProperty, buildInfo);
+		if (file.exists()) {
+			try {
+				InputStream stream = file.getContents();
+				DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
+				Document document = parser.parse(stream);
+				Node rootElement = document.getFirstChild();
+				if (rootElement.getNodeName().equals(ROOT_ELEM_NAME)) {
+					return true;
+				} 
+			} catch (Exception e) {
+				return false;
 			}
-		} catch (Exception e) {
-			buildInfo = null;
 		}
-
-		return buildInfo;
+		return false;
 	}
 
 	private static ManagedBuildInfo findBuildInfo(IResource resource, boolean create) {
@@ -417,6 +450,11 @@
 	 * Answers with an interface to the parse information that has been 
 	 * associated with the resource specified in the argument. 
 	 * 
+	 * NOTE: This method is not part of the registration interface. It has
+	 * been made public as a short-term workaround for the clients of the
+	 * scanner information until the redesign of the build information management
+	 * occurs.
+	 * 
 	 * @param resource
 	 * @return
 	 */
@@ -450,6 +488,9 @@
 		}
 	}
 
+	// TODO Remove all of the IScannerInfoProvider interface methods when
+	// the discovery mechanism is solidified
+	
 	/* (non-Javadoc)
 	 * @see org.eclipse.cdt.core.parser.IScannerInfoProvider#managesResource(org.eclipse.core.resources.IResource)
 	 */
@@ -503,5 +544,6 @@
 			map.put(project, list);
 		}
 	}
+
 
 }
Index: build/org/eclipse/cdt/internal/core/build/managed/ManagedBuildInfo.java
===================================================================
RCS file: /home/tools/org.eclipse.cdt.core/build/org/eclipse/cdt/internal/core/build/managed/ManagedBuildInfo.java,v
retrieving revision 1.1
diff -u -r1.1 ManagedBuildInfo.java
--- build/org/eclipse/cdt/internal/core/build/managed/ManagedBuildInfo.java	4 Jul 2003 18:36:45 -0000	1.1
+++ build/org/eclipse/cdt/internal/core/build/managed/ManagedBuildInfo.java	25 Jul 2003 02:24:04 -0000
@@ -88,6 +88,22 @@
 	}
 
 	/* (non-Javadoc)
+	 * @see org.eclipse.cdt.core.build.managed.IManagedBuildInfo#buildsFileType(java.lang.String)
+	 */
+	public boolean buildsFileType(String srcExt) {
+		// Check to see if there is a rule to build a file with this extension
+		IConfiguration config = getDefaultConfiguration(getDefaultTarget());
+		ITool[] tools = config.getTools();
+		for (int index = 0; index < tools.length; index++) {
+			ITool tool = tools[index];
+			if (tool.buildsFileType(srcExt)) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	/* (non-Javadoc)
 	 * @see org.eclipse.cdt.core.build.managed.IResourceBuildInfo#getBuildArtifactName()
 	 */
 	public String getBuildArtifactName() {
@@ -97,6 +113,23 @@
 	}
 
 	/* (non-Javadoc)
+	 * @see org.eclipse.cdt.core.build.managed.IManagedBuildInfo#getCleanCommand()
+	 */
+	public String getCleanCommand() {
+		// TODO Get from the model
+		return new String("rm -rf");
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.cdt.core.build.managed.IManagedBuildInfo#getConfigurationName()
+	 */
+	public String getConfigurationName() {
+		// Return the human-readable name of the default configuration
+		IConfiguration config = getDefaultConfiguration(getDefaultTarget());
+		return config == null ? new String() : config.getName();
+	}
+
+	/* (non-Javadoc)
 	 * @see org.eclipse.cdt.core.build.managed.IResourceBuildInfo#getDefaultConfiguration()
 	 */
 	public IConfiguration getDefaultConfiguration(ITarget target) {
@@ -169,6 +202,51 @@
 	}
 
 	/* (non-Javadoc)
+	 * @see org.eclipse.cdt.core.build.managed.IScannerInfo#getIncludePaths()
+	 */
+	public String[] getIncludePaths() {
+		// Return the include paths for the default configuration
+		ArrayList paths = new ArrayList();
+		IConfiguration config = getDefaultConfiguration(getDefaultTarget());
+		ITool[] tools = config.getTools();
+		for (int i = 0; i < tools.length; i++) {
+			ITool tool = tools[i];
+			IOption[] opts = tool.getOptions();
+			for (int j = 0; j < opts.length; j++) {
+				IOption option = opts[j];
+				if (option.getValueType() == IOption.INCLUDE_PATH) {
+					try {
+						paths.addAll(Arrays.asList(option.getIncludePaths()));
+					} catch (BuildException e) {
+						// we should never get here
+						continue;
+					}
+				}
+			}
+		}
+		paths.trimToSize();
+		return (String[])paths.toArray(new String[paths.size()]); 
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.cdt.core.build.managed.IManagedBuildInfo#getMakeArguments()
+	 */
+	public String[] getMakeArguments() {
+		// TODO Stop hard-coding this
+		String[] args = {""}; 
+
+		return args;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.cdt.core.build.managed.IManagedBuildInfo#getMakeCommand()
+	 */
+	public String getMakeCommand() {
+		// TODO Don't hard-code this
+		return new String("make");
+	}
+
+	/* (non-Javadoc)
 	 * @see org.eclipse.cdt.core.build.managed.IResourceBuildInfo#getOutputExtension(java.lang.String)
 	 */
 	public String getOutputExtension(String resourceExtension) {
@@ -185,6 +263,15 @@
 		return null;
 	}
 
+	/* (non-Javadoc)
+	 * @see org.eclipse.cdt.core.build.managed.IManagedBuildInfo#getOutputFlag()
+	 */
+	public String getOutputFlag() {
+		// TODO Stop hard-coding this
+		String flag = new String("-o");
+		return flag;
+	}
+
 	public IResource getOwner() {
 		return owner;
 	}
@@ -319,31 +406,5 @@
 		return symbols; 
 	}
 
-	/* (non-Javadoc)
-	 * @see org.eclipse.cdt.core.build.managed.IScannerInfo#getIncludePaths()
-	 */
-	public String[] getIncludePaths() {
-		// Return the include paths for the default configuration
-		ArrayList paths = new ArrayList();
-		IConfiguration config = getDefaultConfiguration(getDefaultTarget());
-		ITool[] tools = config.getTools();
-		for (int i = 0; i < tools.length; i++) {
-			ITool tool = tools[i];
-			IOption[] opts = tool.getOptions();
-			for (int j = 0; j < opts.length; j++) {
-				IOption option = opts[j];
-				if (option.getValueType() == IOption.INCLUDE_PATH) {
-					try {
-						paths.addAll(Arrays.asList(option.getIncludePaths()));
-					} catch (BuildException e) {
-						// we should never get here
-						continue;
-					}
-				}
-			}
-		}
-		paths.trimToSize();
-		return (String[])paths.toArray(new String[paths.size()]); 
-	}
 
 }
Index: src/org/eclipse/cdt/core/ManagedCProjectNature.java
===================================================================
RCS file: /home/tools/org.eclipse.cdt.core/src/org/eclipse/cdt/core/ManagedCProjectNature.java,v
retrieving revision 1.1
diff -u -r1.1 ManagedCProjectNature.java
--- src/org/eclipse/cdt/core/ManagedCProjectNature.java	29 May 2003 14:00:58 -0000	1.1
+++ src/org/eclipse/cdt/core/ManagedCProjectNature.java	25 Jul 2003 02:24:04 -0000
@@ -14,6 +14,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Vector;
 
 import org.eclipse.core.resources.ICommand;
 import org.eclipse.core.resources.IProject;
@@ -45,6 +46,22 @@
 		// Add the builder to the project
 		IProjectDescription description = project.getDescription();
 		ICommand[] commands = description.getBuildSpec();
+
+		// TODO Remove this when the new StandardBuild nature adds the cbuilder
+		for (int i = 0; i < commands.length; i++) {
+			ICommand command = commands[i];
+			if (command.getBuilderName().equals("org.eclipse.cdt.core.cbuilder")) {
+				// Remove the command
+				Vector vec = new Vector(Arrays.asList(commands));
+				vec.removeElementAt(i);
+				vec.trimToSize();
+				ICommand[] tempCommands = (ICommand[]) vec.toArray(new ICommand[commands.length-1]); 
+				description.setBuildSpec(tempCommands);
+				break;
+			}
+		}
+		
+		commands = description.getBuildSpec();
 		boolean found = false;
 		// See if the builder is already there
 		for (int i = 0; i < commands.length; ++i) {
@@ -63,7 +80,7 @@
 		   newCommands[0] = command;
 		   description.setBuildSpec(newCommands);
 		   project.setDescription(description, null);
-		}
+		}		
 	}
 
 	/**
Index: src/org/eclipse/cdt/internal/core/CCorePluginResources.properties
===================================================================
RCS file: /home/tools/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/CCorePluginResources.properties,v
retrieving revision 1.2
diff -u -r1.2 CCorePluginResources.properties
--- src/org/eclipse/cdt/internal/core/CCorePluginResources.properties	29 May 2003 14:00:58 -0000	1.2
+++ src/org/eclipse/cdt/internal/core/CCorePluginResources.properties	25 Jul 2003 02:24:04 -0000
@@ -7,5 +7,15 @@
 CBuilder.build_error= Build Error	
 
 # Generated makefile builder messages
-MakeBuilder.message.rebuild = Regenerating makefile for project {0}
-MakeBuilder.message.incremental = Updating makefile for project {0}
\ No newline at end of file
+MakeBuilder.message.starting = Starting the build for project {0}
+MakeBuilder.message.rebuild = Regenerating makefiles for project {0}
+MakeBuilder.message.incremental = Updating makefiles for project {0}
+MakeBuilder.message.make = Calling {0} for project {1}
+MakeBuilder.message.error = Build error
+MakeBuilder.message.finished = Build complete for project {0}
+MakeBuilder.comment.module.list = # Every module must be described here
+MakeBuilder.comment.source.list = # Each module must contribute its source files here
+MakeBuilder.comment.build.rule = # Each module must supply rules for building sources it contributes
+MakeBuilder.comment.module.make.includes = # Include the makefiles for each source module
+MakeBuilder.comment.module.dep.includes = # Include automatically-generated dependency list:
+MakeBuilder.comment.autodeps = # Automatically-generated dependency list:
Index: src/org/eclipse/cdt/internal/core/GeneratedMakefileBuilder.java
===================================================================
RCS file: /home/tools/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/GeneratedMakefileBuilder.java,v
retrieving revision 1.3
diff -u -r1.3 GeneratedMakefileBuilder.java
--- src/org/eclipse/cdt/internal/core/GeneratedMakefileBuilder.java	4 Jul 2003 18:36:45 -0000	1.3
+++ src/org/eclipse/cdt/internal/core/GeneratedMakefileBuilder.java	25 Jul 2003 02:24:04 -0000
@@ -12,19 +12,34 @@
  * **********************************************************************/
 
 import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
 import java.util.Map;
+import java.util.Properties;
+import java.util.StringTokenizer;
+
 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.build.managed.IManagedBuildInfo;
 import org.eclipse.cdt.core.build.managed.ManagedBuildManager;
+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.core.resources.MakeUtil;
-import org.eclipse.cdt.internal.core.model.Util;
 import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFolder;
+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.IResourceStatus;
+import org.eclipse.core.resources.IWorkspace;
 import org.eclipse.core.resources.IWorkspaceRoot;
 import org.eclipse.core.resources.IncrementalProjectBuilder;
 import org.eclipse.core.runtime.CoreException;
@@ -33,18 +48,27 @@
 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 = "MakeBuilder.message";	//$NON-NLS-1$
-	private static final String REBUILD = MESSAGE + ".rebuild";	//$NON-NLS-1$
+	private static final String BUILD_ERROR = MESSAGE + ".error";	//$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 FILENAME = "makefile";	//$NON-NLS-1$
-	private static final String NEWLINE = System.getProperty("line.separator", "\n");	//$NON-NLS-1$
-	private static final String COLON = ":";
-	private static final String TAB = "\t";	//$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$
+
+	// Status codes
+	public static final int EMPTY_PROJECT_BUILD_ERROR = 1;
+
+	// Local variables
+	protected List resourcesToBuild;
+	protected List ruleList;
+
 	
-	public class MyResourceDeltaVisitor implements IResourceDeltaVisitor {
+	public class ResourceDeltaVisitor implements IResourceDeltaVisitor {
 		boolean bContinue;
 
 		public boolean visit(IResourceDelta delta) throws CoreException {
@@ -67,102 +91,21 @@
 		super();
 	}
 
-	/**
-	 * Add whatever macros we can figure out to the makefile.
-	 * 
-	 * @param buffer
-	 */
-	private void addMacros(StringBuffer buffer, IManagedBuildInfo info) {
-		// TODO this should come from the build model
-		buffer.append("RM = rm -f" + NEWLINE);
-		buffer.append("MAKE = make" + NEWLINE);
-		buffer.append(NEWLINE);
-	}
-
-	private void addRule(StringBuffer buffer, IPath sourcePath, String outputName, IManagedBuildInfo info) {
-		// Add the rule to the makefile
-		buffer.append(outputName + COLON + " " + sourcePath.toString());
-		// Add all of the dependencies on the source file
-		
-		buffer.append(NEWLINE);
-		String ext = sourcePath.getFileExtension();
-		String cmd = info.getToolForSource(ext);
-		String flags = info.getFlagsForSource(ext);
-		buffer.append(TAB + cmd + " " + flags + " " + "$?" + NEWLINE + NEWLINE);
-	}
-	
-	/**
-	 * Creates a list of dependencies on project resources.
-	 *  
-	 * @param buffer
-	 */
-	private void addSources(StringBuffer buffer, IManagedBuildInfo info) throws CoreException {
-		// Add the list of project files to be built
-		buffer.append("OBJS = \\" + NEWLINE);
-		
-		//Get a list of files from the project
-		IResource[] members = getProject().members();
-		for (int i = 0; i < members.length; i++) {
-			IResource resource = members[i];
-			IPath sourcePath = resource.getProjectRelativePath().removeFileExtension(); 
-			String srcExt = resource.getFileExtension();
-			String outExt = info.getOutputExtension(srcExt);
-			if (outExt != null) {
-				// Add the extension back to path
-				IPath outputPath = sourcePath.addFileExtension(outExt);
-				// Add the file to the list of dependencies for the base target
-				buffer.append(outputPath.toString() + " \\" + NEWLINE);
-			} 			
-		}
-		buffer.append(NEWLINE);
-
-		// Add a rule for building each resource to the makefile
-		for (int j = 0; j < members.length; j++) {
-			IResource resource = members[j];
-			IPath sourcePath = resource.getProjectRelativePath().removeFileExtension(); 
-			String srcExt = resource.getFileExtension();
-			String outExt = info.getOutputExtension(srcExt);
-			if (outExt != null) {
-				// Add the extension back to path
-				IPath outputPath = sourcePath.addFileExtension(outExt);
-				addRule(buffer, resource.getProjectRelativePath(), outputPath.toString(), info);
-			}
-		}
-	}
-
-	/**
-	 * @param buffer
-	 */
-	private void addTargets(StringBuffer buffer, IManagedBuildInfo info) {
-		// Generate a rule per source
-
-		// This is the top build rule
-		String flags = info.getFlagsForTarget("exe") + " ";
-		String cmd = info.getToolForTarget("exe") + " ";
-		buffer.append(info.getBuildArtifactName() + COLON + " ${OBJS}" + NEWLINE);
-		buffer.append(TAB + cmd + flags + "$@ ${OBJS}" + NEWLINE);
-		buffer.append(NEWLINE);
-
-		// TODO Generate 'all' for now but determine the real rules from UI
-		buffer.append("all: " + info.getBuildArtifactName() + NEWLINE);
-		buffer.append(NEWLINE);
-		
-		// Always add a clean target
-		buffer.append("clean:" + NEWLINE);
-		buffer.append(TAB + "$(RM) *.o " + info.getBuildArtifactName() + NEWLINE);
-		buffer.append(NEWLINE);
-	}
-
 	/* (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 = CCorePlugin.getFormattedString(START, getProject().getName());
+		if (statusMsg != null) {
+			monitor.subTask(statusMsg);
+		}
+
 		if (kind == IncrementalProjectBuilder.FULL_BUILD) {
 			fullBuild(monitor);
 		}
 		else {
 			// Create a delta visitor to make sure we should be rebuilding
-			MyResourceDeltaVisitor visitor = new MyResourceDeltaVisitor();
+			ResourceDeltaVisitor visitor = new ResourceDeltaVisitor();
 			IResourceDelta delta = getDelta(getProject());
 			if (delta == null) {
 				fullBuild(monitor);
@@ -176,52 +119,107 @@
 		}
 		// Checking to see if the user cancelled the build
 		checkCancel(monitor);
-		// Build referenced projects
+
+		// Ask build mechanism to compute deltas for project dependencies next time
 		return getProject().getReferencedProjects();
 	}
 
 	/**
-	 * Check whether the build has been canceled.
+	 * 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())
+		if (monitor != null && monitor.isCanceled()) {
 			throw new OperationCanceledException();
+		}
 	}
 
 	/**
 	 * @param monitor
 	 */
-	private void fullBuild(IProgressMonitor monitor) throws CoreException {
-		// Rebuild the entire project
-		IProject currentProject = getProject();
-		String statusMsg = null;
-		
-		// Need to report status to the user
+	protected void fullBuild(IProgressMonitor monitor) throws CoreException {
+		// Always need one of these bad boys
 		if (monitor == null) {
 			monitor = new NullProgressMonitor();
 		}
-		statusMsg = CCorePlugin.getFormattedString(REBUILD, currentProject.getName());
-		monitor.subTask(statusMsg);
+		
+		// We also need one of these ...
+		IProject currentProject = getProject();
+		if (currentProject == null) {
+			// Flag some sort of error and bail
+			return;
+		}
+
+		// Regenerate the makefiles for any managed projects this project depends on
+		IProject[] deps = currentProject.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) {
+					// This may be an empty project exception
+					if (e.getStatus().getCode() == GeneratedMakefileBuilder.EMPTY_PROJECT_BUILD_ERROR) {
+						// Just keep looking for other projects
+						continue;
+					}
+				}
+			}
+		}
 
-		// Get a filehandle for the makefile
-		IPath filePath = getWorkingDirectory().append(IPath.SEPARATOR + FILENAME);
-		String temp = filePath.toString();
-		IFile fileHandle = getMakefile(filePath, monitor);
+		// Need to report status to the user
+		String statusMsg = CCorePlugin.getFormattedString(REBUILD, currentProject.getName());
+		monitor.subTask(statusMsg);
 
-		// Add the items to the makefile
-		populateMakefile(fileHandle, monitor);		
+		// Regenerate the makefiles for this project
+		IManagedBuildInfo info = ManagedBuildManager.getBuildInfo(getProject());
+		MakefileGenerator generator = new MakefileGenerator(currentProject, info, monitor);		
+		try {
+			generator.regenerateMakefiles();
+		} catch (CoreException e) {
+			// See if this is an empty project
+			if (e.getStatus().getCode() == GeneratedMakefileBuilder.EMPTY_PROJECT_BUILD_ERROR) {
+				monitor.worked(1);
+				return;
+			}
+		}
+		IPath topBuildDir = generator.getTopBuildDir();
+		
+		// Now call make
+		invokeMake(true, topBuildDir.removeFirstSegments(1), info, monitor);
 		
 		monitor.worked(1);		
 	}
 
+	protected IPath getBuildDirectory(String dirName) throws CoreException {
+		// Create or get the handle for the build directory 
+		IFolder folder = getProject().getFolder(dirName);
+		if (!folder.exists()) {
+			try {
+				folder.create(false, true, null);
+			}
+			catch (CoreException e) {
+				if (e.getStatus().getCode() == IResourceStatus.PATH_OCCUPIED)
+					folder.refreshLocal(IResource.DEPTH_ZERO, null);
+				else
+					throw e;
+			}
+		}
+		return folder.getFullPath();
+	}
+	
 	/**
 	 * Gets the makefile for the project. It may be empty. 
 	 * 
 	 * @return The <code>IFile</code> to generate the makefile into.
 	 */
-	public IFile getMakefile(IPath filePath, IProgressMonitor monitor) throws CoreException {
+	protected IFile getMakefile(IPath filePath, IProgressMonitor monitor) throws CoreException {
 		// Create or get the handle for the makefile
-		IWorkspaceRoot root= CCorePlugin.getWorkspace().getRoot();
+		IWorkspaceRoot root = CCorePlugin.getWorkspace().getRoot();
 		IFile newFile = root.getFileForLocation(filePath);
 		if (newFile == null) {
 			newFile = root.getFile(filePath);
@@ -242,6 +240,45 @@
 		return newFile;
 	}
 	
+	/**
+	 * @param makefilePath
+	 * @param info
+	 * @return
+	 */
+	protected String[] getMakeTargets() {
+		List args = new ArrayList();
+		// Add each target
+		String sessionTarget = MakeUtil.getSessionTarget(getProject());
+		StringTokenizer tokens = new StringTokenizer(sessionTarget);
+		while (tokens.hasMoreTokens()) {
+			args.add(tokens.nextToken().trim());
+		}
+		if (args.isEmpty()) {
+			args.add("all");
+		}
+		return (String[])args.toArray(new String[args.size()]);
+	}
+	
+	/**
+	 * @return
+	 */
+	protected List getResourcesToBuild() {
+		if (resourcesToBuild == null) {
+			resourcesToBuild = new ArrayList();
+		}
+		return resourcesToBuild;
+	}
+
+	/**
+	 * @return
+	 */
+	protected List getRuleList() {
+		if (ruleList == null) {
+			ruleList = new ArrayList();
+		}
+		return ruleList;
+	}
+
 	/* (non-Javadoc)
 	 * @see org.eclipse.cdt.core.resources.ACBuilder#getWorkingDirectory()
 	 */
@@ -257,7 +294,7 @@
 	 * @param delta
 	 * @param monitor
 	 */
-	private void incrementalBuild(IResourceDelta delta, IProgressMonitor monitor) throws CoreException {
+	protected void incrementalBuild(IResourceDelta delta, IProgressMonitor monitor) throws CoreException {
 		// Rebuild the resource tree in the delta
 		IProject currentProject = getProject();
 		String statusMsg = null;
@@ -269,38 +306,140 @@
 		statusMsg = CCorePlugin.getFormattedString(INCREMENTAL, currentProject.getName());
 		monitor.subTask(statusMsg);
 
-		// Get a filehandle for the makefile
-		IPath filePath = getWorkingDirectory().append(IPath.SEPARATOR + FILENAME);
-		IFile fileHandle = getMakefile(filePath, monitor);
-
-		// Now populate it
-		populateMakefile(fileHandle, monitor);
+		IManagedBuildInfo info = ManagedBuildManager.getBuildInfo(getProject());
+		IPath buildDir = new Path(info.getConfigurationName());
+		invokeMake(false, buildDir, info, monitor);
 		
 		monitor.worked(1);		
 	}
 
-	/**
-	 * Recreate the entire contents of the makefile.
-	 * 
-	 * @param fileHandle The file to place the contents in.
-	 */
-	private void populateMakefile(IFile fileHandle, IProgressMonitor monitor) throws CoreException {
-		// Write out the contents of the build model
-		StringBuffer buffer = new StringBuffer();
-		IManagedBuildInfo info = ManagedBuildManager.getBuildInfo(getProject());
-		
-		// Add the macro definitions
-		addMacros(buffer, info);
+	protected void invokeMake(boolean fullBuild, IPath buildDir, IManagedBuildInfo info, IProgressMonitor monitor) {
+		boolean isCanceled = false;
+		IProject currentProject = getProject();
+		SubProgressMonitor subMonitor = null;
+		if (monitor == null) {
+			monitor = new NullProgressMonitor();
+		}
 
-		// Add a list of source files
-		addSources(buffer, info);
+		// Flag to the user that make is about to be called
+		IPath makeCommand = new Path(info.getMakeCommand()); 
+		String[] msgs = new String[2];
+		msgs[0] = info.getMakeCommand();
+		msgs[1] = currentProject.getName();
+		String statusMsg = CCorePlugin.getFormattedString(MAKE, msgs);
+		if (statusMsg != null) {
+			monitor.subTask(statusMsg);
+		}
+
+		// Get a build console for the project
+		IConsole console = null;
+		ConsoleOutputStream consoleOutStream = null;
+		IWorkspace workspace = null;
+		IMarker[] markers = null;
+		try {
+			console = CCorePlugin.getDefault().getConsole();
+			console.start(currentProject);
+			consoleOutStream = console.getOutputStream();
+
+			// Remove all markers for this project
+			workspace = currentProject.getWorkspace();
+			markers = currentProject.findMarkers(ICModelMarker.C_MODEL_PROBLEM_MARKER, true, IResource.DEPTH_INFINITE);
+			if (markers != null) {
+				workspace.deleteMarkers(markers);
+			}
+		} catch (CoreException e) {
+		}
+
+		IPath workingDirectory = getWorkingDirectory().append(buildDir);
+
+		// Get the arguments to be passed to make from build model
+		String[] makeTargets = getMakeTargets();
+		
+		// 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());
+		props.put("PWD", workingDirectory.toOSString());
+		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));
+			}
+			env = (String[]) envList.toArray(new String[envList.size()]);
+		}
 		
-		// Add targets
-		addTargets(buffer, info);
+		// 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 explicitely.
+				// We will never write to it.
+				proc.getOutputStream().close();
+			} catch (IOException e) {
+			}
+			subMonitor = new SubProgressMonitor(monitor, IProgressMonitor.UNKNOWN);
+			if (launcher.waitAndRead(stdout, stderr, subMonitor) != CommandLauncher.OK) {
+				errMsg = launcher.getErrorMessage();
+			
+				isCanceled = monitor.isCanceled();
+				monitor.setCanceled(false);
+				subMonitor = new SubProgressMonitor(monitor, IProgressMonitor.UNKNOWN);
+				subMonitor.subTask("Refresh From Local");
+
+				try {
+					currentProject.refreshLocal(IResource.DEPTH_INFINITE, subMonitor);
+				} catch (CoreException e) {
+				}
 
-		// Save the file
-		Util.save(buffer, fileHandle);
-	}
+				subMonitor = new SubProgressMonitor(monitor, IProgressMonitor.UNKNOWN);
+				subMonitor.subTask("Parsing");
+			} 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 = CCorePlugin.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 = CCorePlugin.getFormattedString(BUILD_FINISHED, currentProject.getName());
+				buf.append(successMsg);
+				buf.append(System.getProperty("line.separator", "\n"));
+			}
+			// Write your message on the pavement
+			try {
+				consoleOutStream.write(buf.toString().getBytes());
+				consoleOutStream.flush();
+				stdout.close();
+				stderr.close();				
+			} catch (IOException e) {
+			}
 
+			epm.reportProblems();
+
+			subMonitor.done();
+			monitor.setCanceled(isCanceled);
+		}
+		monitor.done();
+	}
 
 }
Index: src/org/eclipse/cdt/internal/core/MakefileGenerator.java
===================================================================
RCS file: src/org/eclipse/cdt/internal/core/MakefileGenerator.java
diff -N src/org/eclipse/cdt/internal/core/MakefileGenerator.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/org/eclipse/cdt/internal/core/MakefileGenerator.java	25 Jul 2003 02:24:04 -0000
@@ -0,0 +1,574 @@
+package org.eclipse.cdt.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.ByteArrayInputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+import org.eclipse.cdt.core.CCorePlugin;
+import org.eclipse.cdt.core.build.managed.IManagedBuildInfo;
+import org.eclipse.cdt.core.search.ICSearchConstants;
+import org.eclipse.cdt.internal.core.model.Util;
+import org.eclipse.cdt.internal.core.sourcedependency.DependencyManager;
+import org.eclipse.cdt.internal.core.sourcedependency.DependencyQueryJob;
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceProxy;
+import org.eclipse.core.resources.IResourceProxyVisitor;
+import org.eclipse.core.resources.IResourceStatus;
+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.OperationCanceledException;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Status;
+
+public class MakefileGenerator {
+	// String constants for messages
+	private static final String MESSAGE = "MakeBuilder.message";	//$NON-NLS-1$
+	private static final String BUILD_ERROR = MESSAGE + ".error";	//$NON-NLS-1$
+	private static final String COMMENT = "MakeBuilder.comment";	//$NON-NLS-1$
+	private static final String MOD_LIST = COMMENT + ".module.list";	//$NON-NLS-1$	
+	private static final String SRC_LISTS = COMMENT + ".source.list";	//$NON-NLS-1$	
+	private static final String MOD_RULES = COMMENT + ".build.rule";	//$NON-NLS-1$	
+	private static final String MOD_INCL = COMMENT + ".module.make.includes";	//$NON-NLS-1$	
+	private static final String DEP_INCL = COMMENT + ".module.dep.includes";	//$NON-NLS-1$
+	private static final String AUTO_DEP = COMMENT + ".autodeps";	//$NON-NLS-1$
+
+	// String constants for makefile contents
+	protected static final String COLON = ":";
+	protected static final String DEPFILE_NAME = "module.dep";	//$NON-NLS-1$
+	protected static final String DOT = ".";
+	protected static final String MAKEFILE_NAME = "makefile";	//$NON-NLS-1$
+	protected static final String MODFILE_NAME = "module.mk";	//$NON-NLS-1$
+	protected static final String LINEBREAK = "\\";
+	protected static final String NEWLINE = System.getProperty("line.separator");
+	protected static final String SEMI_COLON = ";";
+	protected static final String SEPARATOR = "/";
+	protected static final String TAB = "\t";	
+	protected static final String WHITESPACE = " ";
+	protected static final String WILDCARD = "%";
+
+	// Local variables needed by generator
+	protected IManagedBuildInfo info;
+	protected List moduleList;
+	protected IProgressMonitor monitor;
+	protected IProject project;
+	protected List ruleList;
+	protected IPath topBuildDir;
+	
+	/**
+	 * This class is used to recursively walk the project and determine which
+	 * modules contribute buildable source files. 
+	 */
+	protected class ResourceProxyVisitor implements IResourceProxyVisitor {
+		private MakefileGenerator generator;
+		private IManagedBuildInfo info;
+
+		/**
+		 * Constructs a new resource proxy visitor to quickly visit project
+		 * resources.
+		 */
+		public ResourceProxyVisitor(MakefileGenerator generator, IManagedBuildInfo info) {
+			this.generator = generator;
+			this.info = info;
+		}
+
+		/* (non-Javadoc)
+		 * @see org.eclipse.core.resources.IResourceProxyVisitor#visit(org.eclipse.core.resources.IResourceProxy)
+		 */
+		public boolean visit(IResourceProxy proxy) throws CoreException {
+			// No point in proceeding, is there 
+			if (generator == null) {
+				return false;
+			}
+			
+			// Is this a resource we should even consider
+			if (proxy.getType() == IResource.FILE) {
+				// Check extension to see if build model should build this file
+				IResource resource = proxy.requestResource();
+				String ext = resource.getFileExtension();
+				if (info.buildsFileType(ext)) {
+					generator.appendModule(resource);
+				}
+				return false;
+			}
+
+			// Recurse into subdirectories
+			return true;
+		}
+
+	}
+
+	public MakefileGenerator(IProject project, IManagedBuildInfo info, IProgressMonitor monitor) {
+		super();
+		// Save the project so we can get path and member information
+		this.project = project;
+		// Save the monitor reference for reporting back to the user
+		this.monitor = monitor;
+		// Get the build info for the project
+		this.info = info;
+	}
+
+	/**
+	 * @param module
+	 * @return
+	 */
+	protected StringBuffer addDeps(IContainer module) throws CoreException {
+		// Calculate the new directory relative to the build output
+		IPath moduleRelativePath = module.getProjectRelativePath();
+		String relativePath = moduleRelativePath.toString();
+		relativePath += relativePath.length() == 0 ? "" : SEPARATOR; 
+
+		// Create the buffer to hold the output for the module and a dep calculator
+		StringBuffer buffer = new StringBuffer();
+		buffer.append(CCorePlugin.getResourceString(AUTO_DEP) + NEWLINE);
+		DependencyManager dependencyManager = CCorePlugin.getDefault().getCoreModel().getDependencyManager();
+
+		/*
+		 * Visit each resource in the folder that we have a rule to build.
+		 * The dependency output for each resource will be in the format
+		 * <relativePath>/<resourceName>.<outputExtension> : <dep1> ... <depn>
+		 * with long lines broken.
+		 */
+		IResource[] resources = module.members();
+		for (int i = 0; i < resources.length; i++) {
+			IResource resource = resources[i];
+			if (resource.getType() == IResource.FILE) {
+				String inputExt = resource.getFileExtension();
+				if (info.buildsFileType(inputExt)) {
+					// Get the filename without an extension
+					String fileName = resource.getFullPath().removeFileExtension().lastSegment();
+					if (fileName == null) continue;
+					String outputExt = info.getOutputExtension(inputExt);
+					if (outputExt != null) {
+						fileName += DOT + outputExt;
+					}
+					// ASk the dep generator to find all the deps for this resource
+					ArrayList dependencies = new ArrayList();
+					try {
+						dependencyManager.performConcurrentJob(new DependencyQueryJob(project, (IFile)resource, dependencyManager, dependencies), ICSearchConstants.WAIT_UNTIL_READY_TO_SEARCH, null);
+					} catch (Exception e) {
+						continue;
+					}
+					if (dependencies.size() == 0) continue;
+					buffer.append(relativePath + fileName + COLON + WHITESPACE);
+					Iterator iter = dependencies.listIterator();
+					while (iter.hasNext()) {
+						buffer.append(LINEBREAK + NEWLINE);
+						String path = (String)iter.next();
+						buffer.append(path + WHITESPACE);
+					}
+					buffer.append(NEWLINE);
+				}
+			}
+		}		
+		return buffer;
+	}
+
+	/**
+	 * @param buffer
+	 * @param info
+	 */
+	protected StringBuffer addMacros() {
+		StringBuffer buffer = new StringBuffer();
+		
+		// Add the ROOT macro
+		buffer.append("ROOT := .." + NEWLINE);
+		
+		// Get the clean command from the build model
+		buffer.append("RM := ");
+		buffer.append(info.getCleanCommand() + NEWLINE);
+		
+		// Add the macro for the output flag
+		buffer.append("OUTPUT_FLAG := ");
+		buffer.append(info.getOutputFlag() + NEWLINE);
+
+		buffer.append(CCorePlugin.getResourceString(SRC_LISTS) + NEWLINE);
+		buffer.append("C_SRCS := " + NEWLINE);
+		buffer.append("CC_SRCS := " + NEWLINE);
+		
+		buffer.append(NEWLINE + NEWLINE);
+		return buffer;
+	}
+
+	/**
+	 * @return
+	 */
+	protected StringBuffer addModules() {
+		StringBuffer buffer = new StringBuffer();
+		// Add the comment
+		buffer.append(CCorePlugin.getResourceString(MOD_LIST) + NEWLINE);
+		buffer.append("MODULES := " + LINEBREAK + NEWLINE);
+		buffer.append("." + LINEBREAK + NEWLINE);
+		
+		// Get all the module names
+		ListIterator iter = getModuleList().listIterator();
+		while (iter.hasNext()) {
+			IContainer container = (IContainer) iter.next();
+			IPath path = container.getProjectRelativePath();
+			buffer.append(path.toString() +  WHITESPACE + LINEBREAK + NEWLINE);
+		}
+
+		buffer.append(NEWLINE);
+		buffer.append(CCorePlugin.getResourceString(MOD_INCL) + NEWLINE);
+		buffer.append("include ${patsubst %, %/module.mk, $(MODULES)}" + NEWLINE);
+
+		buffer.append(NEWLINE + NEWLINE);
+		return buffer;
+	}
+
+
+	/**
+	 * Answers a <code>StringBuffer</code> containing all of the sources contributed by
+	 * a container to the build.
+	 * @param module
+	 * @return
+	 */
+	protected StringBuffer addSources(IContainer module) throws CoreException {
+		// Calculate the new directory relative to the build output
+		IPath moduleRelativePath = module.getProjectRelativePath();
+		String relativePath = moduleRelativePath.toString();
+		relativePath += relativePath.length() == 0 ? "" : SEPARATOR; 
+		
+		// String buffers
+		StringBuffer buffer = new StringBuffer();
+		StringBuffer cBuffer = new StringBuffer("C_SRCS += " + LINEBREAK + NEWLINE);
+		cBuffer.append("${addprefix $(ROOT)/" + relativePath + "," + LINEBREAK + NEWLINE);
+		StringBuffer ccBuffer = new StringBuffer("CC_SRCS += \\" + NEWLINE);
+		ccBuffer.append("${addprefix $(ROOT)/" + relativePath + "," + LINEBREAK + NEWLINE);
+		StringBuffer ruleBuffer = new StringBuffer(CCorePlugin.getResourceString(MOD_RULES) + NEWLINE);
+
+		// Put the comment in		
+		buffer.append(CCorePlugin.getResourceString(SRC_LISTS) + NEWLINE);
+
+		// Visit the resources in this folder
+		IResource[] resources = module.members();
+		for (int i = 0; i < resources.length; i++) {
+			IResource resource = resources[i];
+			if (resource.getType() == IResource.FILE) {
+				String ext = resource.getFileExtension();
+				if (info.buildsFileType(ext)) {
+					// TODO use build model to determine what list the file goes in
+					ccBuffer.append(resource.getName() + WHITESPACE + LINEBREAK + NEWLINE);
+					// Try to add the rule for the file
+					addRule(relativePath, ruleBuffer, resource);
+				}
+			}
+		}
+
+		// Finish the commands in the buffers
+		cBuffer.append("}" + NEWLINE + NEWLINE);
+		ccBuffer.append("}" + NEWLINE + NEWLINE);
+
+		// Append them all together
+		buffer.append(cBuffer).append(ccBuffer).append(ruleBuffer);
+		return buffer;
+	}
+
+	/**
+	 * Answers a <code>StrinBuffer</code> containing all of the required targets to
+	 * properly build the project.
+	 */
+	protected StringBuffer addTargets() {
+		StringBuffer buffer = new StringBuffer();
+
+		// Get the target and it's extension
+		String target = info.getBuildArtifactName();
+		IPath temp = new Path(target);
+		String extension = temp.getFileExtension();
+		
+		/*
+		 * Write out the taqrget rule as:
+		 * <target>.<extension>: $(CC_SRCS:$(ROOT)/%.cpp=%.o) $(C_SRCS:$(ROOT)/%.c=%.o)
+		 * 		$(BUILD_TOOL) $(FLAGS) $(OUTPUT_FLAG) $@ $^ $(LIB_DEPS)
+		 */
+		String cmd = info.getToolForTarget(extension);
+		String flags = info.getFlagsForTarget(extension);
+		buffer.append(target + COLON + WHITESPACE + "$(CC_SRCS:$(ROOT)/%.cpp=%.o) $(C_SRCS:$(ROOT)/%.c=%.o)" + NEWLINE);
+		buffer.append(TAB + cmd + WHITESPACE + flags + WHITESPACE + "$(OUTPUT_FLAG) $@" + WHITESPACE + "$^" + WHITESPACE + NEWLINE);
+		buffer.append(NEWLINE);
+
+		// TODO Generate 'all' for now but determine the real rules from UI
+		buffer.append("all: " + target + NEWLINE);
+		buffer.append(NEWLINE);
+		
+		// Always add a clean target
+		buffer.append(".PHONY: clean" + NEWLINE);
+		buffer.append("clean:" + NEWLINE);
+		buffer.append(TAB + "$(RM)" + WHITESPACE + "${addprefix ., $(CC_SRCS:$(ROOT)%.cpp=%.o)} ${addprefix ., $(C_SRCS:$(ROOT)%.c=%.o)}" + WHITESPACE + target + NEWLINE);
+		buffer.append(NEWLINE);
+		
+		buffer.append(NEWLINE + CCorePlugin.getResourceString(DEP_INCL) + NEWLINE);
+		buffer.append("include ${patsubst %, %/module.dep, $(MODULES)}" + NEWLINE);
+		
+		buffer.append(NEWLINE);
+		return buffer;
+	}
+
+	protected void addRule(String relativePath, StringBuffer buffer, IResource resource) {
+		String rule = null;
+		String cmd = null;
+		String buildFlags = null;
+		String inputExtension = null;
+		String outputExtension = null;
+
+		// Is there a special rule for this file
+		if (false) {
+		} 
+		else {
+			// Get the extension of the resource
+			inputExtension = resource.getFileExtension();
+			// ASk the build model what it will produce from this
+			outputExtension = info.getOutputExtension(inputExtension);
+			/*
+			 * Create the pattern rule in the format
+			 * <relative_path>/%.o: $(ROOT)/<relative_path>/%.cpp
+			 * 		$(CC) $(CFLAGS) $(OUTPUT_FLAG) $@ $<
+			 * 
+			 * Note that CC CFLAGS and OUTPUT_FLAG all come from the build model
+			 * and are resolved to a real command before writing to the module
+			 * makefile, so a real command might look something like
+			 * source1/%.o: $(ROOT)/source1/%.cpp
+			 * 		g++ -g -O2 -c -I/cygdrive/c/eclipse/workspace/Project/headers -o $@ $<
+			 */ 
+			rule = relativePath + WILDCARD + DOT + outputExtension + COLON + WHITESPACE + "$(ROOT)" + SEPARATOR + relativePath + WILDCARD + DOT + inputExtension;
+		}
+
+		// Check if the rule is listed as something we already generated in the makefile
+		if (!getRuleList().contains(rule)) {
+			// Add it to the list
+			getRuleList().add(rule);
+
+			// Add the rule and command to the makefile
+			buffer.append(rule + NEWLINE);
+			cmd = info.getToolForSource(inputExtension);
+			buildFlags = info.getFlagsForSource(inputExtension);
+			
+			buffer.append(TAB + cmd + WHITESPACE + buildFlags + WHITESPACE + "$(OUTPUT_FLAG) $@" + WHITESPACE + "$<" + NEWLINE + NEWLINE);
+		}
+	}
+	
+	/**
+	 * @param resource
+	 */
+	public void appendModule(IResource resource) {
+		// The build model knows how to build this file
+		IContainer container = resource.getParent();
+		if (!getModuleList().contains(container)) {
+			getModuleList().add(container);		
+		}
+	}
+
+	/**
+	 * 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() {
+		if (monitor != null && monitor.isCanceled()) {
+			throw new OperationCanceledException();
+		}
+	}
+
+	/**
+	 * @return
+	 */
+	private List getModuleList() {
+		if (moduleList == null) {
+			moduleList = new ArrayList();
+		}
+		return moduleList;
+	}
+	
+	/**
+	 * 
+	 */
+	private List getRuleList() {
+		if (ruleList == null) {
+			ruleList = new ArrayList();
+		}
+		return ruleList;
+	}
+
+	/**
+	 * @param string
+	 * @return
+	 */
+	private IPath createDirectory(String dirName) throws CoreException {
+		// Create or get the handle for the build directory 
+		IFolder folder = project.getFolder(dirName);
+		if (!folder.exists()) {
+
+			// Make sure that parent folders exist
+			IPath parentPath = (new Path(dirName)).removeLastSegments(1);
+			// Assume that the parent exists if the path is empty
+			if (!parentPath.isEmpty()) {
+				IFolder parent = project.getFolder(parentPath);
+				if (!parent.exists()) {
+					createDirectory(parentPath.toString());
+				}
+			}
+			
+			// Now make the requested folder
+			try {
+				folder.create(true, true, null);
+			}
+			catch (CoreException e) {
+				if (e.getStatus().getCode() == IResourceStatus.PATH_OCCUPIED)
+					folder.refreshLocal(IResource.DEPTH_ZERO, null);
+				else
+					throw e;
+			}
+		}
+		return folder.getFullPath();
+	}
+
+	/**
+	 * @param makefilePath
+	 * @param monitor
+	 * @return
+	 */
+	private IFile createFile(IPath makefilePath) throws CoreException {
+		// Create or get the handle for the makefile
+		IWorkspaceRoot root = CCorePlugin.getWorkspace().getRoot();
+		IFile newFile = root.getFileForLocation(makefilePath);
+		if (newFile == null) {
+			newFile = root.getFile(makefilePath);
+		}
+		// Create the file if it does not exist
+		ByteArrayInputStream contents = new ByteArrayInputStream(new byte[0]);
+		try {
+			newFile.create(contents, false, monitor);
+		}
+		catch (CoreException e) {
+			// If the file already existed locally, just refresh to get contents
+			if (e.getStatus().getCode() == IResourceStatus.PATH_OCCUPIED)
+				newFile.refreshLocal(IResource.DEPTH_ZERO, null);
+			else
+				throw e;
+		}
+		// TODO handle long running file operation
+		return newFile;
+	}
+
+	/**
+	 * Answers the <code>IPath</code> of the top directory generated for the build
+	 * output, or <code>null</code> if none has been generated.
+	 * 
+	 * @return
+	 */
+	public IPath getTopBuildDir() {
+		return topBuildDir;
+	}
+
+	/**
+	 * Create the entire contents of the makefile.
+	 * 
+	 * @param fileHandle The file to place the contents in.
+	 * @param info
+	 * @param monitor
+	 */
+	protected void populateMakefile(IFile fileHandle) {
+		StringBuffer buffer = new StringBuffer();
+		
+		// Add the macro definitions
+		buffer.append(addMacros());
+
+		// Append the module list		
+		buffer.append(addModules()); 
+
+		// Add targets
+		buffer.append(addTargets());
+
+		// Save the file
+		try {
+			Util.save(buffer, fileHandle);
+		} catch (CoreException e1) {
+			// TODO Auto-generated catch block
+			e1.printStackTrace();
+		}
+	}
+
+	/**
+	 * @param module
+	 */
+	protected void populateModMakefile(IContainer module) throws CoreException {
+		// Calcualte the new directory relative to the build output
+		IPath moduleRelativePath = module.getProjectRelativePath();
+		IPath buildRoot = getTopBuildDir().removeFirstSegments(1);
+		if (buildRoot == null) {
+			return;
+		}
+		IPath moduleOutputPath = buildRoot.append(moduleRelativePath);
+		
+		// Now create the directory
+		IPath moduleOutputDir = createDirectory(moduleOutputPath.toString());
+		
+		// Create a module makefile
+		IFile modMakefile = createFile(moduleOutputDir.addTrailingSeparator().append(MODFILE_NAME));
+		StringBuffer makeBuf = new StringBuffer();
+		makeBuf.append(addSources(module));
+
+		// Create a module dep file
+		IFile modDepfile = createFile(moduleOutputDir.addTrailingSeparator().append(DEPFILE_NAME));
+		StringBuffer depBuf = new StringBuffer();
+		depBuf.append(addDeps(module));
+
+		// Save the files
+		Util.save(makeBuf, modMakefile);
+		Util.save(depBuf, modDepfile);
+	}
+
+
+	public void regenerateMakefiles() throws CoreException {
+		// Visit the resources in the project
+		ResourceProxyVisitor visitor = new ResourceProxyVisitor(this, info);
+		project.accept(visitor, IResource.NONE);
+		if (getModuleList().isEmpty()) {
+			// There is nothing to build
+			IStatus status = new Status(IStatus.INFO, CCorePlugin.PLUGIN_ID, GeneratedMakefileBuilder.EMPTY_PROJECT_BUILD_ERROR, "", null);
+			throw new CoreException(status);
+		}
+
+		// See if the user has cancelled the build
+		checkCancel();
+
+		// Create the top-level directory for the build output
+		topBuildDir = createDirectory(info.getConfigurationName());
+		
+		// Create the top-level makefile
+		IPath makefilePath = topBuildDir.addTrailingSeparator().append(MAKEFILE_NAME);
+		IFile makefileHandle = createFile(makefilePath);
+		
+		// Populate the makefile
+		populateMakefile(makefileHandle);
+		checkCancel();
+		
+		// Now populate the module makefiles
+		ListIterator iter = getModuleList().listIterator();
+		while (iter.hasNext()) {
+			populateModMakefile((IContainer)iter.next());
+			checkCancel();
+		}
+	}
+
+}
Index: suite/org/eclipse/cdt/core/suite/AutomatedIntegrationSuite.java
===================================================================
RCS file: /home/tools/org.eclipse.cdt.core.tests/suite/org/eclipse/cdt/core/suite/AutomatedIntegrationSuite.java,v
retrieving revision 1.6
diff -u -r1.6 AutomatedIntegrationSuite.java
--- suite/org/eclipse/cdt/core/suite/AutomatedIntegrationSuite.java	24 Jul 2003 14:20:11 -0000	1.6
+++ suite/org/eclipse/cdt/core/suite/AutomatedIntegrationSuite.java	25 Jul 2003 02:24:28 -0000
@@ -18,6 +18,7 @@
 import junit.textui.TestRunner;
 
 import org.eclipse.cdt.core.build.managed.tests.AllBuildTests;
+import org.eclipse.cdt.core.build.managed.tests.StandardBuildTests;
 import org.eclipse.cdt.core.model.failedTests.CModelElementsFailedTests;
 import org.eclipse.cdt.core.model.tests.AllCoreTests;
 import org.eclipse.cdt.core.model.tests.BinaryTests;
@@ -77,6 +78,7 @@
 
 		// Add all success tests
 		suite.addTest(AllBuildTests.suite());
+		suite.addTest(StandardBuildTests.suite());
 		suite.addTest(ParserTestSuite.suite());
 		suite.addTest(AllCoreTests.suite());
 		suite.addTest(BinaryTests.suite());

Back to the top