diff --git a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/core/IConfiguration.java b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/core/IConfiguration.java index 1b38abfc526..1dfa8052558 100644 --- a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/core/IConfiguration.java +++ b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/core/IConfiguration.java @@ -74,6 +74,15 @@ public interface IConfiguration extends IBuildObject { */ public boolean isDirty(); + /** + * Answers whether the receiver has been changed and requires the + * project to be rebuilt. + * + * @return true if the receiver contains a change + * that needs the project to be rebuilt + */ + public boolean needsRebuild(); + /** * @param isDirty */ @@ -119,6 +128,14 @@ public interface IConfiguration extends IBuildObject { public void setOption(IOption option, String[] value) throws BuildException; + /** + * Sets the rebuild state in the receiver. + * + * @param rebuild true will force a rebuild the next time the project builds + * @see org.eclipse.cdt.managedbuilder.core.IManagedBuildInfo#setRebuildState(boolean) + */ + void setRebuildState(boolean rebuild); + /** * Overrides the tool command for a tool defined in the receiver. * diff --git a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/core/IManagedBuildInfo.java b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/core/IManagedBuildInfo.java index 8c2cd4fffbd..71a8be46fba 100644 --- a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/core/IManagedBuildInfo.java +++ b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/core/IManagedBuildInfo.java @@ -208,6 +208,23 @@ public interface IManagedBuildInfo { */ public String getVersion(); + + /** + * Answers whether the receiver has been changed and requires the + * project to be rebuilt. When a project is first created, it is + * assumed that the user will need it to be fully rebuilt. However + * only option and tool command changes will trigger the build + * information for an existing project to require a rebuild. + *

+ * Clients can reset the state to force or clear the rebuild status + * using setRebuildState() + * @see ManagedBuildInfo#setRebuildState(boolean) + * + * @return true if the resource managed by the + * receiver needs to be rebuilt + */ + public boolean needsRebuild(); + /** * Answers true if the build model has been changed by the user. * @@ -248,6 +265,16 @@ public interface IManagedBuildInfo { */ public void setDefaultTarget(ITarget target); + /** + * Sets the rebuild state in the receiver to the value of the argument. + * This is a potentially expensive option, so setting it to true should + * only be done if a project resource or setting has been modified in a + * way that would invalidate the previous build. + * + * @param true will force a rebuild the next time the project builds + */ + public void setRebuildState(boolean rebuild); + /** * Set the currently selected target. This is used while the project * property pages are displayed diff --git a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/core/ITarget.java b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/core/ITarget.java index c62e85fa60b..0462f35c9cb 100644 --- a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/core/ITarget.java +++ b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/core/ITarget.java @@ -217,6 +217,15 @@ public interface ITarget extends IBuildObject { */ public boolean isTestTarget(); + /** + * Answers whether the receiver has been changed and requires the + * project to be rebuilt. + * + * @return true if the receiver contains a change + * that needs the project to be rebuilt + */ + public boolean needsRebuild(); + /** * Removes the configuration with the ID specified in the argument. * @@ -270,10 +279,19 @@ public interface ITarget extends IBuildObject { */ public void setErrorParserIds(String ids); + /** + * Set the rebuild state of the receiver. + * + * @param true will force a rebuild the next time the project builds + * @see org.eclipse.cdt.managedbuilder.core.IManagedBuildInfo#setRebuildState(boolean) + */ + public void setRebuildState(boolean rebuild); + /** * Sets the resource that owns the receiver. * * @param resource */ public void updateOwner(IResource resource); + } diff --git a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/Configuration.java b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/Configuration.java index 4cd8e42b5d9..d512be1269e 100644 --- a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/Configuration.java +++ b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/Configuration.java @@ -37,6 +37,7 @@ import org.w3c.dom.NodeList; public class Configuration extends BuildObject implements IConfiguration { private boolean isDirty = false; private IConfiguration parent; + private boolean rebuildNeeded = false; private boolean resolved = true; private ITarget target; private List toolReferences; @@ -374,6 +375,12 @@ public class Configuration extends BuildObject implements IConfiguration { return isDirty; } + /* (non-Javadoc) + * @see org.eclipse.cdt.managedbuilder.core.IConfiguration#needsRebuild() + */ + public boolean needsRebuild() { + return rebuildNeeded; + } /* (non-Javadoc) * @see org.eclipse.cdt.core.build.managed.IConfiguration#getParent() @@ -549,6 +556,7 @@ public class Configuration extends BuildObject implements IConfiguration { if (option.getBooleanValue() != value) { createOptionReference(option).setValue(value); isDirty = true; + rebuildNeeded = true; } } @@ -567,6 +575,7 @@ public class Configuration extends BuildObject implements IConfiguration { if (oldValue != null && !oldValue.equals(value)) { createOptionReference(option).setValue(value); isDirty = true; + rebuildNeeded = true; } } @@ -599,9 +608,17 @@ public class Configuration extends BuildObject implements IConfiguration { if(!Arrays.equals(value, oldValue)) { createOptionReference(option).setValue(value); isDirty = true; + rebuildNeeded = true; } } + /* (non-Javadoc) + * @see org.eclipse.cdt.managedbuilder.core.IConfiguration#setRebuildState(boolean) + */ + public void setRebuildState(boolean rebuild) { + rebuildNeeded = rebuild; + } + /* (non-Javadoc) * @see org.eclipse.cdt.managedbuilder.core.IConfiguration#setToolCommand(org.eclipse.cdt.managedbuilder.core.ITool, java.lang.String) */ @@ -617,6 +634,7 @@ public class Configuration extends BuildObject implements IConfiguration { // Set the ref's command if (ref != null) { isDirty = ref.setToolCommand(command); + rebuildNeeded = isDirty; } } } diff --git a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/GeneratedMakefileBuilder.java b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/GeneratedMakefileBuilder.java index ec850616f3d..daa8d9300e2 100644 --- a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/GeneratedMakefileBuilder.java +++ b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/GeneratedMakefileBuilder.java @@ -40,7 +40,6 @@ import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.resources.IResourceDeltaVisitor; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.IWorkspaceRoot; -import org.eclipse.core.resources.IncrementalProjectBuilder; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IExtension; @@ -53,6 +52,12 @@ import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.SubProgressMonitor; +/** + * This is the incremental builder associated with a managed build project. It dynamically + * decides the makefile generator it wants to use for a specific target. + * + * @since 1.2 + */ public class GeneratedMakefileBuilder extends ACBuilder { // String constants private static final String MESSAGE = "ManagedMakeBuilder.message"; //$NON-NLS-1$ @@ -64,6 +69,7 @@ public class GeneratedMakefileBuilder extends ACBuilder { private static final String REFRESH = MESSAGE + ".updating"; //$NON-NLS-1$ private static final String MARKERS = MESSAGE + ".creating.markers"; //$NON-NLS-1$ private static final String CONSOLE_HEADER = MESSAGE + ".console.header"; //$NON-NLS-1$ + private static final String TYPE_CLEAN = "ManagedMakeBuilder.type.clean"; //$NON-NLS-1$ private static final String TYPE_FULL = "ManagedMakeBuilder.type.full"; //$NON-NLS-1$ private static final String TYPE_INC = "ManagedMakeBuider.type.incremental"; //$NON-NLS-1$ @@ -73,13 +79,66 @@ public class GeneratedMakefileBuilder extends ACBuilder { protected IManagedBuilderMakefileGenerator generator; public class ResourceDeltaVisitor implements IResourceDeltaVisitor { - private boolean buildNeeded = false; + private boolean buildNeeded = true; + private IManagedBuildInfo buildInfo; + private List reservedNames; + + /** + * + */ + public ResourceDeltaVisitor(IManagedBuildInfo info) { + buildInfo = info; + reservedNames = Arrays.asList(new String[]{".cdtbuild", ".cdtproject", ".project"}); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + /** + * @param changedResource + * @return + */ + private boolean isGeneratedResource(IResource resource) { + // Is this a generated directory ... + IPath path = resource.getProjectRelativePath(); + String[] configNames = buildInfo.getConfigurationNames(); + for (int i = 0; i < configNames.length; i++) { + String name = configNames[i]; + IPath root = new Path(name); + // It is if it is a root of the resource pathname + if (root.isPrefixOf(path)) return true; + } + return false; + } + + /** + * @param resource + * @return + */ + private boolean isProjectFile(IResource resource) { + return reservedNames.contains(resource.getName()); + } + public boolean visit(IResourceDelta delta) throws CoreException { IResource resource = delta.getResource(); // If the project has changed, then a build is needed and we can stop if (resource != null && resource.getProject() == getProject()) { - buildNeeded = true; + IResourceDelta[] kids = delta.getAffectedChildren(); + for (int index = kids.length - 1; index >= 0; --index) { + IResource changedResource = kids[index].getResource(); + String ext = changedResource.getFileExtension(); + // There are some things we don't care about + if (resource.isDerived()) { + buildNeeded = false; + } else if (isGeneratedResource(changedResource)) { + buildNeeded = false; + } else if (buildInfo.buildsFileType(ext) || buildInfo.isHeaderFile(ext)) { + // We do care and there is no point checking anythings else + buildNeeded = true; + break; + } else if (isProjectFile(changedResource)) { + buildNeeded = false; + } else { + buildNeeded = true; + } + } return false; } @@ -92,7 +151,8 @@ public class GeneratedMakefileBuilder extends ACBuilder { } /** - * + * Zero-argument constructor needed to fulfill the contract of an + * incremental builder. */ public GeneratedMakefileBuilder() { } @@ -101,23 +161,28 @@ public class GeneratedMakefileBuilder extends ACBuilder { * @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 { - IProject[] deps = getProject().getReferencedProjects(); + // We should always tell the build system what projects we reference + IProject[] refdProjects = getProject().getReferencedProjects(); // Get the build information IManagedBuildInfo info = ManagedBuildManager.getBuildInfo(getProject()); if (info == null) { - return deps; + return refdProjects; } - if (kind == IncrementalProjectBuilder.FULL_BUILD || info.isDirty()) { + // So let's figure out why we got called + if (kind == CLEAN_BUILD) { + cleanBuild(monitor, info); + } + else if (kind == FULL_BUILD || info.needsRebuild()) { fullBuild(monitor, info); } - else if (kind == IncrementalProjectBuilder.AUTO_BUILD && info.isDirty()) { + else if (kind == AUTO_BUILD && info.needsRebuild()) { fullBuild(monitor, info); } else { // Create a delta visitor to make sure we should be rebuilding - ResourceDeltaVisitor visitor = new ResourceDeltaVisitor(); + ResourceDeltaVisitor visitor = new ResourceDeltaVisitor(info); IResourceDelta delta = getDelta(getProject()); if (delta == null) { fullBuild(monitor, info); @@ -131,20 +196,33 @@ public class GeneratedMakefileBuilder extends ACBuilder { } // Scrub the build info of all the projects participating in the build - info.setDirty(false); - for (int i = 0; i < deps.length; i++) { - IProject project = deps[i]; + info.setRebuildState(false); + for (int i = 0; i < refdProjects.length; i++) { + IProject project = refdProjects[i]; IManagedBuildInfo depInfo = ManagedBuildManager.getBuildInfo(project); // May not be a managed project if (depInfo != null) { - depInfo.setDirty(false); + depInfo.setRebuildState(false); } } // Ask build mechanism to compute deltas for project dependencies next time - return deps; + return refdProjects; } + /** + * @param monitor + * @param info + */ + protected void cleanBuild(IProgressMonitor monitor, IManagedBuildInfo info) { + // Make sure that there is a top level directory and a set of makefiles + + + // invoke make with the clean argument + + } + + /** * Check whether the build has been canceled. Cancellation requests * propagated to the caller by throwing OperationCanceledException. @@ -203,7 +281,7 @@ public class GeneratedMakefileBuilder extends ACBuilder { monitor.subTask(statusMsg); IPath topBuildDir = generator.getBuildWorkingDir(); if (topBuildDir != null) { - invokeMake(true, topBuildDir, info, monitor); + invokeMake(FULL_BUILD, topBuildDir, info, monitor); } else { statusMsg = ManagedMakeMessages.getFormattedString(NOTHING_BUILT, getProject().getName()); //$NON-NLS-1$ monitor.subTask(statusMsg); @@ -263,12 +341,18 @@ public class GeneratedMakefileBuilder extends ACBuilder { * @param fullBuild * @return */ - protected String[] getMakeTargets(boolean fullBuild) { + protected String[] getMakeTargets(int buildType) { List args = new ArrayList(); - if (fullBuild) { - args.add("clean"); //$NON-NLS-1$ + switch (buildType) { + case CLEAN_BUILD: + args.add("clean"); //$NON-NLS-1$ + break; + case FULL_BUILD: + args.add("clean"); //$NON-NLS-1$ + case INCREMENTAL_BUILD: + args.add("all"); //$NON-NLS-1$ + break; } - args.add("all"); //$NON-NLS-1$ return (String[])args.toArray(new String[args.size()]); } @@ -349,11 +433,11 @@ public class GeneratedMakefileBuilder extends ACBuilder { monitor.worked(1); // Run the build - statusMsg = ManagedMakeMessages.getFormattedString("ManagedMakeBuilder.message.starting", getProject().getName()); + statusMsg = ManagedMakeMessages.getFormattedString("ManagedMakeBuilder.message.starting", getProject().getName()); //$NON-NLS-1$ monitor.subTask(statusMsg); IPath buildDir = new Path(info.getConfigurationName()); if (buildDir != null) { - invokeMake(false, buildDir, info, monitor); + invokeMake(INCREMENTAL_BUILD, buildDir, info, monitor); } else { statusMsg = ManagedMakeMessages.getFormattedString(NOTHING_BUILT, getProject().getName()); //$NON-NLS-1$ monitor.subTask(statusMsg); @@ -383,7 +467,7 @@ public class GeneratedMakefileBuilder extends ACBuilder { * @param info * @param monitor */ - protected void invokeMake(boolean fullBuild, IPath buildDir, IManagedBuildInfo info, IProgressMonitor monitor) { + protected void invokeMake(int buildType, IPath buildDir, IManagedBuildInfo info, IProgressMonitor monitor) { // Get the project and make sure there's a monitor to cancel the build IProject currentProject = getProject(); if (monitor == null) { @@ -420,9 +504,18 @@ public class GeneratedMakefileBuilder extends ACBuilder { console.start(currentProject); ConsoleOutputStream consoleOutStream = console.getOutputStream(); String[] consoleHeader = new String[3]; - consoleHeader[0] = fullBuild ? - ManagedMakeMessages.getResourceString(TYPE_FULL) : - ManagedMakeMessages.getResourceString(TYPE_INC); + switch (buildType) { + case FULL_BUILD: + consoleHeader[0] = ManagedMakeMessages.getResourceString(TYPE_FULL); + break; + case INCREMENTAL_BUILD: + consoleHeader[0] = ManagedMakeMessages.getResourceString(TYPE_INC); + break; + case CLEAN_BUILD: + consoleHeader[0] = ManagedMakeMessages.getResourceString(TYPE_CLEAN); + break; + } + consoleHeader[1] = info.getConfigurationName(); consoleHeader[2] = currentProject.getName(); buf.append(System.getProperty("line.separator", "\n")); //$NON-NLS-1$ //$NON-NLS-2$ @@ -449,7 +542,7 @@ public class GeneratedMakefileBuilder extends ACBuilder { makeArgs.add(args[i]); } } - makeArgs.addAll(Arrays.asList(getMakeTargets(fullBuild))); + makeArgs.addAll(Arrays.asList(getMakeTargets(buildType))); String[] makeTargets = (String[]) makeArgs.toArray(new String[makeArgs.size()]); // Get a launcher for the make command diff --git a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/ManagedBuildInfo.java b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/ManagedBuildInfo.java index 0d4fc9b0311..07b57eddc2b 100644 --- a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/ManagedBuildInfo.java +++ b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/ManagedBuildInfo.java @@ -47,6 +47,7 @@ import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.QualifiedName; +import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -69,6 +70,7 @@ public class ManagedBuildInfo implements IManagedBuildInfo, IScannerInfo { private String defaultTargetId; private ITarget selectedTarget; private boolean isDirty; + private boolean rebuildNeeded; private IResource owner; private Map targetMap; private List targetList; @@ -100,15 +102,16 @@ public class ManagedBuildInfo implements IManagedBuildInfo, IScannerInfo { if (entries.length > 0 && entries[0].equals(containerEntry)) { } else { - Job initJob = new Job("Initializing path container") { + Job initJob = new Job("Initializing path container") { //$NON-NLS-1$ protected IStatus run(IProgressMonitor monitor) { try { // Set the raw path entries cModelElement.setRawPathEntries(new IPathEntry[]{containerEntry}, new NullProgressMonitor()); } catch (CModelException e) { - ManagedBuilderCorePlugin.log(e); + return new Status(IStatus.ERROR, ManagedBuilderCorePlugin.getUniqueIdentifier(), -1, e.getLocalizedMessage(), e); } - return null; + return new Status(IStatus.OK, ManagedBuilderCorePlugin.getUniqueIdentifier(), IStatus.OK, null, null); + } }; @@ -118,7 +121,9 @@ public class ManagedBuildInfo implements IManagedBuildInfo, IScannerInfo { ManagedBuilderCorePlugin.log(e); } + // Does not need a save but should be rebuilt isDirty = false; + rebuildNeeded = true; // The id of the default target from the project persistent settings store IProject project = (IProject)owner; @@ -161,6 +166,9 @@ public class ManagedBuildInfo implements IManagedBuildInfo, IScannerInfo { } initializePathEntries(); + + // Switch the rebuild off since this is an existing project + rebuildNeeded = false; } /* (non-Javadoc) @@ -191,6 +199,17 @@ public class ManagedBuildInfo implements IManagedBuildInfo, IScannerInfo { return false; } + /* (non-Javadoc) + * @see org.eclipse.cdt.managedbuilder.core.IManagedBuildInfo#setRebuildState(boolean) + */ + public void setRebuildState(boolean rebuild) { + Iterator iter = getTargets().listIterator(); + while (iter.hasNext()) { + ((ITarget)iter.next()).setRebuildState(rebuild); + } + rebuildNeeded = rebuild; + } + /* (non-Javadoc) * @see org.eclipse.cdt.managedbuilder.core.IManagedBuildInfo#getBuildArtifactExtension() */ @@ -775,6 +794,21 @@ public class ManagedBuildInfo implements IManagedBuildInfo, IScannerInfo { return false; } + /* (non-Javadoc) + * @see org.eclipse.cdt.managedbuilder.core.IManagedBuildInfo#needsRebuild() + */ + public boolean needsRebuild() { + if (rebuildNeeded) return true; + + Iterator iter = getTargets().listIterator(); + while (iter.hasNext()) { + if (((ITarget)iter.next()).needsRebuild()) { + return true; + } + } + + return false; + } /* (non-Javadoc) * @@ -943,6 +977,14 @@ public class ManagedBuildInfo implements IManagedBuildInfo, IScannerInfo { } } + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + public String toString() { + // Just print out the name of the project + return "Managed build information for " + owner.getName(); //$NON-NLS-1$ + } + /** * Sets the owner of the receiver to be the IResource specified * in the argument. diff --git a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/PluginResources.properties b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/PluginResources.properties index 376545aa42c..0f489178ede 100644 --- a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/PluginResources.properties +++ b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/PluginResources.properties @@ -24,6 +24,7 @@ ManagedMakeBuilder.message.no.build = Nothing to build for {0} ManagedMakeBuilder.message.error = Build error ManagedMakeBuilder.message.error.refresh = Error refreshing project ManagedMakeBuilder.message.finished = Build complete for project {0} +ManagedMakeBuilder.type.clean = Clean-only build ManagedMakeBuilder.type.full = Full rebuild ManagedMakeBuider.type.incremental = Incremental build diff --git a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/Target.java b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/Target.java index 90ebf6fe19e..c9d5d99c44a 100644 --- a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/Target.java +++ b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/Target.java @@ -853,6 +853,17 @@ public class Target extends BuildObject implements ITarget { getLocalToolReferences().add(toolRef); } + public boolean needsRebuild(){ + // Iterate over the configurations and ask them if they need saving + Iterator iter = getConfigurationList().listIterator(); + while (iter.hasNext()) { + if (((IConfiguration)iter.next()).needsRebuild()) { + return true; + } + } + return false; + } + /* (non-Javadoc) * @see org.eclipse.cdt.managedbuilder.core.ITarget#setArtifactExtension(java.lang.String) */ @@ -918,6 +929,17 @@ public class Target extends BuildObject implements ITarget { isDirty = true; } } + + + /* (non-Javadoc) + * @see org.eclipse.cdt.managedbuilder.core.ITarget#setRebuildState(boolean) + */ + public void setRebuildState(boolean rebuild) { + Iterator iter = getConfigurationList().listIterator(); + while (iter.hasNext()) { + ((IConfiguration)iter.next()).setRebuildState(rebuild); + } + } /* (non-Javadoc) * @see org.eclipse.cdt.managedbuilder.core.ITarget#updateOwner(org.eclipse.core.resources.IResource) @@ -929,5 +951,4 @@ public class Target extends BuildObject implements ITarget { } } - } diff --git a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/makegen/gnu/GnuMakefileGenerator.java b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/makegen/gnu/GnuMakefileGenerator.java index b807627b518..97e54d296ef 100644 --- a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/makegen/gnu/GnuMakefileGenerator.java +++ b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/makegen/gnu/GnuMakefileGenerator.java @@ -59,111 +59,6 @@ import org.eclipse.core.runtime.SubProgressMonitor; * @since 1.2 */ public class GnuMakefileGenerator implements IManagedBuilderMakefileGenerator { - - // this is used to draw a "comment line" - private static final int COLS_PER_LINE = 80; - - // String constants for messages - private static final String MESSAGE = "ManagedMakeBuilder.message"; //$NON-NLS-1$ - protected static final String MESSAGE_FINISH_BUILD = ManagedMakeMessages.getResourceString("MakefileGenerator.message.finish.build"); //$NON-NLS-1$ - protected static final String MESSAGE_START_BUILD = ManagedMakeMessages.getResourceString("MakefileGenerator.message.start.build"); //$NON-NLS-1$ - protected static final String MESSAGE_FINISH_FILE = ManagedMakeMessages.getResourceString("MakefileGenerator.message.finish.file"); //$NON-NLS-1$ - protected static final String MESSAGE_START_FILE = ManagedMakeMessages.getResourceString("MakefileGenerator.message.start.file"); //$NON-NLS-1$ - private static final String BUILD_ERROR = MESSAGE + ".error"; //$NON-NLS-1$ - private static final String COMMENT = "MakefileGenerator.comment"; //$NON-NLS-1$ - private static final String HEADER = COMMENT + ".header"; //$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 AT = "@"; //$NON-NLS-1$ - protected static final String COLON = ":"; //$NON-NLS-1$ - protected static final String DEP_EXT = "d"; //$NON-NLS-1$ - protected static final String DEPFILE_NAME = "subdir.dep"; //$NON-NLS-1$ - protected static final String DOT = "."; //$NON-NLS-1$ - protected static final String ECHO = "echo"; //$NON-NLS-1$ - protected static final String IN_MACRO = "$<"; //$NON-NLS-1$ - protected static final String NEWLINE = System.getProperty("line.separator"); //$NON-NLS-1$ - protected static final String LINEBREAK = "\\" + NEWLINE; //$NON-NLS-1$ - protected static final String LOGICAL_AND = "&&"; //$NON-NLS-1$ - protected static final String MAKEFILE_NAME = "makefile"; //$NON-NLS-1$ - protected static final String MODFILE_NAME = "subdir.mk"; //$NON-NLS-1$ - protected static final String OUT_MACRO = "$@"; //$NON-NLS-1$ - protected static final String ROOT = "$(ROOT)"; //$NON-NLS-1$ - protected static final String SRCSFILE_NAME = "sources.mk"; //$NON-NLS-1$ protected static final String MAKEFILE_TARGETS = "makefile.targets"; //$NON-NLS-1$ - protected static final String SEPARATOR = "/"; //$NON-NLS-1$ - protected static final String SINGLE_QUOTE = "'"; //$NON-NLS-1$ - protected static final String TAB = "\t"; //$NON-NLS-1$ - protected static final String WHITESPACE = " "; //$NON-NLS-1$ - protected static final String WILDCARD = "%"; //$NON-NLS-1$ - protected static final String COMMENT_SYMBOL = "#"; //$NON-NLS-1$ - protected static final String MAKEFILE_INIT = "makefile.init"; //$NON-NLS-1$ - protected static final String MAKEFILE_DEFS = "makefile.defs"; //$NON-NLS-1$ - protected static final String MAKEFILE_TARGETS = "makefile.targets"; //$NON-NLS-1$ protected static final String MAKEFILE_TARGETS = "makefile.targets"; //$NON-NLS-1$ - - // Local variables needed by generator - private String buildTargetName; - private Vector deletedFileList; - private Vector dependencyMakefiles; - private String extension; - protected IManagedBuildInfo info; - protected Vector modifiedList; - protected IProgressMonitor monitor; - protected IProject project; - protected Vector ruleList; - protected Vector subdirList; - 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 GnuMakefileGenerator generator; - private IManagedBuildInfo info; - - /** - * Constructs a new resource proxy visitor to quickly visit project - * resources. - */ - public ResourceProxyVisitor(GnuMakefileGenerator 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)) { - if (!generator.isGeneratedResource(resource)) { - generator.appendBuildSubdirectory(resource); - } - } - return false; - } - - // Recurse into subdirectories - return true; - } - - } /** * This class walks the delta supplied by the build system to determine @@ -243,85 +138,112 @@ public class GnuMakefileGenerator implements IManagedBuilderMakefileGenerator { } } - public GnuMakefileGenerator() { - super(); - } + /** - * @param project - * @param info - * @param monitor + * This class is used to recursively walk the project and determine which + * modules contribute buildable source files. */ - public void initialize(IProject project, IManagedBuildInfo info, IProgressMonitor monitor) { - // 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; - // Get the name of the build target - buildTargetName = info.getBuildArtifactName(); - // Get its extension - extension = info.getBuildArtifactExtension(); - if (extension == null) { - extension = new String(); - } - } + protected class ResourceProxyVisitor implements IResourceProxyVisitor { + private GnuMakefileGenerator generator; + private IManagedBuildInfo info; - /* (non-Javadoc) - * Answers the argument with all whitespaces replaced with an escape sequence. - * - * @param path - */ - protected String escapeWhitespaces(String path) { - // Escape the spaces in the path/filename if it has any - String[] segments = path.split("\\s"); //$NON-NLS-1$ - if (segments.length > 1) { - StringBuffer escapedPath = new StringBuffer(); - for (int index = 0; index < segments.length; ++index) { - escapedPath.append(segments[index]); - if (index + 1 < segments.length) { - escapedPath.append("\\ "); //$NON-NLS-1$ - } + /** + * Constructs a new resource proxy visitor to quickly visit project + * resources. + */ + public ResourceProxyVisitor(GnuMakefileGenerator 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; } - return escapedPath.toString().trim(); - } else { - return path; + + // 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)) { + if (!generator.isGeneratedResource(resource)) { + generator.appendBuildSubdirectory(resource); + } + } + return false; + } + + // Recurse into subdirectories + return true; } + } - /* (non-Javadoc) - * Answers a StringBuffer containing the comment(s) - * for the top-level makefile. - */ - protected StringBuffer addTopHeader() { - return addDefaultHeader(); - } + // String constants for makefile contents and messages + protected static final String AT = "@"; //$NON-NLS-1$ + private static final String COMMENT = "MakefileGenerator.comment"; //$NON-NLS-1$ + private static final String AUTO_DEP = COMMENT + ".autodeps"; //$NON-NLS-1$ + private static final String MESSAGE = "ManagedMakeBuilder.message"; //$NON-NLS-1$ + private static final String BUILD_ERROR = MESSAGE + ".error"; //$NON-NLS-1$ + protected static final String COLON = ":"; //$NON-NLS-1$ - /* (non-Javadoc) - * Answers a StringBuffer containing the comment(s) - * for a fragment makefile. - */ - protected StringBuffer addFragmentMakefileHeader() { - return addDefaultHeader(); - } + private static final int COLS_PER_LINE = 80; + protected static final String COMMENT_SYMBOL = "#"; //$NON-NLS-1$ + protected static final String DEP_EXT = "d"; //$NON-NLS-1$ + private static final String DEP_INCL = COMMENT + ".module.dep.includes"; //$NON-NLS-1$ + protected static final String DEPFILE_NAME = "subdir.dep"; //$NON-NLS-1$ + protected static final String DOT = "."; //$NON-NLS-1$ + protected static final String ECHO = "echo"; //$NON-NLS-1$ + private static final String HEADER = COMMENT + ".header"; //$NON-NLS-1$ + protected static final String IN_MACRO = "$<"; //$NON-NLS-1$ + protected static final String NEWLINE = System.getProperty("line.separator"); //$NON-NLS-1$ + protected static final String LINEBREAK = "\\" + NEWLINE; //$NON-NLS-1$ + protected static final String LOGICAL_AND = "&&"; //$NON-NLS-1$ + protected static final String MAKEFILE_DEFS = "makefile.defs"; //$NON-NLS-1$ + protected static final String MAKEFILE_INIT = "makefile.init"; //$NON-NLS-1$ + protected static final String MAKEFILE_NAME = "makefile"; //$NON-NLS-1$ + protected static final String MAKEFILE_TARGETS = "makefile.targets"; //$NON-NLS-1$ + + protected static final String MESSAGE_FINISH_BUILD = ManagedMakeMessages.getResourceString("MakefileGenerator.message.finish.build"); //$NON-NLS-1$ + protected static final String MESSAGE_FINISH_FILE = ManagedMakeMessages.getResourceString("MakefileGenerator.message.finish.file"); //$NON-NLS-1$ + protected static final String MESSAGE_START_BUILD = ManagedMakeMessages.getResourceString("MakefileGenerator.message.start.build"); //$NON-NLS-1$ + protected static final String MESSAGE_START_FILE = ManagedMakeMessages.getResourceString("MakefileGenerator.message.start.file"); //$NON-NLS-1$ + private static final String MOD_INCL = COMMENT + ".module.make.includes"; //$NON-NLS-1$ + private static final String MOD_LIST = COMMENT + ".module.list"; //$NON-NLS-1$ + private static final String MOD_RULES = COMMENT + ".build.rule"; //$NON-NLS-1$ + protected static final String MODFILE_NAME = "subdir.mk"; //$NON-NLS-1$ + protected static final String OBJECTS_MAKFILE = "objects.mk"; //$NON-NLS-1$ + protected static final String OUT_MACRO = "$@"; //$NON-NLS-1$ + protected static final String ROOT = "$(ROOT)"; //$NON-NLS-1$ + protected static final String SEPARATOR = "/"; //$NON-NLS-1$ + protected static final String SINGLE_QUOTE = "'"; //$NON-NLS-1$ + private static final String SRC_LISTS = COMMENT + ".source.list"; //$NON-NLS-1$ + protected static final String SRCSFILE_NAME = "sources.mk"; //$NON-NLS-1$ + protected static final String TAB = "\t"; //$NON-NLS-1$ + protected static final String WHITESPACE = " "; //$NON-NLS-1$ + protected static final String WILDCARD = "%"; //$NON-NLS-1$ + + // Local variables needed by generator + private String buildTargetName; + private Vector deletedFileList; + private Vector dependencyMakefiles; + private String extension; + protected IManagedBuildInfo info; + protected Vector modifiedList; + protected IProgressMonitor monitor; + protected IProject project; + protected Vector ruleList; + protected Vector subdirList; + protected IPath topBuildDir; - /* (non-Javadoc) - * Answers a StringBuffer containing the comment(s) - * for a dependency makefile. - */ - protected StringBuffer addFragmentDependenciesHeader() { - return addDefaultHeader(); - } - - /* (non-Javadoc) - * Put COLS_PER_LINE comment charaters in the argument. - */ - protected void outputCommentLine(StringBuffer buffer) { - for (int i = 0; i < COLS_PER_LINE; i++) { - buffer.append(COMMENT_SYMBOL); - } - buffer.append(NEWLINE); + public GnuMakefileGenerator() { + super(); } /* (non-Javadoc) @@ -338,6 +260,22 @@ public class GnuMakefileGenerator implements IManagedBuilderMakefileGenerator { buffer.append(NEWLINE); return buffer; } + + /* (non-Javadoc) + * Answers a StringBuffer containing the comment(s) + * for a dependency makefile. + */ + protected StringBuffer addFragmentDependenciesHeader() { + return addDefaultHeader(); + } + + /* (non-Javadoc) + * Answers a StringBuffer containing the comment(s) + * for a fragment makefile. + */ + protected StringBuffer addFragmentMakefileHeader() { + return addDefaultHeader(); + } /* (non-javadoc) */ @@ -371,30 +309,65 @@ public class GnuMakefileGenerator implements IManagedBuilderMakefileGenerator { } /* (non-javadoc) - * @return + * Create the pattern rule in the format: + * /.: $(ROOT)//. + * @echo 'Building file: $<' + * @ $@ $< && \ + * echo -n '/.d /' >> /.d && \ + * -P -MM -MG $< >> /.d + * @echo 'Finished building: $<' + * @echo + * + * Note that the macros 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/foo.o: $(ROOT)/source1/foo.cpp + * @echo 'Building file: $<' + * @ g++ -g -O2 -c -I/cygdrive/c/eclipse/workspace/Project/headers -o $@ $< && \ + * echo -n 'source1/foo.d source1/' >> source1/foo.d && \ + * g++ -P -MM -MG -g -O2 -c -I/cygdrive/c/eclipse/workspace/Project/headers $< >> source1/foo.d + * @echo 'Finished building: $<' + * @echo + * + * @param relativePath + * @param buffer + * @param resource */ - private StringBuffer addSubdirectories() { - StringBuffer buffer = new StringBuffer(); - // Add the comment - buffer.append(COMMENT_SYMBOL + WHITESPACE + ManagedMakeMessages.getResourceString(MOD_LIST) + NEWLINE); + private void addRule(String relativePath, StringBuffer buffer, IResource resource) { + String buildFlags = null; + String resourceName = getFileName(resource); + String inputExtension = resource.getFileExtension(); + String cmd = info.getToolForSource(inputExtension); + String outputExtension = info.getOutputExtension(inputExtension); + String outflag = null; + String outputPrefix = null; - buffer.append("SUBDIRS := " + LINEBREAK); //$NON-NLS-1$ + // Add the rule and command to the makefile + String buildRule = relativePath + resourceName + DOT + outputExtension + COLON + WHITESPACE + ROOT + SEPARATOR + relativePath + resourceName + DOT + inputExtension; + buffer.append(buildRule + NEWLINE); + buffer.append(TAB + AT + ECHO + WHITESPACE + SINGLE_QUOTE + MESSAGE_START_FILE + WHITESPACE + IN_MACRO + SINGLE_QUOTE + NEWLINE); + buildFlags = info.getFlagsForSource(inputExtension); + outflag = info.getOutputFlag(outputExtension); + outputPrefix = info.getOutputPrefix(outputExtension); - // Get all the module names - Iterator iter = getSubdirList().listIterator(); - while (iter.hasNext()) { - IContainer container = (IContainer) iter.next(); - // Check the special case where the module is the project root - if (container.getFullPath() == project.getFullPath()) { - buffer.append(DOT + WHITESPACE + LINEBREAK); - } else { - IPath path = container.getProjectRelativePath(); - buffer.append(path.toString() + WHITESPACE + LINEBREAK); - } + // The command to build + buffer.append(TAB + AT + ECHO + WHITESPACE + cmd + WHITESPACE + buildFlags + WHITESPACE + outflag + WHITESPACE + outputPrefix + OUT_MACRO + WHITESPACE + IN_MACRO + NEWLINE); + buffer.append(TAB + AT + cmd + WHITESPACE + buildFlags + WHITESPACE + outflag + WHITESPACE + outputPrefix + OUT_MACRO + WHITESPACE + IN_MACRO); + + // TODO determine if there are any deps to calculate + if (true) { + buffer.append(WHITESPACE + LOGICAL_AND + WHITESPACE + LINEBREAK); + // TODO get the dep rule out of the tool + String depRule = relativePath + resourceName + DOT + DEP_EXT; + getDependencyMakefiles().add(depRule); + buffer.append(TAB + ECHO + WHITESPACE + "-n" + WHITESPACE + SINGLE_QUOTE + depRule + WHITESPACE + relativePath + SINGLE_QUOTE + WHITESPACE + ">" + WHITESPACE + depRule + WHITESPACE + LOGICAL_AND + WHITESPACE + LINEBREAK); //$NON-NLS-1$ //$NON-NLS-2$ + buffer.append(TAB + cmd + WHITESPACE + "-MM -MG -P -w" + WHITESPACE + buildFlags + WHITESPACE + IN_MACRO + WHITESPACE + ">>" + WHITESPACE + depRule); //$NON-NLS-1$ //$NON-NLS-2$ + } - + + // Say goodbye to the nice user buffer.append(NEWLINE); - return buffer; + buffer.append(TAB + AT + ECHO + WHITESPACE + SINGLE_QUOTE + MESSAGE_FINISH_FILE + WHITESPACE + IN_MACRO + SINGLE_QUOTE + NEWLINE + TAB + AT + ECHO + NEWLINE + NEWLINE); } @@ -440,8 +413,8 @@ public class GnuMakefileGenerator implements IManagedBuilderMakefileGenerator { // there is no entry in the map, so create a buffer for this extension StringBuffer tempBuffer = new StringBuffer(); - tempBuffer.append(macroName + WHITESPACE + "+=" + WHITESPACE + LINEBREAK); //$NON-NLS$-1 - tempBuffer.append("${addprefix $(ROOT)/" + relativePath + "," + LINEBREAK); //$NON-NLS$-1 //$NON-NLS$-2 + tempBuffer.append(macroName + WHITESPACE + "+=" + WHITESPACE + LINEBREAK); //$NON-NLS-1$ + tempBuffer.append("${addprefix $(ROOT)/" + relativePath + "," + LINEBREAK); //$NON-NLS-1$ //$NON-NLS-2$ // have to store the buffer in String form as StringBuffer is not a sublcass of Object extensionToRuleStringMap.put(extensionName, tempBuffer.toString()); @@ -494,6 +467,33 @@ public class GnuMakefileGenerator implements IManagedBuilderMakefileGenerator { return buffer.append(ruleBuffer + NEWLINE); } + /* (non-javadoc) + * @return + */ + private StringBuffer addSubdirectories() { + StringBuffer buffer = new StringBuffer(); + // Add the comment + buffer.append(COMMENT_SYMBOL + WHITESPACE + ManagedMakeMessages.getResourceString(MOD_LIST) + NEWLINE); + + buffer.append("SUBDIRS := " + LINEBREAK); //$NON-NLS-1$ + + // Get all the module names + Iterator iter = getSubdirList().listIterator(); + while (iter.hasNext()) { + IContainer container = (IContainer) iter.next(); + // Check the special case where the module is the project root + if (container.getFullPath() == project.getFullPath()) { + buffer.append(DOT + WHITESPACE + LINEBREAK); + } else { + IPath path = container.getProjectRelativePath(); + buffer.append(path.toString() + WHITESPACE + LINEBREAK); + } + } + + buffer.append(NEWLINE); + return buffer; + } + /* (non-javadoc) * Answers a StrinBuffer containing all of the required targets to * properly build the project. @@ -511,20 +511,17 @@ public class GnuMakefileGenerator implements IManagedBuilderMakefileGenerator { String targets = rebuild ? "clean all" : "all"; //$NON-NLS-1$ //$NON-NLS-2$ // Get all the projects the build target depends on - IProject[] deps = null; + IProject[] refdProjects = null; try { - deps = project.getReferencedProjects(); + refdProjects = project.getReferencedProjects(); } catch (CoreException e) { // There are 2 exceptions; the project does not exist or it is not open // and neither conditions apply if we are building for it .... } // Write out the all target first in case someone just runs make - // all: targ_ [deps] + // all: targ_ String defaultTarget = "all:"; //$NON-NLS-1$ - if (deps.length > 0) { - defaultTarget += WHITESPACE + "deps"; //$NON-NLS-1$ - } buffer.append(defaultTarget + WHITESPACE + outputPrefix + buildTargetName); if (extension.length() > 0) { buffer.append(DOT + extension); @@ -538,11 +535,11 @@ public class GnuMakefileGenerator implements IManagedBuilderMakefileGenerator { * ; $(MAKE) [clean all | all]> */ Vector managedProjectOutputs = new Vector(); - if (deps.length > 0) { - buffer.append("deps:" + NEWLINE); //$NON-NLS-1$ - if (deps != null) { - for (int i = 0; i < deps.length; i++) { - IProject dep = deps[i]; + if (refdProjects.length > 0) { + buffer.append("dependents:" + NEWLINE); //$NON-NLS-1$ + if (refdProjects != null) { + for (int i = 0; i < refdProjects.length; i++) { + IProject dep = refdProjects[i]; String buildDir = dep.getLocation().toString(); String depTargets = targets; if (ManagedBuildManager.manages(dep)) { @@ -554,7 +551,7 @@ public class GnuMakefileGenerator implements IManagedBuilderMakefileGenerator { String depTarget = depInfo.getBuildArtifactName(); String depExt = depInfo.getBuildArtifactExtension(); String depPrefix = depInfo.getOutputPrefix(depExt); - if (depInfo.isDirty()) { + if (depInfo.needsRebuild()) { depTargets = "clean all"; //$NON-NLS-1$ } String dependency = buildDir + SEPARATOR + depPrefix + depTarget; @@ -571,7 +568,7 @@ public class GnuMakefileGenerator implements IManagedBuilderMakefileGenerator { /* * Write out the target rule as: - * targ_.: $(OBJS) [ ... ] + * targ_.: $(OBJS) * @echo 'Building target: $@' * $(BUILD_TOOL) $(FLAGS) $(OUTPUT_FLAG) $@ $(OBJS) $(USER_OBJS) $(LIB_DEPS) * @echo 'Finished building: $@' @@ -582,10 +579,6 @@ public class GnuMakefileGenerator implements IManagedBuilderMakefileGenerator { buffer.append(DOT + extension); } buffer.append(COLON + WHITESPACE + "$(OBJS)"); //$NON-NLS-1$ - Iterator iter = managedProjectOutputs.listIterator(); - while (iter.hasNext()) { - buffer.append(WHITESPACE + (String)iter.next()); - } buffer.append(NEWLINE); buffer.append(TAB + AT + ECHO + WHITESPACE + SINGLE_QUOTE + MESSAGE_START_BUILD + WHITESPACE + OUT_MACRO + SINGLE_QUOTE + NEWLINE); buffer.append(TAB + cmd + WHITESPACE + flags + WHITESPACE + outflag + WHITESPACE + OUT_MACRO + WHITESPACE + "$(OBJS) $(USER_OBJS) $(LIBS)" + NEWLINE); //$NON-NLS-1$ @@ -593,13 +586,13 @@ public class GnuMakefileGenerator implements IManagedBuilderMakefileGenerator { // Always add a clean target buffer.append("clean:" + NEWLINE); //$NON-NLS-1$ - buffer.append(TAB + "-$(RM)" + WHITESPACE + "$(OBJS)" + WHITESPACE + "$(OBJS:%.o=%.d)" + WHITESPACE + outputPrefix + buildTargetName); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS$-3 + buffer.append(TAB + "-$(RM)" + WHITESPACE + "$(OBJS)" + WHITESPACE + "$(OBJS:%.o=%.d)" + WHITESPACE + outputPrefix + buildTargetName); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ if (extension.length() > 0) { buffer.append(DOT + extension); } buffer.append(NEWLINE + NEWLINE); - buffer.append(".PHONY: all clean deps" + NEWLINE + NEWLINE); //$NON-NLS-1$ + buffer.append(".PHONY: all clean dependents" + NEWLINE + NEWLINE); //$NON-NLS-1$ // Include makefile.targets supplemental makefile buffer.append("-include $(ROOT)" + SEPARATOR + MAKEFILE_TARGETS + NEWLINE); //$NON-NLS-1$ @@ -607,66 +600,12 @@ public class GnuMakefileGenerator implements IManagedBuilderMakefileGenerator { return buffer; } - /* (non-javadoc) - * Create the pattern rule in the format: - * /.: $(ROOT)//. - * @echo 'Building file: $<' - * @ $@ $< && \ - * echo -n '/.d /' >> /.d && \ - * -P -M -MG $< >> /.d - * @echo 'Finished building: $<' - * @echo - * - * Note that the macros 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/foo.o: $(ROOT)/source1/foo.cpp - * @echo 'Building file: $<' - * @ g++ -g -O2 -c -I/cygdrive/c/eclipse/workspace/Project/headers -o $@ $< && \ - * echo -n 'source1/foo.d source1/' >> source1/foo.d && \ - * g++ -P -M -MG -g -O2 -c -I/cygdrive/c/eclipse/workspace/Project/headers $< >> source1/foo.d - * @echo 'Finished building: $<' - * @echo - * - * @param relativePath - * @param buffer - * @param resource + /* (non-Javadoc) + * Answers a StringBuffer containing the comment(s) + * for the top-level makefile. */ - private void addRule(String relativePath, StringBuffer buffer, IResource resource) { - String buildFlags = null; - String resourceName = getFileName(resource); - String inputExtension = resource.getFileExtension(); - String cmd = info.getToolForSource(inputExtension); - String outputExtension = info.getOutputExtension(inputExtension); - String outflag = null; - String outputPrefix = null; - - // Add the rule and command to the makefile - String buildRule = relativePath + resourceName + DOT + outputExtension + COLON + WHITESPACE + ROOT + SEPARATOR + relativePath + resourceName + DOT + inputExtension; - buffer.append(buildRule + NEWLINE); - buffer.append(TAB + AT + ECHO + WHITESPACE + SINGLE_QUOTE + MESSAGE_START_FILE + WHITESPACE + IN_MACRO + SINGLE_QUOTE + NEWLINE); - buildFlags = info.getFlagsForSource(inputExtension); - outflag = info.getOutputFlag(outputExtension); - outputPrefix = info.getOutputPrefix(outputExtension); - - // The command to build - buffer.append(TAB + AT + ECHO + WHITESPACE + cmd + WHITESPACE + buildFlags + WHITESPACE + outflag + WHITESPACE + outputPrefix + OUT_MACRO + WHITESPACE + IN_MACRO + NEWLINE); - buffer.append(TAB + AT + cmd + WHITESPACE + buildFlags + WHITESPACE + outflag + WHITESPACE + outputPrefix + OUT_MACRO + WHITESPACE + IN_MACRO); - - // TODO determine if there are any deps to calculate - if (true) { - buffer.append(WHITESPACE + LOGICAL_AND + WHITESPACE + LINEBREAK); - // TODO get the dep rule out of the tool - String depRule = relativePath + resourceName + DOT + DEP_EXT; - getDependencyMakefiles().add(depRule); - buffer.append(TAB + ECHO + WHITESPACE + "-n" + WHITESPACE + SINGLE_QUOTE + depRule + WHITESPACE + relativePath + SINGLE_QUOTE + WHITESPACE + ">" + WHITESPACE + depRule + WHITESPACE + LOGICAL_AND + WHITESPACE + LINEBREAK); //$NON-NLS-1$ //$NON-NLS-2$ - buffer.append(TAB + cmd + WHITESPACE + "-MM -MG -P -w" + WHITESPACE + buildFlags + WHITESPACE + IN_MACRO + WHITESPACE + ">>" + WHITESPACE + depRule); //$NON-NLS-1$ //$NON-NLS-2$ - - } - - // Say goodbye to the nice user - buffer.append(NEWLINE); - buffer.append(TAB + AT + ECHO + WHITESPACE + SINGLE_QUOTE + MESSAGE_FINISH_FILE + WHITESPACE + IN_MACRO + SINGLE_QUOTE + NEWLINE + TAB + AT + ECHO + NEWLINE + NEWLINE); + protected StringBuffer addTopHeader() { + return addDefaultHeader(); } /** @@ -683,6 +622,18 @@ public class GnuMakefileGenerator implements IManagedBuilderMakefileGenerator { getSubdirList().add(container); } } + + /** + * If a file is removed from a source folder (either because of a delete + * or move action on the part of the user), the makefilegenerator has to + * remove the dependency makefile along with the old build goal + * + * @param resource + */ + protected void appendDeletedFile(IResource resource) { + // Cache this for now + getDeletedFileList().add(resource); + } /** * Adds the container of the argument to a list of subdirectories that are part @@ -697,39 +648,6 @@ public class GnuMakefileGenerator implements IManagedBuilderMakefileGenerator { getModifiedList().add(container); } } - - /** - * If a file is removed from a source folder (either because of a delete - * or move action on the part of the user), the makefilegenerator has to - * remove the dependency makefile along with the old build goal - * - * @param resource - */ - protected void appendDeletedFile(IResource resource) { - // Cache this for now - getDeletedFileList().add(resource); - } - - - /** - * @return - */ - private Vector getDeletedFileList() { - if (deletedFileList == null) { - deletedFileList = new Vector(); - } - return deletedFileList; - } - - /** - * @return - */ - private Vector getDependencyMakefiles() { - if (dependencyMakefiles == null) { - dependencyMakefiles = new Vector(); - } - return dependencyMakefiles; - } /* (non-Javadoc) * @param message @@ -752,6 +670,150 @@ public class GnuMakefileGenerator implements IManagedBuilderMakefileGenerator { } } + /* (non-Javadoc) + * Return or create the folder needed for the build output. If we are + * creating the folder, set the derived bit to true so the CM system + * ignores the contents. If the resource exists, respect the existing + * derived setting. + * + * @param string + * @return IPath + */ + 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; + } + + // Make sure the folder is marked as derived so it is not added to CM + if (!folder.isDerived()) { + folder.setDerived(true); + } + } + + return folder.getFullPath(); + } + + /* (non-Javadoc) + * Return or create the makefile needed for the build. If we are creating + * the resource, set the derived bit to true so the CM system ignores + * the contents. If the resource exists, respect the existing derived + * setting. + * + * @param makefilePath + * @return IFile + */ + 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, new SubProgressMonitor(monitor, 1)); + // Make sure the new file is marked as derived + if (!newFile.isDerived()) { + newFile.setDerived(true); + } + + } + 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; + } + + return newFile; + } + + /** + * @param deletedFile + */ + private void deleteBuildTarget(IResource deletedFile) { + // Get the project relative path of the file + String fileName = getFileName(deletedFile); + String srcExtension = deletedFile.getFileExtension(); + String targetExtension = info.getOutputExtension(srcExtension); + fileName += DOT + targetExtension; + IPath projectRelativePath = deletedFile.getProjectRelativePath().removeLastSegments(1); + IPath targetFilePath = getBuildWorkingDir().append(projectRelativePath).append(fileName); + IResource depFile = project.findMember(targetFilePath); + if (depFile != null && depFile.exists()) { + try { + depFile.delete(true, new SubProgressMonitor(monitor, 1)); + } catch (CoreException e) { + // This had better be allowed during a build + ManagedBuilderCorePlugin.log(e); + } + } + } + + /** + * @param deletedFile + */ + private void deleteDepFile(IResource deletedFile) { + // Get the project relative path of the file + String fileName = getFileName(deletedFile); + fileName += DOT + DEP_EXT; + IPath projectRelativePath = deletedFile.getProjectRelativePath().removeLastSegments(1); + IPath depFilePath = getBuildWorkingDir().append(projectRelativePath).append(fileName); + IResource depFile = project.findMember(depFilePath); + if (depFile != null && depFile.exists()) { + try { + depFile.delete(true, new SubProgressMonitor(monitor, 1)); + } catch (CoreException e) { + // This had better be allowed during a build + ManagedBuilderCorePlugin.log(e); + } + } + } + + /* (non-Javadoc) + * Answers the argument with all whitespaces replaced with an escape sequence. + * + * @param path + */ + protected String escapeWhitespaces(String path) { + // Escape the spaces in the path/filename if it has any + String[] segments = path.split("\\s"); //$NON-NLS-1$ + if (segments.length > 1) { + StringBuffer escapedPath = new StringBuffer(); + for (int index = 0; index < segments.length; ++index) { + escapedPath.append(segments[index]); + if (index + 1 < segments.length) { + escapedPath.append("\\ "); //$NON-NLS-1$ + } + } + return escapedPath.toString().trim(); + } else { + return path; + } + } + /* (non-Javadoc) * @see org.eclipse.cdt.managedbuilder.makegen.IManagedBuilderMakefileGenerator#generateDependencies() */ @@ -858,6 +920,37 @@ public class GnuMakefileGenerator implements IManagedBuilderMakefileGenerator { checkCancel(); } } + + /* (non-Javadoc) + * @see org.eclipse.cdt.managedbuilder.makegen.IManagedBuilderMakefileGenerator#getTopBuildDir() + */ + public IPath getBuildWorkingDir() { + if (topBuildDir != null) { + return topBuildDir.removeFirstSegments(1); + } + return null; + } + + + /** + * @return + */ + private Vector getDeletedFileList() { + if (deletedFileList == null) { + deletedFileList = new Vector(); + } + return deletedFileList; + } + + /** + * @return + */ + private Vector getDependencyMakefiles() { + if (dependencyMakefiles == null) { + dependencyMakefiles = new Vector(); + } + return dependencyMakefiles; + } /** * Strips off the file extension from the argument and returns @@ -886,12 +979,12 @@ public class GnuMakefileGenerator implements IManagedBuilderMakefileGenerator { // practically nil so it doesn't seem worth the hassle of generating a truly // unique name. if(extensionName.equals(extensionName.toUpperCase())) { - macroName.append(extensionName.toUpperCase() + "_UPPER"); //$NON-NLS$-1 + macroName.append(extensionName.toUpperCase() + "_UPPER"); //$NON-NLS-1$ } else { // lower case... no need for "UPPER_" macroName.append(extensionName.toUpperCase()); } - macroName.append("_SRCS"); //$NON-NLS$-1 + macroName.append("_SRCS"); //$NON-NLS-1$ return macroName; } @@ -959,138 +1052,27 @@ public class GnuMakefileGenerator implements IManagedBuilderMakefileGenerator { } return subdirList; } - - /* (non-Javadoc) - * Return or create the folder needed for the build output. If we are - * creating the folder, set the derived bit to true so the CM system - * ignores the contents. If the resource exists, respect the existing - * derived setting. - * - * @param string - * @return IPath - */ - 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; - } - - // Make sure the folder is marked as derived so it is not added to CM - if (!folder.isDerived()) { - folder.setDerived(true); - } - } - - return folder.getFullPath(); - } - - /** - * @param deletedFile - */ - private void deleteBuildTarget(IResource deletedFile) { - // Get the project relative path of the file - String fileName = getFileName(deletedFile); - String srcExtension = deletedFile.getFileExtension(); - String targetExtension = info.getOutputExtension(srcExtension); - fileName += DOT + targetExtension; - IPath projectRelativePath = deletedFile.getProjectRelativePath().removeLastSegments(1); - IPath targetFilePath = getBuildWorkingDir().append(projectRelativePath).append(fileName); - IResource depFile = project.findMember(targetFilePath); - if (depFile != null && depFile.exists()) { - try { - depFile.delete(true, new SubProgressMonitor(monitor, 1)); - } catch (CoreException e) { - // This had better be allowed during a build - ManagedBuilderCorePlugin.log(e); - } - } - } /** - * @param deletedFile + * @param project + * @param info + * @param monitor */ - private void deleteDepFile(IResource deletedFile) { - // Get the project relative path of the file - String fileName = getFileName(deletedFile); - fileName += DOT + DEP_EXT; - IPath projectRelativePath = deletedFile.getProjectRelativePath().removeLastSegments(1); - IPath depFilePath = getBuildWorkingDir().append(projectRelativePath).append(fileName); - IResource depFile = project.findMember(depFilePath); - if (depFile != null && depFile.exists()) { - try { - depFile.delete(true, new SubProgressMonitor(monitor, 1)); - } catch (CoreException e) { - // This had better be allowed during a build - ManagedBuilderCorePlugin.log(e); - } + public void initialize(IProject project, IManagedBuildInfo info, IProgressMonitor monitor) { + // 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; + // Get the name of the build target + buildTargetName = info.getBuildArtifactName(); + // Get its extension + extension = info.getBuildArtifactExtension(); + if (extension == null) { + extension = new String(); } } - - /* (non-Javadoc) - * Return or create the makefile needed for the build. If we are creating - * the resource, set the derived bit to true so the CM system ignores - * the contents. If the resource exists, respect the existing derived - * setting. - * - * @param makefilePath - * @return IFile - */ - 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, new SubProgressMonitor(monitor, 1)); - // Make sure the new file is marked as derived - if (!newFile.isDerived()) { - newFile.setDerived(true); - } - - } - 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; - } - - return newFile; - } - - /* (non-Javadoc) - * @see org.eclipse.cdt.managedbuilder.makegen.IManagedBuilderMakefileGenerator#getTopBuildDir() - */ - public IPath getBuildWorkingDir() { - if (topBuildDir != null) { - return topBuildDir.removeFirstSegments(1); - } - return null; - } /** * Answers true if the argument is found in a generated container @@ -1111,27 +1093,14 @@ public class GnuMakefileGenerator implements IManagedBuilderMakefileGenerator { return false; } - /* (non-javadoc) - * Create the entire contents of the makefile. - * - * @param fileHandle The file to place the contents in. - * @param rebuild FLag signalling that the user is doing a full rebuild - * @throws CoreException + /* (non-Javadoc) + * Put COLS_PER_LINE comment charaters in the argument. */ - protected void populateTopMakefile(IFile fileHandle, boolean rebuild) throws CoreException { - StringBuffer buffer = new StringBuffer(); - - // Add the header - buffer.append(addTopHeader()); - - // Add the macro definitions - buffer.append(addMacros()); - - // Add targets - buffer.append(addTargets(rebuild)); - - // Save the file - Util.save(buffer, fileHandle); + protected void outputCommentLine(StringBuffer buffer) { + for (int i = 0; i < COLS_PER_LINE; i++) { + buffer.append(COMMENT_SYMBOL); + } + buffer.append(NEWLINE); } protected void populateDummyTargets(IFile makefile, boolean force) throws CoreException, IOException { @@ -1156,7 +1125,7 @@ public class GnuMakefileGenerator implements IManagedBuilderMakefileGenerator { StringBuffer outBuffer = null; if (inBuffer != null) { // Here are the tokens in the file - String[] dependencies = inBuffer.toString().split("\\s"); + String[] dependencies = inBuffer.toString().split("\\s"); //$NON-NLS-1$ // If we are doing an incremental build, only update the files that do not have a comment if (dependencies.length > 0 && !force) { @@ -1291,9 +1260,9 @@ public class GnuMakefileGenerator implements IManagedBuilderMakefileGenerator { // create dependency rule of the form // OBJS = $(macroName1: $(ROOT)/%.input1=%.output1) ... $(macroNameN: $(ROOT)/%.inputN=%.outputN) - objectsBuffer.append(WHITESPACE + "$(" + macroName + COLON + "$(ROOT)" + SEPARATOR + WILDCARD - + DOT + extensionName + "=" + WILDCARD + DOT + - toolArray[k].getOutputExtension(extensionName) + ")" ); + objectsBuffer.append(WHITESPACE + "$(" + macroName + COLON + "$(ROOT)" + SEPARATOR + WILDCARD //$NON-NLS-1$ //$NON-NLS-2$ + + DOT + extensionName + "=" + WILDCARD + DOT + //$NON-NLS-1$ + toolArray[k].getOutputExtension(extensionName) + ")" ); //$NON-NLS-1$ } } } @@ -1326,7 +1295,7 @@ public class GnuMakefileGenerator implements IManagedBuilderMakefileGenerator { if(!outputExtensionsSet.contains(extensionName) && !handledInputExtensionsSet.contains(extensionName)) { handledInputExtensionsSet.add(extensionName); StringBuffer macroName = getMacroName(extensionName); - buffer.append(macroName + WHITESPACE + ":=" + WHITESPACE + NEWLINE); + buffer.append(macroName + WHITESPACE + ":=" + WHITESPACE + NEWLINE); //$NON-NLS-1$ } } } @@ -1338,6 +1307,29 @@ public class GnuMakefileGenerator implements IManagedBuilderMakefileGenerator { Util.save(buffer, fileHandle); } + /* (non-javadoc) + * Create the entire contents of the makefile. + * + * @param fileHandle The file to place the contents in. + * @param rebuild FLag signalling that the user is doing a full rebuild + * @throws CoreException + */ + protected void populateTopMakefile(IFile fileHandle, boolean rebuild) throws CoreException { + StringBuffer buffer = new StringBuffer(); + + // Add the header + buffer.append(addTopHeader()); + + // Add the macro definitions + buffer.append(addMacros()); + + // Add targets + buffer.append(addTargets(rebuild)); + + // Save the file + Util.save(buffer, fileHandle); + } + /* (non-Javadoc) * @see org.eclipse.cdt.managedbuilder.makegen.IManagedBuilderMakefileGenerator#regenerateDependencies() */ @@ -1410,7 +1402,7 @@ public class GnuMakefileGenerator implements IManagedBuilderMakefileGenerator { checkCancel(); // Now finish up by adding all the object files - IPath objFilePath = topBuildDir.addTrailingSeparator().append("objects.mk"); + IPath objFilePath = topBuildDir.addTrailingSeparator().append(OBJECTS_MAKFILE); IFile objsFileHandle = createFile(objFilePath); populateObjectsMakefile(objsFileHandle); checkCancel();