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 }