From: Ankit Jain Date: Tue, 22 Feb 2011 18:36:07 +0000 (+0530) Subject: [xbuild] Add new reserved properties $(MSBuildThisFile*). X-Git-Url: http://wien.tomnetworks.com/gitweb/?p=mono.git;a=commitdiff_plain;h=3bc6741c3037000eebd46b91efad54e5e6915c74 [xbuild] Add new reserved properties $(MSBuildThisFile*). 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. --- diff --git a/mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/BuildChoose.cs b/mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/BuildChoose.cs index 7abb245ea0a..7cf3632ec4a 100644 --- a/mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/BuildChoose.cs +++ b/mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/BuildChoose.cs @@ -36,13 +36,20 @@ namespace Microsoft.Build.BuildEngine { BuildWhen otherwise; Project project; + ImportedProject importedProject; XmlElement xmlElement; List 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 (); 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; } } } diff --git a/mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/BuildItemGroup.cs b/mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/BuildItemGroup.cs index fe3113b1e3e..f590cd57745 100644 --- a/mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/BuildItemGroup.cs +++ b/mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/BuildItemGroup.cs @@ -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; diff --git a/mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/BuildPropertyGroup.cs b/mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/BuildPropertyGroup.cs index dd3259675c9..ef1bf78cc39 100644 --- a/mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/BuildPropertyGroup.cs +++ b/mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/BuildPropertyGroup.cs @@ -71,6 +71,9 @@ namespace Microsoft.Build.BuildEngine { } } else this.propertiesByName = new Dictionary (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; } diff --git a/mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/GroupingCollection.cs b/mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/GroupingCollection.cs index 35b321d2816..0ebb654aa25 100644 --- a/mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/GroupingCollection.cs +++ b/mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/GroupingCollection.cs @@ -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 (); } } diff --git a/mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/Project.cs b/mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/Project.cs index d8f0a92061f..6be6c2170b1 100644 --- a/mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/Project.cs +++ b/mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/Project.cs @@ -76,6 +76,11 @@ namespace Microsoft.Build.BuildEngine { bool building; BuildSettings current_settings; Stack batches; + + // This is used to keep track of "current" file, + // which is then used to set the reserved properties + // $(MSBuildThisFile*) + Stack this_file_property_stack; ProjectLoadSettings project_load_settings; @@ -113,6 +118,7 @@ namespace Microsoft.Build.BuildEngine { initialTargets = new List (); defaultTargets = new string [0]; batches = new Stack (); + this_file_property_stack = new Stack (); 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 ( diff --git a/mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/Target.cs b/mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/Target.cs index beb7c581dab..439c56fd94b 100644 --- a/mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/Target.cs +++ b/mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/Target.cs @@ -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; diff --git a/mcs/class/Microsoft.Build.Engine/Test/Microsoft.Build.BuildEngine/ProjectTest.cs b/mcs/class/Microsoft.Build.Engine/Test/Microsoft.Build.BuildEngine/ProjectTest.cs index 5d92a43f5a5..325986470d7 100644 --- a/mcs/class/Microsoft.Build.Engine/Test/Microsoft.Build.BuildEngine/ProjectTest.cs +++ b/mcs/class/Microsoft.Build.Engine/Test/Microsoft.Build.BuildEngine/ProjectTest.cs @@ -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 = @" + + $(MSBuildThisFileDirectory) + + + + + + "; + + string second = @" + + $(MSBuildThisFileDirectory) + + + $(MSBuildThisFileDirectory) + + + + + + + "; + + string third = @" + + $(MSBuildThisFileFullPath) + + + + + + + + + + + + + + + + + "; + + 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 ()