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