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