[xbuild] Evaluate Import with MSBuildThisFile* properties set.
[mono.git] / mcs / class / Microsoft.Build.Engine / Microsoft.Build.BuildEngine / Project.cs
index 766bbfda84f8f018e0fa9256b2aefcc5abd11187..d2f466121212990779e30341db5ba6941c510b23 100644 (file)
@@ -3,8 +3,10 @@
 //
 // Author:
 //   Marek Sieradzki (marek.sieradzki@gmail.com)
+//   Ankit Jain (jankit@novell.com)
 //
 // (C) 2005 Marek Sieradzki
+// Copyright 2011 Novell, Inc (http://www.novell.com)
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
@@ -32,6 +34,7 @@ using System.Collections;
 using System.Collections.Generic;
 using System.Collections.Specialized;
 using System.IO;
+using System.Linq;
 using System.Reflection;
 using System.Text;
 using System.Xml;
@@ -73,9 +76,16 @@ namespace Microsoft.Build.BuildEngine {
                XmlDocument                     xmlDocument;
                bool                            unloaded;
                bool                            initialTargetsBuilt;
-               List<string>                    builtTargetKeys;
                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;
+
 
                static string extensions_path;
                static XmlNamespaceManager      manager;
@@ -86,9 +96,14 @@ namespace Microsoft.Build.BuildEngine {
                {
                }
 
-               public Project (Engine engine)
+               public Project (Engine engine) : this (engine, null)
+               {
+               }
+               
+               public Project (Engine engine, string toolsVersion)
                {
                        parentEngine  = engine;
+                       ToolsVersion = toolsVersion;
 
                        buildEnabled = ParentEngine.BuildEnabled;
                        xmlDocument = new XmlDocument ();
@@ -99,17 +114,21 @@ namespace Microsoft.Build.BuildEngine {
                        fullFileName = String.Empty;
                        timeOfLastDirty = DateTime.Now;
                        current_settings = BuildSettings.None;
+                       project_load_settings = ProjectLoadSettings.None;
+
                        encoding = null;
 
-                       builtTargetKeys = new List<string> ();
                        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)
                                GlobalProperties.AddProperty (bp.Clone (true));
                        
                        ProcessXml ();
+
                }
 
                [MonoTODO ("Not tested")]
@@ -125,8 +144,7 @@ namespace Microsoft.Build.BuildEngine {
                        if (!String.IsNullOrEmpty (importCondition))
                                importElement.SetAttribute ("Condition", importCondition);
 
-                       Import import = new Import (importElement, this, null);
-                       imports.Add (import);
+                       AddImport (importElement, null, false);
                        MarkProjectAsDirty ();
                        NeedToReevaluate ();
                }
@@ -258,7 +276,7 @@ namespace Microsoft.Build.BuildEngine {
                {
                        return Build (targetNames, targetOutputs, BuildSettings.None);
                }
-               
+
                [MonoTODO ("Not tested")]
                public bool Build (string [] targetNames,
                                   IDictionary targetOutputs,
@@ -267,6 +285,11 @@ namespace Microsoft.Build.BuildEngine {
                {
                        bool result = false;
                        ParentEngine.StartProjectBuild (this, targetNames);
+
+                       // Invoking this to emit a warning in case of unsupported
+                       // ToolsVersion
+                       GetToolsVersionToUse (true);
+
                        string current_directory = Environment.CurrentDirectory;
                        try {
                                current_settings = buildFlags;
@@ -289,16 +312,28 @@ namespace Microsoft.Build.BuildEngine {
                                   BuildSettings buildFlags)
                {
                        CheckUnloaded ();
-                       if (buildFlags == BuildSettings.None)
+                       if (buildFlags == BuildSettings.None) {
+                               needToReevaluate = false;
                                Reevaluate ();
-                       
+                       }
+
+#if NET_4_0
+                       ProcessBeforeAndAfterTargets ();
+#endif
+
                        if (targetNames == null || targetNames.Length == 0) {
-                               if (defaultTargets != null && defaultTargets.Length != 0)
+                               if (defaultTargets != null && defaultTargets.Length != 0) {
                                        targetNames = defaultTargets;
-                               else if (firstTargetName != null)
+                               } else if (firstTargetName != null) {
                                        targetNames = new string [1] { firstTargetName};
-                               else
+                               } else {
+                                       if (targets == null || targets.Count == 0) {
+                                               LogError (fullFileName, "No target found in the project");
+                                               return false;
+                                       }
+
                                        return false;
+                               }
                        }
 
                        if (!initialTargetsBuilt) {
@@ -339,9 +374,16 @@ namespace Microsoft.Build.BuildEngine {
                }
 
                internal string GetKeyForTarget (string target_name)
+               {
+                       return GetKeyForTarget (target_name, true);
+               }
+
+               internal string GetKeyForTarget (string target_name, bool include_global_properties)
                {
                        // target name is case insensitive
-                       return fullFileName + ":" + target_name.ToLower () + ":" + GlobalPropertiesToString (GlobalProperties);
+                       return fullFileName + ":" + target_name.ToLower () +
+                                       (include_global_properties ? (":" + GlobalPropertiesToString (GlobalProperties))
+                                                                  : String.Empty);
                }
 
                string GlobalPropertiesToString (BuildPropertyGroup bgp)
@@ -352,6 +394,44 @@ namespace Microsoft.Build.BuildEngine {
                        return sb.ToString ();
                }
 
+#if NET_4_0
+               void ProcessBeforeAndAfterTargets ()
+               {
+                       var beforeTable = Targets.AsIEnumerable ()
+                                               .SelectMany (target => GetTargetNamesFromString (target.BeforeTargets),
+                                                               (target, before_target) => new {before_target, name = target.Name})
+                                               .ToLookup (x => x.before_target, x => x.name)
+                                               .ToDictionary (x => x.Key, x => x.Distinct ().ToList ());
+
+                       foreach (var pair in beforeTable) {
+                               if (targets.Exists (pair.Key))
+                                       targets [pair.Key].BeforeThisTargets = pair.Value;
+                               else
+                                       LogWarning (FullFileName, "Target '{0}', not found in the project", pair.Key);
+                       }
+
+                       var afterTable = Targets.AsIEnumerable ()
+                                               .SelectMany (target => GetTargetNamesFromString (target.AfterTargets),
+                                                               (target, after_target) => new {after_target, name = target.Name})
+                                               .ToLookup (x => x.after_target, x => x.name)
+                                               .ToDictionary (x => x.Key, x => x.Distinct ().ToList ());
+
+                       foreach (var pair in afterTable) {
+                               if (targets.Exists (pair.Key))
+                                       targets [pair.Key].AfterThisTargets = pair.Value;
+                               else
+                                       LogWarning (FullFileName, "Target '{0}', not found in the project", pair.Key);
+                       }
+               }
+
+               string[] GetTargetNamesFromString (string targets)
+               {
+                       Expression expr = new Expression ();
+                       expr.Parse (targets, ParseOptions.AllowItemsNoMetadataAndSplit);
+                       return (string []) expr.ConvertTo (this, typeof (string []));
+               }
+#endif
+
                [MonoTODO]
                public string [] GetConditionedPropertyValues (string propertyName)
                {
@@ -419,6 +499,12 @@ namespace Microsoft.Build.BuildEngine {
 
                public void Load (string projectFileName)
                {
+                       Load (projectFileName, ProjectLoadSettings.None);
+               }
+
+               public void Load (string projectFileName, ProjectLoadSettings settings)
+               {
+                       project_load_settings = settings;
                        if (String.IsNullOrEmpty (projectFileName))
                                throw new ArgumentNullException ("projectFileName");
 
@@ -427,10 +513,12 @@ 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) {
                                Project tmp_project = ParentEngine.CreateNewProject ();
+                               tmp_project.FullFileName = filename;
                                SolutionParser sln_parser = new SolutionParser ();
                                sln_parser.ParseSolution (fullFileName, tmp_project, delegate (int errorNumber, string message) {
                                                LogWarning (filename, message);
@@ -452,12 +540,24 @@ namespace Microsoft.Build.BuildEngine {
                [MonoTODO ("Not tested")]
                public void Load (TextReader textReader)
                {
+                       Load (textReader, ProjectLoadSettings.None);
+               }
+
+               public void Load (TextReader textReader, ProjectLoadSettings projectLoadSettings)
+               {
+                       project_load_settings = projectLoadSettings;
                        fullFileName = String.Empty;
                        DoLoad (textReader);
                }
 
                public void LoadXml (string projectXml)
                {
+                       LoadXml (projectXml, ProjectLoadSettings.None);
+               }
+
+               public void LoadXml (string projectXml, ProjectLoadSettings projectLoadSettings)
+               {
+                       project_load_settings = projectLoadSettings;
                        fullFileName = String.Empty;
                        DoLoad (new StringReader (projectXml));
                        MarkProjectAsDirty ();
@@ -768,13 +868,13 @@ namespace Microsoft.Build.BuildEngine {
                        targets = new TargetCollection (this);
                        last_item_group_containing = new Dictionary <string, BuildItemGroup> ();
                        
+                       string effective_tools_version = GetToolsVersionToUse (false);
                        taskDatabase = new TaskDatabase ();
-                       if (ParentEngine.DefaultTasksRegistered)
-                               taskDatabase.CopyTasks (ParentEngine.DefaultTasks);     
+                       taskDatabase.CopyTasks (ParentEngine.GetDefaultTasks (effective_tools_version));
 
                        initialTargets = new List<string> ();
                        defaultTargets = new string [0];
-                       PrepareForEvaluate ();
+                       PrepareForEvaluate (effective_tools_version);
                        ProcessElements (xmlDocument.DocumentElement, null);
                        
                        isDirty = false;
@@ -824,7 +924,7 @@ namespace Microsoft.Build.BuildEngine {
                                                AddUsingTask (xe, ip);
                                                break;
                                        case "Import":
-                                               AddImport (xe, ip);
+                                               AddImport (xe, ip, true);
                                                break;
                                        case "ItemGroup":
                                                AddItemGroup (xe, ip);
@@ -833,16 +933,16 @@ namespace Microsoft.Build.BuildEngine {
                                                AddPropertyGroup (xe, ip);
                                                break;
                                        case  "Choose":
-                                               AddChoose (xe);
+                                               AddChoose (xe, ip);
                                                break;
                                        default:
-                                               throw new InvalidProjectFileException ("Invalid element in project file.");
+                                               throw new InvalidProjectFileException (String.Format ("Invalid element '{0}' in project file.", xe.Name));
                                        }
                                }
                        }
                }
                
-               void PrepareForEvaluate ()
+               void PrepareForEvaluate (string effective_tools_version)
                {
                        evaluatedItems = new BuildItemGroup (null, this, null, true);
                        evaluatedItemsIgnoringCondition = new BuildItemGroup (null, this, null, true);
@@ -851,7 +951,7 @@ namespace Microsoft.Build.BuildEngine {
                        if (building && current_settings == BuildSettings.None)
                                RemoveBuiltTargets ();
 
-                       InitializeProperties ();
+                       InitializeProperties (effective_tools_version);
                }
 
                void Evaluate ()
@@ -866,19 +966,19 @@ namespace Microsoft.Build.BuildEngine {
                // Removes entries of all earlier built targets for this project
                void RemoveBuiltTargets ()
                {
-                       foreach (string key in builtTargetKeys)
-                               ParentEngine.BuiltTargetsOutputByName.Remove (key);
+                       ParentEngine.ClearBuiltTargetsForProject (this);
                }
 
-               void InitializeProperties ()
+               void InitializeProperties (string effective_tools_version)
                {
                        BuildProperty bp;
 
                        evaluatedProperties = new BuildPropertyGroup (null, null, null, true);
+                       conditionedProperties = new Dictionary<string, List<string>> ();
 
                        foreach (BuildProperty gp in GlobalProperties) {
                                bp = new BuildProperty (gp.Name, gp.Value, PropertyType.Global);
-                               EvaluatedProperties.AddProperty (bp);
+                               evaluatedProperties.AddProperty (bp);
                        }
                        
                        foreach (BuildProperty gp in GlobalProperties)
@@ -887,27 +987,37 @@ namespace Microsoft.Build.BuildEngine {
                        // add properties that we dont have from parent engine's
                        // global properties
                        foreach (BuildProperty gp in ParentEngine.GlobalProperties) {
-                               if (EvaluatedProperties [gp.Name] == null) {
+                               if (evaluatedProperties [gp.Name] == null) {
                                        bp = new BuildProperty (gp.Name, gp.Value, PropertyType.Global);
-                                       EvaluatedProperties.AddProperty (bp);
+                                       evaluatedProperties.AddProperty (bp);
                                }
                        }
 
                        foreach (DictionaryEntry de in Environment.GetEnvironmentVariables ()) {
                                bp = new BuildProperty ((string) de.Key, (string) de.Value, PropertyType.Environment);
-                               EvaluatedProperties.AddProperty (bp);
+                               evaluatedProperties.AddProperty (bp);
                        }
 
-                       EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectFile", Path.GetFileName (fullFileName),
+                       evaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectFile", Path.GetFileName (fullFileName),
                                                PropertyType.Reserved));
-                       EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectName",
+                       evaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectFullPath", fullFileName, PropertyType.Reserved));
+                       evaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectName",
                                                Path.GetFileNameWithoutExtension (fullFileName),
                                                PropertyType.Reserved));
-                       EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildBinPath", parentEngine.BinPath, PropertyType.Reserved));
-                       EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildToolsPath", parentEngine.BinPath, PropertyType.Reserved));
-                       EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildExtensionsPath", ExtensionsPath, PropertyType.Reserved));
-                       EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectDefaultTargets", DefaultTargets, PropertyType.Reserved));
-                       EvaluatedProperties.AddProperty (new BuildProperty ("OS", OS, PropertyType.Environment));
+                       evaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectExtension",
+                                               Path.GetExtension (fullFileName),
+                                               PropertyType.Reserved));
+
+                       string toolsPath = parentEngine.Toolsets [effective_tools_version].ToolsPath;
+                       if (toolsPath == null)
+                               throw new Exception (String.Format ("Invalid tools version '{0}', no tools path set for this.", effective_tools_version));
+                       evaluatedProperties.AddProperty (new BuildProperty ("MSBuildBinPath", toolsPath, PropertyType.Reserved));
+                       evaluatedProperties.AddProperty (new BuildProperty ("MSBuildToolsPath", toolsPath, PropertyType.Reserved));
+                       evaluatedProperties.AddProperty (new BuildProperty ("MSBuildToolsRoot", Path.GetDirectoryName (toolsPath), PropertyType.Reserved));
+                       evaluatedProperties.AddProperty (new BuildProperty ("MSBuildToolsVersion", effective_tools_version, PropertyType.Reserved));
+                       SetExtensionsPathProperties (DefaultExtensionsPath);
+                       evaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectDefaultTargets", DefaultTargets, PropertyType.Reserved));
+                       evaluatedProperties.AddProperty (new BuildProperty ("OS", OS, PropertyType.Environment));
 
                        // FIXME: make some internal method that will work like GetDirectoryName but output String.Empty on null/String.Empty
                        string projectDir;
@@ -916,7 +1026,43 @@ namespace Microsoft.Build.BuildEngine {
                        else
                                projectDir = Path.GetDirectoryName (FullFileName);
 
-                       EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectDirectory", projectDir, PropertyType.Reserved));
+                       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 ());
+               }
+
+               internal void SetExtensionsPathProperties (string extn_path)
+               {
+                       if (!String.IsNullOrEmpty (extn_path)) {
+                               evaluatedProperties.AddProperty (new BuildProperty ("MSBuildExtensionsPath", extn_path, PropertyType.Reserved));
+                               evaluatedProperties.AddProperty (new BuildProperty ("MSBuildExtensionsPath32", extn_path, PropertyType.Reserved));
+                               evaluatedProperties.AddProperty (new BuildProperty ("MSBuildExtensionsPath64", extn_path, PropertyType.Reserved));
+                       }
+               }
+
+               // precedence:
+               // ToolsVersion property
+               // ToolsVersion attribute on the project
+               // parentEngine's DefaultToolsVersion
+               string GetToolsVersionToUse (bool emitWarning)
+               {
+                       if (!String.IsNullOrEmpty (ToolsVersion))
+                               return ToolsVersion;
+
+                       if (!HasToolsVersionAttribute)
+                               return parentEngine.DefaultToolsVersion;
+
+                       if (parentEngine.Toolsets [DefaultToolsVersion] == null) {
+                               if (emitWarning)
+                                       LogWarning (FullFileName, "Project has unknown ToolsVersion '{0}'. Using the default tools version '{1}' instead.",
+                                               DefaultToolsVersion, parentEngine.DefaultToolsVersion);
+                               return parentEngine.DefaultToolsVersion;
+                       }
+
+                       return DefaultToolsVersion;
                }
                
                void AddProjectExtensions (XmlElement xmlElement)
@@ -943,23 +1089,52 @@ namespace Microsoft.Build.BuildEngine {
                        usingTask = new UsingTask (xmlElement, this, importedProject);
                        UsingTasks.Add (usingTask);
                }
-               
-               void AddImport (XmlElement xmlElement, ImportedProject importingProject)
+
+               void AddImport (XmlElement xmlElement, ImportedProject importingProject, bool evaluate_properties)
                {
                        // eval all the properties etc till the import
-                       groupingCollection.Evaluate (EvaluationType.Property);
+                       if (evaluate_properties)
+                               groupingCollection.Evaluate (EvaluationType.Property);
 
-                       Import import = new Import (xmlElement, this, importingProject);
-                       if (!ConditionParser.ParseAndEvaluate (import.Condition, this))
-                               return;
+                       try {
+                               PushThisFileProperty (importingProject != null ? importingProject.FullFileName : FullFileName);
 
-                       if (Imports.Contains (import)) {
-                               LogWarning (importingProject != null ? importingProject.FullFileName : fullFileName,
-                                               "A circular reference was found involving the import of {0}. Only" +
-                                               " the first import of this file will be used, ignoring others.",
-                                               import.ProjectPath);
+                               string project_attribute = xmlElement.GetAttribute ("Project");
+                               if (String.IsNullOrEmpty (project_attribute))
+                                       throw new InvalidProjectFileException ("The required attribute \"Project\" is missing from element <Import>.");
 
-                               return;
+                               Import.ForEachExtensionPathTillFound (xmlElement, this, importingProject,
+                                       (importPath, from_source_msg) => AddSingleImport (xmlElement, importPath, importingProject, from_source_msg));
+                       } finally {
+                               PopThisFileProperty ();
+                       }
+               }
+
+               bool AddSingleImport (XmlElement xmlElement, string projectPath, ImportedProject importingProject, string from_source_msg)
+               {
+                       Import import = new Import (xmlElement, projectPath, this, importingProject);
+                       if (!ConditionParser.ParseAndEvaluate (import.Condition, this)) {
+                               ParentEngine.LogMessage (MessageImportance.Low,
+                                               "Not importing project '{0}' as the condition '{1}' is false",
+                                               import.ProjectPath, import.Condition);
+                               return false;
+                       }
+
+                       Import existingImport;
+                       if (Imports.TryGetImport (import, out existingImport)) {
+                               if (importingProject == null)
+                                       LogWarning (fullFileName,
+                                                       "Cannot import project '{0}' again. It was already imported by " +
+                                                       "'{1}'. Ignoring.",
+                                                       projectPath, existingImport.ContainedInProjectFileName);
+                               else
+                                       LogWarning (importingProject != null ? importingProject.FullFileName : fullFileName,
+                                               "A circular reference was found involving the import of '{0}'. " +
+                                               "It was earlier imported by '{1}'. Only " +
+                                               "the first import of this file will be used, ignoring others.",
+                                               import.EvaluatedProjectPath, existingImport.ContainedInProjectFileName);
+
+                               return false;
                        }
 
                        if (String.Compare (fullFileName, import.EvaluatedProjectPath) == 0) {
@@ -967,13 +1142,19 @@ namespace Microsoft.Build.BuildEngine {
                                                "The main project file was imported here, which creates a circular " +
                                                "reference. Ignoring this import.");
 
-                               return;
+                               return false;
                        }
 
                        Imports.Add (import);
-                       import.Evaluate ();
+                       string importingFile = importingProject != null ? importingProject.FullFileName : FullFileName;
+                       ParentEngine.LogMessage (MessageImportance.Low,
+                                       "{0}: Importing project {1} {2}",
+                                       importingFile, import.EvaluatedProjectPath, from_source_msg);
+
+                       import.Evaluate (project_load_settings == ProjectLoadSettings.IgnoreMissingImports);
+                       return true;
                }
-               
+
                void AddItemGroup (XmlElement xmlElement, ImportedProject importedProject)
                {
                        BuildItemGroup big = new BuildItemGroup (xmlElement, this, importedProject, false);
@@ -986,9 +1167,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);
                }
                
@@ -1053,7 +1234,19 @@ namespace Microsoft.Build.BuildEngine {
                                return evaluatedItemsByName;
                        }
                }
-               
+
+               internal IEnumerable EvaluatedItemsByNameAsDictionaryEntries {
+                       get {
+                               if (EvaluatedItemsByName.Count == 0)
+                                       yield break;
+
+                               foreach (KeyValuePair<string, BuildItemGroup> pair in EvaluatedItemsByName) {
+                                       foreach (BuildItem bi in pair.Value)
+                                               yield return new DictionaryEntry (pair.Key, bi.ConvertToITaskItem (null, ExpressionOptions.ExpandItemRefs));
+                               }
+                       }
+               }
+
                internal IDictionary <string, BuildItemGroup> EvaluatedItemsByNameIgnoringCondition {
                        get {
                                // FIXME: do we need to do this here?
@@ -1069,7 +1262,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;
@@ -1078,17 +1303,14 @@ namespace Microsoft.Build.BuildEngine {
                // Honors batching
                internal bool TryGetEvaluatedItemByNameBatched (string itemName, out BuildItemGroup group)
                {
-                       if (perBatchItemsByName == null && commonItemsByName == null)
-                               return EvaluatedItemsByName.TryGetValue (itemName, out group);
+                       if (perBatchItemsByName != null && perBatchItemsByName.TryGetValue (itemName, out group))
+                               return true;
 
-                       if (perBatchItemsByName != null)
-                               return perBatchItemsByName.TryGetValue (itemName, out group);
-
-                       if (commonItemsByName != null)
-                               return commonItemsByName.TryGetValue (itemName, out group);
+                       if (commonItemsByName != null && commonItemsByName.TryGetValue (itemName, out group))
+                               return true;
 
                        group = null;
-                       return false;
+                       return EvaluatedItemsByName.TryGetValue (itemName, out group);
                }
 
                internal string GetMetadataBatched (string itemName, string metadataName)
@@ -1140,7 +1362,42 @@ namespace Microsoft.Build.BuildEngine {
                        return default (T);
                }
 
-               void LogWarning (string filename, string message, params object[] messageArgs)
+               // 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 (
                                null, null, filename, 0, 0, 0, 0, String.Format (message, messageArgs),
@@ -1148,7 +1405,7 @@ namespace Microsoft.Build.BuildEngine {
                        ParentEngine.EventSource.FireWarningRaised (this, bwea);
                }
 
-               void LogError (string filename, string message,
+               internal void LogError (string filename, string message,
                                     params object[] messageArgs)
                {
                        BuildErrorEventArgs beea = new BuildErrorEventArgs (
@@ -1157,7 +1414,7 @@ namespace Microsoft.Build.BuildEngine {
                        ParentEngine.EventSource.FireErrorRaised (this, beea);
                }
 
-               static string ExtensionsPath {
+               internal static string DefaultExtensionsPath {
                        get {
                                if (extensions_path == null) {
                                        // NOTE: code from mcs/tools/gacutil/driver.cs
@@ -1185,6 +1442,13 @@ namespace Microsoft.Build.BuildEngine {
                        }
                }
 
+               internal IEnumerable EvaluatedPropertiesAsDictionaryEntries {
+                       get {
+                               foreach (BuildProperty bp in EvaluatedProperties)
+                                       yield return new DictionaryEntry (bp.Name, bp.Value);
+                       }
+               }
+
                public string FullFileName {
                        get { return fullFileName; }
                        set { fullFileName = value; }
@@ -1200,7 +1464,6 @@ namespace Microsoft.Build.BuildEngine {
                                        throw new InvalidOperationException ("GlobalProperties can not be set to persisted property group.");
                                
                                globalProperties = value;
-                               NeedToReevaluate ();
                        }
                }
 
@@ -1264,8 +1527,27 @@ namespace Microsoft.Build.BuildEngine {
                        get { return xmlDocument.InnerXml; }
                }
 
-               internal List<string> BuiltTargetKeys {
-                       get { return builtTargetKeys; }
+               // corresponds to the xml attribute
+               public string DefaultToolsVersion {
+                       get {
+                               if (xmlDocument != null)
+                                       return xmlDocument.DocumentElement.GetAttribute ("ToolsVersion");
+                               return null;
+                       }
+                       set {
+                               if (xmlDocument != null)
+                                       xmlDocument.DocumentElement.SetAttribute ("ToolsVersion", value);
+                       }
+               }
+
+               public bool HasToolsVersionAttribute {
+                       get {
+                               return xmlDocument != null && xmlDocument.DocumentElement.HasAttribute ("ToolsVersion");
+                       }
+               }
+               
+               public string ToolsVersion {
+                       get; internal set;
                }
 
                internal Dictionary <string, BuildItemGroup> LastItemGroupContaining {