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