Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/org.eclipse.cdt.core.tests/suite/org/eclipse/cdt/core/suite/AutomatedIntegrationSuite.java2
-rw-r--r--core/org.eclipse.cdt.core/build/org/eclipse/cdt/core/build/managed/IManagedBuildInfo.java51
-rw-r--r--core/org.eclipse.cdt.core/build/org/eclipse/cdt/core/build/managed/ManagedBuildManager.java76
-rw-r--r--core/org.eclipse.cdt.core/build/org/eclipse/cdt/internal/core/build/managed/ManagedBuildInfo.java113
-rw-r--r--core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/ManagedCProjectNature.java19
-rw-r--r--core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/CCorePluginResources.properties14
-rw-r--r--core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/GeneratedMakefileBuilder.java415
-rw-r--r--core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/MakefileGenerator.java574
8 files changed, 1080 insertions, 184 deletions
diff --git a/core/org.eclipse.cdt.core.tests/suite/org/eclipse/cdt/core/suite/AutomatedIntegrationSuite.java b/core/org.eclipse.cdt.core.tests/suite/org/eclipse/cdt/core/suite/AutomatedIntegrationSuite.java
index 13356e9c02a..8829b751be0 100644
--- a/core/org.eclipse.cdt.core.tests/suite/org/eclipse/cdt/core/suite/AutomatedIntegrationSuite.java
+++ b/core/org.eclipse.cdt.core.tests/suite/org/eclipse/cdt/core/suite/AutomatedIntegrationSuite.java
@@ -18,6 +18,7 @@ import junit.framework.TestSuite;
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 @@ public class AutomatedIntegrationSuite extends TestSuite
// Add all success tests
suite.addTest(AllBuildTests.suite());
+ suite.addTest(StandardBuildTests.suite());
suite.addTest(ParserTestSuite.suite());
suite.addTest(AllCoreTests.suite());
suite.addTest(BinaryTests.suite());
diff --git a/core/org.eclipse.cdt.core/build/org/eclipse/cdt/core/build/managed/IManagedBuildInfo.java b/core/org.eclipse.cdt.core/build/org/eclipse/cdt/core/build/managed/IManagedBuildInfo.java
index 7684d41f982..baf3146f5c8 100644
--- a/core/org.eclipse.cdt.core/build/org/eclipse/cdt/core/build/managed/IManagedBuildInfo.java
+++ b/core/org.eclipse.cdt.core/build/org/eclipse/cdt/core/build/managed/IManagedBuildInfo.java
@@ -23,6 +23,15 @@ public interface IManagedBuildInfo {
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 interface IManagedBuildInfo {
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 interface IManagedBuildInfo {
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 interface IManagedBuildInfo {
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 @@ public interface IManagedBuildInfo {
* @param target
*/
public void setDefaultTarget(ITarget target);
+
+
}
diff --git a/core/org.eclipse.cdt.core/build/org/eclipse/cdt/core/build/managed/ManagedBuildManager.java b/core/org.eclipse.cdt.core/build/org/eclipse/cdt/core/build/managed/ManagedBuildManager.java
index 54d2e7920e4..727b30681ca 100644
--- a/core/org.eclipse.cdt.core/build/org/eclipse/cdt/core/build/managed/ManagedBuildManager.java
+++ b/core/org.eclipse.cdt.core/build/org/eclipse/cdt/core/build/managed/ManagedBuildManager.java
@@ -328,6 +328,28 @@ public class ManagedBuildManager implements IScannerInfoProvider {
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 @@ public class ManagedBuildManager implements IScannerInfoProvider {
}
}
- 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 @@ public class ManagedBuildManager implements IScannerInfoProvider {
* 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 @@ public class ManagedBuildManager implements IScannerInfoProvider {
}
}
+ // 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)
*/
@@ -504,4 +545,5 @@ public class ManagedBuildManager implements IScannerInfoProvider {
}
}
+
}
diff --git a/core/org.eclipse.cdt.core/build/org/eclipse/cdt/internal/core/build/managed/ManagedBuildInfo.java b/core/org.eclipse.cdt.core/build/org/eclipse/cdt/internal/core/build/managed/ManagedBuildInfo.java
index 04502ea575e..3f6f451d67d 100644
--- a/core/org.eclipse.cdt.core/build/org/eclipse/cdt/internal/core/build/managed/ManagedBuildInfo.java
+++ b/core/org.eclipse.cdt.core/build/org/eclipse/cdt/internal/core/build/managed/ManagedBuildInfo.java
@@ -88,6 +88,22 @@ public class ManagedBuildInfo implements IManagedBuildInfo, IScannerInfo {
}
/* (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 @@ public class ManagedBuildInfo implements IManagedBuildInfo, IScannerInfo {
}
/* (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 @@ public class ManagedBuildInfo implements IManagedBuildInfo, IScannerInfo {
}
/* (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 @@ public class ManagedBuildInfo implements IManagedBuildInfo, IScannerInfo {
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 @@ public class ManagedBuildInfo implements IManagedBuildInfo, IScannerInfo {
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()]);
- }
}
diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/ManagedCProjectNature.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/ManagedCProjectNature.java
index 1c3a6f7d231..cf77ba2800d 100644
--- a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/ManagedCProjectNature.java
+++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/ManagedCProjectNature.java
@@ -14,6 +14,7 @@ package org.eclipse.cdt.core;
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 @@ public class ManagedCProjectNature implements IProjectNature {
// 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 @@ public class ManagedCProjectNature implements IProjectNature {
newCommands[0] = command;
description.setBuildSpec(newCommands);
project.setDescription(description, null);
- }
+ }
}
/**
diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/CCorePluginResources.properties b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/CCorePluginResources.properties
index 8e6744b513d..89a5370785c 100644
--- a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/CCorePluginResources.properties
+++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/CCorePluginResources.properties
@@ -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:
diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/GeneratedMakefileBuilder.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/GeneratedMakefileBuilder.java
index 34bbb1811e5..5f81f777a85 100644
--- a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/GeneratedMakefileBuilder.java
+++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/GeneratedMakefileBuilder.java
@@ -12,19 +12,34 @@ package org.eclipse.cdt.internal.core;
* **********************************************************************/
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.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.SubProgressMonitor;
public class GeneratedMakefileBuilder extends ACBuilder {
// String constants
private static final String MESSAGE = "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 @@ public class GeneratedMakefileBuilder extends ACBuilder {
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 @@ public class GeneratedMakefileBuilder extends ACBuilder {
}
// 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;
+ }
- // Get a filehandle for the makefile
- IPath filePath = getWorkingDirectory().append(IPath.SEPARATOR + FILENAME);
- String temp = filePath.toString();
- IFile fileHandle = getMakefile(filePath, monitor);
+ // 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;
+ }
+ }
+ }
+ }
- // Add the items to the makefile
- populateMakefile(fileHandle, monitor);
+ // Need to report status to the user
+ String statusMsg = CCorePlugin.getFormattedString(REBUILD, currentProject.getName());
+ monitor.subTask(statusMsg);
+
+ // 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 @@ public class GeneratedMakefileBuilder extends ACBuilder {
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 @@ public class GeneratedMakefileBuilder extends ACBuilder {
* @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 @@ public class GeneratedMakefileBuilder extends ACBuilder {
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());
+ 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();
+ }
+
+ // 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();
- // Add the macro definitions
- addMacros(buffer, info);
+ // Get a launcher for the make command
+ String errMsg = null;
+ CommandLauncher launcher = new CommandLauncher();
+ launcher.showCommand(true);
- // Add a list of source files
- addSources(buffer, info);
+ // 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");
- // Save the file
- Util.save(buffer, fileHandle);
- }
+ try {
+ currentProject.refreshLocal(IResource.DEPTH_INFINITE, subMonitor);
+ } catch (CoreException e) {
+ }
+ 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();
+ }
}
diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/MakefileGenerator.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/MakefileGenerator.java
new file mode 100644
index 00000000000..c27bb1d03a6
--- /dev/null
+++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/MakefileGenerator.java
@@ -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();
+ }
+ }
+
+}

Back to the top