Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/eclipse/jdt/internal/core/builder/JavaBuilder.java


1   /*******************************************************************************
2    * Copyright (c) 2000, 2004 IBM Corporation and others.
3    * All rights reserved. This program and the accompanying materials 
4    * are made available under the terms of the Common Public License v1.0
5    * which accompanies this distribution, and is available at
6    * http://www.eclipse.org/legal/cpl-v10.html
7    * 
8    * Contributors:
9    *     IBM Corporation - initial API and implementation
10   *******************************************************************************/
11  package org.eclipse.jdt.internal.core.builder;
12  
13  import org.eclipse.core.resources.*;
14  import org.eclipse.core.runtime.*;
15  
16  import org.eclipse.jdt.core.*;
17  import org.eclipse.jdt.core.compiler.CharOperation;
18  import org.eclipse.jdt.internal.core.*;
19  import org.eclipse.jdt.internal.core.util.SimpleLookupTable;
20  import org.eclipse.jdt.internal.core.util.Util;
21  
22  import java.io.*;
23  import java.util.*;
24  
25  public class JavaBuilder extends IncrementalProjectBuilder {
26  
27  IProject currentProject;
28  JavaProject javaProject;
29  IWorkspaceRoot workspaceRoot;
30  NameEnvironment nameEnvironment;
31  SimpleLookupTable binaryLocationsPerProject; // maps a project to its binary resources (output folders, class folders, zip/jar files)
32  State lastState;
33  BuildNotifier notifier;
34  char[][] extraResourceFileFilters;
35  String[] extraResourceFolderFilters;
36  
37  public static boolean DEBUG = false;
38  
39  /**
40   * A list of project names that have been built.
41   * This list is used to reset the JavaModel.existingExternalFiles cache when a build cycle begins
42   * so that deleted external jars are discovered.
43   */
44  static ArrayList builtProjects = null;
45  
46  public static IMarker[] getProblemsFor(IResource resource) {
47    try {
48      if (resource != null && resource.exists())
49        return resource.findMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE);
50    } catch (CoreException e) {
51      // assume there are no problems
52    }
53    return new IMarker[0];
54  }
55  
56  public static IMarker[] getTasksFor(IResource resource) {
57    try {
58      if (resource != null && resource.exists())
59        return resource.findMarkers(IJavaModelMarker.TASK_MARKER, false, IResource.DEPTH_INFINITE);
60    } catch (CoreException e) {
61      // assume there are no tasks
62    }
63    return new IMarker[0];
64  }
65  
66  /**
67   * Hook allowing to initialize some static state before a complete build iteration.
68   * This hook is invoked during PRE_AUTO_BUILD notification
69   */
70  public static void buildStarting() {
71    // do nothing
72    // TODO (philippe) is it still needed?
73  }
74  
75  /**
76   * Hook allowing to reset some static state after a complete build iteration.
77   * This hook is invoked during POST_AUTO_BUILD notification
78   */
79  public static void buildFinished() {
80    BuildNotifier.resetProblemCounters();
81  }
82  
83  public static void removeProblemsFor(IResource resource) {
84    try {
85      if (resource != null && resource.exists())
86        resource.deleteMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE);
87    } catch (CoreException e) {
88      // assume there were no problems
89    }
90  }
91  
92  public static void removeTasksFor(IResource resource) {
93    try {
94      if (resource != null && resource.exists())
95        resource.deleteMarkers(IJavaModelMarker.TASK_MARKER, false, IResource.DEPTH_INFINITE);
96    } catch (CoreException e) {
97      // assume there were no problems
98    }
99  }
100 
101 public static void removeProblemsAndTasksFor(IResource resource) {
102   try {
103     if (resource != null && resource.exists()) {
104       resource.deleteMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE);
105       resource.deleteMarkers(IJavaModelMarker.TASK_MARKER, false, IResource.DEPTH_INFINITE);
106     }
107   } catch (CoreException e) {
108     // assume there were no problems
109   }
110 }
111 
112 public static State readState(IProject project, DataInputStream in) throws IOException {
113   return State.read(project, in);
114 }
115 
116 public static void writeState(Object state, DataOutputStream out) throws IOException {
117   ((State) state).write(out);
118 }
119 
120 protected IProject[] build(int kind, Map ignored, IProgressMonitor monitor) throws CoreException {
121   this.currentProject = getProject();
122   if (currentProject == null || !currentProject.isAccessible()) return new IProject[0];
123 
124   if (DEBUG)
125     System.out.println("\nStarting build of " + currentProject.getName() //$NON-NLS-1$
126       + " @ " + new Date(System.currentTimeMillis())); //$NON-NLS-1$
127   this.notifier = new BuildNotifier(monitor, currentProject);
128   notifier.begin();
129   boolean ok = false;
130   try {
131     notifier.checkCancel();
132     initializeBuilder();
133 
134     if (isWorthBuilding()) {
135       if (kind == FULL_BUILD) {
136         buildAll();
137       } else {
138         if ((this.lastState = getLastState(currentProject)) == null) {
139           if (DEBUG)
140             System.out.println("Performing full build since last saved state was not found"); //$NON-NLS-1$
141           buildAll();
142         } else if (hasClasspathChanged()) {
143           // if the output location changes, do not delete the binary files from old location
144           // the user may be trying something
145           buildAll();
146         } else if (nameEnvironment.sourceLocations.length > 0) {
147           // if there is no source to compile & no classpath changes then we are done
148           SimpleLookupTable deltas = findDeltas();
149           if (deltas == null)
150             buildAll();
151           else if (deltas.elementSize > 0)
152             buildDeltas(deltas);
153           else if (DEBUG)
154             System.out.println("Nothing to build since deltas were empty"); //$NON-NLS-1$
155         } else {
156           if (hasStructuralDelta()) { // double check that a jar file didn't get replaced in a binary project
157             buildAll();
158           } else {
159             if (DEBUG)
160               System.out.println("Nothing to build since there are no source folders and no deltas"); //$NON-NLS-1$
161             lastState.tagAsNoopBuild();
162           }
163         }
164       }
165       ok = true;
166     }
167   } catch (CoreException e) {
168     Util.log(e, "JavaBuilder handling CoreException while building: " + currentProject.getName()); //$NON-NLS-1$
169     IMarker marker = currentProject.createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER);
170     marker.setAttribute(IMarker.MESSAGE, Util.bind("build.inconsistentProject", e.getLocalizedMessage())); //$NON-NLS-1$
171     marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
172   } catch (ImageBuilderInternalException e) {
173     Util.log(e.getThrowable(), "JavaBuilder handling ImageBuilderInternalException while building: " + currentProject.getName()); //$NON-NLS-1$
174     IMarker marker = currentProject.createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER);
175     marker.setAttribute(IMarker.MESSAGE, Util.bind("build.inconsistentProject", e.getLocalizedMessage())); //$NON-NLS-1$
176     marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
177   } catch (MissingClassFileException e) {
178     // do not log this exception since its thrown to handle aborted compiles because of missing class files
179     if (DEBUG)
180       System.out.println(Util.bind("build.incompleteClassPath", e.missingClassFile)); //$NON-NLS-1$
181     IMarker marker = currentProject.createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER);
182     marker.setAttribute(IMarker.MESSAGE, Util.bind("build.incompleteClassPath", e.missingClassFile)); //$NON-NLS-1$
183     marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
184   } catch (MissingSourceFileException e) {
185     // do not log this exception since its thrown to handle aborted compiles because of missing source files
186     if (DEBUG)
187       System.out.println(Util.bind("build.missingSourceFile", e.missingSourceFile)); //$NON-NLS-1$
188     removeProblemsAndTasksFor(currentProject); // make this the only problem for this project
189     IMarker marker = currentProject.createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER);
190     marker.setAttribute(IMarker.MESSAGE, Util.bind("build.missingSourceFile", e.missingSourceFile)); //$NON-NLS-1$
191     marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
192   } finally {
193     if (!ok)
194       // If the build failed, clear the previously built state, forcing a full build next time.
195       clearLastState();
196     notifier.done();
197     cleanup();
198   }
199   IProject[] requiredProjects = getRequiredProjects(true);
200   if (DEBUG)
201     System.out.println("Finished build of " + currentProject.getName() //$NON-NLS-1$
202       + " @ " + new Date(System.currentTimeMillis())); //$NON-NLS-1$
203   return requiredProjects;
204 }
205 
206 private void buildAll() {
207   notifier.checkCancel();
208   notifier.subTask(Util.bind("build.preparingBuild")); //$NON-NLS-1$
209   if (DEBUG && lastState != null)
210     System.out.println("Clearing last state : " + lastState); //$NON-NLS-1$
211   clearLastState();
212   BatchImageBuilder imageBuilder = new BatchImageBuilder(this);
213   imageBuilder.build();
214   recordNewState(imageBuilder.newState);
215 }
216 
217 private void buildDeltas(SimpleLookupTable deltas) {
218   notifier.checkCancel();
219   notifier.subTask(Util.bind("build.preparingBuild")); //$NON-NLS-1$
220   if (DEBUG && lastState != null)
221     System.out.println("Clearing last state : " + lastState); //$NON-NLS-1$
222   clearLastState(); // clear the previously built state so if the build fails, a full build will occur next time
223   IncrementalImageBuilder imageBuilder = new IncrementalImageBuilder(this);
224   if (imageBuilder.build(deltas))
225     recordNewState(imageBuilder.newState);
226   else
227     buildAll();
228 }
229 
230 protected void clean(IProgressMonitor monitor) throws CoreException {
231   this.currentProject = getProject();
232   if (currentProject == null || !currentProject.isAccessible()) return;
233 
234   if (DEBUG)
235     System.out.println("\nCleaning " + currentProject.getName() //$NON-NLS-1$
236       + " @ " + new Date(System.currentTimeMillis())); //$NON-NLS-1$
237   this.notifier = new BuildNotifier(monitor, currentProject);
238   notifier.begin();
239   try {
240     notifier.checkCancel();
241 
242     initializeBuilder();
243     if (DEBUG)
244       System.out.println("Clearing last state as part of clean : " + lastState); //$NON-NLS-1$
245     clearLastState();
246     removeProblemsAndTasksFor(currentProject);
247     new BatchImageBuilder(this).cleanOutputFolders(false);
248   } catch (CoreException e) {
249     Util.log(e, "JavaBuilder handling CoreException while cleaning: " + currentProject.getName()); //$NON-NLS-1$
250     IMarker marker = currentProject.createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER);
251     marker.setAttribute(IMarker.MESSAGE, Util.bind("build.inconsistentProject", e.getLocalizedMessage())); //$NON-NLS-1$
252     marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
253   } finally {
254     notifier.done();
255     cleanup();
256   }
257   if (DEBUG)
258     System.out.println("Finished cleaning " + currentProject.getName() //$NON-NLS-1$
259       + " @ " + new Date(System.currentTimeMillis())); //$NON-NLS-1$
260 }
261 
262 private void cleanup() {
263   this.nameEnvironment = null;
264   this.binaryLocationsPerProject = null;
265   this.lastState = null;
266   this.notifier = null;
267   this.extraResourceFileFilters = null;
268   this.extraResourceFolderFilters = null;
269 }
270 
271 private void clearLastState() {
272   JavaModelManager.getJavaModelManager().setLastBuiltState(currentProject, null);
273 }
274 
275 boolean filterExtraResource(IResource resource) {
276   if (extraResourceFileFilters != null) {
277     char[] name = resource.getName().toCharArray();
278     for (int i = 0, l = extraResourceFileFilters.length; i < l; i++)
279       if (CharOperation.match(extraResourceFileFilters[i], name, true))
280         return true;
281   }
282   if (extraResourceFolderFilters != null) {
283     IPath path = resource.getProjectRelativePath();
284     String pathName = path.toString();
285     int count = path.segmentCount();
286     if (resource.getType() == IResource.FILE) count--;
287     for (int i = 0, l = extraResourceFolderFilters.length; i < l; i++)
288       if (pathName.indexOf(extraResourceFolderFilters[i]) != -1)
289         for (int j = 0; j < count; j++)
290           if (extraResourceFolderFilters[i].equals(path.segment(j)))
291             return true;
292   }
293   return false;
294 }
295 
296 private SimpleLookupTable findDeltas() {
297   notifier.subTask(Util.bind("build.readingDelta", currentProject.getName())); //$NON-NLS-1$
298   IResourceDelta delta = getDelta(currentProject);
299   SimpleLookupTable deltas = new SimpleLookupTable(3);
300   if (delta != null) {
301     if (delta.getKind() != IResourceDelta.NO_CHANGE) {
302       if (DEBUG)
303         System.out.println("Found source delta for: " + currentProject.getName()); //$NON-NLS-1$
304       deltas.put(currentProject, delta);
305     }
306   } else {
307     if (DEBUG)
308       System.out.println("Missing delta for: " + currentProject.getName()); //$NON-NLS-1$
309     notifier.subTask(""); //$NON-NLS-1$
310     return null;
311   }
312 
313   Object[] keyTable = binaryLocationsPerProject.keyTable;
314   Object[] valueTable = binaryLocationsPerProject.valueTable;
315   nextProject : for (int i = 0, l = keyTable.length; i < l; i++) {
316     IProject p = (IProject) keyTable[i];
317     if (p != null && p != currentProject) {
318       State s = getLastState(p);
319       if (!lastState.wasStructurallyChanged(p, s)) { // see if we can skip its delta
320         if (s.wasNoopBuild())
321           continue nextProject; // project has no source folders and can be skipped
322         ClasspathLocation[] classFoldersAndJars = (ClasspathLocation[]) valueTable[i];
323         boolean canSkip = true;
324         for (int j = 0, m = classFoldersAndJars.length; j < m; j++) {
325           if (classFoldersAndJars[j].isOutputFolder())
326             classFoldersAndJars[j] = null; // can ignore output folder since project was not structurally changed
327           else
328             canSkip = false;
329         }
330         if (canSkip) continue nextProject; // project has no structural changes in its output folders
331       }
332 
333       notifier.subTask(Util.bind("build.readingDelta", p.getName())); //$NON-NLS-1$
334       delta = getDelta(p);
335       if (delta != null) {
336         if (delta.getKind() != IResourceDelta.NO_CHANGE) {
337           if (DEBUG)
338             System.out.println("Found binary delta for: " + p.getName()); //$NON-NLS-1$
339           deltas.put(p, delta);
340         }
341       } else {
342         if (DEBUG)
343           System.out.println("Missing delta for: " + p.getName());   //$NON-NLS-1$
344         notifier.subTask(""); //$NON-NLS-1$
345         return null;
346       }
347     }
348   }
349   notifier.subTask(""); //$NON-NLS-1$
350   return deltas;
351 }
352 
353 public State getLastState(IProject project) {
354   return (State) JavaModelManager.getJavaModelManager().getLastBuiltState(project, notifier.monitor);
355 }
356 
357 /* Return the list of projects for which it requires a resource delta. This builder's project
358 * is implicitly included and need not be specified. Builders must re-specify the list 
359 * of interesting projects every time they are run as this is not carried forward
360 * beyond the next build. Missing projects should be specified but will be ignored until
361 * they are added to the workspace.
362 */
363 private IProject[] getRequiredProjects(boolean includeBinaryPrerequisites) {
364   if (javaProject == null || workspaceRoot == null) return new IProject[0];
365 
366   ArrayList projects = new ArrayList();
367   try {
368     IClasspathEntry[] entries = javaProject.getExpandedClasspath(true);
369     for (int i = 0, l = entries.length; i < l; i++) {
370       IClasspathEntry entry = entries[i];
371       IPath path = entry.getPath();
372       IProject p = null;
373       switch (entry.getEntryKind()) {
374         case IClasspathEntry.CPE_PROJECT :
375           p = workspaceRoot.getProject(path.lastSegment()); // missing projects are considered too
376           break;
377         case IClasspathEntry.CPE_LIBRARY :
378           if (includeBinaryPrerequisites && path.segmentCount() > 1) {
379             // some binary resources on the class path can come from projects that are not included in the project references
380             IResource resource = workspaceRoot.findMember(path.segment(0));
381             if (resource instanceof IProject)
382               p = (IProject) resource;
383           }
384       }
385       if (p != null && !projects.contains(p))
386         projects.add(p);
387     }
388   } catch(JavaModelException e) {
389     return new IProject[0];
390   }
391   IProject[] result = new IProject[projects.size()];
392   projects.toArray(result);
393   return result;
394 }
395 
396 private boolean hasClasspathChanged() {
397   ClasspathMultiDirectory[] newSourceLocations = nameEnvironment.sourceLocations;
398   ClasspathMultiDirectory[] oldSourceLocations = lastState.sourceLocations;
399   int newLength = newSourceLocations.length;
400   int oldLength = oldSourceLocations.length;
401   int n, o;
402   for (n = o = 0; n < newLength && o < oldLength; n++, o++) {
403     if (newSourceLocations[n].equals(oldSourceLocations[o])) continue; // checks source & output folders
404     try {
405       if (newSourceLocations[n].sourceFolder.members().length == 0) { // added new empty source folder
406         o--;
407         continue;
408       }
409     } catch (CoreException ignore) { // skip it
410     }
411     if (DEBUG)
412       System.out.println(newSourceLocations[n] + " != " + oldSourceLocations[o]); //$NON-NLS-1$
413     return true;
414   }
415   while (n < newLength) {
416     try {
417       if (newSourceLocations[n].sourceFolder.members().length == 0) { // added new empty source folder
418         n++;
419         continue;
420       }
421     } catch (CoreException ignore) { // skip it
422     }
423     if (DEBUG)
424       System.out.println("Added non-empty source folder"); //$NON-NLS-1$
425     return true;
426   }
427   if (o < oldLength) {
428     if (DEBUG)
429       System.out.println("Removed source folder"); //$NON-NLS-1$
430     return true;
431   }
432 
433   ClasspathLocation[] newBinaryLocations = nameEnvironment.binaryLocations;
434   ClasspathLocation[] oldBinaryLocations = lastState.binaryLocations;
435   newLength = newBinaryLocations.length;
436   oldLength = oldBinaryLocations.length;
437   for (n = o = 0; n < newLength && o < oldLength; n++, o++) {
438     if (newBinaryLocations[n].equals(oldBinaryLocations[o])) continue;
439     if (DEBUG)
440       System.out.println(newBinaryLocations[n] + " != " + oldBinaryLocations[o]); //$NON-NLS-1$
441     return true;
442   }
443   if (n < newLength || o < oldLength) {
444     if (DEBUG)
445       System.out.println("Number of binary folders/jar files has changed"); //$NON-NLS-1$
446     return true;
447   }
448   return false;
449 }
450 
451 private boolean hasStructuralDelta() {
452   // handle case when currentProject has only .class file folders and/or jar files... no source/output folders
453   IResourceDelta delta = getDelta(currentProject);
454   if (delta != null && delta.getKind() != IResourceDelta.NO_CHANGE) {
455     ClasspathLocation[] classFoldersAndJars = (ClasspathLocation[]) binaryLocationsPerProject.get(currentProject);
456     if (classFoldersAndJars != null) {
457       for (int i = 0, l = classFoldersAndJars.length; i < l; i++) {
458         ClasspathLocation classFolderOrJar = classFoldersAndJars[i]; // either a .class file folder or a zip/jar file
459         if (classFolderOrJar != null) {
460           IPath p = classFolderOrJar.getProjectRelativePath();
461           if (p != null) {
462             IResourceDelta binaryDelta = delta.findMember(p);
463             if (binaryDelta != null && binaryDelta.getKind() != IResourceDelta.NO_CHANGE)
464               return true;
465           }
466         }
467       }
468     }
469   }
470   return false;
471 }
472 
473 private void initializeBuilder() throws CoreException {
474   this.javaProject = (JavaProject) JavaCore.create(currentProject);
475   this.workspaceRoot = currentProject.getWorkspace().getRoot();
476 
477   // Flush the existing external files cache if this is the beginning of a build cycle
478   String projectName = currentProject.getName();
479   if (builtProjects == null || builtProjects.contains(projectName)) {
480     JavaModel.flushExternalFileCache();
481     builtProjects = new ArrayList();
482   }
483   builtProjects.add(projectName);
484 
485   this.binaryLocationsPerProject = new SimpleLookupTable(3);
486   this.nameEnvironment = new NameEnvironment(workspaceRoot, javaProject, binaryLocationsPerProject);
487 
488   String filterSequence = javaProject.getOption(JavaCore.CORE_JAVA_BUILD_RESOURCE_COPY_FILTER, true);
489   char[][] filters = filterSequence != null && filterSequence.length() > 0
490     ? CharOperation.splitAndTrimOn(',', filterSequence.toCharArray())
491     : null;
492   if (filters == null) {
493     this.extraResourceFileFilters = null;
494     this.extraResourceFolderFilters = null;
495   } else {
496     int fileCount = 0, folderCount = 0;
497     for (int i = 0, l = filters.length; i < l; i++) {
498       char[] f = filters[i];
499       if (f.length == 0) continue;
500       if (f[f.length - 1] == '/') folderCount++; else fileCount++;
501     }
502     this.extraResourceFileFilters = new char[fileCount][];
503     this.extraResourceFolderFilters = new String[folderCount];
504     for (int i = 0, l = filters.length; i < l; i++) {
505       char[] f = filters[i];
506       if (f.length == 0) continue;
507       if (f[f.length - 1] == '/')
508         extraResourceFolderFilters[--folderCount] = new String(CharOperation.subarray(f, 0, f.length - 1));
509       else
510         extraResourceFileFilters[--fileCount] = f;
511     }
512   }
513 }
514 
515 private boolean isClasspathBroken(IClasspathEntry[] classpath, IProject p) throws CoreException {
516   if (classpath == JavaProject.INVALID_CLASSPATH) // the .classpath file could not be read
517     return true;
518 
519   IMarker[] markers = p.findMarkers(IJavaModelMarker.BUILDPATH_PROBLEM_MARKER, false, IResource.DEPTH_ZERO);
520   for (int i = 0, l = markers.length; i < l; i++)
521     if (((Integer) markers[i].getAttribute(IMarker.SEVERITY)).intValue() == IMarker.SEVERITY_ERROR)
522       return true;
523   return false;
524 }
525 
526 private boolean isWorthBuilding() throws CoreException {
527   boolean abortBuilds =
528     JavaCore.ABORT.equals(javaProject.getOption(JavaCore.CORE_JAVA_BUILD_INVALID_CLASSPATH, true));
529   if (!abortBuilds) return true;
530 
531   // Abort build only if there are classpath errors
532   if (isClasspathBroken(javaProject.getRawClasspath(), currentProject)) {
533     if (DEBUG)
534       System.out.println("Aborted build because project has classpath errors (incomplete or involved in cycle)"); //$NON-NLS-1$
535 
536     removeProblemsAndTasksFor(currentProject); // remove all compilation problems
537 
538     IMarker marker = currentProject.createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER);
539     marker.setAttribute(IMarker.MESSAGE, Util.bind("build.abortDueToClasspathProblems")); //$NON-NLS-1$
540     marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
541     return false;
542   }
543 
544   // make sure all prereq projects have valid build states... only when aborting builds since projects in cycles do not have build states
545   // except for projects involved in a 'warning' cycle (see below)
546   IProject[] requiredProjects = getRequiredProjects(false);
547   next : for (int i = 0, l = requiredProjects.length; i < l; i++) {
548     IProject p = requiredProjects[i];
549     if (getLastState(p) == null)  {
550       // The prereq project has no build state: if this prereq project has a 'warning' cycle marker then allow build (see bug id 23357)
551       JavaProject prereq = (JavaProject) JavaCore.create(p);
552       if (prereq.hasCycleMarker() && JavaCore.WARNING.equals(javaProject.getOption(JavaCore.CORE_CIRCULAR_CLASSPATH, true)))
553         continue;
554       if (DEBUG)
555         System.out.println("Aborted build because prereq project " + p.getName() //$NON-NLS-1$
556           + " was not built"); //$NON-NLS-1$
557 
558       removeProblemsAndTasksFor(currentProject); // make this the only problem for this project
559       IMarker marker = currentProject.createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER);
560       marker.setAttribute(IMarker.MESSAGE,
561         isClasspathBroken(prereq.getRawClasspath(), p)
562           ? Util.bind("build.prereqProjectHasClasspathProblems", p.getName()) //$NON-NLS-1$
563           : Util.bind("build.prereqProjectMustBeRebuilt", p.getName())); //$NON-NLS-1$
564       marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
565       return false;
566     }
567   }
568   return true;
569 }
570 
571 /*
572  * Instruct the build manager that this project is involved in a cycle and
573  * needs to propagate structural changes to the other projects in the cycle.
574  */
575 void mustPropagateStructuralChanges() {
576   HashSet cycleParticipants = new HashSet(3);
577   javaProject.updateCycleParticipants(new ArrayList(), cycleParticipants, workspaceRoot, new HashSet(3), null);
578   IPath currentPath = javaProject.getPath();
579   Iterator i= cycleParticipants.iterator();
580   while (i.hasNext()) {
581     IPath participantPath = (IPath) i.next();
582     if (participantPath != currentPath) {
583       IProject project = workspaceRoot.getProject(participantPath.segment(0));
584       if (hasBeenBuilt(project)) {
585         if (DEBUG) 
586           System.out.println("Requesting another build iteration since cycle participant " + project.getName() //$NON-NLS-1$
587             + " has not yet seen some structural changes"); //$NON-NLS-1$
588         needRebuild();
589         return;
590       }
591     }
592   }
593 }
594 
595 private void recordNewState(State state) {
596   Object[] keyTable = binaryLocationsPerProject.keyTable;
597   for (int i = 0, l = keyTable.length; i < l; i++) {
598     IProject prereqProject = (IProject) keyTable[i];
599     if (prereqProject != null && prereqProject != currentProject)
600       state.recordStructuralDependency(prereqProject, getLastState(prereqProject));
601   }
602 
603   if (DEBUG)
604     System.out.println("Recording new state : " + state); //$NON-NLS-1$
605   // state.dump();
606   JavaModelManager.getJavaModelManager().setLastBuiltState(currentProject, state);
607 }
608 
609 /**
610  * String representation for debugging purposes
611  */
612 public String toString() {
613   return currentProject == null
614     ? "JavaBuilder for unknown project" //$NON-NLS-1$
615     : "JavaBuilder for " + currentProject.getName(); //$NON-NLS-1$
616 }
617 }