View | Details | Raw Unified | Return to bug 343256 | Differences between
and this patch

Collapse All | Expand All

(-)src/org/eclipse/core/internal/events/BuildManager.java (-40 / +58 lines)
Lines 1083-1137 Link Here
1083
	}
1083
	}
1084
1084
1085
	/**
1085
	/**
1086
	 * Returns the scheduling rule that is required for building the project.
1086
	 * Discover the scheduling rule required to build the passed in build configuration array
1087
	 * @param buildConfigs Array of build configurations to build
1088
	 * @param trigger
1089
	 * @param builderName name of the builder, or null for all build commands
1090
	 * @param args arguments to the IProject.build command, or null
1091
	 * @return the scheduling rule that is required for building the passed in build configurations.
1087
	 */
1092
	 */
1088
	public ISchedulingRule getRule(IBuildConfiguration buildConfiguration, int trigger, String builderName, Map<String, String> args) {
1093
	public ISchedulingRule getRule(IBuildConfiguration[] buildConfigs, int trigger, String builderName, Map<String, String> args) {
1089
		IProject project = buildConfiguration.getProject();
1094
		ISchedulingRule wrRule = workspace.getRoot();
1090
		MultiStatus status = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.INTERNAL_ERROR, Messages.events_errors, null);
1095
		MultiStatus status = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.INTERNAL_ERROR, Messages.events_errors, null);
1091
		if (builderName == null) {
1096
		Set<ISchedulingRule> rules = new HashSet<ISchedulingRule>();
1092
			final ICommand[] commands;
1097
		boolean hasNullBuildRule = false;
1093
			if (project.isAccessible()) {
1098
1094
				Set<ISchedulingRule> rules = new HashSet<ISchedulingRule>();
1099
		// Loop over all the build configurations collecting together the build rules.
1095
				commands = ((Project) project).internalGetDescription().getBuildSpec(false);
1100
		for (IBuildConfiguration buildConfiguration : buildConfigs) {
1096
				boolean hasNullBuildRule = false;
1101
			IProject project = buildConfiguration.getProject();
1097
				BuildContext context = new BuildContext(buildConfiguration);
1102
			if (builderName == null) {
1098
				for (int i = 0; i < commands.length; i++) {
1103
				final ICommand[] commands;
1099
					BuildCommand command = (BuildCommand) commands[i];
1104
				if (project.isAccessible()) {
1100
					try {
1105
					commands = ((Project) project).internalGetDescription().getBuildSpec(false);
1101
						IncrementalProjectBuilder builder = getBuilder(buildConfiguration, command, i, status, context);
1106
					BuildContext context = new BuildContext(buildConfiguration);
1102
						if (builder != null) {
1107
					for (int i = 0; i < commands.length; i++) {
1103
							ISchedulingRule builderRule = builder.getRule(trigger, args);
1108
						BuildCommand command = (BuildCommand) commands[i];
1104
							if (builderRule != null)
1109
						try {
1105
								rules.add(builderRule);
1110
							IncrementalProjectBuilder builder = getBuilder(buildConfiguration, command, i, status, context);
1106
							else 
1111
							if (builder != null) {
1107
								hasNullBuildRule = true;
1112
								ISchedulingRule builderRule = builder.getRule(trigger, args);
1113
								if (builderRule != null)
1114
									rules.add(builderRule);
1115
								else 
1116
									hasNullBuildRule = true;
1117
							}
1118
						} catch (CoreException e) {
1119
							status.add(e.getStatus());
1108
						}
1120
						}
1109
					} catch (CoreException e) {
1110
						status.add(e.getStatus());
1111
					}
1121
					}
1122
					// Can short-circuit if there's a null rule && a non-null rule
1123
					if (!rules.isEmpty() && hasNullBuildRule)
1124
						break;
1125
				}
1126
			} else {
1127
				// Returns the derived resources for the specified builderName
1128
				ICommand command = getCommand(project, builderName, args);
1129
				try {
1130
					IncrementalProjectBuilder builder = getBuilder(buildConfiguration, command, -1, status);
1131
					if (builder != null) {
1132
						ISchedulingRule rule = builder.getRule(trigger, args);
1133
						if (wrRule.equals(rule))
1134
							return wrRule;
1135
						rules.add(rule);
1136
					}
1137
				} catch (CoreException e) {
1138
					status.add(e.getStatus());
1112
				}
1139
				}
1113
				if (rules.isEmpty())
1114
					return null;
1115
				// Bug 306824 - Builders returning a null rule can't work safely if other builders require a non-null rule
1116
				// Be pessimistic and fall back to the default build rule (workspace root) in this case.
1117
				if (!hasNullBuildRule)
1118
					return new MultiRule(rules.toArray(new ISchedulingRule[rules.size()]));
1119
			}
1120
		} else {
1121
			// Returns the derived resources for the specified builderName
1122
			ICommand command = getCommand(project, builderName, args);
1123
			try {
1124
				IncrementalProjectBuilder builder = getBuilder(buildConfiguration, command, -1, status);
1125
				if (builder != null)
1126
					return builder.getRule(trigger, args);
1127
1128
			} catch (CoreException e) {
1129
				status.add(e.getStatus());
1130
			}
1140
			}
1131
		}
1141
		}
1132
		// Log any errors
1142
		// Log any errors
1133
		if (!status.isOK())
1143
		if (!status.isOK())
1134
			Policy.log(status);
1144
			Policy.log(status);
1135
		return workspace.getRoot();
1145
1146
		// If no rules required, return null
1147
		if (rules.isEmpty())
1148
			return null;
1149
		// Bug 306824 - Builders returning a null rule can't work safely if other builders require a non-null rule
1150
		// Be pessimistic and fall back to the default build rule (workspace root) in this case.
1151
		if (!hasNullBuildRule)
1152
			return new MultiRule(rules.toArray(new ISchedulingRule[rules.size()]));
1153
		return wrRule;
1136
	}
1154
	}
1137
}
1155
}
(-)src/org/eclipse/core/internal/resources/Project.java (-1 / +1 lines)
Lines 609-615 Link Here
609
					} finally {
609
					} finally {
610
						workspace.endOperation(rule, false, innerMonitor);
610
						workspace.endOperation(rule, false, innerMonitor);
611
					}
611
					}
612
					final ISchedulingRule buildRule = workspace.getBuildManager().getRule(config, trigger, builderName, args);
612
					final ISchedulingRule buildRule = workspace.getBuildManager().getRule(new IBuildConfiguration[]{config}, trigger, builderName, args);
613
					try {
613
					try {
614
						IStatus result;
614
						IStatus result;
615
						workspace.prepareOperation(buildRule, innerMonitor);
615
						workspace.prepareOperation(buildRule, innerMonitor);
(-)src/org/eclipse/core/internal/resources/Workspace.java (-47 / +59 lines)
Lines 410-417 Link Here
410
				recursivelyAddBuildConfigs(configs, referenced[i]);
410
				recursivelyAddBuildConfigs(configs, referenced[i]);
411
			}
411
			}
412
		} catch (CoreException e) {
412
		} catch (CoreException e) {
413
			// Not possible, we've checked that the project + configuration are accessible.
413
			// This shouldn't be possible, but currently is possible, as computing the references occurs
414
			Assert.isTrue(false);
414
			// outside of an operation... See Bug 343256 for how this should be fixed.
415
		}
415
		}
416
	}
416
	}
417
417
Lines 440-501 Link Here
440
	 */
440
	 */
441
	private void buildInternal(IBuildConfiguration[] configs, int trigger, boolean buildReferences, IProgressMonitor monitor) throws CoreException {
441
	private void buildInternal(IBuildConfiguration[] configs, int trigger, boolean buildReferences, IProgressMonitor monitor) throws CoreException {
442
		monitor = Policy.monitorFor(monitor);
442
		monitor = Policy.monitorFor(monitor);
443
		final ISchedulingRule rule = getRuleFactory().buildRule();
443
		final ISchedulingRule wrRule = getRuleFactory().buildRule();
444
		try {
444
		try {
445
			monitor.beginTask("", Policy.opWork); //$NON-NLS-1$
445
			monitor.beginTask("", Policy.opWork); //$NON-NLS-1$
446
			try {
446
			try {
447
				prepareOperation(rule, monitor);
447
				prepareOperation(wrRule, monitor);
448
				beginOperation(true);
448
				beginOperation(true);
449
				aboutToBuild(this, trigger);
449
				aboutToBuild(this, trigger);
450
				IStatus result;
450
			} finally {
451
				try {
451
				endOperation(wrRule, false, monitor);
452
452
			}
453
					// Calculate the build-order having called the pre-build notification (which may change build order)
453
			IStatus result;
454
					// If configs == EMPTY_BUILD_CONFIG_ARRAY => This is a full workspace build.
454
			ISchedulingRule buildRule = null;
455
					IBuildConfiguration[] requestedConfigs = configs;
455
			try {
456
					if (configs == EMPTY_BUILD_CONFIG_ARRAY) {
457
						if (trigger != IncrementalProjectBuilder.CLEAN_BUILD)
458
							configs = getBuildOrder();
459
						else {
460
							// clean all accessible configurations
461
							List<IBuildConfiguration> configArr = new ArrayList<IBuildConfiguration>();
462
							IProject[] prjs = getRoot().getProjects();
463
							for (int i = 0; i < prjs.length; i++)
464
								if (prjs[i].isAccessible())
465
									configArr.addAll(Arrays.asList(prjs[i].getBuildConfigs()));
466
							configs = configArr.toArray(new IBuildConfiguration[configArr.size()]);										
467
						}
468
					} else {
469
						// Order the passed in build configurations + resolve references if requested
470
						Set<IBuildConfiguration> refsList = new HashSet<IBuildConfiguration>();
471
						for (int i = 0 ; i < configs.length ; i++) {
472
							// Check project + build configuration are accessible.
473
							if (!configs[i].getProject().isAccessible() || !configs[i].getProject().hasBuildConfig(configs[i].getName()))
474
								continue;
475
							refsList.add(configs[i]);
476
							// Find transitive closure of referenced project buildConfigs
477
							if (buildReferences)
478
								recursivelyAddBuildConfigs(refsList, configs[i]);
479
						}
480
456
481
						// Order the referenced project buildConfigs
457
				// Calculate the build-order having called the pre-build notification (which may change build order)
482
						ProjectBuildConfigOrder order = computeProjectBuildConfigOrder(refsList.toArray(new IBuildConfiguration[refsList.size()]));
458
				// If configs == EMPTY_BUILD_CONFIG_ARRAY => This is a full workspace build.
483
						configs = order.buildConfigurations;
459
				IBuildConfiguration[] requestedConfigs = configs;
460
				if (configs == EMPTY_BUILD_CONFIG_ARRAY) {
461
					if (trigger != IncrementalProjectBuilder.CLEAN_BUILD)
462
						configs = getBuildOrder();
463
					else {
464
						// clean all accessible configurations
465
						List<IBuildConfiguration> configArr = new ArrayList<IBuildConfiguration>();
466
						IProject[] prjs = getRoot().getProjects();
467
						for (int i = 0; i < prjs.length; i++)
468
							if (prjs[i].isAccessible())
469
								configArr.addAll(Arrays.asList(prjs[i].getBuildConfigs()));
470
						configs = configArr.toArray(new IBuildConfiguration[configArr.size()]);										
471
					}
472
				} else {
473
					// Order the passed in build configurations + resolve references if requested
474
					Set<IBuildConfiguration> refsList = new HashSet<IBuildConfiguration>();
475
					for (int i = 0 ; i < configs.length ; i++) {
476
						// Check project + build configuration are accessible.
477
						if (!configs[i].getProject().isAccessible() || !configs[i].getProject().hasBuildConfig(configs[i].getName()))
478
							continue;
479
						refsList.add(configs[i]);
480
						// Find transitive closure of referenced project buildConfigs
481
						if (buildReferences)
482
							recursivelyAddBuildConfigs(refsList, configs[i]);
484
					}
483
					}
485
484
486
					result = getBuildManager().build(configs, requestedConfigs, trigger, Policy.subMonitorFor(monitor, Policy.opWork));
485
					// Order the referenced project buildConfigs
487
				} finally {
486
					ProjectBuildConfigOrder order = computeProjectBuildConfigOrder(refsList.toArray(new IBuildConfiguration[refsList.size()]));
488
					//must fire POST_BUILD if PRE_BUILD has occurred
487
					configs = order.buildConfigurations;
489
					broadcastBuildEvent(this, IResourceChangeEvent.POST_BUILD, trigger);
490
				}
488
				}
491
				if (!result.isOK())
489
				buildRule = getBuildManager().getRule(configs, trigger, null, null);
492
					throw new ResourceException(result);
490
				prepareOperation(buildRule, monitor);
491
				//don't open the tree eagerly because it will be wasted if no build occurs
492
				beginOperation(false);
493
				result = getBuildManager().build(configs, requestedConfigs, trigger, Policy.subMonitorFor(monitor, Policy.opWork));
493
			} finally {
494
			} finally {
494
				//building may close the tree, but we are still inside an operation so open it
495
				//must fire POST_BUILD if PRE_BUILD has occurred
495
				if (tree.isImmutable())
496
				endOperation(buildRule, false, monitor);
496
					newWorkingTree();
497
				try {
497
				endOperation(rule, false, Policy.subMonitorFor(monitor, Policy.endOpWork));
498
					prepareOperation(wrRule, monitor);
499
					//don't open the tree eagerly because it will be wasted if no change occurs
500
					beginOperation(false);
501
					broadcastBuildEvent(this, IResourceChangeEvent.POST_BUILD, trigger);
502
					//building may close the tree, so open it
503
					if (getElementTree().isImmutable())
504
						newWorkingTree();
505
				} finally {
506
					endOperation(wrRule, false, Policy.subMonitorFor(monitor, Policy.endOpWork));
507
				}
498
			}
508
			}
509
			if (!result.isOK())
510
				throw new ResourceException(result);
499
		} finally {
511
		} finally {
500
			monitor.done();
512
			monitor.done();
501
		}
513
		}
(-)src/org/eclipse/core/tests/internal/builders/RelaxedSchedRuleBuilderTest.java (-3 / +162 lines)
Lines 10-22 Link Here
10
 *******************************************************************************/
10
 *******************************************************************************/
11
package org.eclipse.core.tests.internal.builders;
11
package org.eclipse.core.tests.internal.builders;
12
12
13
import java.util.Map;
13
import java.util.*;
14
import junit.framework.Test;
14
import junit.framework.Test;
15
import junit.framework.TestSuite;
15
import junit.framework.TestSuite;
16
import org.eclipse.core.resources.*;
16
import org.eclipse.core.resources.*;
17
import org.eclipse.core.runtime.*;
17
import org.eclipse.core.runtime.*;
18
import org.eclipse.core.runtime.jobs.ISchedulingRule;
18
import org.eclipse.core.runtime.jobs.*;
19
import org.eclipse.core.runtime.jobs.Job;
20
import org.eclipse.core.tests.harness.TestBarrier;
19
import org.eclipse.core.tests.harness.TestBarrier;
21
import org.eclipse.core.tests.internal.builders.TestBuilder.BuilderRuleCallback;
20
import org.eclipse.core.tests.internal.builders.TestBuilder.BuilderRuleCallback;
22
21
Lines 205-208 Link Here
205
		tb2.waitForStatus(TestBarrier.STATUS_DONE);
204
		tb2.waitForStatus(TestBarrier.STATUS_DONE);
206
	}
205
	}
207
206
207
	private HashSet<ISchedulingRule> getRulesAsSet(ISchedulingRule rule) {
208
		HashSet<ISchedulingRule> rules = new HashSet<ISchedulingRule>();
209
		if (rule == null)
210
			return rules;
211
		if (rule instanceof MultiRule)
212
			rules.addAll(Arrays.asList(((MultiRule) rule).getChildren()));
213
		else
214
			rules.add(rule);
215
		return rules;
216
	}
217
218
	/**
219
	 * Tests for regression in running the build with reduced scheduling rules.
220
	 * @throws Exception
221
	 */
222
	public void testBug343256() throws Exception {
223
		String name = "testBug343256";
224
		setAutoBuilding(false);
225
		final IProject project = getWorkspace().getRoot().getProject(name);
226
		create(project, false);
227
228
		IProjectDescription desc = project.getDescription();
229
		desc.setBuildSpec(new ICommand[] {createCommand(desc, EmptyDeltaBuilder.BUILDER_NAME, "Project1Build1"), createCommand(desc, EmptyDeltaBuilder2.BUILDER_NAME, "Project1Build2")});
230
		project.setDescription(desc, getMonitor());
231
232
		// Ensure the builder is instantiated
233
		project.build(IncrementalProjectBuilder.CLEAN_BUILD, getMonitor());
234
235
		final TestBarrier tb1 = new TestBarrier(TestBarrier.STATUS_WAIT_FOR_START);
236
		final TestBarrier tb2 = new TestBarrier(TestBarrier.STATUS_WAIT_FOR_START);
237
238
		// Scheduling rules to returng from #getRule
239
		final ISchedulingRule[] getRules = new ISchedulingRule[2];
240
		final ISchedulingRule[] buildRules = new ISchedulingRule[2];
241
242
		// Create a builder set a null scheduling rule
243
		EmptyDeltaBuilder builder = EmptyDeltaBuilder.getInstance();
244
		EmptyDeltaBuilder2 builder2 = EmptyDeltaBuilder2.getInstance();
245
246
		// Set the rule call-back
247
		builder.setRuleCallback(new BuilderRuleCallback() {
248
			public ISchedulingRule getRule(String name, IncrementalProjectBuilder builder, int trigger, Map args) {
249
				tb1.waitForStatus(TestBarrier.STATUS_START);
250
				return getRules[0];
251
			}
252
253
			public IProject[] build(int kind, Map<String, String> args, IProgressMonitor monitor) throws CoreException {
254
				HashSet<ISchedulingRule> h1 = getRulesAsSet(Job.getJobManager().currentRule());
255
				HashSet<ISchedulingRule> h2 = getRulesAsSet(buildRules[0]);
256
				// shared scheduling rule
257
				assertTrue(h1.equals(h2));
258
				tb1.setStatus(TestBarrier.STATUS_DONE);
259
				return super.build(kind, args, monitor);
260
			}
261
		});
262
		// Set the rule call-back
263
		builder2.setRuleCallback(new BuilderRuleCallback() {
264
			public ISchedulingRule getRule(String name, IncrementalProjectBuilder builder, int trigger, Map<String, String> args) {
265
				tb2.waitForStatus(TestBarrier.STATUS_START);
266
				return getRules[1];
267
			}
268
269
			public IProject[] build(int kind, Map<String, String> args, IProgressMonitor monitor) throws CoreException {
270
				HashSet<ISchedulingRule> h1 = getRulesAsSet(Job.getJobManager().currentRule());
271
				HashSet<ISchedulingRule> h2 = getRulesAsSet(buildRules[1]);
272
				assertTrue(h1.equals(h2));
273
				tb2.setStatus(TestBarrier.STATUS_DONE);
274
				return super.build(kind, args, monitor);
275
			}
276
		});
277
278
		// IProject.build()
279
		Job j = new Job("IProject.build()") {
280
			protected IStatus run(IProgressMonitor monitor) {
281
				try {
282
					project.build(IncrementalProjectBuilder.FULL_BUILD, monitor);
283
				} catch (CoreException e) {
284
					fail(e.toString());
285
				}
286
				return Status.OK_STATUS;
287
			}
288
		};
289
		invokeTestBug343256(project, getRules, buildRules, tb1, tb2, j);
290
291
		// IWorkspace.build()
292
		j = new Job("IWorkspace.build()") {
293
			protected IStatus run(IProgressMonitor monitor) {
294
				try {
295
					getWorkspace().build(IncrementalProjectBuilder.FULL_BUILD, monitor);
296
				} catch (CoreException e) {
297
					fail(e.toString());
298
				}
299
				return Status.OK_STATUS;
300
			}
301
		};
302
		invokeTestBug343256(project, getRules, buildRules, tb1, tb2, j);
303
304
		// IWorkspace.build(IBuildConfiguration[],...)
305
		j = new Job("IWorkspace.build(IBuildConfiguration[],...)") {
306
			protected IStatus run(IProgressMonitor monitor) {
307
				try {
308
					getWorkspace().build(new IBuildConfiguration[] {project.getActiveBuildConfig()}, IncrementalProjectBuilder.FULL_BUILD, true, monitor);
309
				} catch (CoreException e) {
310
					fail(e.toString());
311
				}
312
				return Status.OK_STATUS;
313
			}
314
		};
315
		invokeTestBug343256(project, getRules, buildRules, tb1, tb2, j);
316
317
	}
318
319
	/**
320
	 * Helper method do invoke a set of tests on Bug343256 using the different sets of builder API
321
	 */
322
	private void invokeTestBug343256(IProject project, ISchedulingRule[] getRules, ISchedulingRule[] buildRules, TestBarrier tb1, TestBarrier tb2, Job j) {
323
		// Test 1 - IProject#build project sched rule
324
		getRules[0] = getRules[1] = project;
325
		buildRules[0] = buildRules[1] = new MultiRule(new ISchedulingRule[] {getRules[0]});
326
		tb1.setStatus(TestBarrier.STATUS_START);
327
		tb2.setStatus(TestBarrier.STATUS_START);
328
		j.schedule();
329
		tb1.waitForStatus(TestBarrier.STATUS_DONE);
330
		tb2.waitForStatus(TestBarrier.STATUS_DONE);
331
332
		// Test 2 - IProject#build null rule
333
		getRules[0] = getRules[1] = null;
334
		buildRules[0] = buildRules[1] = null;
335
		tb1.setStatus(TestBarrier.STATUS_START);
336
		tb2.setStatus(TestBarrier.STATUS_START);
337
		j.schedule();
338
		tb1.waitForStatus(TestBarrier.STATUS_DONE);
339
		tb2.waitForStatus(TestBarrier.STATUS_DONE);
340
341
		// Test 3 - IProject#build mixed projects
342
		getRules[0] = project;
343
		getRules[1] = getWorkspace().getRoot().getProject("other");
344
		buildRules[0] = buildRules[1] = new MultiRule(new ISchedulingRule[] {getRules[0], getRules[1]});
345
		// TODO it should be:
346
		// buildRules[0] = getRules[0]
347
		// buildRules[1] = getRules[1]
348
		tb1.setStatus(TestBarrier.STATUS_START);
349
		tb2.setStatus(TestBarrier.STATUS_START);
350
		j.schedule();
351
		tb1.waitForStatus(TestBarrier.STATUS_DONE);
352
		tb2.waitForStatus(TestBarrier.STATUS_DONE);
353
354
		// Test 4 - IProject#build project + null
355
		getRules[0] = project;
356
		getRules[1] = null;
357
		buildRules[0] = buildRules[1] = getWorkspace().getRoot();
358
		// TODO it should be:
359
		// buildRules[0] = getRules[0]
360
		// buildRules[1] = null
361
		tb1.setStatus(TestBarrier.STATUS_START);
362
		tb2.setStatus(TestBarrier.STATUS_START);
363
		j.schedule();
364
		tb1.waitForStatus(TestBarrier.STATUS_DONE);
365
		tb2.waitForStatus(TestBarrier.STATUS_DONE);
366
	}
208
}
367
}

Return to bug 343256