[xbuild] Add new reserved properties $(MSBuildThisFile*).
authorAnkit Jain <radical@corewars.org>
Tue, 22 Feb 2011 18:36:07 +0000 (00:06 +0530)
committerAnkit Jain <radical@corewars.org>
Tue, 22 Feb 2011 18:39:33 +0000 (00:09 +0530)
Also, fixes bug #668955.

Add new reserved properties $(MSBuildThisFile*).
Unlike the $(MSBuildProjectFile), the *This* properties are evaluated
in the context where they were used. Eg. if such a property was
referenced in a PropertyGroup, then it would refer to the file
containing the that definition, and *not* the main project file.
It applies to items and tasks/targets also.

* Microsoft.Build.Engine/Microsoft.Build.BuildEngine/BuildChoose.cs:
* Microsoft.Build.Engine/Microsoft.Build.BuildEngine/BuildItemGroup.cs:
* Microsoft.Build.Engine/Microsoft.Build.BuildEngine/BuildPropertyGroup.cs:
Add DefinedInFileName property.

* Microsoft.Build.Engine/Microsoft.Build.BuildEngine/Project.cs:
Add a stack to keep track the "current" file, which is used to set
the $(MSBuildThisFile*) properties. Update the properties on
Push/Pop.

* Microsoft.Build.Engine/Microsoft.Build.BuildEngine/GroupingCollection.cs:
Push/Pop the "current" file, when evaluating a property/item/etc.

* Microsoft.Build.Engine/Microsoft.Build.BuildEngine/Target.cs:
Push/Pop the "current" file, when building. This ensures that the
references in the target definition get evaluated correctly.

* Microsoft.Build.Engine/Test/Microsoft.Build.BuildEngine/ProjectTest.cs:
Add corresponding test.

mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/BuildChoose.cs
mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/BuildItemGroup.cs
mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/BuildPropertyGroup.cs
mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/GroupingCollection.cs
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/Test/Microsoft.Build.BuildEngine/ProjectTest.cs

index 7abb245ea0a737ecc197cea3c9c08728c3576f00..7cf3632ec4a00beeec00b5b396dcff95ee188a0e 100644 (file)
@@ -36,13 +36,20 @@ namespace Microsoft.Build.BuildEngine {
                
                BuildWhen       otherwise;
                Project         project;
+               ImportedProject importedProject;
                XmlElement      xmlElement;
                List <BuildWhen>        whens;
                
                public BuildChoose (XmlElement xmlElement, Project project)
+                       : this (xmlElement, project, null)
+               {
+               }
+
+               internal BuildChoose (XmlElement xmlElement, Project project, ImportedProject importedProject)
                {
                        this.xmlElement = xmlElement;
                        this.project = project;
+                       this.importedProject = importedProject;
                        this.whens = new List <BuildWhen> ();
 
                        foreach (XmlNode xn in xmlElement.ChildNodes) {
@@ -65,6 +72,9 @@ namespace Microsoft.Build.BuildEngine {
                                        otherwise = new BuildWhen (xe, project);
                                }
                        }
+
+                       DefinedInFileName = importedProject != null ? importedProject.FullFileName :
+                                               project != null ? project.FullFileName : null;
                }
                
                public void Evaluate ()
@@ -85,6 +95,8 @@ namespace Microsoft.Build.BuildEngine {
                        get { return whens; }
                        set { whens = value; }
                }
+
+               internal string DefinedInFileName { get; private set; }
        }
 }
 
index fe3113b1e3e7a31e58124e15b00d4bb1777a9f83..f590cd57745eb90f639ccea22e7f3f059f962afe 100644 (file)
@@ -76,6 +76,9 @@ namespace Microsoft.Build.BuildEngine {
                                buildItems.Add (bi);
                                project.LastItemGroupContaining [bi.Name] = this;
                        }
+
+                       DefinedInFileName = importedProject != null ? importedProject.FullFileName :
+                                               project != null ? project.FullFileName : null;
                }
 
                public BuildItem AddNewItem (string itemName,
@@ -303,6 +306,8 @@ namespace Microsoft.Build.BuildEngine {
                        }
                }
 
+               internal string DefinedInFileName { get; private set; }
+
                internal bool FromXml {
                        get {
                                return itemGroupElement != null;
index dd3259675c920b8bb89c01c44b980f1b58a6fe08..ef1bf78cc3926db30ef708418e4c69afc2fe14dd 100644 (file)
@@ -71,6 +71,9 @@ namespace Microsoft.Build.BuildEngine {
                                } 
                        } else
                                this.propertiesByName = new Dictionary <string, BuildProperty> (StringComparer.InvariantCultureIgnoreCase);
+
+                       DefinedInFileName = importedProject != null ? importedProject.FullFileName :
+                                               (project != null ? project.FullFileName : null);
                }
 
                public BuildProperty AddNewProperty (string propertyName,
@@ -287,7 +290,9 @@ namespace Microsoft.Build.BuildEngine {
                                propertiesByName [propertyName] = value;
                        }
                }
-               
+
+               internal string DefinedInFileName { get; private set; }
+
                internal GroupingCollection GroupingCollection {
                        get { return parentCollection; }
                        set { parentCollection = value; }
index 35b321d281626ef646b35fcb64bdf2a46061cf33..0ebb654aa25912f25c14345767dbfc7beb041865 100644 (file)
@@ -180,8 +180,13 @@ namespace Microsoft.Build.BuildEngine {
                                while (evaluate_iterator != null) {
                                        if (evaluate_iterator.Value is BuildPropertyGroup) {
                                                bpg = (BuildPropertyGroup) evaluate_iterator.Value;
-                                               if (ConditionParser.ParseAndEvaluate (bpg.Condition, project))
-                                                       bpg.Evaluate ();
+                                               project.PushThisFileProperty (bpg.DefinedInFileName);
+                                               try {
+                                                       if (ConditionParser.ParseAndEvaluate (bpg.Condition, project))
+                                                               bpg.Evaluate ();
+                                               } finally {
+                                                       project.PopThisFileProperty ();
+                                               }
                                        }
 
                                        // if it wasn't moved by adding anything because of evaluating a Import shift it
@@ -197,8 +202,13 @@ namespace Microsoft.Build.BuildEngine {
                                while (evaluate_iterator != null) {
                                        if (evaluate_iterator.Value is BuildItemGroup) {
                                                big = (BuildItemGroup) evaluate_iterator.Value;
-                                               if (ConditionParser.ParseAndEvaluate (big.Condition, project))
-                                                       big.Evaluate ();
+                                               project.PushThisFileProperty (big.DefinedInFileName);
+                                               try {
+                                                       if (ConditionParser.ParseAndEvaluate (big.Condition, project))
+                                                               big.Evaluate ();
+                                               } finally {
+                                                       project.PopThisFileProperty ();
+                                               }
                                        }
 
                                        evaluate_iterator = evaluate_iterator.Next;
@@ -210,17 +220,22 @@ namespace Microsoft.Build.BuildEngine {
                                while (evaluate_iterator != null) {
                                        if (evaluate_iterator.Value is BuildChoose) {
                                                BuildChoose bc = (BuildChoose)evaluate_iterator.Value;
-                                               bool whenUsed = false;
-                                               foreach (BuildWhen bw in bc.Whens) {
-                                                       if (ConditionParser.ParseAndEvaluate (bw.Condition, project)) {
-                                                               bw.Evaluate ();
-                                                               whenUsed = true;
-                                                               break;
+                                               project.PushThisFileProperty (bc.DefinedInFileName);
+                                               try {
+                                                       bool whenUsed = false;
+                                                       foreach (BuildWhen bw in bc.Whens) {
+                                                               if (ConditionParser.ParseAndEvaluate (bw.Condition, project)) {
+                                                                       bw.Evaluate ();
+                                                                       whenUsed = true;
+                                                                       break;
+                                                               }
                                                        }
-                                               }
-                                               if (!whenUsed && bc.Otherwise != null &&
-                                                       ConditionParser.ParseAndEvaluate (bc.Otherwise.Condition, project)) {
-                                                       bc.Otherwise.Evaluate ();
+                                                       if (!whenUsed && bc.Otherwise != null &&
+                                                               ConditionParser.ParseAndEvaluate (bc.Otherwise.Condition, project)) {
+                                                               bc.Otherwise.Evaluate ();
+                                                       }
+                                               } finally {
+                                                       project.PopThisFileProperty ();
                                                }
                                        }
 
index d8f0a92061f122bff013b68ace698981a5f09306..6be6c2170b1d8f3b07338db5609aa8f0797ea6f7 100644 (file)
@@ -76,6 +76,11 @@ namespace Microsoft.Build.BuildEngine {
                bool                            building;
                BuildSettings                   current_settings;
                Stack<Batch>                    batches;
+
+               // This is used to keep track of "current" file,
+               // which is then used to set the reserved properties
+               // $(MSBuildThisFile*)
+               Stack<string> this_file_property_stack;
                ProjectLoadSettings             project_load_settings;
 
 
@@ -113,6 +118,7 @@ namespace Microsoft.Build.BuildEngine {
                        initialTargets = new List<string> ();
                        defaultTargets = new string [0];
                        batches = new Stack<Batch> ();
+                       this_file_property_stack = new Stack<string> ();
 
                        globalProperties = new BuildPropertyGroup (null, this, null, false);
                        foreach (BuildProperty bp in parentEngine.GlobalProperties)
@@ -463,6 +469,7 @@ namespace Microsoft.Build.BuildEngine {
                                                "projectFileName");
 
                        this.fullFileName = Utilities.FromMSBuildPath (Path.GetFullPath (projectFileName));
+                       PushThisFileProperty (fullFileName);
 
                        string filename = fullFileName;
                        if (String.Compare (Path.GetExtension (fullFileName), ".sln", true) == 0) {
@@ -881,7 +888,7 @@ namespace Microsoft.Build.BuildEngine {
                                                AddPropertyGroup (xe, ip);
                                                break;
                                        case  "Choose":
-                                               AddChoose (xe);
+                                               AddChoose (xe, ip);
                                                break;
                                        default:
                                                throw new InvalidProjectFileException (String.Format ("Invalid element '{0}' in project file.", xe.Name));
@@ -975,6 +982,11 @@ namespace Microsoft.Build.BuildEngine {
                                projectDir = Path.GetDirectoryName (FullFileName);
 
                        evaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectDirectory", projectDir, PropertyType.Reserved));
+
+                       if (this_file_property_stack.Count > 0)
+                               // Just re-inited the properties, but according to the stack,
+                               // we should have a MSBuild*This* property set
+                               SetMSBuildThisFileProperties (this_file_property_stack.Peek ());
                }
 
                // precedence:
@@ -1066,9 +1078,9 @@ namespace Microsoft.Build.BuildEngine {
                        PropertyGroups.Add (bpg);
                }
                
-               void AddChoose (XmlElement xmlElement)
+               void AddChoose (XmlElement xmlElement, ImportedProject importedProject)
                {
-                       BuildChoose bc = new BuildChoose (xmlElement, this);
+                       BuildChoose bc = new BuildChoose (xmlElement, this, importedProject);
                        groupingCollection.Add (bc);
                }
                
@@ -1261,6 +1273,41 @@ namespace Microsoft.Build.BuildEngine {
                        return default (T);
                }
 
+               // Used for MSBuild*This* set of properties
+               internal void PushThisFileProperty (string full_filename)
+               {
+                       string last_file = this_file_property_stack.Count == 0 ? String.Empty : this_file_property_stack.Peek ();
+                       this_file_property_stack.Push (full_filename);
+                       if (last_file != full_filename)
+                               // first time, or different from previous one
+                               SetMSBuildThisFileProperties (full_filename);
+               }
+
+               internal void PopThisFileProperty ()
+               {
+                       string last_file = this_file_property_stack.Pop ();
+                       if (this_file_property_stack.Count > 0 && last_file != this_file_property_stack.Peek ())
+                               SetMSBuildThisFileProperties (this_file_property_stack.Peek ());
+               }
+
+               void SetMSBuildThisFileProperties (string full_filename)
+               {
+                       if (String.IsNullOrEmpty (full_filename))
+                               return;
+
+                       evaluatedProperties.AddProperty (new BuildProperty ("MSBuildThisFile", Path.GetFileName (full_filename), PropertyType.Reserved));
+                       evaluatedProperties.AddProperty (new BuildProperty ("MSBuildThisFileFullPath", full_filename, PropertyType.Reserved));
+                       evaluatedProperties.AddProperty (new BuildProperty ("MSBuildThisFileName", Path.GetFileNameWithoutExtension (full_filename), PropertyType.Reserved));
+                       evaluatedProperties.AddProperty (new BuildProperty ("MSBuildThisFileExtension", Path.GetExtension (full_filename), PropertyType.Reserved));
+
+                       string project_dir = Path.GetDirectoryName (full_filename) + Path.DirectorySeparatorChar;
+                       evaluatedProperties.AddProperty (new BuildProperty ("MSBuildThisFileDirectory", project_dir, PropertyType.Reserved));
+                       evaluatedProperties.AddProperty (new BuildProperty ("MSBuildThisFileDirectoryNoRoot",
+                                               project_dir.Substring (Path.GetPathRoot (project_dir).Length),
+                                               PropertyType.Reserved));
+               }
+
+
                internal void LogWarning (string filename, string message, params object[] messageArgs)
                {
                        BuildWarningEventArgs bwea = new BuildWarningEventArgs (
index beb7c581dab54b864d0a62148b62ec5f5c64bee7..439c56fd94b14b4887d57ace200ec0d5f5e82e71 100644 (file)
@@ -122,6 +122,16 @@ namespace Microsoft.Build.BuildEngine {
                }
 
                bool Build (string built_targets_key, out bool executeOnErrors)
+               {
+                       project.PushThisFileProperty (TargetFile);
+                       try {
+                               return BuildActual (built_targets_key, out executeOnErrors);
+                       } finally {
+                               project.PopThisFileProperty ();
+                       }
+               }
+
+               bool BuildActual (string built_targets_key, out bool executeOnErrors)
                {
                        bool result = false;
                        executeOnErrors = false;
index 5d92a43f5a544d6d8ae6ca7d8efbed7a0a4bb2c9..325986470d7e1dc50054510f33353f744776a725 100644 (file)
@@ -1831,6 +1831,97 @@ namespace MonoTests.Microsoft.Build.BuildEngine {
                        }
                }
 
+               [Test]
+               public void TestMSBuildThisProperties ()
+               {
+                       Engine engine = new Engine (Consts.BinPath);
+                       Project project = engine.CreateNewProject ();
+
+                       string base_dir = Path.GetFullPath (Path.Combine ("Test", "resources")) + Path.DirectorySeparatorChar;
+                       string tmp_dir = Path.GetFullPath (Path.Combine (base_dir, "tmp")) + Path.DirectorySeparatorChar;
+
+                       string first_project = Path.Combine (base_dir, "first.proj");
+                       string second_project = Path.Combine (tmp_dir, "second.proj");
+                       string third_project = Path.Combine (tmp_dir, "third.proj");
+
+                       string first = @"<Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"" " + Consts.ToolsVersionString + @">
+                               <PropertyGroup>
+                                       <FooInMain>$(MSBuildThisFileDirectory)</FooInMain>
+                               </PropertyGroup>
+                               <ItemGroup>
+                                       <ItemInMain Include=""$(MSBuildThisFileFullPath)"" />
+                               </ItemGroup>
+                               <Import Project=""tmp\second.proj""/>
+                       </Project>";
+
+                       string second = @"<Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"" " + Consts.ToolsVersionString + @">
+                               <PropertyGroup>
+                                       <FooInImport1>$(MSBuildThisFileDirectory)</FooInImport1>
+                               </PropertyGroup>
+                               <PropertyGroup>
+                                       <FooInImport2>$(MSBuildThisFileDirectory)</FooInImport2>
+                               </PropertyGroup>
+                               <ItemGroup>
+                                       <ItemInImport1 Include=""$(MSBuildThisFileFullPath)"" />
+                               </ItemGroup>
+
+                               <Import Project=""third.proj""/>
+                       </Project>";
+
+                       string third = @"<Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"" " + Consts.ToolsVersionString + @">
+                               <PropertyGroup>
+                                       <FooInTwo>$(MSBuildThisFileFullPath)</FooInTwo>
+                               </PropertyGroup>
+                               <ItemGroup>
+                                       <ItemInTwo Include=""$(MSBuildThisFileFullPath)"" />
+                               </ItemGroup>
+
+                               <Target Name=""TargetInTwo"">
+                                       <Message Text=""FooInMain: $(FooInMain)""/>
+                                       <Message Text=""FooInImport1: $(FooInImport1)""/>
+                                       <Message Text=""FooInImport2: $(FooInImport2)""/>
+                                       <Message Text=""FooInTwo: $(FooInTwo)""/>
+
+                                       <Message Text=""ItemInMain: %(ItemInMain.Identity)""/>
+                                       <Message Text=""ItemInImport1: %(ItemInImport1.Identity)""/>
+                                       <Message Text=""ItemInTwo: %(ItemInTwo.Identity)""/>
+                                       <Message Text=""Full path: $(MSBuildThisFileFullPath)""/>
+                               </Target>
+                       </Project>";
+
+                       File.WriteAllText (first_project, first);
+
+                       Directory.CreateDirectory (Path.Combine (base_dir, "tmp"));
+                       File.WriteAllText (second_project, second);
+                       File.WriteAllText (third_project, third);
+
+                       MonoTests.Microsoft.Build.Tasks.TestMessageLogger logger =
+                               new MonoTests.Microsoft.Build.Tasks.TestMessageLogger ();
+                       engine.RegisterLogger (logger);
+
+                       project.Load (first_project);
+                       try {
+                               Assert.IsTrue (project.Build (), "Build failed");
+
+                               logger.CheckLoggedMessageHead ("FooInMain: " + base_dir, "A1");
+                               logger.CheckLoggedMessageHead ("FooInImport1: " + tmp_dir, "A2");
+                               logger.CheckLoggedMessageHead ("FooInImport2: " + tmp_dir, "A3");
+                               logger.CheckLoggedMessageHead ("FooInTwo: " + third_project, "A4");
+                               logger.CheckLoggedMessageHead ("ItemInMain: " + first_project, "A5");
+                               logger.CheckLoggedMessageHead ("ItemInImport1: " + second_project, "A6");
+                               logger.CheckLoggedMessageHead ("ItemInTwo: " + third_project, "A7");
+                               logger.CheckLoggedMessageHead ("Full path: " + third_project, "A8");
+
+                               Assert.AreEqual (0, logger.NormalMessageCount, "Unexpected extra messages found");
+                       } catch {
+                               logger.DumpMessages ();
+                               throw;
+                       } finally {
+                               File.Delete (first_project);
+                               File.Delete (second_project);
+                               File.Delete (third_project);
+                       }
+               }
 
                [Test]
                public void TestRequiredTask_String1 ()