mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-04-29 19:45:01 +02:00
Patch for Sean Evoy:
- Initial makefile generator for managed build.
This commit is contained in:
parent
196aedf345
commit
2ea02e1982
8 changed files with 1084 additions and 188 deletions
|
@ -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());
|
||||
|
|
|
@ -22,6 +22,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.
|
||||
*
|
||||
|
@ -29,6 +38,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
|
||||
*
|
||||
|
@ -53,6 +77,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.
|
||||
*
|
||||
|
@ -88,6 +122,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.
|
||||
|
@ -121,4 +170,6 @@ public interface IManagedBuildInfo {
|
|||
*/
|
||||
public void setDefaultTarget(ITarget target);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -328,25 +328,6 @@ public class ManagedBuildManager implements IScannerInfoProvider {
|
|||
getExtensionTargetMap().put(target.getId(), target);
|
||||
}
|
||||
|
||||
private static void loadExtensions() {
|
||||
if (extensionTargetsLoaded)
|
||||
return;
|
||||
extensionTargetsLoaded = true;
|
||||
|
||||
IExtensionPoint extensionPoint = CCorePlugin.getDefault().getDescriptor().getExtensionPoint("ManagedBuildInfo");
|
||||
IExtension[] extensions = extensionPoint.getExtensions();
|
||||
for (int i = 0; i < extensions.length; ++i) {
|
||||
IExtension extension = extensions[i];
|
||||
IConfigurationElement[] elements = extension.getConfigurationElements();
|
||||
for (int j = 0; j < elements.length; ++j) {
|
||||
IConfigurationElement element = elements[j];
|
||||
if (element.getName().equals("target")) {
|
||||
new Target(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static ManagedBuildInfo loadBuildInfo(IProject project) {
|
||||
ManagedBuildInfo buildInfo = null;
|
||||
IFile file = project.getFile(FILE_NAME);
|
||||
|
@ -369,6 +350,58 @@ public class ManagedBuildManager implements IScannerInfoProvider {
|
|||
return buildInfo;
|
||||
}
|
||||
|
||||
private static void loadExtensions() {
|
||||
if (extensionTargetsLoaded)
|
||||
return;
|
||||
extensionTargetsLoaded = true;
|
||||
|
||||
IExtensionPoint extensionPoint = CCorePlugin.getDefault().getDescriptor().getExtensionPoint("ManagedBuildInfo");
|
||||
IExtension[] extensions = extensionPoint.getExtensions();
|
||||
for (int i = 0; i < extensions.length; ++i) {
|
||||
IExtension extension = extensions[i];
|
||||
IConfigurationElement[] elements = extension.getConfigurationElements();
|
||||
for (int j = 0; j < elements.length; ++j) {
|
||||
IConfigurationElement element = elements[j];
|
||||
if (element.getName().equals("target")) {
|
||||
new Target(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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()) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static ManagedBuildInfo findBuildInfo(IResource resource, boolean create) {
|
||||
// Make sure the extension information is loaded first
|
||||
loadExtensions();
|
||||
|
@ -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 {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -87,6 +87,22 @@ public class ManagedBuildInfo implements IManagedBuildInfo, IScannerInfo {
|
|||
targets.add(target);
|
||||
}
|
||||
|
||||
/* (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()
|
||||
*/
|
||||
|
@ -96,6 +112,23 @@ public class ManagedBuildInfo implements IManagedBuildInfo, IScannerInfo {
|
|||
return name == null ? new String() : name;
|
||||
}
|
||||
|
||||
/* (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()
|
||||
*/
|
||||
|
@ -168,6 +201,51 @@ public class ManagedBuildInfo implements IManagedBuildInfo, IScannerInfo {
|
|||
return null;
|
||||
}
|
||||
|
||||
/* (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)
|
||||
*/
|
||||
|
@ -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()]);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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}
|
||||
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:
|
||||
|
|
|
@ -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$
|
||||
|
||||
public class MyResourceDeltaVisitor implements IResourceDeltaVisitor {
|
||||
// Status codes
|
||||
public static final int EMPTY_PROJECT_BUILD_ERROR = 1;
|
||||
|
||||
// Local variables
|
||||
protected List resourcesToBuild;
|
||||
protected List ruleList;
|
||||
|
||||
|
||||
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());
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Need to report status to the user
|
||||
String statusMsg = CCorePlugin.getFormattedString(REBUILD, currentProject.getName());
|
||||
monitor.subTask(statusMsg);
|
||||
|
||||
// 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 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();
|
||||
|
||||
// Add the items to the makefile
|
||||
populateMakefile(fileHandle, monitor);
|
||||
// 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();
|
||||
}
|
||||
|
||||
// Add the macro definitions
|
||||
addMacros(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);
|
||||
}
|
||||
|
||||
// Add a list of source files
|
||||
addSources(buffer, info);
|
||||
// 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();
|
||||
|
||||
// Add targets
|
||||
addTargets(buffer, info);
|
||||
// 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) {
|
||||
}
|
||||
|
||||
// Save the file
|
||||
Util.save(buffer, fileHandle);
|
||||
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()]);
|
||||
}
|
||||
|
||||
// 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) {
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Reference in a new issue