[xbuild] Fix bug #664349 .
[mono.git] / mcs / class / Microsoft.Build.Engine / Microsoft.Build.BuildEngine / Project.cs
1 //
2 // Project.cs: Project class
3 //
4 // Author:
5 //   Marek Sieradzki (marek.sieradzki@gmail.com)
6 //
7 // (C) 2005 Marek Sieradzki
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27
28 #if NET_2_0
29
30 using System;
31 using System.Collections;
32 using System.Collections.Generic;
33 using System.Collections.Specialized;
34 using System.IO;
35 using System.Reflection;
36 using System.Text;
37 using System.Xml;
38 using System.Xml.Schema;
39 using Microsoft.Build.Framework;
40 using Mono.XBuild.Framework;
41 using Mono.XBuild.CommandLine;
42
43 namespace Microsoft.Build.BuildEngine {
44         public class Project {
45         
46                 bool                            buildEnabled;
47                 Dictionary <string, List <string>>      conditionedProperties;
48                 string[]                        defaultTargets;
49                 Encoding                        encoding;
50                 BuildItemGroup                  evaluatedItems;
51                 BuildItemGroup                  evaluatedItemsIgnoringCondition;
52                 Dictionary <string, BuildItemGroup>     evaluatedItemsByName;
53                 Dictionary <string, BuildItemGroup>     evaluatedItemsByNameIgnoringCondition;
54                 BuildPropertyGroup              evaluatedProperties;
55                 string                          firstTargetName;
56                 string                          fullFileName;
57                 BuildPropertyGroup              globalProperties;
58                 GroupingCollection              groupingCollection;
59                 bool                            isDirty;
60                 bool                            isValidated;
61                 BuildItemGroupCollection        itemGroups;
62                 ImportCollection                imports;
63                 List<string>                    initialTargets;
64                 Dictionary <string, BuildItemGroup> last_item_group_containing;
65                 bool                            needToReevaluate;
66                 Engine                          parentEngine;
67                 BuildPropertyGroupCollection    propertyGroups;
68                 string                          schemaFile;
69                 TaskDatabase                    taskDatabase;
70                 TargetCollection                targets;
71                 DateTime                        timeOfLastDirty;
72                 UsingTaskCollection             usingTasks;
73                 XmlDocument                     xmlDocument;
74                 bool                            unloaded;
75                 bool                            initialTargetsBuilt;
76                 bool                            building;
77                 BuildSettings                   current_settings;
78                 Stack<Batch>                    batches;
79                 ProjectLoadSettings             project_load_settings;
80
81
82                 static string extensions_path;
83                 static XmlNamespaceManager      manager;
84                 static string ns = "http://schemas.microsoft.com/developer/msbuild/2003";
85
86                 public Project ()
87                         : this (Engine.GlobalEngine)
88                 {
89                 }
90
91                 public Project (Engine engine) : this (engine, null)
92                 {
93                 }
94                 
95                 public Project (Engine engine, string toolsVersion)
96                 {
97                         parentEngine  = engine;
98                         ToolsVersion = toolsVersion;
99
100                         buildEnabled = ParentEngine.BuildEnabled;
101                         xmlDocument = new XmlDocument ();
102                         xmlDocument.PreserveWhitespace = false;
103                         xmlDocument.AppendChild (xmlDocument.CreateElement ("Project", XmlNamespace));
104                         xmlDocument.DocumentElement.SetAttribute ("xmlns", ns);
105                         
106                         fullFileName = String.Empty;
107                         timeOfLastDirty = DateTime.Now;
108                         current_settings = BuildSettings.None;
109                         project_load_settings = ProjectLoadSettings.None;
110
111                         encoding = null;
112
113                         initialTargets = new List<string> ();
114                         defaultTargets = new string [0];
115                         batches = new Stack<Batch> ();
116
117                         globalProperties = new BuildPropertyGroup (null, this, null, false);
118                         foreach (BuildProperty bp in parentEngine.GlobalProperties)
119                                 GlobalProperties.AddProperty (bp.Clone (true));
120                         
121                         ProcessXml ();
122
123                 }
124
125                 [MonoTODO ("Not tested")]
126                 public void AddNewImport (string importLocation,
127                                           string importCondition)
128                 {
129                         if (importLocation == null)
130                                 throw new ArgumentNullException ("importLocation");
131
132                         XmlElement importElement = xmlDocument.CreateElement ("Import", XmlNamespace);
133                         xmlDocument.DocumentElement.AppendChild (importElement);
134                         importElement.SetAttribute ("Project", importLocation);
135                         if (!String.IsNullOrEmpty (importCondition))
136                                 importElement.SetAttribute ("Condition", importCondition);
137
138                         Import import = new Import (importElement, this, null);
139                         imports.Add (import);
140                         MarkProjectAsDirty ();
141                         NeedToReevaluate ();
142                 }
143
144                 public BuildItem AddNewItem (string itemName,
145                                              string itemInclude)
146                 {
147                         return AddNewItem (itemName, itemInclude, false);
148                 }
149                 
150                 [MonoTODO ("Adds item not in the same place as MS")]
151                 public BuildItem AddNewItem (string itemName,
152                                              string itemInclude,
153                                              bool treatItemIncludeAsLiteral)
154                 {
155                         BuildItemGroup big;
156
157                         if (itemGroups.Count == 0)
158                                 big = AddNewItemGroup ();
159                         else {
160                                 if (last_item_group_containing.ContainsKey (itemName)) {
161                                         big = last_item_group_containing [itemName];
162                                 } else {
163                                         // FIXME: not tested
164                                         BuildItemGroup [] groups = new BuildItemGroup [itemGroups.Count];
165                                         itemGroups.CopyTo (groups, 0);
166                                         big = groups [0];
167                                 }
168                         }
169
170                         BuildItem item = big.AddNewItem (itemName, itemInclude, treatItemIncludeAsLiteral);
171                                 
172                         MarkProjectAsDirty ();
173                         NeedToReevaluate ();
174
175                         return item;
176                 }
177
178                 [MonoTODO ("Not tested")]
179                 public BuildItemGroup AddNewItemGroup ()
180                 {
181                         XmlElement element = xmlDocument.CreateElement ("ItemGroup", XmlNamespace);
182                         xmlDocument.DocumentElement.AppendChild (element);
183
184                         BuildItemGroup big = new BuildItemGroup (element, this, null, false);
185                         itemGroups.Add (big);
186                         MarkProjectAsDirty ();
187                         NeedToReevaluate ();
188
189                         return big;
190                 }
191
192                 [MonoTODO ("Ignores insertAtEndOfProject")]
193                 public BuildPropertyGroup AddNewPropertyGroup (bool insertAtEndOfProject)
194                 {
195                         XmlElement element = xmlDocument.CreateElement ("PropertyGroup", XmlNamespace);
196                         xmlDocument.DocumentElement.AppendChild (element);
197
198                         BuildPropertyGroup bpg = new BuildPropertyGroup (element, this, null, false);
199                         propertyGroups.Add (bpg);
200                         MarkProjectAsDirty ();
201                         NeedToReevaluate ();
202
203                         return bpg;
204                 }
205                 
206                 [MonoTODO ("Not tested, isn't added to TaskDatabase (no reevaluation)")]
207                 public void AddNewUsingTaskFromAssemblyFile (string taskName,
208                                                              string assemblyFile)
209                 {
210                         if (taskName == null)
211                                 throw new ArgumentNullException ("taskName");
212                         if (assemblyFile == null)
213                                 throw new ArgumentNullException ("assemblyFile");
214
215                         XmlElement element = xmlDocument.CreateElement ("UsingTask", XmlNamespace);
216                         xmlDocument.DocumentElement.AppendChild (element);
217                         element.SetAttribute ("TaskName", taskName);
218                         element.SetAttribute ("AssemblyFile", assemblyFile);
219
220                         UsingTask ut = new UsingTask (element, this, null);
221                         usingTasks.Add (ut);
222                         MarkProjectAsDirty ();
223                 }
224                 
225                 [MonoTODO ("Not tested, isn't added to TaskDatabase (no reevaluation)")]
226                 public void AddNewUsingTaskFromAssemblyName (string taskName,
227                                                              string assemblyName)
228                 {
229                         if (taskName == null)
230                                 throw new ArgumentNullException ("taskName");
231                         if (assemblyName == null)
232                                 throw new ArgumentNullException ("assemblyName");
233
234                         XmlElement element = xmlDocument.CreateElement ("UsingTask", XmlNamespace);
235                         xmlDocument.DocumentElement.AppendChild (element);
236                         element.SetAttribute ("TaskName", taskName);
237                         element.SetAttribute ("AssemblyName", assemblyName);
238
239                         UsingTask ut = new UsingTask (element, this, null);
240                         usingTasks.Add (ut);
241                         MarkProjectAsDirty ();
242                 }
243                 
244                 [MonoTODO ("Not tested")]
245                 public bool Build ()
246                 {
247                         return Build (new string [0]);
248                 }
249                 
250                 [MonoTODO ("Not tested")]
251                 public bool Build (string targetName)
252                 {
253                         if (targetName == null)
254                                 return Build ((string[]) null);
255                         else
256                                 return Build (new string [1] { targetName });
257                 }
258                 
259                 [MonoTODO ("Not tested")]
260                 public bool Build (string [] targetNames)
261                 {
262                         return Build (targetNames, null);
263                 }
264                 
265                 [MonoTODO ("Not tested")]
266                 public bool Build (string [] targetNames,
267                                    IDictionary targetOutputs)
268                 {
269                         return Build (targetNames, targetOutputs, BuildSettings.None);
270                 }
271                 
272                 [MonoTODO ("Not tested")]
273                 public bool Build (string [] targetNames,
274                                    IDictionary targetOutputs,
275                                    BuildSettings buildFlags)
276                 
277                 {
278                         bool result = false;
279                         ParentEngine.StartProjectBuild (this, targetNames);
280                         string current_directory = Environment.CurrentDirectory;
281                         try {
282                                 current_settings = buildFlags;
283                                 if (!String.IsNullOrEmpty (fullFileName))
284                                         Directory.SetCurrentDirectory (Path.GetDirectoryName (fullFileName));
285                                 building = true;
286                                 result = BuildInternal (targetNames, targetOutputs, buildFlags);
287                         } finally {
288                                 ParentEngine.EndProjectBuild (this, result);
289                                 current_settings = BuildSettings.None;
290                                 Directory.SetCurrentDirectory (current_directory);
291                                 building = false;
292                         }
293
294                         return result;
295                 }
296
297                 bool BuildInternal (string [] targetNames,
298                                    IDictionary targetOutputs,
299                                    BuildSettings buildFlags)
300                 {
301                         CheckUnloaded ();
302                         if (buildFlags == BuildSettings.None) {
303                                 needToReevaluate = false;
304                                 Reevaluate ();
305                         }
306                         
307                         if (targetNames == null || targetNames.Length == 0) {
308                                 if (defaultTargets != null && defaultTargets.Length != 0)
309                                         targetNames = defaultTargets;
310                                 else if (firstTargetName != null)
311                                         targetNames = new string [1] { firstTargetName};
312                                 else
313                                         return false;
314                         }
315
316                         if (!initialTargetsBuilt) {
317                                 foreach (string target in initialTargets) {
318                                         if (!BuildTarget (target.Trim (), targetOutputs))
319                                                 return false;
320                                 }
321                                 initialTargetsBuilt = true;
322                         }
323
324                         foreach (string target in targetNames)
325                                 if (!BuildTarget (target.Trim (), targetOutputs))
326                                         return false;
327                                 
328                         return true;
329                 }
330
331                 bool BuildTarget (string target_name, IDictionary targetOutputs)
332                 {
333                         if (target_name == null)
334                                 throw new ArgumentException ("targetNames cannot contain null strings");
335
336                         if (!targets.Exists (target_name)) {
337                                 LogError (fullFileName, "Target named '{0}' not found in the project.", target_name);
338                                 return false;
339                         }
340
341                         string key = GetKeyForTarget (target_name);
342                         if (!targets [target_name].Build (key))
343                                 return false;
344
345                         ITaskItem[] outputs;
346                         if (ParentEngine.BuiltTargetsOutputByName.TryGetValue (key, out outputs)) {
347                                 if (targetOutputs != null)
348                                         targetOutputs.Add (target_name, outputs);
349                         }
350                         return true;
351                 }
352
353                 internal string GetKeyForTarget (string target_name)
354                 {
355                         return GetKeyForTarget (target_name, true);
356                 }
357
358                 internal string GetKeyForTarget (string target_name, bool include_global_properties)
359                 {
360                         // target name is case insensitive
361                         return fullFileName + ":" + target_name.ToLower () +
362                                         (include_global_properties ? (":" + GlobalPropertiesToString (GlobalProperties))
363                                                                    : String.Empty);
364                 }
365
366                 string GlobalPropertiesToString (BuildPropertyGroup bgp)
367                 {
368                         StringBuilder sb = new StringBuilder ();
369                         foreach (BuildProperty bp in bgp)
370                                 sb.AppendFormat (" {0}:{1}", bp.Name, bp.FinalValue);
371                         return sb.ToString ();
372                 }
373
374                 [MonoTODO]
375                 public string [] GetConditionedPropertyValues (string propertyName)
376                 {
377                         if (conditionedProperties.ContainsKey (propertyName))
378                                 return conditionedProperties [propertyName].ToArray ();
379                         else
380                                 return new string [0];
381                 }
382
383                 public BuildItemGroup GetEvaluatedItemsByName (string itemName)
384                 {                       
385                         if (needToReevaluate) {
386                                 needToReevaluate = false;
387                                 Reevaluate ();
388                         }
389
390                         if (evaluatedItemsByName.ContainsKey (itemName))
391                                 return evaluatedItemsByName [itemName];
392                         else
393                                 return new BuildItemGroup (this);
394                 }
395
396                 public BuildItemGroup GetEvaluatedItemsByNameIgnoringCondition (string itemName)
397                 {
398                         if (needToReevaluate) {
399                                 needToReevaluate = false;
400                                 Reevaluate ();
401                         }
402
403                         if (evaluatedItemsByNameIgnoringCondition.ContainsKey (itemName))
404                                 return evaluatedItemsByNameIgnoringCondition [itemName];
405                         else
406                                 return new BuildItemGroup (this);
407                 }
408
409                 public string GetEvaluatedProperty (string propertyName)
410                 {
411                         if (needToReevaluate) {
412                                 needToReevaluate = false;
413                                 Reevaluate ();
414                         }
415
416                         if (propertyName == null)
417                                 throw new ArgumentNullException ("propertyName");
418
419                         BuildProperty bp = evaluatedProperties [propertyName];
420
421                         return bp == null ? null : (string) bp;
422                 }
423
424                 [MonoTODO ("We should remember that node and not use XPath to get it")]
425                 public string GetProjectExtensions (string id)
426                 {
427                         if (id == null || id == String.Empty)
428                                 return String.Empty;
429
430                         XmlNode node = xmlDocument.SelectSingleNode (String.Format ("/tns:Project/tns:ProjectExtensions/tns:{0}", id), XmlNamespaceManager);
431
432                         if (node == null)
433                                 return String.Empty;
434                         else
435                                 return node.InnerXml;
436                 }
437
438
439                 public void Load (string projectFileName)
440                 {
441                         Load (projectFileName, ProjectLoadSettings.None);
442                 }
443
444                 public void Load (string projectFileName, ProjectLoadSettings settings)
445                 {
446                         project_load_settings = settings;
447                         if (String.IsNullOrEmpty (projectFileName))
448                                 throw new ArgumentNullException ("projectFileName");
449
450                         if (!File.Exists (projectFileName))
451                                 throw new ArgumentException (String.Format ("Project file {0} not found", projectFileName),
452                                                 "projectFileName");
453
454                         this.fullFileName = Utilities.FromMSBuildPath (Path.GetFullPath (projectFileName));
455
456                         string filename = fullFileName;
457                         if (String.Compare (Path.GetExtension (fullFileName), ".sln", true) == 0) {
458                                 Project tmp_project = ParentEngine.CreateNewProject ();
459                                 SolutionParser sln_parser = new SolutionParser ();
460                                 sln_parser.ParseSolution (fullFileName, tmp_project, delegate (int errorNumber, string message) {
461                                                 LogWarning (filename, message);
462                                         });
463                                 filename = fullFileName + ".proj";
464                                 try {
465                                         tmp_project.Save (filename);
466                                         ParentEngine.RemoveLoadedProject (tmp_project);
467                                         DoLoad (new StreamReader (filename));
468                                 } finally {
469                                         if (Environment.GetEnvironmentVariable ("XBUILD_EMIT_SOLUTION") == null)
470                                                 File.Delete (filename);
471                                 }
472                         } else {
473                                 DoLoad (new StreamReader (filename));
474                         }
475                 }
476                 
477                 [MonoTODO ("Not tested")]
478                 public void Load (TextReader textReader)
479                 {
480                         Load (textReader, ProjectLoadSettings.None);
481                 }
482
483                 public void Load (TextReader textReader, ProjectLoadSettings projectLoadSettings)
484                 {
485                         project_load_settings = projectLoadSettings;
486                         fullFileName = String.Empty;
487                         DoLoad (textReader);
488                 }
489
490                 public void LoadXml (string projectXml)
491                 {
492                         LoadXml (projectXml, ProjectLoadSettings.None);
493                 }
494
495                 public void LoadXml (string projectXml, ProjectLoadSettings projectLoadSettings)
496                 {
497                         project_load_settings = projectLoadSettings;
498                         fullFileName = String.Empty;
499                         DoLoad (new StringReader (projectXml));
500                         MarkProjectAsDirty ();
501                 }
502
503
504                 public void MarkProjectAsDirty ()
505                 {
506                         isDirty = true;
507                         timeOfLastDirty = DateTime.Now;
508                 }
509
510                 [MonoTODO ("Not tested")]
511                 public void RemoveAllItemGroups ()
512                 {
513                         int length = ItemGroups.Count;
514                         BuildItemGroup [] groups = new BuildItemGroup [length];
515                         ItemGroups.CopyTo (groups, 0);
516
517                         for (int i = 0; i < length; i++)
518                                 RemoveItemGroup (groups [i]);
519
520                         MarkProjectAsDirty ();
521                         NeedToReevaluate ();
522                 }
523
524                 [MonoTODO ("Not tested")]
525                 public void RemoveAllPropertyGroups ()
526                 {
527                         int length = PropertyGroups.Count;
528                         BuildPropertyGroup [] groups = new BuildPropertyGroup [length];
529                         PropertyGroups.CopyTo (groups, 0);
530
531                         for (int i = 0; i < length; i++)
532                                 RemovePropertyGroup (groups [i]);
533
534                         MarkProjectAsDirty ();
535                         NeedToReevaluate ();
536                 }
537
538                 [MonoTODO]
539                 public void RemoveItem (BuildItem itemToRemove)
540                 {
541                         if (itemToRemove == null)
542                                 throw new ArgumentNullException ("itemToRemove");
543
544                         if (!itemToRemove.FromXml && !itemToRemove.HasParentItem)
545                                 throw new InvalidOperationException ("The object passed in is not part of the project.");
546
547                         BuildItemGroup big = itemToRemove.ParentItemGroup;
548
549                         if (big.Count == 1) {
550                                 // ParentItemGroup for items from xml and that have parent is the same
551                                 groupingCollection.Remove (big);
552                         } else {
553                                 if (big.ParentProject != this)
554                                         throw new InvalidOperationException ("The object passed in is not part of the project.");
555
556                                 if (itemToRemove.FromXml)
557                                         big.RemoveItem (itemToRemove);
558                                 else
559                                         big.RemoveItem (itemToRemove.ParentItem);
560                         }
561
562                         MarkProjectAsDirty ();
563                         NeedToReevaluate ();
564                 }
565
566                 [MonoTODO ("Not tested")]
567                 public void RemoveItemGroup (BuildItemGroup itemGroupToRemove)
568                 {
569                         if (itemGroupToRemove == null)
570                                 throw new ArgumentNullException ("itemGroupToRemove");
571
572                         groupingCollection.Remove (itemGroupToRemove);
573                         MarkProjectAsDirty ();
574                 }
575                 
576                 [MonoTODO]
577                 // NOTE: does not modify imported projects
578                 public void RemoveItemGroupsWithMatchingCondition (string matchingCondition)
579                 {
580                         throw new NotImplementedException ();
581                 }
582
583                 [MonoTODO]
584                 public void RemoveItemsByName (string itemName)
585                 {
586                         if (itemName == null)
587                                 throw new ArgumentNullException ("itemName");
588
589                         throw new NotImplementedException ();
590                 }
591
592                 [MonoTODO ("Not tested")]
593                 public void RemovePropertyGroup (BuildPropertyGroup propertyGroupToRemove)
594                 {
595                         if (propertyGroupToRemove == null)
596                                 throw new ArgumentNullException ("propertyGroupToRemove");
597
598                         groupingCollection.Remove (propertyGroupToRemove);
599                         MarkProjectAsDirty ();
600                 }
601                 
602                 [MonoTODO]
603                 // NOTE: does not modify imported projects
604                 public void RemovePropertyGroupsWithMatchingCondition (string matchCondition)
605                 {
606                         throw new NotImplementedException ();
607                 }
608
609                 [MonoTODO]
610                 public void ResetBuildStatus ()
611                 {
612                         // hack to allow built targets to be removed
613                         building = true;
614                         Reevaluate ();
615                         building = false;
616                 }
617
618                 public void Save (string projectFileName)
619                 {
620                         Save (projectFileName, Encoding.Default);
621                         isDirty = false;
622                 }
623
624                 [MonoTODO ("Ignores encoding")]
625                 public void Save (string projectFileName, Encoding encoding)
626                 {
627                         xmlDocument.Save (projectFileName);
628                         isDirty = false;
629                 }
630
631                 public void Save (TextWriter outTextWriter)
632                 {
633                         xmlDocument.Save (outTextWriter);
634                         isDirty = false;
635                 }
636
637                 public void SetImportedProperty (string propertyName,
638                                                  string propertyValue,
639                                                  string condition,
640                                                  Project importProject)
641                 {
642                         SetImportedProperty (propertyName, propertyValue, condition, importProject,
643                                 PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup);
644                 }
645
646                 public void SetImportedProperty (string propertyName,
647                                                  string propertyValue,
648                                                  string condition,
649                                                  Project importedProject,
650                                                  PropertyPosition position)
651                 {
652                         SetImportedProperty (propertyName, propertyValue, condition, importedProject,
653                                 PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup, false);
654                 }
655
656                 [MonoTODO]
657                 public void SetImportedProperty (string propertyName,
658                                                  string propertyValue,
659                                                  string condition,
660                                                  Project importedProject,
661                                                  PropertyPosition position,
662                                                  bool treatPropertyValueAsLiteral)
663                 {
664                         throw new NotImplementedException ();
665                 }
666
667                 public void SetProjectExtensions (string id, string xmlText)
668                 {
669                         if (id == null)
670                                 throw new ArgumentNullException ("id");
671                         if (xmlText == null)
672                                 throw new ArgumentNullException ("xmlText");
673
674                         XmlNode projectExtensions, node;
675
676                         projectExtensions = xmlDocument.SelectSingleNode ("/tns:Project/tns:ProjectExtensions", XmlNamespaceManager);
677                         
678                         if (projectExtensions == null) {
679                                 projectExtensions = xmlDocument.CreateElement ("ProjectExtensions", XmlNamespace);
680                                 xmlDocument.DocumentElement.AppendChild (projectExtensions);
681
682                                 node = xmlDocument.CreateElement (id, XmlNamespace);
683                                 node.InnerXml = xmlText;
684                                 projectExtensions.AppendChild (node);
685                         } else {
686                                 node = xmlDocument.SelectSingleNode (String.Format ("/tns:Project/tns:ProjectExtensions/tns:{0}", id), XmlNamespaceManager);
687
688                                 if (node == null) {
689                                         node = xmlDocument.CreateElement (id, XmlNamespace);
690                                         projectExtensions.AppendChild (node);
691                                 }
692                                 
693                                 node.InnerXml = xmlText;
694                                 
695                         }
696
697                         MarkProjectAsDirty ();
698                 }
699                 
700                 public void SetProperty (string propertyName,
701                                          string propertyValue)
702                 {
703                         SetProperty (propertyName, propertyValue, "true",
704                                 PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup, false);
705                 }
706
707                 public void SetProperty (string propertyName,
708                                          string propertyValue,
709                                          string condition)
710                 {
711                         SetProperty (propertyName, propertyValue, condition,
712                                 PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup);
713                 }
714
715                 public void SetProperty (string propertyName,
716                                          string propertyValue,
717                                          string condition,
718                                          PropertyPosition position)
719                 {
720                         SetProperty (propertyName, propertyValue, condition,
721                                 PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup, false);
722                 }
723
724                 [MonoTODO]
725                 public void SetProperty (string propertyName,
726                                          string propertyValue,
727                                          string condition,
728                                          PropertyPosition position,
729                                          bool treatPropertyValueAsLiteral)
730                 {
731                         throw new NotImplementedException ();
732                 }
733
734                 internal void Unload ()
735                 {
736                         unloaded = true;
737                 }
738
739                 internal void CheckUnloaded ()
740                 {
741                         if (unloaded)
742                                 throw new InvalidOperationException ("This project object has been unloaded from the MSBuild engine and is no longer valid.");
743                 }
744
745                 internal void NeedToReevaluate ()
746                 {
747                         needToReevaluate = true;
748                 }
749                                 
750                 // Does the actual loading.
751                 void DoLoad (TextReader textReader)
752                 {
753                         try {
754                                 ParentEngine.RemoveLoadedProject (this);
755         
756                                 xmlDocument.Load (textReader);
757
758                                 if (xmlDocument.DocumentElement.Name == "VisualStudioProject")
759                                         throw new InvalidProjectFileException (String.Format (
760                                                         "Project file '{0}' is a VS2003 project, which is not " +
761                                                         "supported by xbuild. You need to convert it to msbuild " +
762                                                         "format to build with xbuild.", fullFileName));
763
764                                 if (SchemaFile != null) {
765                                         xmlDocument.Schemas.Add (XmlSchema.Read (
766                                                                 new StreamReader (SchemaFile), ValidationCallBack));
767                                         xmlDocument.Validate (ValidationCallBack);
768                                 }
769
770                                 if (xmlDocument.DocumentElement.Name != "Project") {
771                                         throw new InvalidProjectFileException (String.Format (
772                                                 "The element <{0}> is unrecognized, or not supported in this context.", xmlDocument.DocumentElement.Name));
773                                 }
774         
775                                 if (xmlDocument.DocumentElement.GetAttribute ("xmlns") != ns) {
776                                         throw new InvalidProjectFileException (
777                                                 @"The default XML namespace of the project must be the MSBuild XML namespace." + 
778                                                 " If the project is authored in the MSBuild 2003 format, please add " +
779                                                 "xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\" to the <Project> element. " +
780                                                 "If the project has been authored in the old 1.0 or 1.2 format, please convert it to MSBuild 2003 format.  ");
781                                 }
782                                 ProcessXml ();
783                                 ParentEngine.AddLoadedProject (this);
784                         } catch (Exception e) {
785                                 throw new InvalidProjectFileException (String.Format ("{0}: {1}",
786                                                         fullFileName, e.Message), e);
787                         } finally {
788                                 if (textReader != null)
789                                         textReader.Close ();
790                         }
791                 }
792
793                 void Reevaluate ()
794                 {
795                         ProcessXml ();
796                 }
797
798                 void ProcessXml ()
799                 {
800                         groupingCollection = new GroupingCollection (this);
801                         imports = new ImportCollection (groupingCollection);
802                         usingTasks = new UsingTaskCollection (this);
803                         itemGroups = new BuildItemGroupCollection (groupingCollection);
804                         propertyGroups = new BuildPropertyGroupCollection (groupingCollection);
805                         targets = new TargetCollection (this);
806                         last_item_group_containing = new Dictionary <string, BuildItemGroup> ();
807                         
808                         string effective_tools_version = GetToolsVersionToUse ();
809                         taskDatabase = new TaskDatabase ();
810                         taskDatabase.CopyTasks (ParentEngine.GetDefaultTasks (effective_tools_version));
811
812                         initialTargets = new List<string> ();
813                         defaultTargets = new string [0];
814                         PrepareForEvaluate (effective_tools_version);
815                         ProcessElements (xmlDocument.DocumentElement, null);
816                         
817                         isDirty = false;
818                         Evaluate ();
819                 }
820
821                 void ProcessProjectAttributes (XmlAttributeCollection attributes)
822                 {
823                         foreach (XmlAttribute attr in attributes) {
824                                 switch (attr.Name) {
825                                 case "InitialTargets":
826                                         initialTargets.AddRange (attr.Value.Split (
827                                                                         new char [] {';', ' '},
828                                                                         StringSplitOptions.RemoveEmptyEntries));
829                                         break;
830                                 case "DefaultTargets":
831                                         // first non-empty DefaultTargets found is used
832                                         if (defaultTargets == null || defaultTargets.Length == 0)
833                                                 defaultTargets = attr.Value.Split (new char [] {';', ' '},
834                                                         StringSplitOptions.RemoveEmptyEntries);
835                                         EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectDefaultTargets",
836                                                                 DefaultTargets, PropertyType.Reserved));
837                                         break;
838                                 }
839                         }
840                 }
841
842                 internal void ProcessElements (XmlElement rootElement, ImportedProject ip)
843                 {
844                         ProcessProjectAttributes (rootElement.Attributes);
845                         foreach (XmlNode xn in rootElement.ChildNodes) {
846                                 if (xn is XmlElement) {
847                                         XmlElement xe = (XmlElement) xn;
848                                         switch (xe.Name) {
849                                         case "ProjectExtensions":
850                                                 AddProjectExtensions (xe);
851                                                 break;
852                                         case "Warning":
853                                         case "Message":
854                                         case "Error":
855                                                 AddMessage (xe);
856                                                 break;
857                                         case "Target":
858                                                 AddTarget (xe, ip);
859                                                 break;
860                                         case "UsingTask":
861                                                 AddUsingTask (xe, ip);
862                                                 break;
863                                         case "Import":
864                                                 AddImport (xe, ip);
865                                                 break;
866                                         case "ItemGroup":
867                                                 AddItemGroup (xe, ip);
868                                                 break;
869                                         case "PropertyGroup":
870                                                 AddPropertyGroup (xe, ip);
871                                                 break;
872                                         case  "Choose":
873                                                 AddChoose (xe);
874                                                 break;
875                                         default:
876                                                 throw new InvalidProjectFileException (String.Format ("Invalid element '{0}' in project file.", xe.Name));
877                                         }
878                                 }
879                         }
880                 }
881                 
882                 void PrepareForEvaluate (string effective_tools_version)
883                 {
884                         evaluatedItems = new BuildItemGroup (null, this, null, true);
885                         evaluatedItemsIgnoringCondition = new BuildItemGroup (null, this, null, true);
886                         evaluatedItemsByName = new Dictionary <string, BuildItemGroup> (StringComparer.InvariantCultureIgnoreCase);
887                         evaluatedItemsByNameIgnoringCondition = new Dictionary <string, BuildItemGroup> (StringComparer.InvariantCultureIgnoreCase);
888                         if (building && current_settings == BuildSettings.None)
889                                 RemoveBuiltTargets ();
890
891                         InitializeProperties (effective_tools_version);
892                 }
893
894                 void Evaluate ()
895                 {
896                         groupingCollection.Evaluate ();
897
898                         //FIXME: UsingTasks aren't really evaluated. (shouldn't use expressions or anything)
899                         foreach (UsingTask usingTask in UsingTasks)
900                                 usingTask.Evaluate ();
901                 }
902
903                 // Removes entries of all earlier built targets for this project
904                 void RemoveBuiltTargets ()
905                 {
906                         ParentEngine.ClearBuiltTargetsForProject (this);
907                 }
908
909                 void InitializeProperties (string effective_tools_version)
910                 {
911                         BuildProperty bp;
912
913                         evaluatedProperties = new BuildPropertyGroup (null, null, null, true);
914
915                         foreach (BuildProperty gp in GlobalProperties) {
916                                 bp = new BuildProperty (gp.Name, gp.Value, PropertyType.Global);
917                                 EvaluatedProperties.AddProperty (bp);
918                         }
919                         
920                         foreach (BuildProperty gp in GlobalProperties)
921                                 ParentEngine.GlobalProperties.AddProperty (gp);
922
923                         // add properties that we dont have from parent engine's
924                         // global properties
925                         foreach (BuildProperty gp in ParentEngine.GlobalProperties) {
926                                 if (EvaluatedProperties [gp.Name] == null) {
927                                         bp = new BuildProperty (gp.Name, gp.Value, PropertyType.Global);
928                                         EvaluatedProperties.AddProperty (bp);
929                                 }
930                         }
931
932                         foreach (DictionaryEntry de in Environment.GetEnvironmentVariables ()) {
933                                 bp = new BuildProperty ((string) de.Key, (string) de.Value, PropertyType.Environment);
934                                 EvaluatedProperties.AddProperty (bp);
935                         }
936
937                         EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectFile", Path.GetFileName (fullFileName),
938                                                 PropertyType.Reserved));
939                         EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectFullPath", fullFileName, PropertyType.Reserved));
940                         EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectName",
941                                                 Path.GetFileNameWithoutExtension (fullFileName),
942                                                 PropertyType.Reserved));
943                         EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectExtension",
944                                                 Path.GetExtension (fullFileName),
945                                                 PropertyType.Reserved));
946
947                         string toolsPath = parentEngine.Toolsets [effective_tools_version].ToolsPath;
948                         if (toolsPath == null)
949                                 throw new Exception (String.Format ("Invalid tools version '{0}', no tools path set for this.", effective_tools_version));
950                         EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildBinPath", toolsPath, PropertyType.Reserved));
951                         EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildToolsPath", toolsPath, PropertyType.Reserved));
952                         EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildToolsVersion", effective_tools_version, PropertyType.Reserved));
953                         EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildExtensionsPath", ExtensionsPath, PropertyType.Reserved));
954                         EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildExtensionsPath32", ExtensionsPath, PropertyType.Reserved));
955                         EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildExtensionsPath64", ExtensionsPath, PropertyType.Reserved));
956                         EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectDefaultTargets", DefaultTargets, PropertyType.Reserved));
957                         EvaluatedProperties.AddProperty (new BuildProperty ("OS", OS, PropertyType.Environment));
958
959                         // FIXME: make some internal method that will work like GetDirectoryName but output String.Empty on null/String.Empty
960                         string projectDir;
961                         if (FullFileName == String.Empty)
962                                 projectDir = Environment.CurrentDirectory;
963                         else
964                                 projectDir = Path.GetDirectoryName (FullFileName);
965
966                         EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectDirectory", projectDir, PropertyType.Reserved));
967                 }
968
969                 // precedence:
970                 // ToolsVersion property
971                 // ToolsVersion attribute on the project
972                 // parentEngine's DefaultToolsVersion
973                 string GetToolsVersionToUse ()
974                 {
975                         if (!String.IsNullOrEmpty (ToolsVersion))
976                                 return ToolsVersion;
977
978                         if (!HasToolsVersionAttribute)
979                                 return parentEngine.DefaultToolsVersion;
980
981                         if (parentEngine.Toolsets [DefaultToolsVersion] == null) {
982                                 LogWarning (FullFileName, "Project has unknown ToolsVersion '{0}'. Using the default tools version '{1}' instead.",
983                                                 DefaultToolsVersion, parentEngine.DefaultToolsVersion);
984                                 return parentEngine.DefaultToolsVersion;
985                         }
986
987                         return DefaultToolsVersion;
988                 }
989                 
990                 void AddProjectExtensions (XmlElement xmlElement)
991                 {
992                 }
993                 
994                 void AddMessage (XmlElement xmlElement)
995                 {
996                 }
997                 
998                 void AddTarget (XmlElement xmlElement, ImportedProject importedProject)
999                 {
1000                         Target target = new Target (xmlElement, this, importedProject);
1001                         targets.AddTarget (target);
1002                         
1003                         if (firstTargetName == null)
1004                                 firstTargetName = target.Name;
1005                 }
1006                 
1007                 void AddUsingTask (XmlElement xmlElement, ImportedProject importedProject)
1008                 {
1009                         UsingTask usingTask;
1010
1011                         usingTask = new UsingTask (xmlElement, this, importedProject);
1012                         UsingTasks.Add (usingTask);
1013                 }
1014                 
1015                 void AddImport (XmlElement xmlElement, ImportedProject importingProject)
1016                 {
1017                         // eval all the properties etc till the import
1018                         groupingCollection.Evaluate (EvaluationType.Property);
1019
1020                         Import import = new Import (xmlElement, this, importingProject);
1021                         if (!ConditionParser.ParseAndEvaluate (import.Condition, this))
1022                                 return;
1023
1024                         if (Imports.Contains (import)) {
1025                                 LogWarning (importingProject != null ? importingProject.FullFileName : fullFileName,
1026                                                 "A circular reference was found involving the import of {0}. Only" +
1027                                                 " the first import of this file will be used, ignoring others.",
1028                                                 import.ProjectPath);
1029
1030                                 return;
1031                         }
1032
1033                         if (String.Compare (fullFileName, import.EvaluatedProjectPath) == 0) {
1034                                 LogWarning (importingProject != null ? importingProject.FullFileName : fullFileName,
1035                                                 "The main project file was imported here, which creates a circular " +
1036                                                 "reference. Ignoring this import.");
1037
1038                                 return;
1039                         }
1040
1041                         Imports.Add (import);
1042                         import.Evaluate (project_load_settings == ProjectLoadSettings.IgnoreMissingImports);
1043                 }
1044                 
1045                 void AddItemGroup (XmlElement xmlElement, ImportedProject importedProject)
1046                 {
1047                         BuildItemGroup big = new BuildItemGroup (xmlElement, this, importedProject, false);
1048                         ItemGroups.Add (big);
1049                 }
1050                 
1051                 void AddPropertyGroup (XmlElement xmlElement, ImportedProject importedProject)
1052                 {
1053                         BuildPropertyGroup bpg = new BuildPropertyGroup (xmlElement, this, importedProject, false);
1054                         PropertyGroups.Add (bpg);
1055                 }
1056                 
1057                 void AddChoose (XmlElement xmlElement)
1058                 {
1059                         BuildChoose bc = new BuildChoose (xmlElement, this);
1060                         groupingCollection.Add (bc);
1061                 }
1062                 
1063                 static void ValidationCallBack (object sender, ValidationEventArgs e)
1064                 {
1065                         Console.WriteLine ("Validation Error: {0}", e.Message);
1066                 }
1067                 
1068                 public bool BuildEnabled {
1069                         get {
1070                                 return buildEnabled;
1071                         }
1072                         set {
1073                                 buildEnabled = value;
1074                         }
1075                 }
1076
1077                 [MonoTODO]
1078                 public Encoding Encoding {
1079                         get { return encoding; }
1080                 }
1081
1082                 public string DefaultTargets {
1083                         get {
1084                                 return String.Join ("; ", defaultTargets);
1085                         }
1086                         set {
1087                                 xmlDocument.DocumentElement.SetAttribute ("DefaultTargets", value);
1088                                 if (value != null)
1089                                         defaultTargets = value.Split (new char [] {';', ' '},
1090                                                         StringSplitOptions.RemoveEmptyEntries);
1091                         }
1092                 }
1093
1094                 public BuildItemGroup EvaluatedItems {
1095                         get {
1096                                 if (needToReevaluate) {
1097                                         needToReevaluate = false;
1098                                         Reevaluate ();
1099                                 }
1100                                 return evaluatedItems;
1101                         }
1102                 }
1103
1104                 public BuildItemGroup EvaluatedItemsIgnoringCondition {
1105                         get {
1106                                 if (needToReevaluate) {
1107                                         needToReevaluate = false;
1108                                         Reevaluate ();
1109                                 }
1110                                 return evaluatedItemsIgnoringCondition;
1111                         }
1112                 }
1113                 
1114                 internal IDictionary <string, BuildItemGroup> EvaluatedItemsByName {
1115                         get {
1116                                 // FIXME: do we need to do this here?
1117                                 if (needToReevaluate) {
1118                                         needToReevaluate = false;
1119                                         Reevaluate ();
1120                                 }
1121                                 return evaluatedItemsByName;
1122                         }
1123                 }
1124
1125                 internal IEnumerable EvaluatedItemsByNameAsDictionaryEntries {
1126                         get {
1127                                 if (EvaluatedItemsByName.Count == 0)
1128                                         yield break;
1129
1130                                 foreach (KeyValuePair<string, BuildItemGroup> pair in EvaluatedItemsByName) {
1131                                         foreach (BuildItem bi in pair.Value)
1132                                                 yield return new DictionaryEntry (pair.Key, bi.ConvertToITaskItem (null, ExpressionOptions.ExpandItemRefs));
1133                                 }
1134                         }
1135                 }
1136
1137                 internal IDictionary <string, BuildItemGroup> EvaluatedItemsByNameIgnoringCondition {
1138                         get {
1139                                 // FIXME: do we need to do this here?
1140                                 if (needToReevaluate) {
1141                                         needToReevaluate = false;
1142                                         Reevaluate ();
1143                                 }
1144                                 return evaluatedItemsByNameIgnoringCondition;
1145                         }
1146                 }
1147
1148                 // For batching implementation
1149                 Dictionary<string, BuildItemGroup> perBatchItemsByName;
1150                 Dictionary<string, BuildItemGroup> commonItemsByName;
1151
1152                 struct Batch {
1153                         public Dictionary<string, BuildItemGroup> perBatchItemsByName;
1154                         public Dictionary<string, BuildItemGroup> commonItemsByName;
1155
1156                         public Batch (Dictionary<string, BuildItemGroup> perBatchItemsByName, Dictionary<string, BuildItemGroup> commonItemsByName)
1157                         {
1158                                 this.perBatchItemsByName = perBatchItemsByName;
1159                                 this.commonItemsByName = commonItemsByName;
1160                         }
1161                 }
1162
1163                 Stack<Batch> Batches {
1164                         get { return batches; }
1165                 }
1166
1167                 internal void PushBatch (Dictionary<string, BuildItemGroup> perBatchItemsByName, Dictionary<string, BuildItemGroup> commonItemsByName)
1168                 {
1169                         batches.Push (new Batch (perBatchItemsByName, commonItemsByName));
1170                         SetBatchedItems (perBatchItemsByName, commonItemsByName);
1171                 }
1172
1173                 internal void PopBatch ()
1174                 {
1175                         batches.Pop ();
1176                         if (batches.Count > 0) {
1177                                 Batch b = batches.Peek ();
1178                                 SetBatchedItems (b.perBatchItemsByName, b.commonItemsByName);
1179                         } else {
1180                                 SetBatchedItems (null, null);
1181                         }
1182                 }
1183
1184                 void SetBatchedItems (Dictionary<string, BuildItemGroup> perBatchItemsByName, Dictionary<string, BuildItemGroup> commonItemsByName)
1185                 {
1186                         this.perBatchItemsByName = perBatchItemsByName;
1187                         this.commonItemsByName = commonItemsByName;
1188                 }
1189
1190                 // Honors batching
1191                 internal bool TryGetEvaluatedItemByNameBatched (string itemName, out BuildItemGroup group)
1192                 {
1193                         if (perBatchItemsByName != null && perBatchItemsByName.TryGetValue (itemName, out group))
1194                                 return true;
1195
1196                         if (commonItemsByName != null && commonItemsByName.TryGetValue (itemName, out group))
1197                                 return true;
1198
1199                         group = null;
1200                         return EvaluatedItemsByName.TryGetValue (itemName, out group);
1201                 }
1202
1203                 internal string GetMetadataBatched (string itemName, string metadataName)
1204                 {
1205                         BuildItemGroup group = null;
1206                         if (itemName == null) {
1207                                 //unqualified, all items in a batch(bucket) have the
1208                                 //same metadata values
1209                                 group = GetFirst<BuildItemGroup> (perBatchItemsByName.Values);
1210                                 if (group == null)
1211                                         group = GetFirst<BuildItemGroup> (commonItemsByName.Values);
1212                         } else {
1213                                 //qualified
1214                                 TryGetEvaluatedItemByNameBatched (itemName, out group);
1215                         }
1216
1217                         if (group != null) {
1218                                 foreach (BuildItem item in group) {
1219                                         if (item.HasMetadata (metadataName))
1220                                                 return item.GetEvaluatedMetadata (metadataName);
1221                                 }
1222                         }
1223                         return String.Empty;
1224                 }
1225
1226                 internal IEnumerable<BuildItemGroup> GetAllItemGroups ()
1227                 {
1228                         if (perBatchItemsByName == null && commonItemsByName == null)
1229                                 foreach (BuildItemGroup group in EvaluatedItemsByName.Values)
1230                                         yield return group;
1231
1232                         if (perBatchItemsByName != null)
1233                                 foreach (BuildItemGroup group in perBatchItemsByName.Values)
1234                                         yield return group;
1235
1236                         if (commonItemsByName != null)
1237                                 foreach (BuildItemGroup group in commonItemsByName.Values)
1238                                         yield return group;
1239                 }
1240
1241                 T GetFirst<T> (ICollection<T> list)
1242                 {
1243                         if (list == null)
1244                                 return default (T);
1245
1246                         foreach (T t in list)
1247                                 return t;
1248
1249                         return default (T);
1250                 }
1251
1252                 internal void LogWarning (string filename, string message, params object[] messageArgs)
1253                 {
1254                         BuildWarningEventArgs bwea = new BuildWarningEventArgs (
1255                                 null, null, filename, 0, 0, 0, 0, String.Format (message, messageArgs),
1256                                 null, null);
1257                         ParentEngine.EventSource.FireWarningRaised (this, bwea);
1258                 }
1259
1260                 internal void LogError (string filename, string message,
1261                                      params object[] messageArgs)
1262                 {
1263                         BuildErrorEventArgs beea = new BuildErrorEventArgs (
1264                                 null, null, filename, 0, 0, 0, 0, String.Format (message, messageArgs),
1265                                 null, null);
1266                         ParentEngine.EventSource.FireErrorRaised (this, beea);
1267                 }
1268
1269                 static string ExtensionsPath {
1270                         get {
1271                                 if (extensions_path == null) {
1272                                         // NOTE: code from mcs/tools/gacutil/driver.cs
1273                                         PropertyInfo gac = typeof (System.Environment).GetProperty (
1274                                                         "GacPath", BindingFlags.Static | BindingFlags.NonPublic);
1275
1276                                         if (gac != null) {
1277                                                 MethodInfo get_gac = gac.GetGetMethod (true);
1278                                                 string gac_path = (string) get_gac.Invoke (null, null);
1279                                                 extensions_path = Path.GetFullPath (Path.Combine (
1280                                                                         gac_path, Path.Combine ("..", "xbuild")));
1281                                         }
1282                                 }
1283                                 return extensions_path;
1284                         }
1285                 }
1286
1287                 public BuildPropertyGroup EvaluatedProperties {
1288                         get {
1289                                 if (needToReevaluate) {
1290                                         needToReevaluate = false;
1291                                         Reevaluate ();
1292                                 }
1293                                 return evaluatedProperties;
1294                         }
1295                 }
1296
1297                 internal IEnumerable EvaluatedPropertiesAsDictionaryEntries {
1298                         get {
1299                                 foreach (BuildProperty bp in EvaluatedProperties)
1300                                         yield return new DictionaryEntry (bp.Name, bp.Value);
1301                         }
1302                 }
1303
1304                 public string FullFileName {
1305                         get { return fullFileName; }
1306                         set { fullFileName = value; }
1307                 }
1308
1309                 public BuildPropertyGroup GlobalProperties {
1310                         get { return globalProperties; }
1311                         set {
1312                                 if (value == null)
1313                                         throw new ArgumentNullException ("value");
1314                                 
1315                                 if (value.FromXml)
1316                                         throw new InvalidOperationException ("GlobalProperties can not be set to persisted property group.");
1317                                 
1318                                 globalProperties = value;
1319                         }
1320                 }
1321
1322                 public bool IsDirty {
1323                         get { return isDirty; }
1324                 }
1325
1326                 public bool IsValidated {
1327                         get { return isValidated; }
1328                         set { isValidated = value; }
1329                 }
1330
1331                 public BuildItemGroupCollection ItemGroups {
1332                         get { return itemGroups; }
1333                 }
1334                 
1335                 public ImportCollection Imports {
1336                         get { return imports; }
1337                 }
1338                 
1339                 public string InitialTargets {
1340                         get {
1341                                 return String.Join ("; ", initialTargets.ToArray ());
1342                         }
1343                         set {
1344                                 initialTargets.Clear ();
1345                                 xmlDocument.DocumentElement.SetAttribute ("InitialTargets", value);
1346                                 if (value != null)
1347                                         initialTargets.AddRange (value.Split (
1348                                                                 new char [] {';', ' '}, StringSplitOptions.RemoveEmptyEntries));
1349                         }
1350                 }
1351
1352                 public Engine ParentEngine {
1353                         get { return parentEngine; }
1354                 }
1355
1356                 public BuildPropertyGroupCollection PropertyGroups {
1357                         get { return propertyGroups; }
1358                 }
1359
1360                 public string SchemaFile {
1361                         get { return schemaFile; }
1362                         set { schemaFile = value; }
1363                 }
1364
1365                 public TargetCollection Targets {
1366                         get { return targets; }
1367                 }
1368
1369                 public DateTime TimeOfLastDirty {
1370                         get { return timeOfLastDirty; }
1371                 }
1372                 
1373                 public UsingTaskCollection UsingTasks {
1374                         get { return usingTasks; }
1375                 }
1376
1377                 [MonoTODO]
1378                 public string Xml {
1379                         get { return xmlDocument.InnerXml; }
1380                 }
1381
1382                 // corresponds to the xml attribute
1383                 public string DefaultToolsVersion {
1384                         get {
1385                                 if (xmlDocument != null)
1386                                         return xmlDocument.DocumentElement.GetAttribute ("ToolsVersion");
1387                                 return null;
1388                         }
1389                         set {
1390                                 if (xmlDocument != null)
1391                                         xmlDocument.DocumentElement.SetAttribute ("ToolsVersion", value);
1392                         }
1393                 }
1394
1395                 public bool HasToolsVersionAttribute {
1396                         get {
1397                                 return xmlDocument != null && xmlDocument.DocumentElement.HasAttribute ("ToolsVersion");
1398                         }
1399                 }
1400                 
1401                 public string ToolsVersion {
1402                         get; internal set;
1403                 }
1404
1405                 internal Dictionary <string, BuildItemGroup> LastItemGroupContaining {
1406                         get { return last_item_group_containing; }
1407                 }
1408                 
1409                 internal static XmlNamespaceManager XmlNamespaceManager {
1410                         get {
1411                                 if (manager == null) {
1412                                         manager = new XmlNamespaceManager (new NameTable ());
1413                                         manager.AddNamespace ("tns", ns);
1414                                 }
1415                                 
1416                                 return manager;
1417                         }
1418                 }
1419                 
1420                 internal TaskDatabase TaskDatabase {
1421                         get { return taskDatabase; }
1422                 }
1423                 
1424                 internal XmlDocument XmlDocument {
1425                         get { return xmlDocument; }
1426                 }
1427                 
1428                 internal static string XmlNamespace {
1429                         get { return ns; }
1430                 }
1431
1432                 static string OS {
1433                         get {
1434                                 PlatformID pid = Environment.OSVersion.Platform;
1435                                 switch ((int)pid) {
1436                                 case 128:
1437                                 case 4:
1438                                         return "Unix";
1439                                 case 6:
1440                                         return "OSX";
1441                                 default:
1442                                         return "Windows_NT";
1443                                 }
1444                         }
1445                 }
1446
1447         }
1448 }
1449
1450 #endif