}
}
}
+
+ public void DumpBuckets (Dictionary<string, Dictionary<string, BuildItemGroup>> buckets)
+ {
+ foreach (KeyValuePair<string, Dictionary<string, BuildItemGroup>> pair in buckets) {
+ Console.WriteLine ("Bucket> {0} {", pair.Key);
+ DumpBucket (pair.Value);
+ Console.WriteLine ("}");
+ }
+ }
+
+ public static void DumpBucket (Dictionary<string, BuildItemGroup> bucket)
+ {
+ foreach (KeyValuePair<string, BuildItemGroup> bpair in bucket) {
+ Console.WriteLine ("\t{0} [", bpair.Key);
+ foreach (BuildItem item in bpair.Value)
+ Console.WriteLine ("\t\t{0} - {1}", item.Name, item.FinalItemSpec);
+ Console.WriteLine ("\t]");
+ }
+ }
+
+
}
}
+2009-11-28 Ankit Jain <jankit@novell.com>
+
+ Fix bug #547753.
+ * BatchingImplBase.cs (DumpBucket*): New. Useful for debugging.
+ * Project.cs (Batch): New. Contains the perBatchItems and the
+ commonItems for the batch.
+ Use a stack of batches, instead of directly setting the current
+ batch items.
+ (PushBatch): New.
+ (PopBatch): New.
+ (SetBatchedItems): Make this private.
+ (GlobalProperties.set): Re-evaluating the project is not required
+ for this.
+ * TargetBatchingImpl.cs: Use the new Push/PopBatch instead of directly
+ setting the batches.
+ * TaskBatchingImpl.cs: Likewise. Refactor slightly to PopBatch in
+ finally block.
+ * Target.cs (Build): Reset the current batch to nil before evaluating
+ and building this target.
+
2009-11-28 Ankit Jain <jankit@novell.com>
* Project.cs (TryGetEvaluatedItemByNameBatched): Item could be
List<string> builtTargetKeys;
bool building;
BuildSettings current_settings;
+ Stack<Batch> batches;
+
static string extensions_path;
static XmlNamespaceManager manager;
builtTargetKeys = new List<string> ();
initialTargets = new List<string> ();
defaultTargets = new string [0];
+ batches = new Stack<Batch> ();
globalProperties = new BuildPropertyGroup (null, this, null, false);
foreach (BuildProperty bp in parentEngine.GlobalProperties)
Dictionary<string, BuildItemGroup> perBatchItemsByName;
Dictionary<string, BuildItemGroup> commonItemsByName;
- internal void SetBatchedItems (Dictionary<string, BuildItemGroup> perBatchItemsByName, Dictionary<string, BuildItemGroup> commonItemsByName)
+ struct Batch {
+ public Dictionary<string, BuildItemGroup> perBatchItemsByName;
+ public Dictionary<string, BuildItemGroup> commonItemsByName;
+
+ public Batch (Dictionary<string, BuildItemGroup> perBatchItemsByName, Dictionary<string, BuildItemGroup> commonItemsByName)
+ {
+ this.perBatchItemsByName = perBatchItemsByName;
+ this.commonItemsByName = commonItemsByName;
+ }
+ }
+
+ Stack<Batch> Batches {
+ get { return batches; }
+ }
+
+ internal void PushBatch (Dictionary<string, BuildItemGroup> perBatchItemsByName, Dictionary<string, BuildItemGroup> commonItemsByName)
+ {
+ batches.Push (new Batch (perBatchItemsByName, commonItemsByName));
+ SetBatchedItems (perBatchItemsByName, commonItemsByName);
+ }
+
+ internal void PopBatch ()
+ {
+ batches.Pop ();
+ if (batches.Count > 0) {
+ Batch b = batches.Peek ();
+ SetBatchedItems (b.perBatchItemsByName, b.commonItemsByName);
+ } else {
+ SetBatchedItems (null, null);
+ }
+ }
+
+ void SetBatchedItems (Dictionary<string, BuildItemGroup> perBatchItemsByName, Dictionary<string, BuildItemGroup> commonItemsByName)
{
this.perBatchItemsByName = perBatchItemsByName;
this.commonItemsByName = commonItemsByName;
throw new InvalidOperationException ("GlobalProperties can not be set to persisted property group.");
globalProperties = value;
- NeedToReevaluate ();
}
}
return true;
}
+ // Push a null/empty batch, effectively clearing it
+ project.PushBatch (null, null);
if (!ConditionParser.ParseAndEvaluate (Condition, Project)) {
LogMessage (MessageImportance.Low,
"Target {0} skipped due to false condition: {1}",
Name, Condition);
+ project.PopBatch ();
return true;
}
} catch (Exception e) {
LogError ("Error building target {0}: {1}", Name, e.ToString ());
return false;
+ } finally {
+ project.PopBatch ();
}
project.ParentEngine.BuiltTargetsOutputByName [built_targets_key] = (ITaskItem[]) Outputs.Clone ();
executeOnErrors = false;
foreach (Dictionary<string, BuildItemGroup> bucket in buckets) {
LogTargetStarted (target);
+ project.PushBatch (bucket, commonItemsByName);
try {
- project.SetBatchedItems (bucket, commonItemsByName);
if (!BuildTargetNeeded ()) {
LogTargetSkipped (target);
continue;
}
for (int i = 0; i < target.BuildTasks.Count; i ++) {
- //required setting here, as batchtask.Run resets
- //these to null before returning!
- project.SetBatchedItems (bucket, commonItemsByName);
-
//FIXME: parsing attributes repeatedly
BuildTask task = target.BuildTasks [i];
result = new TaskBatchingImpl (project).Build (task, out executeOnErrors);
}
}
} finally {
+ project.PopBatch ();
LogTargetFinished (target, result);
}
}
- project.SetBatchedItems (null, null);
-
return result;
}
// Run the task in batches
bool retval = true;
- if (buckets.Count > 0) {
- foreach (Dictionary<string, BuildItemGroup> bucket in buckets) {
- project.SetBatchedItems (bucket, commonItemsByName);
- if (ConditionParser.ParseAndEvaluate (buildTask.Condition, project)) {
- retval = buildTask.Execute ();
- if (!retval && !buildTask.ContinueOnError) {
- executeOnErrors = true;
- break;
- }
- }
- }
- project.SetBatchedItems (null, null);
- } else {
+ if (buckets.Count == 0) {
// batched mode, but no values in the corresponding items!
if (ConditionParser.ParseAndEvaluate (buildTask.Condition, project)) {
retval = buildTask.Execute ();
if (!retval && !buildTask.ContinueOnError)
executeOnErrors = true;
}
+
+ return retval;
+ }
+
+ // batched
+ foreach (Dictionary<string, BuildItemGroup> bucket in buckets) {
+ project.PushBatch (bucket, commonItemsByName);
+ try {
+ if (ConditionParser.ParseAndEvaluate (buildTask.Condition, project)) {
+ retval = buildTask.Execute ();
+ if (!retval && !buildTask.ContinueOnError) {
+ executeOnErrors = true;
+ break;
+ }
+ }
+ } finally {
+ project.PopBatch ();
+ }
}
return retval;
+2009-11-28 Ankit Jain <jankit@novell.com>
+
+ * TaskBatchingTest.cs: Add new tests for batching.
+
2009-11-28 Ankit Jain <jankit@novell.com>
* TaskBatchingTest.cs: Add new tests for batching, use on unbatched
}
}
+ [Test]
+ public void TestTargetInvocationFromBatchedTask () {
+ string projectString = @"<Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"" ToolsVersion=""3.5"">
+ <ItemGroup>
+ <Item1 Include=""One""/>
+ <Item1 Include=""Two""/>
+
+ <Item1Ref Include=""@(Item1)"" />
+ </ItemGroup>
+ <PropertyGroup>
+ <Prop1>@(Item1)</Prop1>
+ <Prop2>@(Item1Ref)</Prop2>
+ </PropertyGroup>
+ <Target Name='1'>
+ <CallTarget Targets='foo' Condition="" '%(Item1.Identity)' != ''"" />
+ <Message Text=""Item1: @(Item1) Item1Ref: @(Item1Ref)""/>
+ <Message Text=""Prop1: $(Prop1) Prop2: $(Prop2)""/>
+ </Target>
+ <Target Name='foo'>
+ <Message Text=""(foo) Item1: @(Item1) Item1Ref: @(Item1Ref)""/>
+ <Message Text=""(foo) Prop1: $(Prop1) Prop2: $(Prop2)""/>
+ </Target>
+</Project>";
+
+ Engine engine = new Engine (Consts.BinPath);
+ Project project = engine.CreateNewProject ();
+
+ TestMessageLogger testLogger = new TestMessageLogger ();
+ engine.RegisterLogger (testLogger);
+
+ project.LoadXml (projectString);
+ if (!project.Build ("1")) {
+
+ testLogger.DumpMessages ();
+ Assert.Fail ("Build failed");
+ }
+
+ try {
+ testLogger.CheckLoggedMessageHead ("(foo) Item1: One;Two Item1Ref: One;Two", "A1");
+ testLogger.CheckLoggedMessageHead ("(foo) Prop1: One;Two Prop2: One;Two", "A2");
+
+ testLogger.CheckLoggedMessageHead ("Item1: One;Two Item1Ref: One;Two", "A3");
+ testLogger.CheckLoggedMessageHead ("Prop1: One;Two Prop2: One;Two", "A4");
+
+ Assert.AreEqual (0, testLogger.NormalMessageCount, "Unexpected extra messages found");
+ } catch (AssertionException) {
+ testLogger.DumpMessages ();
+ throw;
+ }
+ }
+
+ [Test]
+ public void TestTargetInvocationFromBatchedTarget () {
+ string projectString = @"<Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"" ToolsVersion=""3.5"">
+ <ItemGroup>
+ <Item1 Include=""One""/>
+ <Item1 Include=""Two""/>
+
+ <Item1Ref Include=""@(Item1)"" />
+ </ItemGroup>
+ <PropertyGroup>
+ <Prop1>@(Item1)</Prop1>
+ <Prop2>@(Item1Ref)</Prop2>
+ </PropertyGroup>
+ <Target Name='1' Inputs=""%(Item1.Identity)"" Outputs=""Nonexistant.foobar"">
+ <Message Text=""Item1: @(Item1) Item1Ref: @(Item1Ref)""/>
+ <Message Text=""Prop1: $(Prop1) Prop2: $(Prop2)""/>
+
+ <CallTarget Targets='foo' />
+
+ <Message Text=""Item1: @(Item1) Item1Ref: @(Item1Ref)""/>
+ <Message Text=""Prop1: $(Prop1) Prop2: $(Prop2)""/>
+ </Target>
+ <Target Name='foo' Condition="" '@(Item1)' != 'One' "">
+ <Message Text=""(foo) Item1: @(Item1) Item1Ref: @(Item1Ref)""/>
+ <Message Text=""(foo) Prop1: $(Prop1) Prop2: $(Prop2)""/>
+ </Target>
+</Project>";
+
+ Engine engine = new Engine (Consts.BinPath);
+ Project project = engine.CreateNewProject ();
+
+ TestMessageLogger testLogger = new TestMessageLogger ();
+ engine.RegisterLogger (testLogger);
+
+ project.LoadXml (projectString);
+ if (!project.Build ("1")) {
+
+ testLogger.DumpMessages ();
+ Assert.Fail ("Build failed");
+ }
+
+ try {
+ testLogger.CheckLoggedMessageHead ("Item1: One Item1Ref: One;Two", "A1");
+ testLogger.CheckLoggedMessageHead ("Prop1: One Prop2: One;Two", "A2");
+
+ testLogger.CheckLoggedMessageHead ("(foo) Item1: One;Two Item1Ref: One;Two", "A3");
+ testLogger.CheckLoggedMessageHead ("(foo) Prop1: One;Two Prop2: One;Two", "A4");
+
+ testLogger.CheckLoggedMessageHead ("Item1: One Item1Ref: One;Two", "A5");
+ testLogger.CheckLoggedMessageHead ("Prop1: One Prop2: One;Two", "A6");
+
+ //second batch, foo has already run, so doesn't execute again
+ testLogger.CheckLoggedMessageHead ("Item1: Two Item1Ref: One;Two", "A7");
+ testLogger.CheckLoggedMessageHead ("Prop1: Two Prop2: One;Two", "A8");
+
+ testLogger.CheckLoggedMessageHead ("Item1: Two Item1Ref: One;Two", "A9");
+ testLogger.CheckLoggedMessageHead ("Prop1: Two Prop2: One;Two", "A10");
+
+ Assert.AreEqual (0, testLogger.NormalMessageCount, "Unexpected extra messages found");
+ } catch (AssertionException) {
+ testLogger.DumpMessages ();
+ throw;
+ }
+ }
+
[Test]
public void TestBatchingWithUnqualifiedMetadataReference () {
string projectString = @"<Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"" ToolsVersion=""3.5"">