### Eclipse Workspace Patch 1.0 #P org.eclipse.pde.core Index: src/org/eclipse/pde/internal/core/ICoreConstants.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/ICoreConstants.java,v retrieving revision 1.55 diff -u -r1.55 ICoreConstants.java --- src/org/eclipse/pde/internal/core/ICoreConstants.java 19 Apr 2007 22:29:49 -0000 1.55 +++ src/org/eclipse/pde/internal/core/ICoreConstants.java 8 May 2007 05:01:39 -0000 @@ -119,6 +119,7 @@ public static IPath PLUGIN_PATH = new Path("plugin.xml"); //$NON-NLS-1$ public static IPath FRAGMENT_PATH = new Path("fragment.xml"); //$NON-NLS-1$ public static IPath FEATURE_PATH = new Path("feature.xml"); //$NON-NLS-1$ + public static IPath BUILD_PROPERTIES_PATH = new Path("build.properties"); //$NON-NLS-1$ } Index: src/org/eclipse/pde/internal/core/PDECore.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/PDECore.java,v retrieving revision 1.100 diff -u -r1.100 PDECore.java --- src/org/eclipse/pde/internal/core/PDECore.java 3 Feb 2007 05:44:29 -0000 1.100 +++ src/org/eclipse/pde/internal/core/PDECore.java 8 May 2007 05:01:39 -0000 @@ -28,6 +28,7 @@ import org.eclipse.pde.core.plugin.IPluginModelBase; import org.eclipse.pde.internal.core.builders.CompilerFlags; import org.eclipse.pde.internal.core.builders.FeatureRebuilder; +import org.eclipse.pde.internal.core.builders.PluginRebuilder; import org.eclipse.pde.internal.core.schema.SchemaRegistry; import org.eclipse.update.configurator.ConfiguratorUtils; import org.osgi.framework.BundleContext; @@ -42,7 +43,8 @@ public static final String BINARY_REPOSITORY_PROVIDER = PLUGIN_ID + "." + "BinaryRepositoryProvider"; //$NON-NLS-1$ //$NON-NLS-2$ public static final QualifiedName EXTERNAL_PROJECT_PROPERTY = new QualifiedName(PLUGIN_ID, "imported"); //$NON-NLS-1$ - + public static final QualifiedName TOUCH_PROJECT = new QualifiedName(PLUGIN_ID, "TOUCH_PROJECT"); //$NON-NLS-1$ + // Shared instance private static PDECore inst; @@ -133,6 +135,8 @@ private FeatureRebuilder fFeatureRebuilder; + private PluginRebuilder fPluginRebuilder; + public PDECore() { inst = this; } @@ -227,6 +231,8 @@ CompilerFlags.initializeDefaults(); fJavaElementChangeListener = new JavaElementChangeListener(); fJavaElementChangeListener.start(); + fPluginRebuilder = new PluginRebuilder(); + fPluginRebuilder.start(); fFeatureRebuilder = new FeatureRebuilder(); fFeatureRebuilder.start(); } @@ -239,6 +245,7 @@ PDECore.getDefault().savePluginPreferences(); fJavaElementChangeListener.shutdown(); + fPluginRebuilder.stop(); fFeatureRebuilder.stop(); if (fSchemaRegistry != null) { Index: src/org/eclipse/pde/internal/core/builders/ManifestConsistencyChecker.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/builders/ManifestConsistencyChecker.java,v retrieving revision 1.15 diff -u -r1.15 ManifestConsistencyChecker.java --- src/org/eclipse/pde/internal/core/builders/ManifestConsistencyChecker.java 20 Feb 2007 00:13:01 -0000 1.15 +++ src/org/eclipse/pde/internal/core/builders/ManifestConsistencyChecker.java 8 May 2007 05:01:39 -0000 @@ -17,48 +17,234 @@ import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceDelta; +import org.eclipse.core.resources.IResourceDeltaVisitor; import org.eclipse.core.resources.IncrementalProjectBuilder; 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.Platform; import org.eclipse.osgi.util.NLS; +import org.eclipse.pde.core.plugin.IPluginModelBase; +import org.eclipse.pde.core.plugin.PluginRegistry; +import org.eclipse.pde.internal.core.ICoreConstants; import org.eclipse.pde.internal.core.PDECore; -import org.eclipse.pde.internal.core.WorkspaceModelManager; import org.eclipse.pde.internal.core.PDECoreMessages; +import org.eclipse.pde.internal.core.WorkspaceModelManager; +import org.eclipse.pde.internal.core.ibundle.IBundlePluginModelBase; import org.osgi.framework.Bundle; public class ManifestConsistencyChecker extends IncrementalProjectBuilder { - protected IProject[] build(int kind, Map args, IProgressMonitor monitor) - throws CoreException { + + private int MANIFEST = 0x1; + private int EXTENSIONS = 0x2; + private int BUILD = 0x4; + + private static boolean DEBUG = false; + private static IProject[] EMPTY_LIST = new IProject[0]; + + static { + DEBUG = PDECore.getDefault().isDebugging() + && "true".equals(Platform.getDebugOption("org.eclipse.pde.core/validation")); //$NON-NLS-1$ //$NON-NLS-2$ + } + + private SelfVisitor fSelfVisitor = new SelfVisitor(); + + private ClassChangeVisitor fClassFileVisitor = new ClassChangeVisitor(); + + class ClassChangeVisitor implements IResourceDeltaVisitor { + boolean hasChanged = false; + boolean veto = false; + + public boolean visit(IResourceDelta delta) throws CoreException { + if (delta != null && !veto) { + int kind = delta.getKind(); + if (kind == IResourceDelta.CHANGED) { + IResource resource = delta.getResource(); + if (resource instanceof IFile) { + String extension = resource.getFileExtension(); + // do nothing if a java file has changed. + if ("java".equals(extension)) { //$NON-NLS-1$ + veto = true; + } else if ("class".equals(extension) && !hasChanged) { //$NON-NLS-1$ + // only interested in .class file changes + hasChanged = true; + } + } + return !veto; + } + } + return false; + } + + public void reset() { + veto = false; + hasChanged = false; + } - if (PDECore.getDefault().getBundle().getState() != Bundle.ACTIVE || monitor.isCanceled()) - return new IProject[0]; + public boolean hasChanged() { + return hasChanged && !veto; + } + + } + + class SelfVisitor implements IResourceDeltaVisitor { + int type = 0; + public boolean visit(IResourceDelta delta) throws CoreException { + if (delta != null && type != (MANIFEST|EXTENSIONS|BUILD)) { + int kind = delta.getKind(); + if (kind == IResourceDelta.ADDED || kind == IResourceDelta.REMOVED) { + type = MANIFEST | EXTENSIONS | BUILD; + if (DEBUG) { + System.out.print("Needs to rebuild project [" + getProject().getName() + "]: "); //$NON-NLS-1$ //$NON-NLS-2$ + System.out.print(delta.getResource().getProjectRelativePath().toString()); + System.out.print(" - "); //$NON-NLS-1$ + System.out.println(kind == IResourceDelta.ADDED ? "added" : "removed"); //$NON-NLS-1$ //$NON-NLS-2$ + } + return false; + } + IResource resource = delta.getResource(); + if (resource instanceof IFile) { + String name = resource.getName(); + IPath path = resource.getProjectRelativePath(); + if (isLocalizationFile(resource)) { + type |= MANIFEST | EXTENSIONS; + if (DEBUG) { + System.out.print("Needs to rebuild manifest and extensions in project [" + getProject().getName() + "]: "); //$NON-NLS-1$ //$NON-NLS-2$ + System.out.print(delta.getResource().getProjectRelativePath().toString()); + System.out.println(" - changed"); //$NON-NLS-1$ + } + } else if (path.equals(ICoreConstants.MANIFEST_PATH)) { + type |= MANIFEST | EXTENSIONS | BUILD; + if (DEBUG) { + System.out.print("Needs to rebuild project [" + getProject().getName() + "]: "); //$NON-NLS-1$ //$NON-NLS-2$ + System.out.print(delta.getResource().getProjectRelativePath().toString()); + System.out.println(" - changed"); //$NON-NLS-1$ + } + } else if (name.endsWith(".exsd") || path.equals(ICoreConstants.PLUGIN_PATH) || path.equals(ICoreConstants.FRAGMENT_PATH)) { //$NON-NLS-1$ + type |= EXTENSIONS; + if (DEBUG) { + System.out.print("Needs to rebuild extensions in project [" + getProject().getName() + "]: "); //$NON-NLS-1$ //$NON-NLS-2$ + System.out.print(delta.getResource().getProjectRelativePath().toString()); + System.out.println(" - changed"); //$NON-NLS-1$ + } + } else if (path.equals(ICoreConstants.BUILD_PROPERTIES_PATH)) { + type |= BUILD; + if (DEBUG) { + System.out.print("Needs to rebuild build.properties in project [" + getProject().getName() + "]: "); //$NON-NLS-1$ //$NON-NLS-2$ + System.out.print(delta.getResource().getProjectRelativePath().toString()); + System.out.println(" - changed"); //$NON-NLS-1$ + } + } + } + } + return type != (MANIFEST|EXTENSIONS|BUILD); + } + public int getType() { + return type; + } + public void reset() { + type = 0; + } + } + + private boolean isLocalizationFile(IResource file) { + IPluginModelBase model = PluginRegistry.findModel(getProject()); + String localization = null; + if (model instanceof IBundlePluginModelBase) { + localization = ((IBundlePluginModelBase)model).getBundleLocalization(); + } else { + localization = "plugin"; //$NON-NLS-1$ + } + if (localization != null) + return file.getProjectRelativePath().equals(new Path(localization + ".properties")); //$NON-NLS-1$ + return false; + } + + protected IProject[] build(int kind, Map args, IProgressMonitor monitor) + throws CoreException { + if (PDECore.getDefault().getBundle().getState() != Bundle.ACTIVE + || monitor.isCanceled()) + return EMPTY_LIST; IProject project = getProject(); - IResourceDelta delta = getDelta(project); - boolean shouldBuild = delta == null || delta.getAffectedChildren().length > 0; - if (shouldBuild && !WorkspaceModelManager.isBinaryProject(project)) - checkThisProject(monitor); - return new IProject[0]; + if (!WorkspaceModelManager.isBinaryProject(project)) { + int type = getDeltaType(project); + if (type != 0) { + validateProject(type, monitor); + } + } + return EMPTY_LIST; } + + private int getDeltaType(IProject project) throws CoreException { + IResourceDelta delta = getDelta(project); - private void checkThisProject(IProgressMonitor monitor) { - IProject project = getProject(); - IFile file = project.getFile("plugin.xml"); //$NON-NLS-1$ - if (!file.exists()) - file = project.getFile("fragment.xml"); //$NON-NLS-1$ - - if (file.exists()) { - checkFile(file, monitor); - } else { - IFile manifestFile = project.getFile("META-INF/MANIFEST.MF"); //$NON-NLS-1$ - if (manifestFile.exists()) - checkManifestFile(manifestFile, monitor); + // always do a build of the project if a full build or an unspecified change has occurred + if (delta == null) { + if (DEBUG) { + System.out.println("Project [" + getProject().getName() + "] - full build"); //$NON-NLS-1$ //$NON-NLS-2$ + } + return MANIFEST|EXTENSIONS|BUILD; + } + + // the project has been "touched" by PluginRebuilder to indicate + // that one of the dependencies (either in the target or workspace) + // has changed and a StateDelta was fired + if (Boolean.TRUE.equals(project.getSessionProperty(PDECore.TOUCH_PROJECT))) { + project.setSessionProperty(PDECore.TOUCH_PROJECT, null); + if (DEBUG) { + System.out.println("Dependencies Changed: Project [" + getProject().getName() + "] - full build"); //$NON-NLS-1$ //$NON-NLS-2$ + } + return MANIFEST|EXTENSIONS|BUILD; + } + + // check if any "significant" files have been changed/added/removed + // and build a subset or all manifest files accordingly + fSelfVisitor.reset(); + delta.accept(fSelfVisitor); + int type = fSelfVisitor.getType(); + + // catch anything we have missed + // For example, upon startup, when target has changed since shutdown + // we depend on class file changes in the project that have resulted + // from the Java compiler detecting the change in classpath. + // Note that we do NOT validate anything if there was a change + // in a .java file. A change in a .java file means that the user has modified + // its content and this does not warrant a rebuild of manifest files. + if ((type & MANIFEST|EXTENSIONS) != (MANIFEST|EXTENSIONS)) { + fClassFileVisitor.reset(); + delta.accept(fClassFileVisitor); + if (fClassFileVisitor.hasChanged()) { + type |= MANIFEST|EXTENSIONS; + if (DEBUG) { + System.out.println("Class files changed due to dependency changes: Project [" + getProject().getName() + "] - rebuild manifest files"); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + } + return type; + } + + private void validateProject(int type, IProgressMonitor monitor) { + if ((type & MANIFEST|EXTENSIONS) != 0) { + IProject project = getProject(); + IFile file = project.getFile("plugin.xml"); //$NON-NLS-1$ + if (!file.exists()) + file = project.getFile("fragment.xml"); //$NON-NLS-1$ + + if (file.exists()) { + validateFiles(file, type, monitor); + } else if ((type & MANIFEST) != 0){ + IFile manifestFile = project.getFile("META-INF/MANIFEST.MF"); //$NON-NLS-1$ + if (manifestFile.exists()) + validateManifestFile(manifestFile, monitor); + } } - checkProjectDescription(monitor); - checkBuildProperties(monitor); + if ((type & BUILD) != 0) + validateBuildProperties(monitor); } - private void checkManifestFile(IFile file, IProgressMonitor monitor) { + private void validateManifestFile(IFile file, IProgressMonitor monitor) { if (monitor.isCanceled()) return; String message = NLS.bind(PDECoreMessages.Builders_verifying, file.getFullPath().toString()); @@ -72,7 +258,7 @@ monitor.done(); } - private void checkFile(IFile file, IProgressMonitor monitor) { + private void validateFiles(IFile file, int type, IProgressMonitor monitor) { if (monitor.isCanceled()) return; String message = NLS.bind(PDECoreMessages.Builders_verifying, file.getFullPath().toString()); @@ -82,12 +268,16 @@ XMLErrorReporter reporter = null; BundleErrorReporter bundleReporter = null; if (bundleManifest.exists()) { - reporter = new ExtensionsErrorReporter(file); - bundleReporter = new BundleErrorReporter(bundleManifest); - } else if (file.getName().equals("plugin.xml")) { //$NON-NLS-1$ - reporter = new PluginErrorReporter(file); - } else if (file.getName().equals("fragment.xml")){ //$NON-NLS-1$ - reporter = new FragmentErrorReporter(file); + if ((type & EXTENSIONS) != 0) + reporter = new ExtensionsErrorReporter(file); + if ((type & MANIFEST) != 0) + bundleReporter = new BundleErrorReporter(bundleManifest); + } else if ((type & MANIFEST) != 0 || (type & EXTENSIONS) != 0){ + if (file.getName().equals("plugin.xml")) { //$NON-NLS-1$ + reporter = new PluginErrorReporter(file); + } else if (file.getName().equals("fragment.xml")){ //$NON-NLS-1$ + reporter = new FragmentErrorReporter(file); + } } if (reporter != null) { DefaultSAXParser.parse(file, reporter); @@ -101,33 +291,7 @@ monitor.done(); } - private void checkProjectDescription(IProgressMonitor monitor) { - if (monitor.isCanceled()) - return; - monitor.subTask(NLS.bind(PDECoreMessages.Builders_verifying, ".project")); //$NON-NLS-1$ - IProject project = getProject(); - IFile file = project.getFile(".project"); //$NON-NLS-1$ - if (!file.exists()) - return; - try { - file.deleteMarkers(PDEMarkerFactory.MARKER_ID, true, IResource.DEPTH_ZERO); - IProject[] refProjects = project.getDescription().getReferencedProjects(); - if (refProjects.length > 0) { - try { - IMarker marker = new PDEMarkerFactory().createMarker(file, PDEMarkerFactory.M_PROJECT_BUILD_ORDER_ENTRIES, PDEMarkerFactory.CAT_OTHER); - marker.setAttribute(IMarker.MESSAGE, PDECoreMessages.ManifestConsistencyChecker_projectCheck); - marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_WARNING); - marker.setAttribute(IMarker.LINE_NUMBER, 5); - } catch (CoreException e) { - PDECore.logException(e); - } - } - } catch (CoreException e) { - } - monitor.done(); - } - - private void checkBuildProperties(IProgressMonitor monitor) { + private void validateBuildProperties(IProgressMonitor monitor) { if (monitor.isCanceled()) return; IProject project = getProject(); @@ -153,4 +317,5 @@ ber.validateContent(monitor); } } + } Index: src/org/eclipse/pde/internal/core/builders/PluginRebuilder.java =================================================================== RCS file: src/org/eclipse/pde/internal/core/builders/PluginRebuilder.java diff -N src/org/eclipse/pde/internal/core/builders/PluginRebuilder.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/pde/internal/core/builders/PluginRebuilder.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,115 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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 + *******************************************************************************/ +package org.eclipse.pde.internal.core.builders; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IResourceChangeEvent; +import org.eclipse.core.resources.IResourceChangeListener; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.jdt.core.JavaCore; +import org.eclipse.osgi.service.resolver.BundleDelta; +import org.eclipse.osgi.service.resolver.State; +import org.eclipse.osgi.service.resolver.StateDelta; +import org.eclipse.pde.core.plugin.IPluginModelBase; +import org.eclipse.pde.core.plugin.PluginRegistry; +import org.eclipse.pde.internal.core.IStateDeltaListener; +import org.eclipse.pde.internal.core.PDECore; +import org.eclipse.pde.internal.core.WorkspaceModelManager; + +/** + * Revalidates workspace features, on change in plug-ins or features + */ +public class PluginRebuilder implements IStateDeltaListener, IResourceChangeListener { + + private Set fProjectNames = new HashSet(); + + private boolean fTouchWorkspace = false; + + public void start() { + PDECore.getDefault().getModelManager().addStateDeltaListener(this); + JavaCore.addPreProcessingResourceChangedListener(this, IResourceChangeEvent.PRE_BUILD); + } + + public void stop() { + PDECore.getDefault().getModelManager().removeStateDeltaListener(this); + JavaCore.removePreProcessingResourceChangedListener(this); + } + + public void resourceChanged(IResourceChangeEvent event) { + if (event.getType() == IResourceChangeEvent.PRE_BUILD) { + IWorkspaceRoot root = PDECore.getWorkspace().getRoot(); + if (fTouchWorkspace) { + IProject[] projects = root.getProjects(); + for (int i = 0; i < projects.length; i++) { + touchProject(projects[i]); + } + } else { + Iterator iter = fProjectNames.iterator(); + while (iter.hasNext()) { + touchProject(root.getProject((String)iter.next())); + } + } + fTouchWorkspace = false; + fProjectNames.clear(); + } + } + + private void touchProject(IProject project) { + if (WorkspaceModelManager.isPluginProject(project) + && !WorkspaceModelManager.isBinaryProject(project)) { + try { + // set session property on project + // to be read and reset in ManifestConsistencyChecker + project.setSessionProperty(PDECore.TOUCH_PROJECT, Boolean.TRUE); + // touch project so that ManifestConsistencyChecker#build(..) gets invoked + project.touch(new NullProgressMonitor()); + } catch (CoreException e) { + PDECore.log(e); + } + } + } + + public void stateChanged(State newState) { + fTouchWorkspace = true; + fProjectNames.clear(); + } + + public void stateResolved(StateDelta delta) { + if (delta == null) { + // if delta is null, then target has changed + // prepare all projects for "touching" + fTouchWorkspace = true; + fProjectNames.clear(); + } else { + BundleDelta[] deltas = delta.getChanges(); + for (int i = 0; i < deltas.length; i++) { + // only interested in workspace plug-ins that are affected by delta + // but not those who have caused it. + int type = deltas[i].getType(); + if ((type & BundleDelta.UPDATED) != BundleDelta.UPDATED + ||(type & BundleDelta.ADDED) != BundleDelta.ADDED) { + IPluginModelBase model = PluginRegistry.findModel(deltas[i].getBundle()); + IResource resource = model == null ? null : model.getUnderlyingResource(); + if (resource != null) + fProjectNames.add(resource.getProject().getName()); + } + } + } + } + +}