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