diff --git a/build/org.eclipse.cdt.managedbuilder.core/ChangeLog b/build/org.eclipse.cdt.managedbuilder.core/ChangeLog index 8b80491cb2b..7085a3aadc4 100644 --- a/build/org.eclipse.cdt.managedbuilder.core/ChangeLog +++ b/build/org.eclipse.cdt.managedbuilder.core/ChangeLog @@ -1,3 +1,39 @@ +2003-09-26 Sean Evoy + A partial implementation for bug 41826. This patch contains the logic to properly + respond in the face of the following project changes: + + 1. A generated project element, such as the build target or an intermediate file, + is deleted in the build project, or any projects it references. + 2. The build settings change in the build project or any projects it + references. + + In order to actually do this correctly, I had to stop being so precious during the + build. The makefile generator was was calculating the "build needed" state as it + walked the change delta. However, the Eclipse core has already determined that I + need to do a build. Further, as I discovered earlier, it doesn't always pass what + has changed in referenced projects as part of the delta. Essentially, that means I + will never be able to fully calculate the change set in the makefile generator's + delta visitor, and to even approximate a decent set of cases, the logic would quickly + bog down in complexity. + + The solution is to trust Eclipse and alway invoke make when my incremental builder + is called. At worst, if there is no significant change, make will execute and + report nothing to be done. + + The modified makefile builder no longer asks the makefile generator if it should + build. It also no longer cares if the change set is empty (make will report that). + Since it responds to changes in referenced project's build information, it also + scrubs all relevant projects after building. Since a build might involve building + referenced project elements, those projects get their project views refreshed after + build. The build markers for referenced projects are removed prior to build. + * src/org/eclipse/cdt/managedbuilder/internal/core/GeneratedMakefileBuilder.java + + The makefile generator has been simplified. The resource delta visitor logic no + longer trie to decide if a build should occur. The method to ask has been removed. + The class no longer throws an exception if the change set is empty. I am also a bit + more careful to call make with the right targets if a referenced project is built. + * src/org/eclipse/cdt/managedbuilder/internal/core/MakefileGenerator.java + 2003-09-26 Sean Evoy I added a fix to the builder and makefile generator to properly handle the following case. Project A depends on Project B. Something changes in project B and the user requests 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 3b45d2b72f2..5b8ed071467 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 @@ -58,9 +58,6 @@ 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$ - // Status codes - public static final int EMPTY_PROJECT_BUILD_ERROR = 1; - // Local variables protected List resourcesToBuild; protected List ruleList; @@ -89,7 +86,6 @@ public class GeneratedMakefileBuilder extends ACBuilder { * */ public GeneratedMakefileBuilder() { - } /* (non-Javadoc) @@ -100,7 +96,8 @@ public class GeneratedMakefileBuilder extends ACBuilder { if (statusMsg != null) { monitor.subTask(statusMsg); } - + + // Get the build information IManagedBuildInfo info = ManagedBuildManager.getBuildInfo(getProject()); if (kind == IncrementalProjectBuilder.FULL_BUILD || info.isDirty()) { @@ -123,10 +120,18 @@ public class GeneratedMakefileBuilder extends ACBuilder { } } } + + // Scrub the build info of all the projects participating in the build info.setDirty(false); + IProject[] deps = getProject().getReferencedProjects(); + for (int i = 0; i < deps.length; i++) { + IProject project = deps[i]; + IManagedBuildInfo depInfo = ManagedBuildManager.getBuildInfo(project); + depInfo.setDirty(false); + } // Ask build mechanism to compute deltas for project dependencies next time - return getProject().getReferencedProjects(); + return deps; } /** @@ -150,15 +155,8 @@ public class GeneratedMakefileBuilder extends ACBuilder { monitor = new NullProgressMonitor(); } - // 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(); + IProject[] deps = getProject().getReferencedProjects(); for (int i = 0; i < deps.length; i++) { IProject depProject = deps[i]; if (ManagedBuildManager.manages(depProject)) { @@ -167,32 +165,23 @@ public class GeneratedMakefileBuilder extends ACBuilder { 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; - } else { - // Throw the exception back to the builder - throw e; - } + // Throw the exception back to the builder + throw e; } } } // Need to report status to the user - String statusMsg = ManagedBuilderCorePlugin.getFormattedString(REBUILD, currentProject.getName()); + String statusMsg = ManagedBuilderCorePlugin.getFormattedString(REBUILD, getProject().getName()); monitor.subTask(statusMsg); // Regenerate the makefiles for this project - MakefileGenerator generator = new MakefileGenerator(currentProject, info, monitor); + MakefileGenerator generator = new MakefileGenerator(getProject(), 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; - } + // Throw the exception back to the builder + throw e; } IPath topBuildDir = generator.getTopBuildDir(); @@ -254,35 +243,43 @@ public class GeneratedMakefileBuilder extends ACBuilder { */ protected void incrementalBuild(IResourceDelta delta, IManagedBuildInfo info, IProgressMonitor monitor) throws CoreException { // Rebuild the resource tree in the delta - IProject currentProject = getProject(); String statusMsg = null; // Need to report status to the user if (monitor == null) { monitor = new NullProgressMonitor(); } - statusMsg = ManagedBuilderCorePlugin.getFormattedString(INCREMENTAL, currentProject.getName()); + statusMsg = ManagedBuilderCorePlugin.getFormattedString(INCREMENTAL, getProject().getName()); monitor.subTask(statusMsg); + // Regenerate the makefiles for any managed projects this project depends on + IProject[] deps = getProject().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) { + // Throw the exception back to the builder + throw e; + } + } + } + // Ask the makefile generator to generate any makefiles needed to build delta - MakefileGenerator generator = new MakefileGenerator(currentProject, info, monitor); + MakefileGenerator generator = new MakefileGenerator(getProject(), info, monitor); try { generator.generateMakefiles(delta); } catch (CoreException e) { - if (e.getStatus().getCode() == GeneratedMakefileBuilder.EMPTY_PROJECT_BUILD_ERROR) { - // There is nothing to build so bail - monitor.worked(1); - return; - } else { - throw e; - } + // Throw the exception back to the builder + throw e; } - // Run the build if there is any relevant change - if (generator.shouldRunBuild()) { - IPath buildDir = new Path(info.getConfigurationName()); - invokeMake(false, buildDir, info, monitor); - } + // Run the build + IPath buildDir = new Path(info.getConfigurationName()); + invokeMake(false, buildDir, info, monitor); monitor.worked(1); } @@ -310,6 +307,11 @@ public class GeneratedMakefileBuilder extends ACBuilder { // Remove all markers for this project removeAllMarkers(currentProject); + IProject[] deps = currentProject.getReferencedProjects(); + for (int i = 0; i < deps.length; i++) { + IProject project = deps[i]; + removeAllMarkers(project); + } IPath workingDirectory = getWorkingDirectory().append(buildDir); @@ -361,12 +363,16 @@ public class GeneratedMakefileBuilder extends ACBuilder { errMsg = launcher.getErrorMessage(); } - // Force a resync of the project without allowing the user to cancel. + // Force a resync of the projects without allowing the user to cancel. // This is probably unkind, but short of this there is no way to insure // the UI is up-to-date with the build results monitor.subTask(ManagedBuilderCorePlugin.getResourceString(REFRESH)); try { currentProject.refreshLocal(IResource.DEPTH_INFINITE, null); + for (int j = 0; j < deps.length; ++j) { + IProject project = deps[j]; + project.refreshLocal(IResource.DEPTH_INFINITE, null); + } } catch (CoreException e) { monitor.subTask(ManagedBuilderCorePlugin.getResourceString(REFRESH_ERROR)); } diff --git a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/MakefileGenerator.java b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/MakefileGenerator.java index 19db6c8247b..14659171fab 100644 --- a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/MakefileGenerator.java +++ b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/MakefileGenerator.java @@ -44,10 +44,8 @@ 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 @@ -83,7 +81,6 @@ public class MakefileGenerator { protected IProject project; protected List ruleList; protected IPath topBuildDir; - protected boolean shouldRunBuild; private String target; @@ -184,21 +181,12 @@ public class MakefileGenerator { generator.appendModifiedSubdirectory(resource); } } - // A build should run - generator.shouldRunBuild(true); } } break; case IResourceDelta.CHANGED: if (info.buildsFileType(ext)) { - switch (delta.getFlags()) { - case IResourceDelta.CONTENT: - // If the contents changed then just do a build - generator.shouldRunBuild(true); - keepLooking = true; - default: - keepLooking = true; - } + keepLooking = true; } break; default: @@ -208,9 +196,7 @@ public class MakefileGenerator { } if (resource.getType() == IResource.PROJECT) { // If there is a zero-length delta, something the project depends on has changed so just call make IResourceDelta[] children = delta.getAffectedChildren(); - if (children != null && children.length == 0) { - generator.shouldRunBuild(true); - } else { + if (children != null && children.length > 0) { keepLooking = true; } } else { @@ -237,8 +223,6 @@ public class MakefileGenerator { this.monitor = monitor; // Get the build info for the project this.info = info; - // By default a build never runs - shouldRunBuild = false; // Get the name of the build target target = info.getBuildArtifactName(); // Get its extension @@ -386,8 +370,9 @@ public class MakefileGenerator { /* (non-javadoc) * Answers a StringBuffer containing all of the sources contributed by * a container to the build. + * * @param module - * @return + * @return StringBuffer */ protected StringBuffer addSources(IContainer module) throws CoreException { // Calculate the new directory relative to the build output @@ -450,6 +435,8 @@ public class MakefileGenerator { /* (non-javadoc) * Answers a StrinBuffer containing all of the required targets to * properly build the project. + * + * @return StringBuffer */ protected StringBuffer addTargets(boolean rebuild) { StringBuffer buffer = new StringBuffer(); @@ -492,6 +479,7 @@ public class MakefileGenerator { for (int i = 0; i < deps.length; i++) { IProject dep = deps[i]; String buildDir = dep.getLocation().toString(); + String depTargets = targets; if (ManagedBuildManager.manages(dep)) { // Add the current configuration to the makefile path IManagedBuildInfo depInfo = ManagedBuildManager.getBuildInfo(dep); @@ -501,9 +489,12 @@ public class MakefileGenerator { String depTarget = depInfo.getBuildArtifactName(); String depExt = (new Path(depTarget)).getFileExtension(); String depPrefix = depInfo.getOutputPrefix(depExt); + if (depInfo.isDirty()) { + depTargets = "clean all"; + } managedProjectOutputs.add(buildDir + SEPARATOR + depPrefix + depTarget); } - buffer.append(TAB + "cd" + WHITESPACE + buildDir + SEMI_COLON + WHITESPACE + "$(MAKE) " + targets + NEWLINE); + buffer.append(TAB + "cd" + WHITESPACE + buildDir + SEMI_COLON + WHITESPACE + "$(MAKE) " + depTargets + NEWLINE); } } buffer.append(NEWLINE); @@ -514,7 +505,6 @@ public class MakefileGenerator { * targ_.: $(OBJS) [ ... ] * $(BUILD_TOOL) $(FLAGS) $(OUTPUT_FLAG) $@ $(OBJS) $(USER_OBJS) $(LIB_DEPS) */ - // buffer.append(outputPrefix + target + COLON + WHITESPACE + "$(OBJS)"); Iterator iter = managedProjectOutputs.listIterator(); while (iter.hasNext()) { @@ -535,6 +525,12 @@ public class MakefileGenerator { return buffer; } + /* (non-javadoc) + * + * @param relativePath + * @param buffer + * @param resource + */ protected void addRule(String relativePath, StringBuffer buffer, IResource resource) { String rule = null; String cmd = null; @@ -633,25 +629,20 @@ public class MakefileGenerator { */ public void generateMakefiles(IResourceDelta delta) throws CoreException { /* - * Let's do a sanity check right now. This is an incremental build, so if the top-level directory is not there, then - * a rebuild is needed anyway. + * Let's do a sanity check right now. + * + * 1. This is an incremental build, so if the top-level directory is not + * there, then a rebuild is needed. */ IFolder folder = project.getFolder(info.getConfigurationName()); if (!folder.exists()) { regenerateMakefiles(); - shouldRunBuild(true); return; } - + // Visit the resources in the delta and compile a list of subdirectories to regenerate ResourceDeltaVisitor visitor = new ResourceDeltaVisitor(this, info); delta.accept(visitor); - // There may be nothing to regenerate and no content changes that require a rebuild - if (getModifiedList().isEmpty() && !shouldRunBuild()) { - // There is nothing to build - IStatus status = new Status(IStatus.INFO, ManagedBuilderCorePlugin.getUniqueIdentifier(), GeneratedMakefileBuilder.EMPTY_PROJECT_BUILD_ERROR, "", null); - throw new CoreException(status); - } // See if the user has cancelled the build checkCancel(); @@ -659,11 +650,6 @@ public class MakefileGenerator { // The top-level makefile needs this information ResourceProxyVisitor resourceVisitor = new ResourceProxyVisitor(this, info); project.accept(resourceVisitor, IResource.NONE); - if (getSubdirList().isEmpty()) { - // There is nothing to build (but we should never throw this exception) - IStatus status = new Status(IStatus.INFO, ManagedBuilderCorePlugin.getUniqueIdentifier(), GeneratedMakefileBuilder.EMPTY_PROJECT_BUILD_ERROR, "", null); - throw new CoreException(status); - } checkCancel(); // Regenerate any fragments that are missing for the exisiting directories NOT modified @@ -697,7 +683,8 @@ public class MakefileGenerator { } /* (non-javadoc) - * @return + * + * @return List */ private List getModifiedList() { if (modifiedList == null) { @@ -710,6 +697,7 @@ public class MakefileGenerator { * Answers the list of known build rules. This keeps me from generating duplicate * rules for known file extensions. * + * @return List */ private List getRuleList() { if (ruleList == null) { @@ -720,7 +708,8 @@ public class MakefileGenerator { /* (non-javadoc) * Answers the list of subdirectories contributing source code to the build - * @return + * + * @return List */ private List getSubdirList() { if (subdirList == null) { @@ -731,7 +720,7 @@ public class MakefileGenerator { /* (non-javadoc) * @param string - * @return + * @return IPath */ private IPath createDirectory(String dirName) throws CoreException { // Create or get the handle for the build directory @@ -765,7 +754,7 @@ public class MakefileGenerator { /* (non-javadoc) * @param makefilePath * @param monitor - * @return + * @return IFile */ private IFile createFile(IPath makefilePath) throws CoreException { // Create or get the handle for the makefile @@ -794,7 +783,7 @@ public class MakefileGenerator { * Answers the IPath of the top directory generated for the build * output, or null if none has been generated. * - * @return + * @return IPath */ public IPath getTopBuildDir() { return topBuildDir; @@ -803,7 +792,7 @@ public class MakefileGenerator { /** * Answers true if the argument is found in a generated container * @param resource - * @return + * @return boolean */ public boolean isGeneratedResource(IResource resource) { // Is this a generated directory ... @@ -824,6 +813,7 @@ public class MakefileGenerator { * * @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(); @@ -843,6 +833,7 @@ public class MakefileGenerator { /* (non-javadoc) * @param module + * @throws CoreException */ protected void populateFragmentMakefile(IContainer module) throws CoreException { // Calcualte the new directory relative to the build output @@ -879,11 +870,6 @@ public class MakefileGenerator { // Visit the resources in the project ResourceProxyVisitor visitor = new ResourceProxyVisitor(this, info); project.accept(visitor, IResource.NONE); - if (getSubdirList().isEmpty()) { - // There is nothing to build - IStatus status = new Status(IStatus.INFO, ManagedBuilderCorePlugin.getUniqueIdentifier(), GeneratedMakefileBuilder.EMPTY_PROJECT_BUILD_ERROR, "", null); - throw new CoreException(status); - } // See if the user has cancelled the build checkCancel(); @@ -906,23 +892,4 @@ public class MakefileGenerator { checkCancel(); } } - /** - * Answers whether a build is required after the makefiles have been - * generated. - * - * @return - */ - public boolean shouldRunBuild() { - return shouldRunBuild; - } - - /** - * Sets the build flag to the value of the argument. - * - * @param b - */ - public void shouldRunBuild(boolean b) { - shouldRunBuild = b; - } - }