Fix bug #547753.
authorAnkit Jain <radical@corewars.org>
Fri, 27 Nov 2009 22:12:52 +0000 (22:12 -0000)
committerAnkit Jain <radical@corewars.org>
Fri, 27 Nov 2009 22:12:52 +0000 (22:12 -0000)
In class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine:
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.

In class/Microsoft.Build.Tasks/Test/Microsoft.Build.Tasks:
* TaskBatchingTest.cs: Add new tests for batching.

svn path=/trunk/mcs/; revision=147050

mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/BatchingImplBase.cs
mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/ChangeLog
mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/Project.cs
mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/Target.cs
mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/TargetBatchingImpl.cs
mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/TaskBatchingImpl.cs
mcs/class/Microsoft.Build.Tasks/Test/Microsoft.Build.Tasks/ChangeLog
mcs/class/Microsoft.Build.Tasks/Test/Microsoft.Build.Tasks/TaskBatchingTest.cs

index c30fd7f747a9c9df7b4b38f9490f790b0ff41fc6..1d0a1d86b25498db8f3090e98458b87237e8599b 100644 (file)
@@ -233,6 +233,27 @@ namespace Microsoft.Build.BuildEngine {
                                }
                        }
                }
+
+               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]");
+                      }
+               }
+
+
        }
 }
 
index a4e1bc5eba4e7215ed9593b4c2d7ba379bf79abb..93a6759da124136800f80bb97a274d6785982536 100644 (file)
@@ -1,3 +1,23 @@
+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
index 83b242ec788829ba8c59531eecc7b0dde351a1f2..7d9ed556221b1893367f3b9d19d445f1fe7b1b42 100644 (file)
@@ -76,6 +76,8 @@ namespace Microsoft.Build.BuildEngine {
                List<string>                    builtTargetKeys;
                bool                            building;
                BuildSettings                   current_settings;
+               Stack<Batch>                    batches;
+
 
                static string extensions_path;
                static XmlNamespaceManager      manager;
@@ -104,6 +106,7 @@ namespace Microsoft.Build.BuildEngine {
                        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)
@@ -1069,7 +1072,39 @@ namespace Microsoft.Build.BuildEngine {
                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;
@@ -1197,7 +1232,6 @@ namespace Microsoft.Build.BuildEngine {
                                        throw new InvalidOperationException ("GlobalProperties can not be set to persisted property group.");
                                
                                globalProperties = value;
-                               NeedToReevaluate ();
                        }
                }
 
index 1fb6bff07c8fe68b59ff4c4b98b5a23ba7a61f26..158ffdef94bc40a0894d7721b18996d587fb8653 100644 (file)
@@ -135,10 +135,13 @@ namespace Microsoft.Build.BuildEngine {
                                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;
                        }
 
@@ -157,6 +160,8 @@ namespace Microsoft.Build.BuildEngine {
                        } 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 ();
index aa6967c1598a2ca0e99b1c479d18d4f8cdec7ddc..e0d8d735d90bdaa87e73912d5c0491565a6fefa0 100644 (file)
@@ -95,18 +95,14 @@ namespace Microsoft.Build.BuildEngine {
                        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);
@@ -116,11 +112,10 @@ namespace Microsoft.Build.BuildEngine {
                                                }
                                        }
                                } finally {
+                                       project.PopBatch ();
                                        LogTargetFinished (target, result);
                                }
                        }
-                       project.SetBatchedItems (null, null);
-
                        return result;
                }
 
index 1b69d37c767afac0cfae7ddaf13fce2d38395fdf..ed85f29e683a9c06471f6935147dd987e079c368 100644 (file)
@@ -76,25 +76,31 @@ namespace Microsoft.Build.BuildEngine {
 
                        // 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;
index 16988507ea4af23957fae3aba7ccedef49c04c36..114758a0835bc5b2046f69b903df298954436c2c 100644 (file)
@@ -1,3 +1,7 @@
+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
index bf904fbf5ec2da5b958215565d52977d1fe74639..6bfaae755e9ce3f38c2ff715d0480869ecf2784a 100755 (executable)
@@ -746,6 +746,122 @@ namespace MonoTests.Microsoft.Build.Tasks
                        }
                }
 
+               [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"">