[xbuild] Evaluate Import with MSBuildThisFile* properties set.
[mono.git] / mcs / class / Microsoft.Build.Engine / Microsoft.Build.BuildEngine / Project.cs
index fc5f1d11eb2b087f9dd37f4357095307fb288301..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,11 +34,14 @@ 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;
 using System.Xml.Schema;
 using Microsoft.Build.Framework;
 using Mono.XBuild.Framework;
+using Mono.XBuild.CommandLine;
 
 namespace Microsoft.Build.BuildEngine {
        public class Project {
@@ -58,7 +63,9 @@ namespace Microsoft.Build.BuildEngine {
                bool                            isValidated;
                BuildItemGroupCollection        itemGroups;
                ImportCollection                imports;
-               string                          initialTargets;
+               List<string>                    initialTargets;
+               Dictionary <string, BuildItemGroup> last_item_group_containing;
+               bool                            needToReevaluate;
                Engine                          parentEngine;
                BuildPropertyGroupCollection    propertyGroups;
                string                          schemaFile;
@@ -68,7 +75,19 @@ namespace Microsoft.Build.BuildEngine {
                UsingTaskCollection             usingTasks;
                XmlDocument                     xmlDocument;
                bool                            unloaded;
+               bool                            initialTargetsBuilt;
+               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;
                static string ns = "http://schemas.microsoft.com/developer/msbuild/2003";
 
@@ -77,137 +96,344 @@ 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 ();
-                       groupingCollection = new GroupingCollection (this);
-                       imports = new ImportCollection (groupingCollection);
-                       usingTasks = new UsingTaskCollection (this);
-                       itemGroups = new BuildItemGroupCollection (groupingCollection);
-                       propertyGroups = new BuildPropertyGroupCollection (groupingCollection);
-                       targets = new TargetCollection (this);
+                       xmlDocument.PreserveWhitespace = false;
+                       xmlDocument.AppendChild (xmlDocument.CreateElement ("Project", XmlNamespace));
+                       xmlDocument.DocumentElement.SetAttribute ("xmlns", ns);
                        
-                       taskDatabase = new TaskDatabase ();
-                       if (engine.DefaultTasksRegistered) {
-                               taskDatabase.CopyTasks (engine.DefaultTasks);
-                       }
-                       
-                       globalProperties = new BuildPropertyGroup ();
                        fullFileName = String.Empty;
+                       timeOfLastDirty = DateTime.Now;
+                       current_settings = BuildSettings.None;
+                       project_load_settings = ProjectLoadSettings.None;
 
-                       foreach (BuildProperty bp in parentEngine.GlobalProperties) {
+                       encoding = null;
+
+                       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 ();
 
-                       // You can evaluate an empty project.
-                       Evaluate ();
                }
 
-               [MonoTODO]
+               [MonoTODO ("Not tested")]
                public void AddNewImport (string importLocation,
                                          string importCondition)
                {
-                       throw new NotImplementedException ();
+                       if (importLocation == null)
+                               throw new ArgumentNullException ("importLocation");
+
+                       XmlElement importElement = xmlDocument.CreateElement ("Import", XmlNamespace);
+                       xmlDocument.DocumentElement.AppendChild (importElement);
+                       importElement.SetAttribute ("Project", importLocation);
+                       if (!String.IsNullOrEmpty (importCondition))
+                               importElement.SetAttribute ("Condition", importCondition);
+
+                       AddImport (importElement, null, false);
+                       MarkProjectAsDirty ();
+                       NeedToReevaluate ();
                }
 
-               [MonoTODO]
                public BuildItem AddNewItem (string itemName,
                                             string itemInclude)
                {
                        return AddNewItem (itemName, itemInclude, false);
                }
                
-               [MonoTODO]
+               [MonoTODO ("Adds item not in the same place as MS")]
                public BuildItem AddNewItem (string itemName,
                                             string itemInclude,
                                             bool treatItemIncludeAsLiteral)
                {
-                       throw new NotImplementedException ();
+                       BuildItemGroup big;
+
+                       if (itemGroups.Count == 0)
+                               big = AddNewItemGroup ();
+                       else {
+                               if (last_item_group_containing.ContainsKey (itemName)) {
+                                       big = last_item_group_containing [itemName];
+                               } else {
+                                       // FIXME: not tested
+                                       BuildItemGroup [] groups = new BuildItemGroup [itemGroups.Count];
+                                       itemGroups.CopyTo (groups, 0);
+                                       big = groups [0];
+                               }
+                       }
+
+                       BuildItem item = big.AddNewItem (itemName, itemInclude, treatItemIncludeAsLiteral);
+                               
+                       MarkProjectAsDirty ();
+                       NeedToReevaluate ();
+
+                       return item;
                }
 
-               [MonoTODO]
+               [MonoTODO ("Not tested")]
                public BuildItemGroup AddNewItemGroup ()
                {
-                       throw new NotImplementedException ();
+                       XmlElement element = xmlDocument.CreateElement ("ItemGroup", XmlNamespace);
+                       xmlDocument.DocumentElement.AppendChild (element);
+
+                       BuildItemGroup big = new BuildItemGroup (element, this, null, false);
+                       itemGroups.Add (big);
+                       MarkProjectAsDirty ();
+                       NeedToReevaluate ();
+
+                       return big;
                }
 
-               [MonoTODO]
+               [MonoTODO ("Ignores insertAtEndOfProject")]
                public BuildPropertyGroup AddNewPropertyGroup (bool insertAtEndOfProject)
                {
-                       throw new NotImplementedException ();
+                       XmlElement element = xmlDocument.CreateElement ("PropertyGroup", XmlNamespace);
+                       xmlDocument.DocumentElement.AppendChild (element);
+
+                       BuildPropertyGroup bpg = new BuildPropertyGroup (element, this, null, false);
+                       propertyGroups.Add (bpg);
+                       MarkProjectAsDirty ();
+                       NeedToReevaluate ();
+
+                       return bpg;
                }
                
-               [MonoTODO]
+               [MonoTODO ("Not tested, isn't added to TaskDatabase (no reevaluation)")]
                public void AddNewUsingTaskFromAssemblyFile (string taskName,
                                                             string assemblyFile)
                {
-                       throw new NotImplementedException ();
+                       if (taskName == null)
+                               throw new ArgumentNullException ("taskName");
+                       if (assemblyFile == null)
+                               throw new ArgumentNullException ("assemblyFile");
+
+                       XmlElement element = xmlDocument.CreateElement ("UsingTask", XmlNamespace);
+                       xmlDocument.DocumentElement.AppendChild (element);
+                       element.SetAttribute ("TaskName", taskName);
+                       element.SetAttribute ("AssemblyFile", assemblyFile);
+
+                       UsingTask ut = new UsingTask (element, this, null);
+                       usingTasks.Add (ut);
+                       MarkProjectAsDirty ();
                }
                
-               [MonoTODO]
+               [MonoTODO ("Not tested, isn't added to TaskDatabase (no reevaluation)")]
                public void AddNewUsingTaskFromAssemblyName (string taskName,
                                                             string assemblyName)
                {
-                       throw new NotImplementedException ();
+                       if (taskName == null)
+                               throw new ArgumentNullException ("taskName");
+                       if (assemblyName == null)
+                               throw new ArgumentNullException ("assemblyName");
+
+                       XmlElement element = xmlDocument.CreateElement ("UsingTask", XmlNamespace);
+                       xmlDocument.DocumentElement.AppendChild (element);
+                       element.SetAttribute ("TaskName", taskName);
+                       element.SetAttribute ("AssemblyName", assemblyName);
+
+                       UsingTask ut = new UsingTask (element, this, null);
+                       usingTasks.Add (ut);
+                       MarkProjectAsDirty ();
                }
                
-               [MonoTODO]
+               [MonoTODO ("Not tested")]
                public bool Build ()
                {
-                       return true;
+                       return Build (new string [0]);
                }
                
-               [MonoTODO]
+               [MonoTODO ("Not tested")]
                public bool Build (string targetName)
                {
-                       return Build (new string [1] { targetName });
+                       if (targetName == null)
+                               return Build ((string[]) null);
+                       else
+                               return Build (new string [1] { targetName });
                }
                
-               [MonoTODO]
-               public bool Build (string[] targetNames)
+               [MonoTODO ("Not tested")]
+               public bool Build (string [] targetNames)
                {
                        return Build (targetNames, null);
                }
                
-               [MonoTODO]
-               public bool Build (string[] targetNames,
+               [MonoTODO ("Not tested")]
+               public bool Build (string [] targetNames,
                                   IDictionary targetOutputs)
                {
                        return Build (targetNames, targetOutputs, BuildSettings.None);
                }
-               
-               [MonoTODO]
-               public bool Build (string[] targetNames,
+
+               [MonoTODO ("Not tested")]
+               public bool Build (string [] targetNames,
                                   IDictionary targetOutputs,
                                   BuildSettings buildFlags)
                
+               {
+                       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;
+                               if (!String.IsNullOrEmpty (fullFileName))
+                                       Directory.SetCurrentDirectory (Path.GetDirectoryName (fullFileName));
+                               building = true;
+                               result = BuildInternal (targetNames, targetOutputs, buildFlags);
+                       } finally {
+                               ParentEngine.EndProjectBuild (this, result);
+                               current_settings = BuildSettings.None;
+                               Directory.SetCurrentDirectory (current_directory);
+                               building = false;
+                       }
+
+                       return result;
+               }
+
+               bool BuildInternal (string [] targetNames,
+                                  IDictionary targetOutputs,
+                                  BuildSettings buildFlags)
                {
                        CheckUnloaded ();
-                       
-                       if (targetNames.Length == 0) {
-                               if (defaultTargets != null && defaultTargets.Length != 0)
+                       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) {
                                        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;
+                               }
                        }
-                       
-                       foreach (string target in targetNames) {
-                               if (!targets.Exists (target))
-                                       // FIXME: test if it's logged
+
+                       if (!initialTargetsBuilt) {
+                               foreach (string target in initialTargets) {
+                                       if (!BuildTarget (target.Trim (), targetOutputs))
+                                               return false;
+                               }
+                               initialTargetsBuilt = true;
+                       }
+
+                       foreach (string target in targetNames)
+                               if (!BuildTarget (target.Trim (), targetOutputs))
                                        return false;
                                
-                               if (!targets [target].Build ())
-                                       return false;
+                       return true;
+               }
+
+               bool BuildTarget (string target_name, IDictionary targetOutputs)
+               {
+                       if (target_name == null)
+                               throw new ArgumentException ("targetNames cannot contain null strings");
+
+                       if (!targets.Exists (target_name)) {
+                               LogError (fullFileName, "Target named '{0}' not found in the project.", target_name);
+                               return false;
+                       }
+
+                       string key = GetKeyForTarget (target_name);
+                       if (!targets [target_name].Build (key))
+                               return false;
+
+                       ITaskItem[] outputs;
+                       if (ParentEngine.BuiltTargetsOutputByName.TryGetValue (key, out outputs)) {
+                               if (targetOutputs != null)
+                                       targetOutputs.Add (target_name, outputs);
                        }
-                               
                        return true;
                }
 
+               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 () +
+                                       (include_global_properties ? (":" + GlobalPropertiesToString (GlobalProperties))
+                                                                  : String.Empty);
+               }
+
+               string GlobalPropertiesToString (BuildPropertyGroup bgp)
+               {
+                       StringBuilder sb = new StringBuilder ();
+                       foreach (BuildProperty bp in bgp)
+                               sb.AppendFormat (" {0}:{1}", bp.Name, bp.FinalValue);
+                       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)
+               public string [] GetConditionedPropertyValues (string propertyName)
                {
                        if (conditionedProperties.ContainsKey (propertyName))
                                return conditionedProperties [propertyName].ToArray ();
@@ -216,23 +442,38 @@ namespace Microsoft.Build.BuildEngine {
                }
 
                public BuildItemGroup GetEvaluatedItemsByName (string itemName)
-               {
+               {                       
+                       if (needToReevaluate) {
+                               needToReevaluate = false;
+                               Reevaluate ();
+                       }
+
                        if (evaluatedItemsByName.ContainsKey (itemName))
                                return evaluatedItemsByName [itemName];
                        else
-                               return new BuildItemGroup ();
+                               return new BuildItemGroup (this);
                }
 
                public BuildItemGroup GetEvaluatedItemsByNameIgnoringCondition (string itemName)
                {
+                       if (needToReevaluate) {
+                               needToReevaluate = false;
+                               Reevaluate ();
+                       }
+
                        if (evaluatedItemsByNameIgnoringCondition.ContainsKey (itemName))
                                return evaluatedItemsByNameIgnoringCondition [itemName];
                        else
-                               return new BuildItemGroup ();
+                               return new BuildItemGroup (this);
                }
 
                public string GetEvaluatedProperty (string propertyName)
                {
+                       if (needToReevaluate) {
+                               needToReevaluate = false;
+                               Reevaluate ();
+                       }
+
                        if (propertyName == null)
                                throw new ArgumentNullException ("propertyName");
 
@@ -241,6 +482,7 @@ namespace Microsoft.Build.BuildEngine {
                        return bp == null ? null : (string) bp;
                }
 
+               [MonoTODO ("We should remember that node and not use XPath to get it")]
                public string GetProjectExtensions (string id)
                {
                        if (id == null || id == String.Empty)
@@ -257,55 +499,141 @@ namespace Microsoft.Build.BuildEngine {
 
                public void Load (string projectFileName)
                {
-                       this.fullFileName = Path.GetFullPath (projectFileName);
-                       try {
-                               DoLoad (new StreamReader (projectFileName));
-                       } catch {
-                               Console.WriteLine ("Failure to load: {0}", projectFileName);
+                       Load (projectFileName, ProjectLoadSettings.None);
+               }
+
+               public void Load (string projectFileName, ProjectLoadSettings settings)
+               {
+                       project_load_settings = settings;
+                       if (String.IsNullOrEmpty (projectFileName))
+                               throw new ArgumentNullException ("projectFileName");
+
+                       if (!File.Exists (projectFileName))
+                               throw new ArgumentException (String.Format ("Project file {0} not found", projectFileName),
+                                               "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);
+                                       });
+                               filename = fullFileName + ".proj";
+                               try {
+                                       tmp_project.Save (filename);
+                                       ParentEngine.RemoveLoadedProject (tmp_project);
+                                       DoLoad (new StreamReader (filename));
+                               } finally {
+                                       if (Environment.GetEnvironmentVariable ("XBUILD_EMIT_SOLUTION") == null)
+                                               File.Delete (filename);
+                               }
+                       } else {
+                               DoLoad (new StreamReader (filename));
                        }
                }
                
-               [MonoTODO]
+               [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 ();
                }
 
 
                public void MarkProjectAsDirty ()
                {
                        isDirty = true;
+                       timeOfLastDirty = DateTime.Now;
                }
 
-               [MonoTODO]
+               [MonoTODO ("Not tested")]
                public void RemoveAllItemGroups ()
                {
-                       throw new NotImplementedException ();
+                       int length = ItemGroups.Count;
+                       BuildItemGroup [] groups = new BuildItemGroup [length];
+                       ItemGroups.CopyTo (groups, 0);
+
+                       for (int i = 0; i < length; i++)
+                               RemoveItemGroup (groups [i]);
+
+                       MarkProjectAsDirty ();
+                       NeedToReevaluate ();
                }
 
-               [MonoTODO]
+               [MonoTODO ("Not tested")]
                public void RemoveAllPropertyGroups ()
                {
-                       throw new NotImplementedException ();
+                       int length = PropertyGroups.Count;
+                       BuildPropertyGroup [] groups = new BuildPropertyGroup [length];
+                       PropertyGroups.CopyTo (groups, 0);
+
+                       for (int i = 0; i < length; i++)
+                               RemovePropertyGroup (groups [i]);
+
+                       MarkProjectAsDirty ();
+                       NeedToReevaluate ();
                }
 
                [MonoTODO]
                public void RemoveItem (BuildItem itemToRemove)
                {
-                       throw new NotImplementedException ();
+                       if (itemToRemove == null)
+                               throw new ArgumentNullException ("itemToRemove");
+
+                       if (!itemToRemove.FromXml && !itemToRemove.HasParentItem)
+                               throw new InvalidOperationException ("The object passed in is not part of the project.");
+
+                       BuildItemGroup big = itemToRemove.ParentItemGroup;
+
+                       if (big.Count == 1) {
+                               // ParentItemGroup for items from xml and that have parent is the same
+                               groupingCollection.Remove (big);
+                       } else {
+                               if (big.ParentProject != this)
+                                       throw new InvalidOperationException ("The object passed in is not part of the project.");
+
+                               if (itemToRemove.FromXml)
+                                       big.RemoveItem (itemToRemove);
+                               else
+                                       big.RemoveItem (itemToRemove.ParentItem);
+                       }
+
+                       MarkProjectAsDirty ();
+                       NeedToReevaluate ();
                }
 
-               [MonoTODO]
+               [MonoTODO ("Not tested")]
                public void RemoveItemGroup (BuildItemGroup itemGroupToRemove)
                {
-                       throw new NotImplementedException ();
+                       if (itemGroupToRemove == null)
+                               throw new ArgumentNullException ("itemGroupToRemove");
+
+                       groupingCollection.Remove (itemGroupToRemove);
+                       MarkProjectAsDirty ();
                }
                
                [MonoTODO]
@@ -318,13 +646,20 @@ namespace Microsoft.Build.BuildEngine {
                [MonoTODO]
                public void RemoveItemsByName (string itemName)
                {
+                       if (itemName == null)
+                               throw new ArgumentNullException ("itemName");
+
                        throw new NotImplementedException ();
                }
 
-               [MonoTODO]
+               [MonoTODO ("Not tested")]
                public void RemovePropertyGroup (BuildPropertyGroup propertyGroupToRemove)
                {
-                       throw new NotImplementedException ();
+                       if (propertyGroupToRemove == null)
+                               throw new ArgumentNullException ("propertyGroupToRemove");
+
+                       groupingCollection.Remove (propertyGroupToRemove);
+                       MarkProjectAsDirty ();
                }
                
                [MonoTODO]
@@ -337,22 +672,29 @@ namespace Microsoft.Build.BuildEngine {
                [MonoTODO]
                public void ResetBuildStatus ()
                {
-                       throw new NotImplementedException ();
+                       // hack to allow built targets to be removed
+                       building = true;
+                       Reevaluate ();
+                       building = false;
                }
 
                public void Save (string projectFileName)
                {
                        Save (projectFileName, Encoding.Default);
+                       isDirty = false;
                }
 
+               [MonoTODO ("Ignores encoding")]
                public void Save (string projectFileName, Encoding encoding)
                {
                        xmlDocument.Save (projectFileName);
+                       isDirty = false;
                }
 
                public void Save (TextWriter outTextWriter)
                {
                        xmlDocument.Save (outTextWriter);
+                       isDirty = false;
                }
 
                public void SetImportedProperty (string propertyName,
@@ -387,14 +729,15 @@ namespace Microsoft.Build.BuildEngine {
 
                public void SetProjectExtensions (string id, string xmlText)
                {
-                       XmlNode projectExtensions, node;
+                       if (id == null)
+                               throw new ArgumentNullException ("id");
+                       if (xmlText == null)
+                               throw new ArgumentNullException ("xmlText");
 
-                       if (id == null || id == String.Empty || xmlText == null)
-                               return;
+                       XmlNode projectExtensions, node;
 
                        projectExtensions = xmlDocument.SelectSingleNode ("/tns:Project/tns:ProjectExtensions", XmlNamespaceManager);
                        
-                       
                        if (projectExtensions == null) {
                                projectExtensions = xmlDocument.CreateElement ("ProjectExtensions", XmlNamespace);
                                xmlDocument.DocumentElement.AppendChild (projectExtensions);
@@ -414,10 +757,9 @@ namespace Microsoft.Build.BuildEngine {
                                
                        }
 
-                       isDirty = true;
+                       MarkProjectAsDirty ();
                }
                
-               [MonoTODO]
                public void SetProperty (string propertyName,
                                         string propertyValue)
                {
@@ -425,7 +767,6 @@ namespace Microsoft.Build.BuildEngine {
                                PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup, false);
                }
 
-               [MonoTODO]
                public void SetProperty (string propertyName,
                                         string propertyValue,
                                         string condition)
@@ -434,7 +775,6 @@ namespace Microsoft.Build.BuildEngine {
                                PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup);
                }
 
-               [MonoTODO]
                public void SetProperty (string propertyName,
                                         string propertyValue,
                                         string condition,
@@ -462,54 +802,109 @@ namespace Microsoft.Build.BuildEngine {
                internal void CheckUnloaded ()
                {
                        if (unloaded)
-                               throw new InvalidOperationException ("This project object is no longer valid.");
+                               throw new InvalidOperationException ("This project object has been unloaded from the MSBuild engine and is no longer valid.");
+               }
+
+               internal void NeedToReevaluate ()
+               {
+                       needToReevaluate = true;
                }
                                
                // Does the actual loading.
-               private void DoLoad (TextReader textReader)
+               void DoLoad (TextReader textReader)
                {
-                       ParentEngine.RemoveLoadedProject (this);
-
-                       XmlReaderSettings settings = new XmlReaderSettings ();
+                       try {
+                               ParentEngine.RemoveLoadedProject (this);
+       
+                               xmlDocument.Load (textReader);
+
+                               if (xmlDocument.DocumentElement.Name == "VisualStudioProject")
+                                       throw new InvalidProjectFileException (String.Format (
+                                                       "Project file '{0}' is a VS2003 project, which is not " +
+                                                       "supported by xbuild. You need to convert it to msbuild " +
+                                                       "format to build with xbuild.", fullFileName));
+
+                               if (SchemaFile != null) {
+                                       xmlDocument.Schemas.Add (XmlSchema.Read (
+                                                               new StreamReader (SchemaFile), ValidationCallBack));
+                                       xmlDocument.Validate (ValidationCallBack);
+                               }
 
-                       if (SchemaFile != null) {
-                               settings.Schemas.Add (null, SchemaFile);
-                               settings.ValidationType = ValidationType.Schema;
-                               settings.ValidationEventHandler += new ValidationEventHandler (ValidationCallBack);
+                               if (xmlDocument.DocumentElement.Name != "Project") {
+                                       throw new InvalidProjectFileException (String.Format (
+                                               "The element <{0}> is unrecognized, or not supported in this context.", xmlDocument.DocumentElement.Name));
+                               }
+       
+                               if (xmlDocument.DocumentElement.GetAttribute ("xmlns") != ns) {
+                                       throw new InvalidProjectFileException (
+                                               @"The default XML namespace of the project must be the MSBuild XML namespace." + 
+                                               " If the project is authored in the MSBuild 2003 format, please add " +
+                                               "xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\" to the <Project> element. " +
+                                               "If the project has been authored in the old 1.0 or 1.2 format, please convert it to MSBuild 2003 format.  ");
+                               }
+                               ProcessXml ();
+                               ParentEngine.AddLoadedProject (this);
+                       } catch (Exception e) {
+                               throw new InvalidProjectFileException (String.Format ("{0}: {1}",
+                                                       fullFileName, e.Message), e);
+                       } finally {
+                               if (textReader != null)
+                                       textReader.Close ();
                        }
+               }
 
-                       XmlReader xmlReader = XmlReader.Create (textReader, settings);
-                       xmlDocument.Load (xmlReader);
-
-                       if (xmlDocument.DocumentElement.GetAttribute ("xmlns") != ns) {
-                               throw new InvalidProjectFileException (
-                                       @"The default XML namespace of the project must be the MSBuild XML namespace." + 
-                                       " If the project is authored in the MSBuild 2003 format, please add " +
-                                       "xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\" to the <Project> element. " +
-                                       "If the project has been authored in the old 1.0 or 1.2 format, please convert it to MSBuild 2003 format.  ");
-                       }
+               void Reevaluate ()
+               {
                        ProcessXml ();
-                       ParentEngine.AddLoadedProject (this);
                }
 
-               private void ProcessXml ()
+               void ProcessXml ()
                {
-                       XmlElement xmlElement = xmlDocument.DocumentElement;
-                       if (xmlElement.Name != "Project")
-                               throw new InvalidProjectFileException ("Invalid root element.");
-                       if (xmlElement.GetAttributeNode ("DefaultTargets") != null)
-                               defaultTargets = xmlElement.GetAttribute ("DefaultTargets").Split (';');
-                       else
-                               defaultTargets = new string [0];
+                       groupingCollection = new GroupingCollection (this);
+                       imports = new ImportCollection (groupingCollection);
+                       usingTasks = new UsingTaskCollection (this);
+                       itemGroups = new BuildItemGroupCollection (groupingCollection);
+                       propertyGroups = new BuildPropertyGroupCollection (groupingCollection);
+                       targets = new TargetCollection (this);
+                       last_item_group_containing = new Dictionary <string, BuildItemGroup> ();
                        
-                       ProcessElements (xmlElement, null);
+                       string effective_tools_version = GetToolsVersionToUse (false);
+                       taskDatabase = new TaskDatabase ();
+                       taskDatabase.CopyTasks (ParentEngine.GetDefaultTasks (effective_tools_version));
+
+                       initialTargets = new List<string> ();
+                       defaultTargets = new string [0];
+                       PrepareForEvaluate (effective_tools_version);
+                       ProcessElements (xmlDocument.DocumentElement, null);
                        
                        isDirty = false;
                        Evaluate ();
                }
-               
+
+               void ProcessProjectAttributes (XmlAttributeCollection attributes)
+               {
+                       foreach (XmlAttribute attr in attributes) {
+                               switch (attr.Name) {
+                               case "InitialTargets":
+                                       initialTargets.AddRange (attr.Value.Split (
+                                                                       new char [] {';', ' '},
+                                                                       StringSplitOptions.RemoveEmptyEntries));
+                                       break;
+                               case "DefaultTargets":
+                                       // first non-empty DefaultTargets found is used
+                                       if (defaultTargets == null || defaultTargets.Length == 0)
+                                               defaultTargets = attr.Value.Split (new char [] {';', ' '},
+                                                       StringSplitOptions.RemoveEmptyEntries);
+                                       EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectDefaultTargets",
+                                                               DefaultTargets, PropertyType.Reserved));
+                                       break;
+                               }
+                       }
+               }
+
                internal void ProcessElements (XmlElement rootElement, ImportedProject ip)
                {
+                       ProcessProjectAttributes (rootElement.Attributes);
                        foreach (XmlNode xn in rootElement.ChildNodes) {
                                if (xn is XmlElement) {
                                        XmlElement xe = (XmlElement) xn;
@@ -529,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);
@@ -538,25 +933,29 @@ 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));
                                        }
                                }
                        }
                }
                
-               internal void Evaluate ()
+               void PrepareForEvaluate (string effective_tools_version)
                {
-                       evaluatedItems = new BuildItemGroup (null, this, null);
-                       evaluatedItemsIgnoringCondition = new BuildItemGroup (null, this, null);
+                       evaluatedItems = new BuildItemGroup (null, this, null, true);
+                       evaluatedItemsIgnoringCondition = new BuildItemGroup (null, this, null, true);
                        evaluatedItemsByName = new Dictionary <string, BuildItemGroup> (StringComparer.InvariantCultureIgnoreCase);
                        evaluatedItemsByNameIgnoringCondition = new Dictionary <string, BuildItemGroup> (StringComparer.InvariantCultureIgnoreCase);
-                       evaluatedProperties = new BuildPropertyGroup ();
+                       if (building && current_settings == BuildSettings.None)
+                               RemoveBuiltTargets ();
 
-                       InitializeProperties ();
+                       InitializeProperties (effective_tools_version);
+               }
 
+               void Evaluate ()
+               {
                        groupingCollection.Evaluate ();
 
                        //FIXME: UsingTasks aren't really evaluated. (shouldn't use expressions or anything)
@@ -564,93 +963,217 @@ namespace Microsoft.Build.BuildEngine {
                                usingTask.Evaluate ();
                }
 
-               private void InitializeProperties ()
+               // Removes entries of all earlier built targets for this project
+               void RemoveBuiltTargets ()
+               {
+                       ParentEngine.ClearBuiltTargetsForProject (this);
+               }
+
+               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)
+                               ParentEngine.GlobalProperties.AddProperty (gp);
+
+                       // add properties that we dont have from parent engine's
+                       // global properties
+                       foreach (BuildProperty gp in ParentEngine.GlobalProperties) {
+                               if (evaluatedProperties [gp.Name] == null) {
+                                       bp = new BuildProperty (gp.Name, gp.Value, PropertyType.Global);
+                                       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);
                        }
 
-                       bp = new BuildProperty ("MSBuildBinPath", parentEngine.BinPath, PropertyType.Reserved);
-                       EvaluatedProperties.AddProperty (bp);
+                       evaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectFile", Path.GetFileName (fullFileName),
+                                               PropertyType.Reserved));
+                       evaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectFullPath", fullFileName, PropertyType.Reserved));
+                       evaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectName",
+                                               Path.GetFileNameWithoutExtension (fullFileName),
+                                               PropertyType.Reserved));
+                       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;
+                       if (FullFileName == String.Empty)
+                               projectDir = Environment.CurrentDirectory;
+                       else
+                               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 ());
+               }
+
+               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;
                }
                
-               private void AddProjectExtensions (XmlElement xmlElement)
+               void AddProjectExtensions (XmlElement xmlElement)
                {
-                       if (xmlElement == null)
-                               throw new ArgumentNullException ("xmlElement");
                }
                
-               private void AddMessage (XmlElement xmlElement)
+               void AddMessage (XmlElement xmlElement)
                {
-                       if (xmlElement == null)
-                               throw new ArgumentNullException ("xmlElement");
                }
                
-               private void AddTarget (XmlElement xmlElement, ImportedProject importedProject)
+               void AddTarget (XmlElement xmlElement, ImportedProject importedProject)
                {
-                       if (xmlElement == null)
-                               throw new ArgumentNullException ("xmlElement");
-                       Target target = new Target (xmlElement, this);
+                       Target target = new Target (xmlElement, this, importedProject);
                        targets.AddTarget (target);
-                       if (importedProject == null) {
-                               target.IsImported = false;
-                               if (firstTargetName == null)
-                                       firstTargetName = target.Name;
-                       } else
-                               target.IsImported = true;
+                       
+                       if (firstTargetName == null)
+                               firstTargetName = target.Name;
                }
                
-               private void AddUsingTask (XmlElement xmlElement, ImportedProject importedProject)
+               void AddUsingTask (XmlElement xmlElement, ImportedProject importedProject)
                {
                        UsingTask usingTask;
 
                        usingTask = new UsingTask (xmlElement, this, importedProject);
                        UsingTasks.Add (usingTask);
                }
-               
-               private void AddImport (XmlElement xmlElement, ImportedProject importingProject)
+
+               void AddImport (XmlElement xmlElement, ImportedProject importingProject, bool evaluate_properties)
                {
-                       Import import;
-                       
-                       import = new Import (xmlElement, this, importingProject);
+                       // eval all the properties etc till the import
+                       if (evaluate_properties)
+                               groupingCollection.Evaluate (EvaluationType.Property);
+
+                       try {
+                               PushThisFileProperty (importingProject != null ? importingProject.FullFileName : FullFileName);
+
+                               string project_attribute = xmlElement.GetAttribute ("Project");
+                               if (String.IsNullOrEmpty (project_attribute))
+                                       throw new InvalidProjectFileException ("The required attribute \"Project\" is missing from element <Import>.");
+
+                               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) {
+                               LogWarning (importingProject != null ? importingProject.FullFileName : fullFileName,
+                                               "The main project file was imported here, which creates a circular " +
+                                               "reference. Ignoring this import.");
+
+                               return false;
+                       }
+
                        Imports.Add (import);
+                       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;
                }
-               
-               private void AddItemGroup (XmlElement xmlElement, ImportedProject importedProject)
+
+               void AddItemGroup (XmlElement xmlElement, ImportedProject importedProject)
                {
-                       if (xmlElement == null)
-                               throw new ArgumentNullException ("xmlElement");
-                       BuildItemGroup big = new BuildItemGroup (xmlElement, this, importedProject);
+                       BuildItemGroup big = new BuildItemGroup (xmlElement, this, importedProject, false);
                        ItemGroups.Add (big);
                }
                
-               private void AddPropertyGroup (XmlElement xmlElement, ImportedProject importedProject)
+               void AddPropertyGroup (XmlElement xmlElement, ImportedProject importedProject)
                {
-                       if (xmlElement == null)
-                               throw new ArgumentNullException ("xmlElement");
-                       BuildPropertyGroup bpg = new BuildPropertyGroup (xmlElement, this, importedProject);
+                       BuildPropertyGroup bpg = new BuildPropertyGroup (xmlElement, this, importedProject, false);
                        PropertyGroups.Add (bpg);
                }
                
-               private void AddChoose (XmlElement xmlElement)
+               void AddChoose (XmlElement xmlElement, ImportedProject importedProject)
                {
-                       if (xmlElement == null)
-                               throw new ArgumentNullException ("xmlElement");
-                               
-                       BuildChoose bc = new BuildChoose (xmlElement, this);
+                       BuildChoose bc = new BuildChoose (xmlElement, this, importedProject);
                        groupingCollection.Add (bc);
-                       
                }
                
-               private static void ValidationCallBack (object sender, ValidationEventArgs e)
+               static void ValidationCallBack (object sender, ValidationEventArgs e)
                {
                        Console.WriteLine ("Validation Error: {0}", e.Message);
                }
@@ -664,38 +1187,266 @@ namespace Microsoft.Build.BuildEngine {
                        }
                }
 
+               [MonoTODO]
                public Encoding Encoding {
                        get { return encoding; }
                }
 
                public string DefaultTargets {
                        get {
-                               return xmlDocument.DocumentElement.GetAttribute ("DefaultTargets");
+                               return String.Join ("; ", defaultTargets);
                        }
                        set {
                                xmlDocument.DocumentElement.SetAttribute ("DefaultTargets", value);
-                               defaultTargets = value.Split (';');
+                               if (value != null)
+                                       defaultTargets = value.Split (new char [] {';', ' '},
+                                                       StringSplitOptions.RemoveEmptyEntries);
                        }
                }
 
                public BuildItemGroup EvaluatedItems {
-                       get { return evaluatedItems; }
+                       get {
+                               if (needToReevaluate) {
+                                       needToReevaluate = false;
+                                       Reevaluate ();
+                               }
+                               return evaluatedItems;
+                       }
                }
 
                public BuildItemGroup EvaluatedItemsIgnoringCondition {
-                       get { return evaluatedItemsIgnoringCondition; }
+                       get {
+                               if (needToReevaluate) {
+                                       needToReevaluate = false;
+                                       Reevaluate ();
+                               }
+                               return evaluatedItemsIgnoringCondition;
+                       }
                }
                
                internal IDictionary <string, BuildItemGroup> EvaluatedItemsByName {
-                       get { return evaluatedItemsByName; }
+                       get {
+                               // FIXME: do we need to do this here?
+                               if (needToReevaluate) {
+                                       needToReevaluate = false;
+                                       Reevaluate ();
+                               }
+                               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 { return evaluatedItemsByNameIgnoringCondition; }
+                       get {
+                               // FIXME: do we need to do this here?
+                               if (needToReevaluate) {
+                                       needToReevaluate = false;
+                                       Reevaluate ();
+                               }
+                               return evaluatedItemsByNameIgnoringCondition;
+                       }
+               }
+
+               // For batching implementation
+               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;
+               }
+
+               // Honors batching
+               internal bool TryGetEvaluatedItemByNameBatched (string itemName, out BuildItemGroup group)
+               {
+                       if (perBatchItemsByName != null && perBatchItemsByName.TryGetValue (itemName, out group))
+                               return true;
+
+                       if (commonItemsByName != null && commonItemsByName.TryGetValue (itemName, out group))
+                               return true;
+
+                       group = null;
+                       return EvaluatedItemsByName.TryGetValue (itemName, out group);
+               }
+
+               internal string GetMetadataBatched (string itemName, string metadataName)
+               {
+                       BuildItemGroup group = null;
+                       if (itemName == null) {
+                               //unqualified, all items in a batch(bucket) have the
+                               //same metadata values
+                               group = GetFirst<BuildItemGroup> (perBatchItemsByName.Values);
+                               if (group == null)
+                                       group = GetFirst<BuildItemGroup> (commonItemsByName.Values);
+                       } else {
+                               //qualified
+                               TryGetEvaluatedItemByNameBatched (itemName, out group);
+                       }
+
+                       if (group != null) {
+                               foreach (BuildItem item in group) {
+                                       if (item.HasMetadata (metadataName))
+                                               return item.GetEvaluatedMetadata (metadataName);
+                               }
+                       }
+                       return String.Empty;
+               }
+
+               internal IEnumerable<BuildItemGroup> GetAllItemGroups ()
+               {
+                       if (perBatchItemsByName == null && commonItemsByName == null)
+                               foreach (BuildItemGroup group in EvaluatedItemsByName.Values)
+                                       yield return group;
+
+                       if (perBatchItemsByName != null)
+                               foreach (BuildItemGroup group in perBatchItemsByName.Values)
+                                       yield return group;
+
+                       if (commonItemsByName != null)
+                               foreach (BuildItemGroup group in commonItemsByName.Values)
+                                       yield return group;
+               }
+
+               T GetFirst<T> (ICollection<T> list)
+               {
+                       if (list == null)
+                               return default (T);
+
+                       foreach (T t in list)
+                               return t;
+
+                       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 (
+                               null, null, filename, 0, 0, 0, 0, String.Format (message, messageArgs),
+                               null, null);
+                       ParentEngine.EventSource.FireWarningRaised (this, bwea);
+               }
+
+               internal void LogError (string filename, string message,
+                                    params object[] messageArgs)
+               {
+                       BuildErrorEventArgs beea = new BuildErrorEventArgs (
+                               null, null, filename, 0, 0, 0, 0, String.Format (message, messageArgs),
+                               null, null);
+                       ParentEngine.EventSource.FireErrorRaised (this, beea);
+               }
+
+               internal static string DefaultExtensionsPath {
+                       get {
+                               if (extensions_path == null) {
+                                       // NOTE: code from mcs/tools/gacutil/driver.cs
+                                       PropertyInfo gac = typeof (System.Environment).GetProperty (
+                                                       "GacPath", BindingFlags.Static | BindingFlags.NonPublic);
+
+                                       if (gac != null) {
+                                               MethodInfo get_gac = gac.GetGetMethod (true);
+                                               string gac_path = (string) get_gac.Invoke (null, null);
+                                               extensions_path = Path.GetFullPath (Path.Combine (
+                                                                       gac_path, Path.Combine ("..", "xbuild")));
+                                       }
+                               }
+                               return extensions_path;
+                       }
                }
 
                public BuildPropertyGroup EvaluatedProperties {
-                       get { return evaluatedProperties; }
+                       get {
+                               if (needToReevaluate) {
+                                       needToReevaluate = false;
+                                       Reevaluate ();
+                               }
+                               return evaluatedProperties;
+                       }
+               }
+
+               internal IEnumerable EvaluatedPropertiesAsDictionaryEntries {
+                       get {
+                               foreach (BuildProperty bp in EvaluatedProperties)
+                                       yield return new DictionaryEntry (bp.Name, bp.Value);
+                       }
                }
 
                public string FullFileName {
@@ -706,12 +1457,12 @@ namespace Microsoft.Build.BuildEngine {
                public BuildPropertyGroup GlobalProperties {
                        get { return globalProperties; }
                        set {
-                               if (value == null) {
+                               if (value == null)
                                        throw new ArgumentNullException ("value");
-                               }
-                               if (value.FromXml) {
-                                       throw new InvalidOperationException ("Can't do that.");
-                               }
+                               
+                               if (value.FromXml)
+                                       throw new InvalidOperationException ("GlobalProperties can not be set to persisted property group.");
+                               
                                globalProperties = value;
                        }
                }
@@ -734,8 +1485,16 @@ namespace Microsoft.Build.BuildEngine {
                }
                
                public string InitialTargets {
-                       get { return initialTargets; }
-                       set { initialTargets = value; }
+                       get {
+                               return String.Join ("; ", initialTargets.ToArray ());
+                       }
+                       set {
+                               initialTargets.Clear ();
+                               xmlDocument.DocumentElement.SetAttribute ("InitialTargets", value);
+                               if (value != null)
+                                       initialTargets.AddRange (value.Split (
+                                                               new char [] {';', ' '}, StringSplitOptions.RemoveEmptyEntries));
+                       }
                }
 
                public Engine ParentEngine {
@@ -767,6 +1526,33 @@ namespace Microsoft.Build.BuildEngine {
                public string Xml {
                        get { return xmlDocument.InnerXml; }
                }
+
+               // 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 {
+                       get { return last_item_group_containing; }
+               }
                
                internal static XmlNamespaceManager XmlNamespaceManager {
                        get {
@@ -790,6 +1576,22 @@ namespace Microsoft.Build.BuildEngine {
                internal static string XmlNamespace {
                        get { return ns; }
                }
+
+               static string OS {
+                       get {
+                               PlatformID pid = Environment.OSVersion.Platform;
+                               switch ((int)pid) {
+                               case 128:
+                               case 4:
+                                       return "Unix";
+                               case 6:
+                                       return "OSX";
+                               default:
+                                       return "Windows_NT";
+                               }
+                       }
+               }
+
        }
 }