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