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