From 094a51f44aa2a1d73373b0db4b0f6f5a842696ec Mon Sep 17 00:00:00 2001 From: James Blackburn Date: Mon, 24 Jan 2011 16:07:59 +0000 Subject: [PATCH] Bug 303953 - Deleted linked resource in managed project is still being built. - Ensure that subdir.mk is re-generated when last file has been removed from a directory + test - Add scaffolding for managedbuilder.core regression tests --- .../builderTests/regressions/README.txt | 4 + .../builderTests/regressions/helloworldC.zip | Bin 0 -> 3756 bytes .../testplugin/AbstractBuilderTest.java | 273 ++++++ .../testplugin/ResourceDeltaVerifier.java | 850 ++++++++++++++++++ .../tests/suite/AllManagedBuildTests.java | 7 +- .../core/regressions/Bug_303953.java | 80 ++ .../core/regressions/RegressionTests.java | 40 + .../makegen/gnu/GnuMakefileGenerator.java | 43 +- 8 files changed, 1276 insertions(+), 21 deletions(-) create mode 100644 build/org.eclipse.cdt.managedbuilder.core.tests/resources/builderTests/regressions/README.txt create mode 100644 build/org.eclipse.cdt.managedbuilder.core.tests/resources/builderTests/regressions/helloworldC.zip create mode 100644 build/org.eclipse.cdt.managedbuilder.core.tests/suite/org/eclipse/cdt/managedbuilder/testplugin/AbstractBuilderTest.java create mode 100644 build/org.eclipse.cdt.managedbuilder.core.tests/suite/org/eclipse/cdt/managedbuilder/testplugin/ResourceDeltaVerifier.java create mode 100644 build/org.eclipse.cdt.managedbuilder.core.tests/tests/org/eclipse/cdt/managedbuilder/core/regressions/Bug_303953.java create mode 100644 build/org.eclipse.cdt.managedbuilder.core.tests/tests/org/eclipse/cdt/managedbuilder/core/regressions/RegressionTests.java diff --git a/build/org.eclipse.cdt.managedbuilder.core.tests/resources/builderTests/regressions/README.txt b/build/org.eclipse.cdt.managedbuilder.core.tests/resources/builderTests/regressions/README.txt new file mode 100644 index 00000000000..3a88835e59a --- /dev/null +++ b/build/org.eclipse.cdt.managedbuilder.core.tests/resources/builderTests/regressions/README.txt @@ -0,0 +1,4 @@ + +helloworldC.zip: + Basic standard Linux GCC C HelloWorld ManagedBuild Project template. + Contains Debug + Release configurations. diff --git a/build/org.eclipse.cdt.managedbuilder.core.tests/resources/builderTests/regressions/helloworldC.zip b/build/org.eclipse.cdt.managedbuilder.core.tests/resources/builderTests/regressions/helloworldC.zip new file mode 100644 index 0000000000000000000000000000000000000000..c5577598963c329069e0e5851a28d2780f4792fc GIT binary patch literal 3756 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 literal 0 HcmV?d00001 diff --git a/build/org.eclipse.cdt.managedbuilder.core.tests/suite/org/eclipse/cdt/managedbuilder/testplugin/AbstractBuilderTest.java b/build/org.eclipse.cdt.managedbuilder.core.tests/suite/org/eclipse/cdt/managedbuilder/testplugin/AbstractBuilderTest.java new file mode 100644 index 00000000000..d71927a1ed2 --- /dev/null +++ b/build/org.eclipse.cdt.managedbuilder.core.tests/suite/org/eclipse/cdt/managedbuilder/testplugin/AbstractBuilderTest.java @@ -0,0 +1,273 @@ +/******************************************************************************* + * Copyright (c) 2011 Broadcom Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Broadcom Corporation - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.managedbuilder.testplugin; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.List; + +import junit.framework.TestCase; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.model.CoreModel; +import org.eclipse.cdt.core.model.ICModelMarker; +import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; +import org.eclipse.cdt.core.settings.model.ICProjectDescription; +import org.eclipse.cdt.core.settings.model.ICProjectDescriptionManager; +import org.eclipse.core.resources.IBuildConfiguration; +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspace; +import org.eclipse.core.resources.IWorkspaceDescription; +import org.eclipse.core.resources.IWorkspaceRunnable; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.jobs.Job; + +/** + * Abstract builder test which provides utility methods for: + *
    + *
  • Importing projects into the workspace
  • + *
  • Adding expected resources to the delta verifier
  • + *
  • Verifying the delta
  • + *
  • Printing markers
  • + *
  • Cleaning up the workspace at the end
  • + *
+ */ +public abstract class AbstractBuilderTest extends TestCase { + static final String PATH = "builderTests"; + + private String workspace; + private List projects; + + @Override + protected void setUp() throws Exception { + super.setUp(); + setAutoBuilding(false); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + // Bug 327126 Stop the indexer before tearing down so we don't deadlock + Job.getJobManager().cancel(CCorePlugin.getPDOMManager()); + Job.getJobManager().join(CCorePlugin.getPDOMManager(), null); + + // Clean-up any projects we were using + for (IProject project : projects) { + project.delete(true, null); + } + projects.clear(); + } + + /** + * Run a build of the specified kind, and verify that the resource changes that + * result match the expectations of the given verifier. + */ + protected void verifyBuild(final IProject project, final int kind, ResourceDeltaVerifier verifier) throws CoreException { + verifyBuild(new IBuildConfiguration[]{project.getActiveBuildConfig()}, kind, verifier); + } + /** + * Build the specified configurations, and verify that the resource changes that + * result match the expectations of the given verifier. + */ + protected void verifyBuild(final IBuildConfiguration[] configs, final int kind, ResourceDeltaVerifier verifier) throws CoreException { + getWorkspace().getRoot().refreshLocal(IResource.DEPTH_INFINITE, null); + getWorkspace().addResourceChangeListener(verifier); + try { + // batch changes + IWorkspaceRunnable body = new IWorkspaceRunnable() { + public void run(IProgressMonitor monitor) throws CoreException { + getWorkspace().build(configs, kind, true, monitor); + } + }; + getWorkspace().run(body, null); + } finally { + assertTrue(verifier.getMessage(), verifier.isDeltaValid()); + getWorkspace().removeResourceChangeListener(verifier); + printAllMarkers(); + } + } + + + /** + * Set the active configuration of a project by configuration name + */ + protected void setActiveConfigurationByName(IProject project, String cfgName) throws CoreException { + ICProjectDescriptionManager mngr = CoreModel.getDefault().getProjectDescriptionManager(); + ICProjectDescription desc = mngr.getProjectDescription(project, true); + ICConfigurationDescription cfg = desc.getConfigurationByName(cfgName); + assertNotNull(cfg); + desc.setActiveConfiguration(cfg); + mngr.setProjectDescription(project, desc); + // FIXME: enable when cdt.core knows about core.resources build configurations +// assertTrue(project.getActiveBuildConfig().getName().equals(cfg.getName())); + } + + protected Collection getProjectBuildExeResources(String projectName, String cfgName, String obj) throws CoreException { + return getProjectBuildExeResources(projectName, cfgName, new String[]{obj}); + } + + protected Collection getProjectBuildLibResources(String projectName, String cfgName, String obj) throws CoreException { + return getProjectBuildLibResources(projectName, cfgName, new String[]{obj}); + } + + protected Collection getProjectBuildSharedLibResources(String projectName, String cfgName, String obj) throws CoreException { + return getProjectBuildSharedLibResources(projectName, cfgName, new String[]{obj}); + } + + protected Collection getProjectBuildExeResources(String projectName, String cfgName, String[] objs) throws CoreException { + Collection resources = getProjectBuildResources(projectName, cfgName, objs); + IProject project = getWorkspace().getRoot().getProject(projectName); + IFolder buildDir = project.getFolder(cfgName); + resources.add(buildDir.getFile(projectName)); + return resources; + } + + protected Collection getProjectBuildLibResources(String projectName, String cfgName, String[] objs) throws CoreException { + Collection resources = getProjectBuildResources(projectName, cfgName, objs); + IProject project = getWorkspace().getRoot().getProject(projectName); + IFolder buildDir = project.getFolder(cfgName); + resources.add(buildDir.getFile("lib" + projectName + ".a")); + return resources; + } + + protected Collection getProjectBuildSharedLibResources(String projectName, String cfgName, String[] objs) throws CoreException { + Collection resources = getProjectBuildResources(projectName, cfgName, objs); + IProject project = getWorkspace().getRoot().getProject(projectName); + IFolder buildDir = project.getFolder(cfgName); + resources.add(buildDir.getFile("lib" + projectName + ".so")); + return resources; + } + + /** + * Returns an array of resources expected to be generated by building a project configuration. + * The object files expected to be output can also be specified. + */ + protected Collection getProjectBuildResources(String projectName, String cfgName, String[] objs) throws CoreException { + IProject project = getWorkspace().getRoot().getProject(projectName); + IFolder buildDir = project.getFolder(cfgName); + Collection resources = new LinkedHashSet(); + resources.add(buildDir); + resources.add(buildDir.getFile("makefile")); + resources.add(buildDir.getFile("objects.mk")); + resources.add(buildDir.getFile("sources.mk")); + for (String obj : objs) { + resources.add(buildDir.getFile(obj + ".d")); + resources.add(buildDir.getFile(obj + ".o")); + // Add subdir.mk in the same directory + resources.add(buildDir.getFile(new Path(obj).removeLastSegments(1).append("subdir.mk"))); + // If the parent of the obj doesn't exist, then ensure we're expecting that too... + IPath p = new Path(obj).removeLastSegments(1); + while (p.segmentCount() > 0) { + IFolder folder = buildDir.getFolder(p); + resources.add(folder); + p = p.removeLastSegments(1); + } + } + return resources; + } + + public AbstractBuilderTest() { + super(); + } + + public AbstractBuilderTest(String name) { + super(name); + } + + protected void setWorkspace(String name) { + workspace = name; + projects = new ArrayList(); + } + + protected IProject loadProject(String name) throws CoreException { + ManagedBuildTestHelper.loadProject(name, PATH + "/" + workspace); + IProject project = getWorkspace().getRoot().getProject(name); + assertTrue(project.exists()); + projects.add(project); + project.refreshLocal(IResource.DEPTH_INFINITE, null); + return project; + } + + private List getAllMarkers() throws CoreException { + List markers = new ArrayList(); + for (IProject project : projects) + markers.addAll(Arrays.asList(project.findMarkers(ICModelMarker.C_MODEL_PROBLEM_MARKER, true, IResource.DEPTH_INFINITE))); + return markers; + } + + protected void printAllMarkers() throws CoreException { + List markers = getAllMarkers(); + String[] attributes = new String[] {IMarker.LINE_NUMBER, IMarker.SEVERITY, IMarker.MESSAGE, IMarker.LOCATION /*, ICModelMarker.C_MODEL_MARKER_CONFIGURATION_NAME*/}; + StringBuilder sb = new StringBuilder(); + for (IMarker m : markers) { + // Project + if (m.getResource().getProject() == null) + sb.append("?"); + else + sb.append(m.getResource().getProject().getName()); + // Resource workspace path + sb.append(", "); //$NON-NLS-1$ + sb.append(m.getResource().getFullPath()); + Object[] attrs = m.getAttributes(attributes); + sb.append(":"); //$NON-NLS-1$ + int i = 0; + // line number + if (attrs[i] != null) + sb.append(" line " + attrs[i]); + // severity + if (attrs[++i] != null) { + switch ((Integer)attrs[i++]) { + case IMarker.SEVERITY_ERROR: + sb.append(" ERROR"); + break; + case IMarker.SEVERITY_WARNING: + sb.append(" WARNING"); + break; + case IMarker.SEVERITY_INFO: + sb.append(" INFO"); + break; + } + } + // append the rest of the string fields + do { + if (attrs[i] != null) + sb.append(" " + attrs[i]); + } while (++i < attrs.length); + // Finally print the string + System.err.println(sb.toString()); + sb.setLength(0); + } + } + + protected void setAutoBuilding(boolean value) throws CoreException { + IWorkspace workspace = getWorkspace(); + if (workspace.isAutoBuilding() == value) + return; + IWorkspaceDescription desc = workspace.getDescription(); + desc.setAutoBuilding(value); + workspace.setDescription(desc); + } + + protected IWorkspace getWorkspace() throws CoreException { + return ResourcesPlugin.getWorkspace(); + } + +} \ No newline at end of file diff --git a/build/org.eclipse.cdt.managedbuilder.core.tests/suite/org/eclipse/cdt/managedbuilder/testplugin/ResourceDeltaVerifier.java b/build/org.eclipse.cdt.managedbuilder.core.tests/suite/org/eclipse/cdt/managedbuilder/testplugin/ResourceDeltaVerifier.java new file mode 100644 index 00000000000..81bd53298e2 --- /dev/null +++ b/build/org.eclipse.cdt.managedbuilder.core.tests/suite/org/eclipse/cdt/managedbuilder/testplugin/ResourceDeltaVerifier.java @@ -0,0 +1,850 @@ +/******************************************************************************* + * Copyright (c) 2000, 2011 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * Alex Collins (Broadcom Corp.) + *******************************************************************************/ +package org.eclipse.cdt.managedbuilder.testplugin; + +import java.util.Arrays; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Set; + +import junit.framework.Assert; + +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IResourceChangeEvent; +import org.eclipse.core.resources.IResourceChangeListener; +import org.eclipse.core.resources.IResourceDelta; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; + +/** + * Based on org.eclipse.core.tests.resources.ResourceDeltaVerifier with + * additional support for ignoring changes in certain resources. + * + * Verifies the state of an IResourceDelta by comparing + * it with a client's expectations. The delta is considered valid + * if it contains exactly the set of changes expected by the client, + * and parents of those changes (having ignore filtered resources). + * + *

Example usage: + * + * ResourceDeltaVerifier verifier = new ResourceDeltaVerifier(); + * IResourceChangeListener listener = (IResourceChangeListener)verifier; + * IWorkspace workspace = ResourcesPlugin.getWorkspace(); + * IProject proj = workspace.getRoot().getProject("MyProject"); + * // Assume the project is accessible + * workspace.addResourceChangeListener(listener); + * verifier.addExpectedChange(proj, REMOVED, 0); + * try { + * proj.delete(true, true, null); + * } catch(CoreException e){ + * fail("1.0", e); + * } + * assert("2.0 "+verifier.getMessage(), verifier.isDeltaValid()); + * + */ +public class ResourceDeltaVerifier extends Assert implements IResourceChangeListener { + private class ExpectedChange { + IResource fResource; + IPath movedFromPath; + IPath movedToPath; + int fKind; + int fChangeFlags; + + public ExpectedChange(IResource resource, int kind, int changeFlags, IPath movedFromPath, IPath movedToPath) { + fResource = resource; + fKind = kind; + fChangeFlags = changeFlags; + this.movedFromPath = movedFromPath; + this.movedToPath = movedToPath; + } + + public int getChangeFlags() { + return fChangeFlags; + } + + public IPath getMovedFromPath() { + if ((fChangeFlags & IResourceDelta.MOVED_FROM) != 0) { + return movedFromPath; + } + return null; + } + + public IPath getMovedToPath() { + if ((fChangeFlags & IResourceDelta.MOVED_TO) != 0) { + return movedToPath; + } + return null; + } + + public int getKind() { + return fKind; + } + + @Override + public String toString() { + StringBuffer buf = new StringBuffer("ExpectedChange("); + buf.append(fResource); + buf.append(", "); + buf.append(convertKind(fKind)); + buf.append(", "); + buf.append(convertChangeFlags(fChangeFlags)); + buf.append(")"); + return buf.toString(); + } + + } + + /** + * Table of IPath -> ExpectedChange + */ + private Hashtable fExpectedChanges = new Hashtable(); + boolean fIsDeltaValid = true; + private StringBuffer fMessage = new StringBuffer(); + /** + * The verifier can be in one of three states. In the initial + * state, the verifier is still receiving inputs via the + * addExpectedChange() methods, and the state is RECEIVING_INPUTS. + * After a call to verifyDelta(), the state becomes DELTA_VERIFIED + * The verifier remains in the second state for any number of delta + * verifications. When a getMessage() or isDeltaValid() method is + * called, the verification completes, and the state becomes + * VERIFICATION_COMPLETE. While in this state, any number of + * getMessage() and isDeltaValid() methods can be called. + * While in the third state, any call to addExpectedChange() + * resets the verifier and puts it back in its RECEIVING_INPUTS state. + */ + private static final int RECEIVING_INPUTS = 0; + private static final int DELTA_VERIFIED = 1; + private static final int VERIFICATION_COMPLETE = 2; + + private int fState = RECEIVING_INPUTS; + private Set fIgnoreResources = new HashSet(); + + /** + * @see #addExpectedChange + */ + public void addExpectedChange(IResource[] resources, int status, int changeFlags) { + for (int i = 0; i < resources.length; i++) + addExpectedChange(resources[i], null, status, changeFlags, null, null); + } + + /** + * Adds an expected deletion for the given resource and all children. + */ + public void addExpectedDeletion(IResource resource) { + addExpectedChange(resource, IResourceDelta.REMOVED, 0); + if (resource instanceof IContainer) { + try { + IResource[] children = ((IContainer) resource).members(IContainer.INCLUDE_PHANTOMS | IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS | IContainer.INCLUDE_HIDDEN); + for (int i = 0; i < children.length; i++) { + addExpectedDeletion(children[i]); + } + } catch (CoreException e) { + e.printStackTrace(); + fail("Failed to get children in addExpectedDeletion"); + } + } + } + + /** + * Signals to the comparer that the given resource is expected to + * change in the specified way. The change flags should be set to + * zero if no change is expected. + * @param resource the resource that is expected to change + * @param status the type of change (ADDED, REMOVED, CHANGED) + * @param changeFlags the type of change (CONTENT, SYNC, etc) + * @see IResourceConstants + */ + public void addExpectedChange(IResource resource, int status, int changeFlags) { + addExpectedChange(resource, null, status, changeFlags, null, null); + } + + /** + * Signals to the comparer that the given resource is expected to + * change in the specified way. The change flags should be set to + * zero if no change is expected. + * @param resource the resource that is expected to change + * @param status the type of change (ADDED, REMOVED, CHANGED) + * @param changeFlags the type of change (CONTENT, SYNC, etc) + * @param movedPath or null + * @see IResourceConstants + */ + public void addExpectedChange(IResource resource, int status, int changeFlags, IPath movedFromPath, IPath movedToPath) { + addExpectedChange(resource, null, status, changeFlags, movedFromPath, movedToPath); + } + + /** + * Signals to the comparer that the given resource is expected to + * change in the specified way. The change flags should be set to + * zero if no change is expected. + * @param resource the resource that is expected to change + * @param topLevelParent Do not added expected changes above this parent + * @param status the type of change (ADDED, REMOVED, CHANGED) + * @param changeFlags the type of change (CONTENT, SYNC, etc) + * @param movedPath or null + * @see IResourceConstants + */ + public void addExpectedChange(IResource resource, IResource topLevelParent, int status, int changeFlags) { + addExpectedChange(resource, topLevelParent, status, changeFlags, null, null); + } + + /** + * Signals to the comparer that the given resource is expected to + * change in the specified way. The change flags should be set to + * zero if no change is expected. + * @param resource the resource that is expected to change + * @param topLevelParent Do not added expected changes above this parent + * @param status the type of change (ADDED, REMOVED, CHANGED) + * @param changeFlags the type of change (CONTENT, SYNC, etc) + * @param movedPath or null + * @see IResourceConstants + */ + public void addExpectedChange(IResource resource, IResource topLevelParent, int status, int changeFlags, IPath movedFromPath, IPath movedToPath) { + resetIfNecessary(); + + ExpectedChange expectedChange = new ExpectedChange(resource, status, changeFlags, movedFromPath, movedToPath); + fExpectedChanges.put(resource.getFullPath(), expectedChange); + + // Add changes for all resources above this one and limited by the topLevelParent + IResource parentResource = resource.getParent(); + IResource limit = (topLevelParent == null) ? null : topLevelParent.getParent(); + while (parentResource != null && !parentResource.equals(limit)) { + //change table is keyed by resource path + IPath key = parentResource.getFullPath(); + if (fExpectedChanges.get(key) == null) { + ExpectedChange parentExpectedChange = new ExpectedChange(parentResource, IResourceDelta.CHANGED, 0, null, null); + fExpectedChanges.put(key, parentExpectedChange); + } + parentResource = parentResource.getParent(); + } + } + + private void checkChanges(IResourceDelta delta) { + IResource resource = delta.getResource(); + + ExpectedChange expectedChange = (ExpectedChange) fExpectedChanges.remove(resource.getFullPath()); + + int status = delta.getKind(); + int changeFlags = delta.getFlags(); + + if (status == IResourceDelta.NO_CHANGE) + return; + + if (expectedChange == null) { + recordMissingExpectedChange(status, changeFlags); + } else { + int expectedStatus = expectedChange.getKind(); + int expectedChangeFlags = expectedChange.getChangeFlags(); + if (status != expectedStatus || changeFlags != expectedChangeFlags) { + recordConflictingChange(expectedStatus, status, expectedChangeFlags, changeFlags); + } + } + } + + private void checkChildren(IResourceDelta delta) { + IResourceDelta[] affectedChildren = delta.getAffectedChildren(IResourceDelta.ALL_WITH_PHANTOMS, IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS | IContainer.INCLUDE_HIDDEN); + IResourceDelta[] addedChildren = delta.getAffectedChildren(IResourceDelta.ADDED, IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS | IContainer.INCLUDE_HIDDEN); + IResourceDelta[] changedChildren = delta.getAffectedChildren(IResourceDelta.CHANGED, IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS | IContainer.INCLUDE_HIDDEN); + IResourceDelta[] removedChildren = delta.getAffectedChildren(IResourceDelta.REMOVED, IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS | IContainer.INCLUDE_HIDDEN); + + Hashtable h = new Hashtable(affectedChildren.length + 1); + + for (int i = 0; i < addedChildren.length; ++i) { + IResourceDelta childDelta1 = addedChildren[i]; + IResource childResource = childDelta1.getResource(); + IResourceDelta childDelta2 = (IResourceDelta) h.get(childResource); + if (childDelta2 != null) { + recordDuplicateChild(childResource.getFullPath(), childDelta2.getKind(), childDelta1.getKind(), IResourceDelta.ADDED); + } else { + h.put(childResource, childDelta1); + } + if (childDelta1.getKind() != IResourceDelta.ADDED) { + recordIllegalChild(childResource.getFullPath(), IResourceDelta.ADDED, childDelta1.getKind()); + } + } + + for (int i = 0; i < changedChildren.length; ++i) { + IResourceDelta childDelta1 = changedChildren[i]; + IResource childResource = childDelta1.getResource(); + IResourceDelta childDelta2 = (IResourceDelta) h.get(childResource); + if (childDelta2 != null) { + recordDuplicateChild(childResource.getFullPath(), childDelta2.getKind(), childDelta1.getKind(), IResourceDelta.CHANGED); + } else { + h.put(childResource, childDelta1); + } + if (childDelta1.getKind() != IResourceDelta.CHANGED) { + recordIllegalChild(childResource.getFullPath(), IResourceDelta.CHANGED, childDelta1.getKind()); + } + } + + for (int i = 0; i < removedChildren.length; ++i) { + IResourceDelta childDelta1 = removedChildren[i]; + IResource childResource = childDelta1.getResource(); + IResourceDelta childDelta2 = (IResourceDelta) h.get(childResource); + if (childDelta2 != null) { + recordDuplicateChild(childResource.getFullPath(), childDelta2.getKind(), childDelta1.getKind(), IResourceDelta.REMOVED); + } else { + h.put(childResource, childDelta1); + } + if (childDelta1.getKind() != IResourceDelta.REMOVED) { + recordIllegalChild(childResource.getFullPath(), IResourceDelta.REMOVED, childDelta1.getKind()); + } + } + + for (int i = 0; i < affectedChildren.length; ++i) { + IResourceDelta childDelta1 = affectedChildren[i]; + IResource childResource = childDelta1.getResource(); + IResourceDelta childDelta2 = (IResourceDelta) h.remove(childResource); + if (childDelta2 == null) { + int kind = childDelta1.getKind(); + //these kinds should have been added to h earlier + if (kind == IResourceDelta.ADDED || kind == IResourceDelta.REMOVED || kind == IResourceDelta.CHANGED) { + recordMissingChild(childResource.getFullPath(), childDelta1.getKind(), false); + } + } + } + + Enumeration keys = h.keys(); + while (keys.hasMoreElements()) { + IResource childResource = (IResource) keys.nextElement(); + IResourceDelta childDelta = (IResourceDelta) h.get(childResource); + recordMissingChild(childResource.getFullPath(), childDelta.getKind(), true); + } + + for (int i = 0; i < affectedChildren.length; ++i) { + internalVerifyDelta(affectedChildren[i]); + } + + keys = h.keys(); + while (keys.hasMoreElements()) { + IResource childResource = (IResource) keys.nextElement(); + IResourceDelta childDelta = (IResourceDelta) h.get(childResource); + internalVerifyDelta(childDelta); + } + } + + private void checkPaths(IResourceDelta delta) { + IResource resource = delta.getResource(); + + IPath expectedFullPath = resource.getFullPath(); + IPath actualFullPath = delta.getFullPath(); + if (!expectedFullPath.equals(actualFullPath)) { + recordConflictingFullPaths(expectedFullPath, actualFullPath); + } + + IPath expectedProjectRelativePath = resource.getProjectRelativePath(); + IPath actualProjectRelativePath = delta.getProjectRelativePath(); + if (expectedProjectRelativePath != actualProjectRelativePath) { + if (expectedProjectRelativePath == null || !expectedProjectRelativePath.equals(actualProjectRelativePath)) { + recordConflictingProjectRelativePaths(expectedProjectRelativePath, actualProjectRelativePath); + } + } + + ExpectedChange expectedChange = (ExpectedChange) fExpectedChanges.get(resource.getFullPath()); + + if (expectedChange != null) { + IPath expectedMovedFromPath = expectedChange.getMovedFromPath(); + IPath actualMovedFromPath = delta.getMovedFromPath(); + if (expectedMovedFromPath != actualMovedFromPath) { + if (expectedMovedFromPath == null || !expectedMovedFromPath.equals(actualMovedFromPath)) { + recordConflictingMovedFromPaths(expectedMovedFromPath, actualMovedFromPath); + } + } + + IPath expectedMovedToPath = expectedChange.getMovedToPath(); + IPath actualMovedToPath = delta.getMovedToPath(); + if (expectedMovedToPath != actualMovedToPath) { + if (expectedMovedToPath == null || !expectedMovedToPath.equals(actualMovedToPath)) { + recordConflictingMovedToPaths(expectedMovedToPath, actualMovedToPath); + } + } + } + } + + String convertChangeFlags(int changeFlags) { + if (changeFlags == 0) { + return "0"; + } + StringBuffer buf = new StringBuffer(); + + if ((changeFlags & IResourceDelta.CONTENT) != 0) { + changeFlags ^= IResourceDelta.CONTENT; + buf.append("CONTENT | "); + } + if ((changeFlags & IResourceDelta.MOVED_FROM) != 0) { + changeFlags ^= IResourceDelta.MOVED_FROM; + buf.append("MOVED_FROM | "); + } + if ((changeFlags & IResourceDelta.MOVED_TO) != 0) { + changeFlags ^= IResourceDelta.MOVED_TO; + buf.append("MOVED_TO | "); + } + if ((changeFlags & IResourceDelta.OPEN) != 0) { + changeFlags ^= IResourceDelta.OPEN; + buf.append("OPEN | "); + } + if ((changeFlags & IResourceDelta.TYPE) != 0) { + changeFlags ^= IResourceDelta.TYPE; + buf.append("TYPE | "); + } + if ((changeFlags & IResourceDelta.MARKERS) != 0) { + changeFlags ^= IResourceDelta.MARKERS; + buf.append("MARKERS | "); + } + if ((changeFlags & IResourceDelta.REPLACED) != 0) { + changeFlags ^= IResourceDelta.REPLACED; + buf.append("REPLACED | "); + } + if ((changeFlags & IResourceDelta.ENCODING) != 0) { + changeFlags ^= IResourceDelta.ENCODING; + buf.append("ENCODING | "); + } + if ((changeFlags & IResourceDelta.DERIVED_CHANGED) != 0) { + changeFlags ^= IResourceDelta.DERIVED_CHANGED; + buf.append("DERIVED_CHANGED | "); + } + if ((changeFlags & IResourceDelta.DESCRIPTION) != 0) { + changeFlags ^= IResourceDelta.DESCRIPTION; + buf.append("DESCRIPTION | "); + } + if ((changeFlags & IResourceDelta.SYNC) != 0) { + changeFlags ^= IResourceDelta.SYNC; + buf.append("SYNC | "); + } + + if (changeFlags != 0) { + buf.append(changeFlags); + buf.append(" | "); + } + + String result = buf.toString(); + + if (result.length() != 0) { + result = result.substring(0, result.length() - 3); + } + + return result; + } + + String convertKind(int kind) { + switch (kind) { + case IResourceDelta.ADDED : + return "ADDED"; + case IResourceDelta.CHANGED : + return "CHANGED"; + case IResourceDelta.REMOVED : + return "REMOVED"; + case IResourceDelta.ADDED_PHANTOM : + return "ADDED_PHANTOM"; + case IResourceDelta.REMOVED_PHANTOM : + return "REMOVED_PHANTOM"; + default : + return "Unknown(" + kind + ")"; + } + } + + /** + * Called to cleanup internal state and make sure expectations + * are met after iterating over a resource delta. + */ + private void finishVerification() { + Hashtable resourcePaths = new Hashtable(); + + Enumeration keys = fExpectedChanges.keys(); + while (keys.hasMoreElements()) { + Object key = keys.nextElement(); + resourcePaths.put(key, key); + } + + keys = resourcePaths.keys(); + while (keys.hasMoreElements()) { + IPath resourcePath = (IPath) keys.nextElement(); + + fMessage.append("Checking expectations for "); + fMessage.append(resourcePath); + fMessage.append("\n"); + + ExpectedChange expectedChange = (ExpectedChange) fExpectedChanges.remove(resourcePath); + if (expectedChange != null) { + // List an ignored resource + if (fIgnoreResources.contains(expectedChange.fResource)) + fMessage.append("\tIgnored\n"); + else + recordMissingActualChange(expectedChange.getKind(), expectedChange.getChangeFlags()); + } + } + } + + /** + * Returns a message that describes the result of the resource + * delta verification checks. + */ + public String getMessage() { + if (fState == RECEIVING_INPUTS) { + if (hasExpectedChanges()) { + fail("Verifier has not yet been given a resource delta"); + } else { + fState = DELTA_VERIFIED; + } + } + if (fState == DELTA_VERIFIED) { + finishVerification(); + fState = VERIFICATION_COMPLETE; + } + return fMessage.toString(); + } + + /** + * Returns true if this verifier has received a delta notification + * since the last reset, and false otherwise. + */ + public boolean hasBeenNotified() { + return fState == DELTA_VERIFIED; + } + + /** + * Returns true if this verifier currently has an expected + * changes, and false otherwise. + */ + public boolean hasExpectedChanges() { + return !fExpectedChanges.isEmpty(); + } + + /** + * Compares the given delta with the expected changes. Recursively + * compares child deltas. + */ + void internalVerifyDelta(IResourceDelta delta) { + try { + // FIXME: bogus + if (delta == null) + return; + fMessage.append("Verifying delta for "); + fMessage.append(delta.getFullPath()); + fMessage.append("\n"); + + // Don't check changes for the workspace + // or for ignored resources + if (delta.getResource() != null && !fIgnoreResources.contains(delta.getResource())) { + checkPaths(delta); + checkChanges(delta); + } + + checkChildren(delta); + } catch (Exception e) { + e.printStackTrace(); + fMessage.append("Exception during event notification:" + e.getMessage()); + fIsDeltaValid = false; + } + } + + /** + * Returns whether the resource delta passed all verification + * checks. + */ + public boolean isDeltaValid() { + if (fState == RECEIVING_INPUTS) { + if (hasExpectedChanges()) { + fail("Verifier has not yet been given a resource delta"); + } else { + fState = DELTA_VERIFIED; + } + } + if (fState == DELTA_VERIFIED) { + finishVerification(); + fState = VERIFICATION_COMPLETE; + } + return fIsDeltaValid; + } + + /** + * Tests message formatting. This main method does not represent the + * intended use of the ResourceDeltaVerifier. See the class comment + * for instructions on using the verifier. + */ + public static void main(String[] args) { + ResourceDeltaVerifier comparer = new ResourceDeltaVerifier(); + + int status = IResourceDelta.CHANGED; + int changeFlags = IResourceDelta.CONTENT; + int expectedStatus = IResourceDelta.CHANGED; + int actualStatus = IResourceDelta.REMOVED; + int expectedChangeFlags = IResourceDelta.OPEN; + int actualChangeFlags = 0; + int formerChildStatus = expectedStatus; + int latterChildStatus = actualStatus; + + IPath path = new Path("/a/b/c"); + IPath path2 = new Path("/a/b/d"); + IPath expectedFullPath = path; + IPath actualFullPath = path2; + IPath expectedMovedFromPath = path; + IPath actualMovedFromPath = path2; + IPath expectedMovedToPath = path; + IPath actualMovedToPath = path2; + IPath expectedProjectRelativePath = new Path("b/c"); + IPath actualProjectRelativePath = new Path("b/d"); + + comparer.fMessage.append("Checking delta for "); + comparer.fMessage.append(path); + comparer.fMessage.append("\n"); + + comparer.recordConflictingChange(expectedStatus, actualStatus, expectedChangeFlags, actualChangeFlags); + comparer.recordConflictingFullPaths(expectedFullPath, actualFullPath); + comparer.recordConflictingMovedFromPaths(expectedMovedFromPath, actualMovedFromPath); + comparer.recordConflictingMovedToPaths(expectedMovedToPath, actualMovedToPath); + comparer.recordConflictingProjectRelativePaths(expectedProjectRelativePath, actualProjectRelativePath); + comparer.recordDuplicateChild(path, formerChildStatus, latterChildStatus, expectedStatus); + comparer.recordIllegalChild(path, expectedStatus, actualStatus); + comparer.recordMissingActualChange(status, changeFlags); + comparer.recordMissingChild(path, status, true); + comparer.recordMissingChild(path, status, false); + comparer.recordMissingExpectedChange(status, changeFlags); + + System.out.print(comparer.fMessage.toString()); + } + + private void recordConflictingChange(int expectedKind, int kind, int expectedChangeFlags, int changeFlags) { + fIsDeltaValid = false; + + fMessage.append("\tConflicting change\n"); + + if (expectedKind != kind) { + fMessage.append("\t\tExpected kind: <"); + fMessage.append(convertKind(expectedKind)); + fMessage.append("> actual kind: <"); + fMessage.append(convertKind(kind)); + fMessage.append(">\n"); + } + + if (expectedChangeFlags != changeFlags) { + fMessage.append("\t\tExpected change flags: <"); + fMessage.append(convertChangeFlags(expectedChangeFlags)); + fMessage.append("> actual change flags: <"); + fMessage.append(convertChangeFlags(changeFlags)); + fMessage.append(">\n"); + } + } + + private void recordConflictingFullPaths(IPath expectedFullPath, IPath actualFullPath) { + fIsDeltaValid = false; + + fMessage.append("\tConflicting full paths\n"); + + fMessage.append("\t\tExpected full path: "); + fMessage.append(expectedFullPath); + fMessage.append("\n"); + + fMessage.append("\t\tActual full path: "); + fMessage.append(actualFullPath); + fMessage.append("\n"); + } + + private void recordConflictingMovedFromPaths(IPath expectedMovedFromPath, IPath actualMovedFromPath) { + fIsDeltaValid = false; + + fMessage.append("\tConflicting moved from paths\n"); + + fMessage.append("\t\tExpected moved from path: "); + fMessage.append(expectedMovedFromPath); + fMessage.append("\n"); + + fMessage.append("\t\tActual moved from path: "); + fMessage.append(actualMovedFromPath); + fMessage.append("\n"); + } + + private void recordConflictingMovedToPaths(IPath expectedMovedToPath, IPath actualMovedToPath) { + fIsDeltaValid = false; + + fMessage.append("\tConflicting moved to paths\n"); + + fMessage.append("\t\tExpected moved to path: "); + fMessage.append(expectedMovedToPath); + fMessage.append("\n"); + + fMessage.append("\t\tActual moved to path: "); + fMessage.append(actualMovedToPath); + fMessage.append("\n"); + } + + private void recordConflictingProjectRelativePaths(IPath expectedProjectRelativePath, IPath actualProjectRelativePath) { + fIsDeltaValid = false; + + fMessage.append("\tConflicting project relative paths\n"); + + fMessage.append("\t\tExpected project relative path: "); + fMessage.append(expectedProjectRelativePath); + fMessage.append("\n"); + + fMessage.append("\t\tActual project relative path: "); + fMessage.append(actualProjectRelativePath); + fMessage.append("\n"); + } + + private void recordDuplicateChild(IPath path, int formerChildKind, int latterChildKind, int expectedKind) { + fIsDeltaValid = false; + + fMessage.append("\tDuplicate child: "); + fMessage.append(path); + fMessage.append("\n"); + + fMessage.append("\t\tProduced by IResourceDelta.get"); + + switch (expectedKind) { + case IResourceDelta.ADDED : + fMessage.append("Added"); + break; + case IResourceDelta.CHANGED : + fMessage.append("Changed"); + break; + case IResourceDelta.REMOVED : + fMessage.append("Removed"); + break; + } + + fMessage.append("Children()\n"); + + fMessage.append("\t\tFormer child's status: "); + fMessage.append(convertKind(formerChildKind)); + fMessage.append("\n"); + + fMessage.append("\t\tLatter child's status: "); + fMessage.append(convertKind(latterChildKind)); + fMessage.append("\n"); + } + + private void recordIllegalChild(IPath path, int expectedKind, int actualKind) { + fIsDeltaValid = false; + + fMessage.append("\tIllegal child: "); + fMessage.append(path); + fMessage.append("\n"); + + fMessage.append("\t\tProduced by IResourceDelta.get"); + + switch (expectedKind) { + case IResourceDelta.ADDED : + fMessage.append("Added"); + break; + case IResourceDelta.CHANGED : + fMessage.append("Changed"); + break; + case IResourceDelta.REMOVED : + fMessage.append("Removed"); + break; + } + + fMessage.append("Children()\n"); + + fMessage.append("\t\tIlleagal child's status: "); + fMessage.append(convertKind(actualKind)); + fMessage.append("\n"); + } + + private void recordMissingActualChange(int kind, int changeFlags) { + fIsDeltaValid = false; + + fMessage.append("\tMissing actual change\n"); + fMessage.append("\t\tExpected kind: <"); + fMessage.append(convertKind(kind)); + fMessage.append(">\n"); + fMessage.append("\t\tExpected change flags: <"); + fMessage.append(convertChangeFlags(changeFlags)); + fMessage.append(">\n"); + } + + private void recordMissingChild(IPath path, int kind, boolean isMissingFromAffectedChildren) { + fIsDeltaValid = false; + + fMessage.append("\tMissing child: "); + fMessage.append(path); + fMessage.append("\n"); + + fMessage.append("\t\tfrom IResourceDelta.getAffectedChildren("); + + if (!isMissingFromAffectedChildren) { + switch (kind) { + case IResourceDelta.ADDED : + fMessage.append("ADDED"); + break; + case IResourceDelta.CHANGED : + fMessage.append("CHANGED"); + break; + case IResourceDelta.REMOVED : + fMessage.append("REMOVED"); + break; + default : + fMessage.append(kind); + } + } + + fMessage.append(")\n"); + } + + private void recordMissingExpectedChange(int kind, int changeFlags) { + fIsDeltaValid = false; + + fMessage.append("\tMissing expected change\n"); + fMessage.append("\t\tActual kind: <"); + fMessage.append(convertKind(kind)); + fMessage.append(">\n"); + fMessage.append("\t\tActual change flags: <"); + fMessage.append(convertChangeFlags(changeFlags)); + fMessage.append(">\n"); + } + + /** + * Resets the listener to its initial state. + */ + public void reset() { + fExpectedChanges.clear(); + fIgnoreResources.clear(); + fIsDeltaValid = true; + fMessage.setLength(0); + fState = RECEIVING_INPUTS; + } + + private void resetIfNecessary() { + if (fState == DELTA_VERIFIED) { + reset(); + } + } + + /** + * Part of the IResourceChangedListener interface. + * @see IResourceChangedListener + */ + public void resourceChanged(IResourceChangeEvent e) { + fMessage.append("Resource Changed Delta\n"); + verifyDelta(e.getDelta()); + } + + /** + * Compares the given delta with the expected changes. Recursively + * compares child deltas. + */ + public void verifyDelta(IResourceDelta delta) { + internalVerifyDelta(delta); + fState = DELTA_VERIFIED; + } + + /** + * Add resources whose changes should be ignored + */ + public void addIgnore(IResource[] resources) { + fIgnoreResources.addAll(Arrays.asList(resources)); + } +} diff --git a/build/org.eclipse.cdt.managedbuilder.core.tests/suite/org/eclipse/cdt/managedbuilder/tests/suite/AllManagedBuildTests.java b/build/org.eclipse.cdt.managedbuilder.core.tests/suite/org/eclipse/cdt/managedbuilder/tests/suite/AllManagedBuildTests.java index be292e9574b..c6f726efc71 100644 --- a/build/org.eclipse.cdt.managedbuilder.core.tests/suite/org/eclipse/cdt/managedbuilder/tests/suite/AllManagedBuildTests.java +++ b/build/org.eclipse.cdt.managedbuilder.core.tests/suite/org/eclipse/cdt/managedbuilder/tests/suite/AllManagedBuildTests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2004, 2009 IBM Corporation and others. + * Copyright (c) 2004, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -19,6 +19,7 @@ import org.eclipse.cdt.build.core.scannerconfig.tests.CfgScannerConfigProfileMan import org.eclipse.cdt.build.core.scannerconfig.tests.GCCSpecsConsoleParserTest; import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.dom.IPDOMManager; +import org.eclipse.cdt.managedbuilder.core.regressions.RegressionTests; import org.eclipse.cdt.managedbuilder.core.tests.BuildDescriptionModelTests; import org.eclipse.cdt.managedbuilder.core.tests.BuildSystem40Tests; import org.eclipse.cdt.managedbuilder.core.tests.ManagedBuildCoreTests; @@ -85,6 +86,10 @@ public class AllManagedBuildTests { suite.addTest(CProjectDescriptionSerializationTests.suite()); suite.addTest(OptionStringListValueTests.suite()); suite.addTest(ProjectModelTests.suite()); + + // regression tests + suite.addTest(RegressionTests.suite()); + //$JUnit-END$ return suite; } diff --git a/build/org.eclipse.cdt.managedbuilder.core.tests/tests/org/eclipse/cdt/managedbuilder/core/regressions/Bug_303953.java b/build/org.eclipse.cdt.managedbuilder.core.tests/tests/org/eclipse/cdt/managedbuilder/core/regressions/Bug_303953.java new file mode 100644 index 00000000000..1983432e883 --- /dev/null +++ b/build/org.eclipse.cdt.managedbuilder.core.tests/tests/org/eclipse/cdt/managedbuilder/core/regressions/Bug_303953.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2011 Broadcom Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Broadcom Corporation - Initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.managedbuilder.core.regressions; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import org.eclipse.cdt.managedbuilder.testplugin.AbstractBuilderTest; +import org.eclipse.cdt.managedbuilder.testplugin.ResourceDeltaVerifier; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IResourceDelta; +import org.eclipse.core.resources.IncrementalProjectBuilder; +import org.eclipse.core.runtime.CoreException; + +/** + * Tests that removing the last source file from a directory + * causes the subdir.mk to be regenerated, and associated dervied + * files should be deleted. + */ +public class Bug_303953 extends AbstractBuilderTest { + + public void testBuildAfterSourcefileDelete() throws CoreException { + setWorkspace("regressions"); + final IProject app = loadProject("helloworldC"); + + List buildOutputResources = new ArrayList(); + buildOutputResources.addAll(getProjectBuildExeResources("helloworldC", "Debug", "src/helloworldC")); + + // Ensure Debug is the active configuration + setActiveConfigurationByName(app, "Debug"); + + ResourceDeltaVerifier verifier = new ResourceDeltaVerifier(); + verifier.addExpectedChange(buildOutputResources.toArray(new IResource[buildOutputResources.size()]), IResourceDelta.ADDED, IResourceDelta.NO_CHANGE); + verifier.addIgnore(new IResource[]{ + getWorkspace().getRoot(), app, + app.getFile(".project")}); + verifyBuild(app, IncrementalProjectBuilder.FULL_BUILD, verifier); + + // Delete helloworldC + IFile srcFile = app.getFile("src/helloworldC.c"); + assertTrue("1.1", srcFile.exists()); + srcFile.delete(false, null); + + // Build again + // - derived files from helloworldC.c should be removed + // - subdir.mk should be changed + // - ignore other changes in the build tree (not the subject of this bug...) + + verifier = new ResourceDeltaVerifier(); + // These files should be removed + IResource[] removed = new IResource[] {app.getFile("Debug/src/helloworldC.o"), + app.getFile("Debug/src/helloworldC.d")}; + verifier.addExpectedChange(removed, IResourceDelta.REMOVED, IResourceDelta.NO_CHANGE); + // subdir.mk has been updated + IResource[] expected = new IResource[] {app.getFile("Debug/src/subdir.mk")}; + verifier.addExpectedChange(expected, IResourceDelta.CHANGED, IResourceDelta.CONTENT); + + // Ignore other resources + Collection ignored = getProjectBuildExeResources("helloworldC", "Debug", "src/helloworldC"); + ignored.removeAll(Arrays.asList(removed)); + ignored.removeAll(Arrays.asList(expected)); + ignored.add(getWorkspace().getRoot()); + ignored.add(app); + verifier.addIgnore(ignored.toArray(new IResource[ignored.size()])); + verifyBuild(app, IncrementalProjectBuilder.INCREMENTAL_BUILD, verifier); + } + +} diff --git a/build/org.eclipse.cdt.managedbuilder.core.tests/tests/org/eclipse/cdt/managedbuilder/core/regressions/RegressionTests.java b/build/org.eclipse.cdt.managedbuilder.core.tests/tests/org/eclipse/cdt/managedbuilder/core/regressions/RegressionTests.java new file mode 100644 index 00000000000..d7aa1d5c24a --- /dev/null +++ b/build/org.eclipse.cdt.managedbuilder.core.tests/tests/org/eclipse/cdt/managedbuilder/core/regressions/RegressionTests.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2011 Broadcom Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Broadcom Corporation - Initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.managedbuilder.core.regressions; + +import org.eclipse.cdt.managedbuilder.core.regressions.Bug_303953; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +/** + * Regression tests for builder bugs + */ +public class RegressionTests extends TestCase { + + public static Test suite() { + TestSuite suite = new TestSuite(RegressionTests.class.getName()); + + // Test that common builder does the correct amount of work. + suite.addTestSuite(Bug_303953.class); + + return suite; + } + + public RegressionTests() { + super(null); + } + + public RegressionTests(String name) { + super(name); + } +} 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 0d3719da7a3..2cdf5e8885b 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 @@ -10,6 +10,7 @@ * ARM Ltd. - Minor changes to echo commands * IBM Corporation * Anna Dushistova (Mentor Graphics) - [307244] extend visibility of fields in GnuMakefileGenerator + * James Blackburn (Broadcom Corp.) *******************************************************************************/ package org.eclipse.cdt.managedbuilder.makegen.gnu; @@ -20,10 +21,12 @@ import java.io.InputStreamReader; import java.io.Reader; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map.Entry; import java.util.Set; @@ -308,7 +311,7 @@ public class GnuMakefileGenerator implements IManagedBuilderMakefileGenerator2 { private static final String BUILD_TARGETS = COMMENT + ".build.toptargets"; //$NON-NLS-1$ private static final String SRC_LISTS = COMMENT + ".source.list"; //$NON-NLS-1$ - private static final String EMPTY_STRING = new String(); + private static final String EMPTY_STRING = ""; //$NON-NLS-1$ private static final String[] EMPTY_STRING_ARRAY = new String[0]; private static final String OBJS_MACRO = "OBJS"; //$NON-NLS-1$ @@ -349,14 +352,16 @@ public class GnuMakefileGenerator implements IManagedBuilderMakefileGenerator2 { // private IManagedBuildInfo info; // private IConfiguration cfg private Vector invalidDirList; - private Vector modifiedList; + /** Collection of Folders in which sources files have been modified */ + private Collection modifiedList; private IProgressMonitor monitor; private IProject project; private IResource[] projectResources; private Vector ruleList; private Vector depLineList; // String's of additional dependency lines private Vector depRuleList; // String's of rules for generating dependency files - private Vector subdirList; + /** Collection of Containers which contribute source files to the build */ + private Collection subdirList; private IPath topBuildDir; // Build directory - relative to the workspace // private Set outputExtensionsSet; //=== Maps of macro names (String) to values (List) @@ -626,6 +631,10 @@ public class GnuMakefileGenerator implements IManagedBuilderMakefileGenerator2 { project.accept(resourceVisitor, IResource.NONE); checkCancel(); + // Bug 303953: Ensure that if all resources have been removed from a folder, than the folder still + // appears in the subdir list so it's subdir.mk is correctly regenerated + getSubdirList().addAll(getModifiedList()); + // Make sure there is something to build if (getSubdirList().isEmpty()) { String info = ManagedMakeMessages.getFormattedString("MakefileGenerator.warning.no.source", project.getName()); //$NON-NLS-1$ @@ -4151,10 +4160,9 @@ public class GnuMakefileGenerator implements IManagedBuilderMakefileGenerator2 { */ protected void appendBuildSubdirectory(IResource resource) { IContainer container = resource.getParent(); - // Only add the container once - if (!getSubdirList().contains(container)) { - getSubdirList().add(container); - } + // Only add the container once + if (!getSubdirList().contains(container)) + getSubdirList().add(container); } /** @@ -4475,25 +4483,20 @@ public class GnuMakefileGenerator implements IManagedBuilderMakefileGenerator2 { } /** - * - * @return Vector + * @return Collection of Containers which contain modified source files */ - private Vector getModifiedList() { - if (modifiedList == null) { - modifiedList = new Vector(); - } + private Collection getModifiedList() { + if (modifiedList == null) + modifiedList = new LinkedHashSet(); return modifiedList; } /** - * Answers the list of subdirectories (IContainer's) contributing source code to the build - * - * @return List + * @return Collection of subdirectories (IContainers) contributing source code to the build */ - private Vector getSubdirList() { - if (subdirList == null) { - subdirList = new Vector(); - } + private Collection getSubdirList() { + if (subdirList == null) + subdirList = new LinkedHashSet(); return subdirList; }