1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-04-29 19:45:01 +02:00

Patch for Sean Evoy:

I have documented the patch in the change log; the only bonus is that it 
fixes 43214 for free. The managed builder should now respond correctly to 
deletions of any types of file in the project (or referenced projects). 
This includes makefiles, intermediate files, and the build target. It 
should also properly handle changes in the project settings in any 
referenced project. Referenced projects should now get their project views 
refreshed as a result of any changes.
This commit is contained in:
Doug Schaefer 2003-09-30 02:14:44 +00:00
parent 7fab73d44f
commit 9fa37457ae
3 changed files with 120 additions and 111 deletions

View file

@ -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

View file

@ -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));
}

View file

@ -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 <code>StringBuffer</code> 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 <code>StrinBuffer</code> 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_<prefix><target>.<extension>: $(OBJS) [<dep_proj_1_output> ... <dep_proj_n_output>]
* $(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 <code>IPath</code> of the top directory generated for the build
* output, or <code>null</code> if none has been generated.
*
* @return
* @return IPath
*/
public IPath getTopBuildDir() {
return topBuildDir;
@ -803,7 +792,7 @@ public class MakefileGenerator {
/**
* Answers <code>true</code> 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;
}
}