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