/******************************************************************************* * Copyright (c) 2000, 2009 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.jdt.internal.core.builder; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Date; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import org.eclipse.core.resources.ICommand; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.resources.IWorkspaceRoot; 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.IStatus; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJavaModelMarker; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.compiler.CategorizedProblem; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.core.compiler.CompilationParticipant; import org.eclipse.jdt.internal.compiler.util.SimpleLookupTable; import org.eclipse.jdt.internal.core.ClasspathEntry; import org.eclipse.jdt.internal.core.ExternalFoldersManager; import org.eclipse.jdt.internal.core.JavaModel; import org.eclipse.jdt.internal.core.JavaModelManager; import org.eclipse.jdt.internal.core.JavaProject; import org.eclipse.jdt.internal.core.util.Messages; import org.eclipse.jdt.internal.core.util.Util; public class JavaBuilder extends IncrementalProjectBuilder { IProject currentProject; JavaProject javaProject; IWorkspaceRoot workspaceRoot; CompilationParticipant[] participants; NameEnvironment nameEnvironment; SimpleLookupTable binaryLocationsPerProject; // maps a project to its binary resources (output folders, class folders, zip/jar files) public State lastState; BuildNotifier notifier; char[][] extraResourceFileFilters; String[] extraResourceFolderFilters; public static final String SOURCE_ID= "JDT"; //$NON-NLS-1$ public static boolean DEBUG= false; public static boolean SHOW_STATS= false; /** * A list of project names that have been built. This list is used to reset the * JavaModel.existingExternalFiles cache when a build cycle begins so that deleted external jars * are discovered. */ static ArrayList builtProjects= null; public static IMarker[] getProblemsFor(IResource resource) { try { if (resource != null && resource.exists()) { IMarker[] markers= resource.findMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE); Set markerTypes= JavaModelManager.getJavaModelManager().compilationParticipants.managedMarkerTypes(); if (markerTypes.isEmpty()) return markers; ArrayList markerList= new ArrayList(5); for (int i= 0, length= markers.length; i < length; i++) { markerList.add(markers[i]); } Iterator iterator= markerTypes.iterator(); while (iterator.hasNext()) { markers= resource.findMarkers((String)iterator.next(), false, IResource.DEPTH_INFINITE); for (int i= 0, length= markers.length; i < length; i++) { markerList.add(markers[i]); } } IMarker[] result; markerList.toArray(result= new IMarker[markerList.size()]); return result; } } catch (CoreException e) { // assume there are no problems } return new IMarker[0]; } public static IMarker[] getTasksFor(IResource resource) { try { if (resource != null && resource.exists()) return resource.findMarkers(IJavaModelMarker.TASK_MARKER, false, IResource.DEPTH_INFINITE); } catch (CoreException e) { // assume there are no tasks } return new IMarker[0]; } /** * Hook allowing to initialize some static state before a complete build iteration. This hook is * invoked during PRE_AUTO_BUILD notification */ public static void buildStarting() { // build is about to start } /** * Hook allowing to reset some static state after a complete build iteration. This hook is * invoked during POST_AUTO_BUILD notification */ public static void buildFinished() { BuildNotifier.resetProblemCounters(); } public static void removeProblemsFor(IResource resource) { try { if (resource != null && resource.exists()) { resource.deleteMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE); // delete managed markers Set markerTypes= JavaModelManager.getJavaModelManager().compilationParticipants.managedMarkerTypes(); if (markerTypes.size() == 0) return; Iterator iterator= markerTypes.iterator(); while (iterator.hasNext()) resource.deleteMarkers((String)iterator.next(), false, IResource.DEPTH_INFINITE); } } catch (CoreException e) { // assume there were no problems } } public static void removeTasksFor(IResource resource) { try { if (resource != null && resource.exists()) resource.deleteMarkers(IJavaModelMarker.TASK_MARKER, false, IResource.DEPTH_INFINITE); } catch (CoreException e) { // assume there were no problems } } public static void removeProblemsAndTasksFor(IResource resource) { try { if (resource != null && resource.exists()) { resource.deleteMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE); resource.deleteMarkers(IJavaModelMarker.TASK_MARKER, false, IResource.DEPTH_INFINITE); // delete managed markers Set markerTypes= JavaModelManager.getJavaModelManager().compilationParticipants.managedMarkerTypes(); if (markerTypes.size() == 0) return; Iterator iterator= markerTypes.iterator(); while (iterator.hasNext()) resource.deleteMarkers((String)iterator.next(), false, IResource.DEPTH_INFINITE); } } catch (CoreException e) { // assume there were no problems } } public static State readState(IProject project, DataInputStream in) throws IOException { return State.read(project, in); } public static void writeState(Object state, DataOutputStream out) throws IOException { ((State)state).write(out); } protected IProject[] build(int kind, Map ignored, IProgressMonitor monitor) throws CoreException { this.currentProject= getProject(); if (this.currentProject == null || !this.currentProject.isAccessible()) return new IProject[0]; if (DEBUG) System.out.println("\nStarting build of " + this.currentProject.getName() //$NON-NLS-1$ + " @ " + new Date(System.currentTimeMillis())); //$NON-NLS-1$ this.notifier= new BuildNotifier(monitor, this.currentProject); this.notifier.begin(); boolean ok= false; try { this.notifier.checkCancel(); kind= initializeBuilder(kind, true); if (isWorthBuilding()) { if (kind == FULL_BUILD) { if (DEBUG) System.out.println("Performing full build as requested by user"); //$NON-NLS-1$ buildAll(); } else { if ((this.lastState= getLastState(this.currentProject)) == null) { if (DEBUG) System.out.println("Performing full build since last saved state was not found"); //$NON-NLS-1$ buildAll(); } else if (hasClasspathChanged()) { // if the output location changes, do not delete the binary files from old location // the user may be trying something if (DEBUG) System.out.println("Performing full build since classpath has changed"); //$NON-NLS-1$ buildAll(); } else if (this.nameEnvironment.sourceLocations.length > 0) { // if there is no source to compile & no classpath changes then we are done SimpleLookupTable deltas= findDeltas(); if (deltas == null) { if (DEBUG) System.out.println("Performing full build since deltas are missing after incremental request"); //$NON-NLS-1$ buildAll(); } else if (deltas.elementSize > 0) { buildDeltas(deltas); } else if (DEBUG) { System.out.println("Nothing to build since deltas were empty"); //$NON-NLS-1$ } } else { if (hasStructuralDelta()) { // double check that a jar file didn't get replaced in a binary project if (DEBUG) System.out.println("Performing full build since there are structural deltas"); //$NON-NLS-1$ buildAll(); } else { if (DEBUG) System.out.println("Nothing to build since there are no source folders and no deltas"); //$NON-NLS-1$ this.lastState.tagAsNoopBuild(); } } } ok= true; } } catch (CoreException e) { Util.log(e, "JavaBuilder handling CoreException while building: " + this.currentProject.getName()); //$NON-NLS-1$ createInconsistentBuildMarker(e); } catch (ImageBuilderInternalException e) { Util.log(e.getThrowable(), "JavaBuilder handling ImageBuilderInternalException while building: " + this.currentProject.getName()); //$NON-NLS-1$ createInconsistentBuildMarker(e.coreException); } catch (MissingSourceFileException e) { // do not log this exception since its thrown to handle aborted compiles because of missing source files if (DEBUG) System.out.println(Messages.bind(Messages.build_missingSourceFile, e.missingSourceFile)); removeProblemsAndTasksFor(this.currentProject); // make this the only problem for this project IMarker marker= this.currentProject.createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER); marker.setAttributes( new String[] { IMarker.MESSAGE, IMarker.SEVERITY, IMarker.SOURCE_ID }, new Object[] { Messages.bind(Messages.build_missingSourceFile, e.missingSourceFile), new Integer(IMarker.SEVERITY_ERROR), JavaBuilder.SOURCE_ID } ); } finally { for (int i= 0, l= this.participants == null ? 0 : this.participants.length; i < l; i++) this.participants[i].buildFinished(this.javaProject); if (!ok) // If the build failed, clear the previously built state, forcing a full build next time. clearLastState(); this.notifier.done(); cleanup(); } IProject[] requiredProjects= getRequiredProjects(true); if (DEBUG) System.out.println("Finished build of " + this.currentProject.getName() //$NON-NLS-1$ + " @ " + new Date(System.currentTimeMillis())); //$NON-NLS-1$ return requiredProjects; } private void buildAll() { this.notifier.checkCancel(); this.notifier.subTask(Messages.bind(Messages.build_preparingBuild, this.currentProject.getName())); if (DEBUG && this.lastState != null) System.out.println("Clearing last state : " + this.lastState); //$NON-NLS-1$ clearLastState(); BatchImageBuilder imageBuilder= new BatchImageBuilder(this, true); imageBuilder.build(); recordNewState(imageBuilder.newState); } private void buildDeltas(SimpleLookupTable deltas) { this.notifier.checkCancel(); this.notifier.subTask(Messages.bind(Messages.build_preparingBuild, this.currentProject.getName())); if (DEBUG && this.lastState != null) System.out.println("Clearing last state : " + this.lastState); //$NON-NLS-1$ clearLastState(); // clear the previously built state so if the build fails, a full build will occur next time IncrementalImageBuilder imageBuilder= new IncrementalImageBuilder(this); if (imageBuilder.build(deltas)) { recordNewState(imageBuilder.newState); } else { if (DEBUG) System.out.println("Performing full build since incremental build failed"); //$NON-NLS-1$ buildAll(); } } protected void clean(IProgressMonitor monitor) throws CoreException { this.currentProject= getProject(); if (this.currentProject == null || !this.currentProject.isAccessible()) return; if (DEBUG) System.out.println("\nCleaning " + this.currentProject.getName() //$NON-NLS-1$ + " @ " + new Date(System.currentTimeMillis())); //$NON-NLS-1$ this.notifier= new BuildNotifier(monitor, this.currentProject); this.notifier.begin(); try { this.notifier.checkCancel(); initializeBuilder(CLEAN_BUILD, true); if (DEBUG) System.out.println("Clearing last state as part of clean : " + this.lastState); //$NON-NLS-1$ clearLastState(); removeProblemsAndTasksFor(this.currentProject); new BatchImageBuilder(this, false).cleanOutputFolders(false); } catch (CoreException e) { Util.log(e, "JavaBuilder handling CoreException while cleaning: " + this.currentProject.getName()); //$NON-NLS-1$ createInconsistentBuildMarker(e); } finally { this.notifier.done(); cleanup(); } if (DEBUG) System.out.println("Finished cleaning " + this.currentProject.getName() //$NON-NLS-1$ + " @ " + new Date(System.currentTimeMillis())); //$NON-NLS-1$ } private void createInconsistentBuildMarker(CoreException coreException) throws CoreException { String message= null; IStatus status= coreException.getStatus(); if (status.isMultiStatus()) { IStatus[] children= status.getChildren(); if (children != null && children.length > 0) message= children[0].getMessage(); } if (message == null) message= coreException.getMessage(); IMarker marker= this.currentProject.createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER); marker.setAttributes( new String[] { IMarker.MESSAGE, IMarker.SEVERITY, IJavaModelMarker.CATEGORY_ID, IMarker.SOURCE_ID }, new Object[] { Messages.bind(Messages.build_inconsistentProject, message), new Integer(IMarker.SEVERITY_ERROR), new Integer(CategorizedProblem.CAT_BUILDPATH), JavaBuilder.SOURCE_ID } ); } private void cleanup() { this.participants= null; this.nameEnvironment= null; this.binaryLocationsPerProject= null; this.lastState= null; this.notifier= null; this.extraResourceFileFilters= null; this.extraResourceFolderFilters= null; } private void clearLastState() { JavaModelManager.getJavaModelManager().setLastBuiltState(this.currentProject, null); } boolean filterExtraResource(IResource resource) { if (this.extraResourceFileFilters != null) { char[] name= resource.getName().toCharArray(); for (int i= 0, l= this.extraResourceFileFilters.length; i < l; i++) if (CharOperation.match(this.extraResourceFileFilters[i], name, true)) return true; } if (this.extraResourceFolderFilters != null) { IPath path= resource.getProjectRelativePath(); String pathName= path.toString(); int count= path.segmentCount(); if (resource.getType() == IResource.FILE) count--; for (int i= 0, l= this.extraResourceFolderFilters.length; i < l; i++) if (pathName.indexOf(this.extraResourceFolderFilters[i]) != -1) for (int j= 0; j < count; j++) if (this.extraResourceFolderFilters[i].equals(path.segment(j))) return true; } return false; } private SimpleLookupTable findDeltas() { this.notifier.subTask(Messages.bind(Messages.build_readingDelta, this.currentProject.getName())); IResourceDelta delta= getDelta(this.currentProject); SimpleLookupTable deltas= new SimpleLookupTable(3); if (delta != null) { if (delta.getKind() != IResourceDelta.NO_CHANGE) { if (DEBUG) System.out.println("Found source delta for: " + this.currentProject.getName()); //$NON-NLS-1$ deltas.put(this.currentProject, delta); } } else { if (DEBUG) System.out.println("Missing delta for: " + this.currentProject.getName()); //$NON-NLS-1$ this.notifier.subTask(""); //$NON-NLS-1$ return null; } Object[] keyTable= this.binaryLocationsPerProject.keyTable; Object[] valueTable= this.binaryLocationsPerProject.valueTable; nextProject: for (int i= 0, l= keyTable.length; i < l; i++) { IProject p= (IProject)keyTable[i]; if (p != null && p != this.currentProject) { State s= getLastState(p); if (!this.lastState.wasStructurallyChanged(p, s)) { // see if we can skip its delta if (s.wasNoopBuild()) continue nextProject; // project has no source folders and can be skipped ClasspathLocation[] classFoldersAndJars= (ClasspathLocation[])valueTable[i]; boolean canSkip= true; for (int j= 0, m= classFoldersAndJars.length; j < m; j++) { if (classFoldersAndJars[j].isOutputFolder()) classFoldersAndJars[j]= null; // can ignore output folder since project was not structurally changed else canSkip= false; } if (canSkip) continue nextProject; // project has no structural changes in its output folders } this.notifier.subTask(Messages.bind(Messages.build_readingDelta, p.getName())); delta= getDelta(p); if (delta != null) { if (delta.getKind() != IResourceDelta.NO_CHANGE) { if (DEBUG) System.out.println("Found binary delta for: " + p.getName()); //$NON-NLS-1$ deltas.put(p, delta); } } else { if (DEBUG) System.out.println("Missing delta for: " + p.getName()); //$NON-NLS-1$ this.notifier.subTask(""); //$NON-NLS-1$ return null; } } } this.notifier.subTask(""); //$NON-NLS-1$ return deltas; } public State getLastState(IProject project) { return (State)JavaModelManager.getJavaModelManager().getLastBuiltState(project, this.notifier.monitor); } /* Return the list of projects for which it requires a resource delta. This builder's project * is implicitly included and need not be specified. Builders must re-specify the list * of interesting projects every time they are run as this is not carried forward * beyond the next build. Missing projects should be specified but will be ignored until * they are added to the workspace. */ private IProject[] getRequiredProjects(boolean includeBinaryPrerequisites) { if (this.javaProject == null || this.workspaceRoot == null) return new IProject[0]; ArrayList projects= new ArrayList(); ExternalFoldersManager externalFoldersManager= JavaModelManager.getExternalManager(); try { IClasspathEntry[] entries= this.javaProject.getExpandedClasspath(); for (int i= 0, l= entries.length; i < l; i++) { IClasspathEntry entry= entries[i]; IPath path= entry.getPath(); IProject p= null; switch (entry.getEntryKind()) { case IClasspathEntry.CPE_PROJECT: p= this.workspaceRoot.getProject(path.lastSegment()); // missing projects are considered too if (((ClasspathEntry)entry).isOptional() && !JavaProject.hasJavaNature(p)) // except if entry is optional p= null; break; case IClasspathEntry.CPE_LIBRARY: if (includeBinaryPrerequisites && path.segmentCount() > 0) { // some binary resources on the class path can come from projects that are not included in the project references IResource resource= this.workspaceRoot.findMember(path.segment(0)); if (resource instanceof IProject) { p= (IProject)resource; } else { resource= externalFoldersManager.getFolder(path); if (resource != null) p= resource.getProject(); } } } if (p != null && !projects.contains(p)) projects.add(p); } } catch (JavaModelException e) { return new IProject[0]; } IProject[] result= new IProject[projects.size()]; projects.toArray(result); return result; } boolean hasBuildpathErrors() throws CoreException { IMarker[] markers= this.currentProject.findMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, false, IResource.DEPTH_ZERO); for (int i= 0, l= markers.length; i < l; i++) if (markers[i].getAttribute(IJavaModelMarker.CATEGORY_ID, -1) == CategorizedProblem.CAT_BUILDPATH) return true; return false; } private boolean hasClasspathChanged() { ClasspathMultiDirectory[] newSourceLocations= this.nameEnvironment.sourceLocations; ClasspathMultiDirectory[] oldSourceLocations= this.lastState.sourceLocations; int newLength= newSourceLocations.length; int oldLength= oldSourceLocations.length; int n, o; for (n= o= 0; n < newLength && o < oldLength; n++, o++) { if (newSourceLocations[n].equals(oldSourceLocations[o])) continue; // checks source & output folders try { if (newSourceLocations[n].sourceFolder.members().length == 0) { // added new empty source folder o--; continue; } else if (this.lastState.isSourceFolderEmpty(oldSourceLocations[o].sourceFolder)) { n--; continue; } } catch (CoreException ignore) { // skip it } if (DEBUG) { System.out.println("New location: " + newSourceLocations[n] + "\n!= old location: " + oldSourceLocations[o]); //$NON-NLS-1$ //$NON-NLS-2$ printLocations(newSourceLocations, oldSourceLocations); } return true; } while (n < newLength) { try { if (newSourceLocations[n].sourceFolder.members().length == 0) { // added new empty source folder n++; continue; } } catch (CoreException ignore) { // skip it } if (DEBUG) { System.out.println("Added non-empty source folder"); //$NON-NLS-1$ printLocations(newSourceLocations, oldSourceLocations); } return true; } while (o < oldLength) { if (this.lastState.isSourceFolderEmpty(oldSourceLocations[o].sourceFolder)) { o++; continue; } if (DEBUG) { System.out.println("Removed non-empty source folder"); //$NON-NLS-1$ printLocations(newSourceLocations, oldSourceLocations); } return true; } ClasspathLocation[] newBinaryLocations= this.nameEnvironment.binaryLocations; ClasspathLocation[] oldBinaryLocations= this.lastState.binaryLocations; newLength= newBinaryLocations.length; oldLength= oldBinaryLocations.length; for (n= o= 0; n < newLength && o < oldLength; n++, o++) { if (newBinaryLocations[n].equals(oldBinaryLocations[o])) continue; if (DEBUG) { System.out.println("New location: " + newBinaryLocations[n] + "\n!= old location: " + oldBinaryLocations[o]); //$NON-NLS-1$ //$NON-NLS-2$ printLocations(newBinaryLocations, oldBinaryLocations); } return true; } if (n < newLength || o < oldLength) { if (DEBUG) { System.out.println("Number of binary folders/jar files has changed:"); //$NON-NLS-1$ printLocations(newBinaryLocations, oldBinaryLocations); } return true; } return false; } private boolean hasJavaBuilder(IProject project) throws CoreException { ICommand[] buildCommands= project.getDescription().getBuildSpec(); for (int i= 0, l= buildCommands.length; i < l; i++) if (buildCommands[i].getBuilderName().equals(JavaCore.BUILDER_ID)) return true; return false; } private boolean hasStructuralDelta() { // handle case when currentProject has only .class file folders and/or jar files... no source/output folders IResourceDelta delta= getDelta(this.currentProject); if (delta != null && delta.getKind() != IResourceDelta.NO_CHANGE) { ClasspathLocation[] classFoldersAndJars= (ClasspathLocation[])this.binaryLocationsPerProject.get(this.currentProject); if (classFoldersAndJars != null) { for (int i= 0, l= classFoldersAndJars.length; i < l; i++) { ClasspathLocation classFolderOrJar= classFoldersAndJars[i]; // either a .class file folder or a zip/jar file if (classFolderOrJar != null) { IPath p= classFolderOrJar.getProjectRelativePath(); if (p != null) { IResourceDelta binaryDelta= delta.findMember(p); if (binaryDelta != null && binaryDelta.getKind() != IResourceDelta.NO_CHANGE) return true; } } } } } return false; } private int initializeBuilder(int kind, boolean forBuild) throws CoreException { // some calls just need the nameEnvironment initialized so skip the rest this.javaProject= (JavaProject)JavaCore.create(this.currentProject); this.workspaceRoot= this.currentProject.getWorkspace().getRoot(); if (forBuild) { // cache the known participants for this project this.participants= JavaModelManager.getJavaModelManager().compilationParticipants.getCompilationParticipants(this.javaProject); if (this.participants != null) for (int i= 0, l= this.participants.length; i < l; i++) if (this.participants[i].aboutToBuild(this.javaProject) == CompilationParticipant.NEEDS_FULL_BUILD) kind= FULL_BUILD; // Flush the existing external files cache if this is the beginning of a build cycle String projectName= this.currentProject.getName(); if (builtProjects == null || builtProjects.contains(projectName)) { JavaModel.flushExternalFileCache(); builtProjects= new ArrayList(); } builtProjects.add(projectName); } this.binaryLocationsPerProject= new SimpleLookupTable(3); this.nameEnvironment= new NameEnvironment(this.workspaceRoot, this.javaProject, this.binaryLocationsPerProject, this.notifier); if (forBuild) { String filterSequence= this.javaProject.getOption(JavaCore.CORE_JAVA_BUILD_RESOURCE_COPY_FILTER, true); char[][] filters= filterSequence != null && filterSequence.length() > 0 ? CharOperation.splitAndTrimOn(',', filterSequence.toCharArray()) : null; if (filters == null) { this.extraResourceFileFilters= null; this.extraResourceFolderFilters= null; } else { int fileCount= 0, folderCount= 0; for (int i= 0, l= filters.length; i < l; i++) { char[] f= filters[i]; if (f.length == 0) continue; if (f[f.length - 1] == '/') folderCount++; else fileCount++; } this.extraResourceFileFilters= new char[fileCount][]; this.extraResourceFolderFilters= new String[folderCount]; for (int i= 0, l= filters.length; i < l; i++) { char[] f= filters[i]; if (f.length == 0) continue; if (f[f.length - 1] == '/') this.extraResourceFolderFilters[--folderCount]= new String(f, 0, f.length - 1); else this.extraResourceFileFilters[--fileCount]= f; } } } return kind; } private boolean isClasspathBroken(IClasspathEntry[] classpath, IProject p) throws CoreException { IMarker[] markers= p.findMarkers(IJavaModelMarker.BUILDPATH_PROBLEM_MARKER, false, IResource.DEPTH_ZERO); for (int i= 0, l= markers.length; i < l; i++) if (markers[i].getAttribute(IMarker.SEVERITY, -1) == IMarker.SEVERITY_ERROR) return true; return false; } private boolean isWorthBuilding() throws CoreException { boolean abortBuilds= JavaCore.ABORT.equals(this.javaProject.getOption(JavaCore.CORE_JAVA_BUILD_INVALID_CLASSPATH, true)); if (!abortBuilds) return true; // Abort build only if there are classpath errors if (isClasspathBroken(this.javaProject.getRawClasspath(), this.currentProject)) { if (DEBUG) System.out.println("Aborted build because project has classpath errors (incomplete or involved in cycle)"); //$NON-NLS-1$ removeProblemsAndTasksFor(this.currentProject); // remove all compilation problems IMarker marker= this.currentProject.createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER); marker.setAttributes( new String[] { IMarker.MESSAGE, IMarker.SEVERITY, IJavaModelMarker.CATEGORY_ID, IMarker.SOURCE_ID }, new Object[] { Messages.build_abortDueToClasspathProblems, new Integer(IMarker.SEVERITY_ERROR), new Integer(CategorizedProblem.CAT_BUILDPATH), JavaBuilder.SOURCE_ID } ); return false; } if (JavaCore.WARNING.equals(this.javaProject.getOption(JavaCore.CORE_INCOMPLETE_CLASSPATH, true))) return true; // make sure all prereq projects have valid build states... only when aborting builds since projects in cycles do not have build states // except for projects involved in a 'warning' cycle (see below) IProject[] requiredProjects= getRequiredProjects(false); for (int i= 0, l= requiredProjects.length; i < l; i++) { IProject p= requiredProjects[i]; if (getLastState(p) == null) { // The prereq project has no build state: if this prereq project has a 'warning' cycle marker then allow build (see bug id 23357) JavaProject prereq= (JavaProject)JavaCore.create(p); if (prereq.hasCycleMarker() && JavaCore.WARNING.equals(this.javaProject.getOption(JavaCore.CORE_CIRCULAR_CLASSPATH, true))) { if (DEBUG) System.out.println("Continued to build even though prereq project " + p.getName() //$NON-NLS-1$ + " was not built since its part of a cycle"); //$NON-NLS-1$ continue; } if (!hasJavaBuilder(p)) { if (DEBUG) System.out.println("Continued to build even though prereq project " + p.getName() //$NON-NLS-1$ + " is not built by JavaBuilder"); //$NON-NLS-1$ continue; } if (DEBUG) System.out.println("Aborted build because prereq project " + p.getName() //$NON-NLS-1$ + " was not built"); //$NON-NLS-1$ removeProblemsAndTasksFor(this.currentProject); // make this the only problem for this project IMarker marker= this.currentProject.createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER); marker.setAttributes( new String[] { IMarker.MESSAGE, IMarker.SEVERITY, IJavaModelMarker.CATEGORY_ID, IMarker.SOURCE_ID }, new Object[] { isClasspathBroken(prereq.getRawClasspath(), p) ? Messages.bind(Messages.build_prereqProjectHasClasspathProblems, p.getName()) : Messages.bind(Messages.build_prereqProjectMustBeRebuilt, p.getName()), new Integer(IMarker.SEVERITY_ERROR), new Integer(CategorizedProblem.CAT_BUILDPATH), JavaBuilder.SOURCE_ID } ); return false; } } return true; } /* * Instruct the build manager that this project is involved in a cycle and * needs to propagate structural changes to the other projects in the cycle. */ void mustPropagateStructuralChanges() { HashSet cycleParticipants= new HashSet(3); this.javaProject.updateCycleParticipants(new ArrayList(), cycleParticipants, this.workspaceRoot, new HashSet(3), null); IPath currentPath= this.javaProject.getPath(); Iterator i= cycleParticipants.iterator(); while (i.hasNext()) { IPath participantPath= (IPath)i.next(); if (participantPath != currentPath) { IProject project= this.workspaceRoot.getProject(participantPath.segment(0)); if (hasBeenBuilt(project)) { if (DEBUG) System.out.println("Requesting another build iteration since cycle participant " + project.getName() //$NON-NLS-1$ + " has not yet seen some structural changes"); //$NON-NLS-1$ needRebuild(); return; } } } } private void printLocations(ClasspathLocation[] newLocations, ClasspathLocation[] oldLocations) { System.out.println("New locations:"); //$NON-NLS-1$ for (int i= 0, length= newLocations.length; i < length; i++) System.out.println(" " + newLocations[i].debugPathString()); //$NON-NLS-1$ System.out.println("Old locations:"); //$NON-NLS-1$ for (int i= 0, length= oldLocations.length; i < length; i++) System.out.println(" " + oldLocations[i].debugPathString()); //$NON-NLS-1$ } private void recordNewState(State state) { Object[] keyTable= this.binaryLocationsPerProject.keyTable; for (int i= 0, l= keyTable.length; i < l; i++) { IProject prereqProject= (IProject)keyTable[i]; if (prereqProject != null && prereqProject != this.currentProject) state.recordStructuralDependency(prereqProject, getLastState(prereqProject)); } if (DEBUG) System.out.println("Recording new state : " + state); //$NON-NLS-1$ // state.dump(); JavaModelManager.getJavaModelManager().setLastBuiltState(this.currentProject, state); } /** * String representation for debugging purposes */ public String toString() { return this.currentProject == null ? "JavaBuilder for unknown project" //$NON-NLS-1$ : "JavaBuilder for " + this.currentProject.getName(); //$NON-NLS-1$ } }