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