Allow targets from imported projects to be overridden.
[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                                 XmlReaderSettings settings = new XmlReaderSettings ();
713         
714                                 if (SchemaFile != null) {
715                                         settings.Schemas.Add (null, SchemaFile);
716                                         settings.ValidationType = ValidationType.Schema;
717                                         settings.ValidationEventHandler += new ValidationEventHandler (ValidationCallBack);
718                                 }
719         
720                                 XmlReader xmlReader = XmlReader.Create (textReader, settings);
721                                 xmlDocument.Load (xmlReader);
722
723                                 if (xmlDocument.DocumentElement.Name == "VisualStudioProject")
724                                         throw new InvalidProjectFileException (String.Format (
725                                                         "Project file '{0}' is a VS2003 project, which is not " +
726                                                         "supported by xbuild. You need to convert it to msbuild " +
727                                                         "format to build with xbuild.", fullFileName));
728
729                                 if (xmlDocument.DocumentElement.Name != "Project") {
730                                         throw new InvalidProjectFileException (String.Format (
731                                                 "The element <{0}> is unrecognized, or not supported in this context.", xmlDocument.DocumentElement.Name));
732                                 }
733         
734                                 if (xmlDocument.DocumentElement.GetAttribute ("xmlns") != ns) {
735                                         throw new InvalidProjectFileException (
736                                                 @"The default XML namespace of the project must be the MSBuild XML namespace." + 
737                                                 " If the project is authored in the MSBuild 2003 format, please add " +
738                                                 "xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\" to the <Project> element. " +
739                                                 "If the project has been authored in the old 1.0 or 1.2 format, please convert it to MSBuild 2003 format.  ");
740                                 }
741                                 ProcessXml ();
742                                 ParentEngine.AddLoadedProject (this);
743                         } catch (Exception e) {
744                                 throw new InvalidProjectFileException (String.Format ("{0}: {1}",
745                                                         fullFileName, e.Message), e);
746                         } finally {
747                                 if (textReader != null)
748                                         textReader.Close ();
749                         }
750                 }
751
752                 void Reevaluate ()
753                 {
754                         ProcessXml ();
755                 }
756
757                 void ProcessXml ()
758                 {
759                         groupingCollection = new GroupingCollection (this);
760                         imports = new ImportCollection (groupingCollection);
761                         usingTasks = new UsingTaskCollection (this);
762                         itemGroups = new BuildItemGroupCollection (groupingCollection);
763                         propertyGroups = new BuildPropertyGroupCollection (groupingCollection);
764                         targets = new TargetCollection (this);
765                         last_item_group_containing = new Dictionary <string, BuildItemGroup> ();
766                         
767                         taskDatabase = new TaskDatabase ();
768                         if (ParentEngine.DefaultTasksRegistered)
769                                 taskDatabase.CopyTasks (ParentEngine.DefaultTasks);     
770
771                         initialTargets = new List<string> ();
772                         defaultTargets = new string [0];
773                         PrepareForEvaluate ();
774                         ProcessElements (xmlDocument.DocumentElement, null);
775                         
776                         isDirty = false;
777                         Evaluate ();
778                 }
779
780                 void ProcessProjectAttributes (XmlAttributeCollection attributes)
781                 {
782                         foreach (XmlAttribute attr in attributes) {
783                                 switch (attr.Name) {
784                                 case "InitialTargets":
785                                         initialTargets.AddRange (attr.Value.Split (
786                                                                         new char [] {';', ' '},
787                                                                         StringSplitOptions.RemoveEmptyEntries));
788                                         break;
789                                 case "DefaultTargets":
790                                         // first non-empty DefaultTargets found is used
791                                         if (defaultTargets == null || defaultTargets.Length == 0)
792                                                 defaultTargets = attr.Value.Split (new char [] {';', ' '},
793                                                         StringSplitOptions.RemoveEmptyEntries);
794                                         EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectDefaultTargets",
795                                                                 DefaultTargets, PropertyType.Reserved));
796                                         break;
797                                 }
798                         }
799                 }
800
801                 internal void ProcessElements (XmlElement rootElement, ImportedProject ip)
802                 {
803                         ProcessProjectAttributes (rootElement.Attributes);
804                         foreach (XmlNode xn in rootElement.ChildNodes) {
805                                 if (xn is XmlElement) {
806                                         XmlElement xe = (XmlElement) xn;
807                                         switch (xe.Name) {
808                                         case "ProjectExtensions":
809                                                 AddProjectExtensions (xe);
810                                                 break;
811                                         case "Warning":
812                                         case "Message":
813                                         case "Error":
814                                                 AddMessage (xe);
815                                                 break;
816                                         case "Target":
817                                                 AddTarget (xe, ip);
818                                                 break;
819                                         case "UsingTask":
820                                                 AddUsingTask (xe, ip);
821                                                 break;
822                                         case "Import":
823                                                 AddImport (xe, ip);
824                                                 break;
825                                         case "ItemGroup":
826                                                 AddItemGroup (xe, ip);
827                                                 break;
828                                         case "PropertyGroup":
829                                                 AddPropertyGroup (xe, ip);
830                                                 break;
831                                         case  "Choose":
832                                                 AddChoose (xe);
833                                                 break;
834                                         default:
835                                                 throw new InvalidProjectFileException ("Invalid element in project file.");
836                                         }
837                                 }
838                         }
839                 }
840                 
841                 void PrepareForEvaluate ()
842                 {
843                         evaluatedItems = new BuildItemGroup (null, this, null, true);
844                         evaluatedItemsIgnoringCondition = new BuildItemGroup (null, this, null, true);
845                         evaluatedItemsByName = new Dictionary <string, BuildItemGroup> (StringComparer.InvariantCultureIgnoreCase);
846                         evaluatedItemsByNameIgnoringCondition = new Dictionary <string, BuildItemGroup> (StringComparer.InvariantCultureIgnoreCase);
847                         if (building && current_settings == BuildSettings.None)
848                                 RemoveBuiltTargets ();
849
850                         InitializeProperties ();
851                 }
852
853                 void Evaluate ()
854                 {
855                         groupingCollection.Evaluate ();
856
857                         //FIXME: UsingTasks aren't really evaluated. (shouldn't use expressions or anything)
858                         foreach (UsingTask usingTask in UsingTasks)
859                                 usingTask.Evaluate ();
860                 }
861
862                 // Removes entries of all earlier built targets for this project
863                 void RemoveBuiltTargets ()
864                 {
865                         foreach (string key in builtTargetKeys)
866                                 ParentEngine.BuiltTargetsOutputByName.Remove (key);
867                 }
868
869                 void InitializeProperties ()
870                 {
871                         BuildProperty bp;
872
873                         evaluatedProperties = new BuildPropertyGroup (null, null, null, true);
874
875                         foreach (BuildProperty gp in GlobalProperties) {
876                                 bp = new BuildProperty (gp.Name, gp.Value, PropertyType.Global);
877                                 EvaluatedProperties.AddProperty (bp);
878                         }
879                         
880                         foreach (BuildProperty gp in GlobalProperties)
881                                 ParentEngine.GlobalProperties.AddProperty (gp);
882
883                         // add properties that we dont have from parent engine's
884                         // global properties
885                         foreach (BuildProperty gp in ParentEngine.GlobalProperties) {
886                                 if (EvaluatedProperties [gp.Name] == null) {
887                                         bp = new BuildProperty (gp.Name, gp.Value, PropertyType.Global);
888                                         EvaluatedProperties.AddProperty (bp);
889                                 }
890                         }
891
892                         foreach (DictionaryEntry de in Environment.GetEnvironmentVariables ()) {
893                                 bp = new BuildProperty ((string) de.Key, (string) de.Value, PropertyType.Environment);
894                                 EvaluatedProperties.AddProperty (bp);
895                         }
896
897                         EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectFile", Path.GetFileName (fullFileName),
898                                                 PropertyType.Reserved));
899                         EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectName",
900                                                 Path.GetFileNameWithoutExtension (fullFileName),
901                                                 PropertyType.Reserved));
902                         EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildBinPath", parentEngine.BinPath, PropertyType.Reserved));
903                         EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildToolsPath", parentEngine.BinPath, PropertyType.Reserved));
904                         EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildExtensionsPath", ExtensionsPath, PropertyType.Reserved));
905                         EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectDefaultTargets", DefaultTargets, PropertyType.Reserved));
906
907                         // FIXME: make some internal method that will work like GetDirectoryName but output String.Empty on null/String.Empty
908                         string projectDir;
909                         if (FullFileName == String.Empty)
910                                 projectDir = Environment.CurrentDirectory;
911                         else
912                                 projectDir = Path.GetDirectoryName (FullFileName);
913
914                         EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectDirectory", projectDir, PropertyType.Reserved));
915                 }
916                 
917                 void AddProjectExtensions (XmlElement xmlElement)
918                 {
919                 }
920                 
921                 void AddMessage (XmlElement xmlElement)
922                 {
923                 }
924                 
925                 void AddTarget (XmlElement xmlElement, ImportedProject importedProject)
926                 {
927                         Target target = new Target (xmlElement, this, importedProject);
928                         targets.AddTarget (target);
929                         
930                         if (firstTargetName == null)
931                                 firstTargetName = target.Name;
932                 }
933                 
934                 void AddUsingTask (XmlElement xmlElement, ImportedProject importedProject)
935                 {
936                         UsingTask usingTask;
937
938                         usingTask = new UsingTask (xmlElement, this, importedProject);
939                         UsingTasks.Add (usingTask);
940                 }
941                 
942                 void AddImport (XmlElement xmlElement, ImportedProject importingProject)
943                 {
944                         // eval all the properties etc till the import
945                         Evaluate ();
946
947                         Import import = new Import (xmlElement, this, importingProject);
948                         Imports.Add (import);
949
950                         if (ConditionParser.ParseAndEvaluate (import.Condition, this))
951                                 import.Evaluate ();
952                 }
953                 
954                 void AddItemGroup (XmlElement xmlElement, ImportedProject importedProject)
955                 {
956                         BuildItemGroup big = new BuildItemGroup (xmlElement, this, importedProject, false);
957                         ItemGroups.Add (big);
958                 }
959                 
960                 void AddPropertyGroup (XmlElement xmlElement, ImportedProject importedProject)
961                 {
962                         BuildPropertyGroup bpg = new BuildPropertyGroup (xmlElement, this, importedProject, false);
963                         PropertyGroups.Add (bpg);
964                 }
965                 
966                 void AddChoose (XmlElement xmlElement)
967                 {
968                         BuildChoose bc = new BuildChoose (xmlElement, this);
969                         groupingCollection.Add (bc);
970                 }
971                 
972                 static void ValidationCallBack (object sender, ValidationEventArgs e)
973                 {
974                         Console.WriteLine ("Validation Error: {0}", e.Message);
975                 }
976                 
977                 public bool BuildEnabled {
978                         get {
979                                 return buildEnabled;
980                         }
981                         set {
982                                 buildEnabled = value;
983                         }
984                 }
985
986                 [MonoTODO]
987                 public Encoding Encoding {
988                         get { return encoding; }
989                 }
990
991                 public string DefaultTargets {
992                         get {
993                                 return String.Join ("; ", defaultTargets);
994                         }
995                         set {
996                                 xmlDocument.DocumentElement.SetAttribute ("DefaultTargets", value);
997                                 if (value != null)
998                                         defaultTargets = value.Split (new char [] {';', ' '},
999                                                         StringSplitOptions.RemoveEmptyEntries);
1000                         }
1001                 }
1002
1003                 public BuildItemGroup EvaluatedItems {
1004                         get {
1005                                 if (needToReevaluate) {
1006                                         needToReevaluate = false;
1007                                         Reevaluate ();
1008                                 }
1009                                 return evaluatedItems;
1010                         }
1011                 }
1012
1013                 public BuildItemGroup EvaluatedItemsIgnoringCondition {
1014                         get {
1015                                 if (needToReevaluate) {
1016                                         needToReevaluate = false;
1017                                         Reevaluate ();
1018                                 }
1019                                 return evaluatedItemsIgnoringCondition;
1020                         }
1021                 }
1022                 
1023                 internal IDictionary <string, BuildItemGroup> EvaluatedItemsByName {
1024                         get {
1025                                 // FIXME: do we need to do this here?
1026                                 if (needToReevaluate) {
1027                                         needToReevaluate = false;
1028                                         Reevaluate ();
1029                                 }
1030                                 return evaluatedItemsByName;
1031                         }
1032                 }
1033                 
1034                 internal IDictionary <string, BuildItemGroup> EvaluatedItemsByNameIgnoringCondition {
1035                         get {
1036                                 // FIXME: do we need to do this here?
1037                                 if (needToReevaluate) {
1038                                         needToReevaluate = false;
1039                                         Reevaluate ();
1040                                 }
1041                                 return evaluatedItemsByNameIgnoringCondition;
1042                         }
1043                 }
1044
1045                 // For batching implementation
1046                 Dictionary<string, BuildItemGroup> perBatchItemsByName;
1047                 Dictionary<string, BuildItemGroup> commonItemsByName;
1048
1049                 internal void SetBatchedItems (Dictionary<string, BuildItemGroup> perBatchItemsByName, Dictionary<string, BuildItemGroup> commonItemsByName)
1050                 {
1051                         this.perBatchItemsByName = perBatchItemsByName;
1052                         this.commonItemsByName = commonItemsByName;
1053                 }
1054
1055                 // Honors batching
1056                 internal bool TryGetEvaluatedItemByNameBatched (string itemName, out BuildItemGroup group)
1057                 {
1058                         if (perBatchItemsByName == null && commonItemsByName == null)
1059                                 return EvaluatedItemsByName.TryGetValue (itemName, out group);
1060
1061                         if (perBatchItemsByName != null)
1062                                 return perBatchItemsByName.TryGetValue (itemName, out group);
1063
1064                         if (commonItemsByName != null)
1065                                 return commonItemsByName.TryGetValue (itemName, out group);
1066
1067                         group = null;
1068                         return false;
1069                 }
1070
1071                 internal string GetMetadataBatched (string itemName, string metadataName)
1072                 {
1073                         BuildItemGroup group = null;
1074                         if (itemName == null) {
1075                                 //unqualified, all items in a batch(bucket) have the
1076                                 //same metadata values
1077                                 group = GetFirst<BuildItemGroup> (perBatchItemsByName.Values);
1078                                 if (group == null)
1079                                         group = GetFirst<BuildItemGroup> (commonItemsByName.Values);
1080                         } else {
1081                                 //qualified
1082                                 TryGetEvaluatedItemByNameBatched (itemName, out group);
1083                         }
1084
1085                         if (group != null) {
1086                                 foreach (BuildItem item in group) {
1087                                         if (item.HasMetadata (metadataName))
1088                                                 return item.GetEvaluatedMetadata (metadataName);
1089                                 }
1090                         }
1091                         return String.Empty;
1092                 }
1093
1094                 internal IEnumerable<BuildItemGroup> GetAllItemGroups ()
1095                 {
1096                         if (perBatchItemsByName == null && commonItemsByName == null)
1097                                 foreach (BuildItemGroup group in EvaluatedItemsByName.Values)
1098                                         yield return group;
1099
1100                         if (perBatchItemsByName != null)
1101                                 foreach (BuildItemGroup group in perBatchItemsByName.Values)
1102                                         yield return group;
1103
1104                         if (commonItemsByName != null)
1105                                 foreach (BuildItemGroup group in commonItemsByName.Values)
1106                                         yield return group;
1107                 }
1108
1109                 T GetFirst<T> (ICollection<T> list)
1110                 {
1111                         if (list == null)
1112                                 return default (T);
1113
1114                         foreach (T t in list)
1115                                 return t;
1116
1117                         return default (T);
1118                 }
1119
1120                 void LogWarning (string filename, string message, params object[] messageArgs)
1121                 {
1122                         BuildWarningEventArgs bwea = new BuildWarningEventArgs (
1123                                 null, null, filename, 0, 0, 0, 0, String.Format (message, messageArgs),
1124                                 null, null);
1125                         ParentEngine.EventSource.FireWarningRaised (this, bwea);
1126                 }
1127
1128                 static string ExtensionsPath {
1129                         get {
1130                                 if (extensions_path == null) {
1131                                         // NOTE: code from mcs/tools/gacutil/driver.cs
1132                                         PropertyInfo gac = typeof (System.Environment).GetProperty (
1133                                                         "GacPath", BindingFlags.Static | BindingFlags.NonPublic);
1134
1135                                         if (gac != null) {
1136                                                 MethodInfo get_gac = gac.GetGetMethod (true);
1137                                                 string gac_path = (string) get_gac.Invoke (null, null);
1138                                                 extensions_path = Path.GetFullPath (Path.Combine (
1139                                                                         gac_path, Path.Combine ("..", "xbuild")));
1140                                         }
1141                                 }
1142                                 return extensions_path;
1143                         }
1144                 }
1145
1146                 public BuildPropertyGroup EvaluatedProperties {
1147                         get {
1148                                 if (needToReevaluate) {
1149                                         needToReevaluate = false;
1150                                         Reevaluate ();
1151                                 }
1152                                 return evaluatedProperties;
1153                         }
1154                 }
1155
1156                 public string FullFileName {
1157                         get { return fullFileName; }
1158                         set { fullFileName = value; }
1159                 }
1160
1161                 public BuildPropertyGroup GlobalProperties {
1162                         get { return globalProperties; }
1163                         set {
1164                                 if (value == null)
1165                                         throw new ArgumentNullException ("value");
1166                                 
1167                                 if (value.FromXml)
1168                                         throw new InvalidOperationException ("GlobalProperties can not be set to persisted property group.");
1169                                 
1170                                 globalProperties = value;
1171                                 NeedToReevaluate ();
1172                         }
1173                 }
1174
1175                 public bool IsDirty {
1176                         get { return isDirty; }
1177                 }
1178
1179                 public bool IsValidated {
1180                         get { return isValidated; }
1181                         set { isValidated = value; }
1182                 }
1183
1184                 public BuildItemGroupCollection ItemGroups {
1185                         get { return itemGroups; }
1186                 }
1187                 
1188                 public ImportCollection Imports {
1189                         get { return imports; }
1190                 }
1191                 
1192                 public string InitialTargets {
1193                         get {
1194                                 return String.Join ("; ", initialTargets.ToArray ());
1195                         }
1196                         set {
1197                                 initialTargets.Clear ();
1198                                 xmlDocument.DocumentElement.SetAttribute ("InitialTargets", value);
1199                                 if (value != null)
1200                                         initialTargets.AddRange (value.Split (
1201                                                                 new char [] {';', ' '}, StringSplitOptions.RemoveEmptyEntries));
1202                         }
1203                 }
1204
1205                 public Engine ParentEngine {
1206                         get { return parentEngine; }
1207                 }
1208
1209                 public BuildPropertyGroupCollection PropertyGroups {
1210                         get { return propertyGroups; }
1211                 }
1212
1213                 public string SchemaFile {
1214                         get { return schemaFile; }
1215                         set { schemaFile = value; }
1216                 }
1217
1218                 public TargetCollection Targets {
1219                         get { return targets; }
1220                 }
1221
1222                 public DateTime TimeOfLastDirty {
1223                         get { return timeOfLastDirty; }
1224                 }
1225                 
1226                 public UsingTaskCollection UsingTasks {
1227                         get { return usingTasks; }
1228                 }
1229
1230                 [MonoTODO]
1231                 public string Xml {
1232                         get { return xmlDocument.InnerXml; }
1233                 }
1234
1235                 internal List<string> BuiltTargetKeys {
1236                         get { return builtTargetKeys; }
1237                 }
1238
1239                 internal Dictionary <string, BuildItemGroup> LastItemGroupContaining {
1240                         get { return last_item_group_containing; }
1241                 }
1242                 
1243                 internal static XmlNamespaceManager XmlNamespaceManager {
1244                         get {
1245                                 if (manager == null) {
1246                                         manager = new XmlNamespaceManager (new NameTable ());
1247                                         manager.AddNamespace ("tns", ns);
1248                                 }
1249                                 
1250                                 return manager;
1251                         }
1252                 }
1253                 
1254                 internal TaskDatabase TaskDatabase {
1255                         get { return taskDatabase; }
1256                 }
1257                 
1258                 internal XmlDocument XmlDocument {
1259                         get { return xmlDocument; }
1260                 }
1261                 
1262                 internal static string XmlNamespace {
1263                         get { return ns; }
1264                 }
1265         }
1266 }
1267
1268 #endif