Move from xbuild into mcs
[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 using System;
29 using System.Collections;
30 using System.Collections.Specialized;
31 using System.IO;
32 using System.Text;
33 using System.Xml;
34 using System.Xml.Schema;
35 using Microsoft.Build.Framework;
36 using Mono.XBuild.Shared;
37
38 namespace Microsoft.Build.BuildEngine {
39         public class Project : IProject {
40                 static string separator = ";";
41         
42                 bool                            buildEnabled;
43                 IDictionary                     conditionedProperties;
44                 Encoding                                currentEncoding;
45                 string[]                        defaultTargets;
46                 IList                           directlyImportedProjects;
47                 BuildPropertyGroup              environmentProperties;
48                 BuildItemGroup                  evaluatedItems;
49                 BuildItemGroup                  evaluatedItemsIgnoringCondition;
50                 IDictionary                     evaluatedItemsByName;
51                 IDictionary                     evaluatedItemsByNameIgnoringCondition;
52                 BuildPropertyGroup              evaluatedProperties;
53                 string                          firstTargetName;
54                 string                          fullFileName;
55                 BuildPropertyGroup              globalProperties;
56                 GroupingCollection              groups;
57                 bool                            isDirty;
58                 bool                            isValidated;
59                 bool                            isReset;
60                 BuildItemGroupCollection        itemGroups;
61                 IDictionary                     importedProjects;
62                 Engine                          parentEngine;
63                 BuildPropertyGroupCollection    propertyGroups;
64                 BuildPropertyGroup              reservedProperties;
65                 string                          schemaFile;
66                 TaskDatabase                    taskDatabase;
67                 TargetCollection                targets;
68                 DateTime                        timeOfLastDirty;
69                 IList                           usingTaskElements;
70                 XmlDocument                     xmlDocument;
71                 XmlElement                      xmlElement;
72
73                 public Project ()
74                         : this (null)
75                 {
76                 }
77
78                 public Project (Engine engine)
79                 {
80                         parentEngine  = engine;
81                         xmlDocument = new XmlDocument ();
82                         evaluatedItems = new BuildItemGroup (this);
83                         evaluatedItemsByName = new Hashtable (new CaseInsensitiveHashCodeProvider (), new CaseInsensitiveComparer ());
84                         evaluatedItemsByNameIgnoringCondition = new Hashtable (new CaseInsensitiveHashCodeProvider (), new CaseInsensitiveComparer ());
85                         evaluatedItemsIgnoringCondition = new BuildItemGroup (this);
86                         evaluatedProperties = new BuildPropertyGroup (false, null);
87                         groups = new GroupingCollection ();
88                         itemGroups = new BuildItemGroupCollection (groups);
89                         propertyGroups = new BuildPropertyGroupCollection (groups);
90                         targets = new TargetCollection (this);
91                         usingTaskElements = new ArrayList ();
92                         taskDatabase = new TaskDatabase ();
93                 }
94
95                 public void AddNewImport (string importLocation,
96                                           string importCondition)
97                 {
98                 }
99
100                 public BuildItem AddNewItem (string itemName,
101                                              string itemInclude)
102                 {
103                         // add it to rawitemgroups, maybe to evaluated items then?
104                         return null;
105                 }
106
107                 public BuildItemGroup AddNewItemGroup ()
108                 {
109                         return null;
110                 }
111
112                 public BuildPropertyGroup AddNewPropertyGroup (bool insertAtEndOfProject)
113                 {
114                         return null;
115                 }
116
117                 public bool Build (string[] targetNamesToBuild,
118                                    IDictionary targetOutputs)
119                 {
120                         if (targetNamesToBuild.Length == 0) {
121                                 if (defaultTargets.Length != 0) {
122                                         targetNamesToBuild = defaultTargets;
123                                 }
124                                 else if (firstTargetName != null) {
125                                         targetNamesToBuild = new string [1] { firstTargetName};
126                                 }
127                                 else
128                                         return false;
129                         }
130                         foreach (string target in targetNamesToBuild) {
131                                 if (BuildTarget (target, targetOutputs) == false) {
132                                         return false;
133                                 }
134                         }
135                         return true;
136                 }
137
138                 public bool BuildTarget (string targetName,
139                                          IDictionary targetOutputs)
140                 {
141                         return BuildTargetWithFlags (targetName, targetOutputs, BuildSettings.None);
142                 }
143
144                 public bool BuildTargetWithFlags (string targetName,
145                                                   IDictionary targetOutputs,
146                                                   BuildSettings buildFlags)
147                 {
148                         if (targets.Exists (targetName) == false)
149                                 throw new Exception ("Target specified to build does not exist.");
150                         
151                         this.targets [targetName].Build ();
152                         return true;
153                 }
154                 
155                 public string[] GetConditionedPropertyValues (string propertyName)
156                 {
157                         StringCollection sc = (StringCollection) conditionedProperties [propertyName];
158                         string[] propertyValues = new string [sc.Count];
159                         int i  = 0;
160                         foreach (string propertyValue in sc)
161                                 propertyValues [i++] = propertyValue;
162                         return propertyValues;
163                 }
164
165                 public string[] GetDirectlyImportedProjects ()
166                 {
167                         string[] dip = new string [directlyImportedProjects.Count];
168                         int i = 0;
169                         foreach (string importedProject in directlyImportedProjects)
170                                 dip [i++] = importedProject;
171                         return dip;
172                 }
173
174                 public BuildItemGroup GetEvaluatedItemsByName (string itemName)
175                 {
176                         return (BuildItemGroup) evaluatedItemsByName [itemName];
177                 }
178
179                 public BuildItemGroup GetEvaluatedItemsByNameIgnoringCondition (string itemName)
180                 {
181                         return (BuildItemGroup) evaluatedItemsByNameIgnoringCondition [itemName];
182                 }
183
184                 public string GetEvaluatedProperty (string propertyName)
185                 {
186                         return evaluatedProperties [propertyName];
187                 }
188
189                 public string[] GetNonImportedItemNames ()
190                 {
191                         return null;
192                 }
193
194                 public string[] GetNonImportedPropertyNames ()
195                 {
196                         return null;
197                 }
198
199                 public string[] GetNonImportedTargetNames ()
200                 {
201                         ArrayList temporaryNonImportedTargets = new ArrayList ();
202                         foreach (Target target in targets)
203                                 if (target.IsImported == false)
204                                         temporaryNonImportedTargets.Add (target);
205                         string[] nonImportedTargetNames = new string [temporaryNonImportedTargets.Count];
206                         int i = 0;
207                         foreach (Target target in temporaryNonImportedTargets)
208                                 nonImportedTargetNames [i++] = target.Name;
209                         return nonImportedTargetNames;
210                 }
211
212                 public string[] GetNonImportedUsingTasks ()
213                 {
214                         return null;
215                 }
216
217                 public string GetProjectExtensions (string id)
218                 {
219                         return null;
220                 }
221
222                 public void LoadFromFile (string projectFileName)
223                 {
224                         this.fullFileName = Path.GetFullPath (projectFileName);
225                         XmlSchemaCollection xmlSchemaCollection = null;
226                         XmlTextReader xmlTextReader = null;
227                         XmlValidatingReader xmlValidatingReader = null;
228                         
229                         if (this.schemaFile != null) {
230                                 xmlSchemaCollection = new XmlSchemaCollection ();
231                                 xmlSchemaCollection.ValidationEventHandler += new ValidationEventHandler (ValidationCallBack);
232                                 xmlSchemaCollection.Add (null, this.schemaFile);
233                                 if (xmlSchemaCollection.Count > 0) {
234                                         xmlTextReader = new XmlTextReader (projectFileName);
235                                         xmlValidatingReader = new XmlValidatingReader (xmlTextReader);
236                                         xmlValidatingReader.ValidationType = ValidationType.Schema;
237                                         xmlValidatingReader.Schemas.Add (xmlSchemaCollection);
238                                         xmlValidatingReader.ValidationEventHandler += new ValidationEventHandler (ValidationCallBack);
239                                 }
240                         } else {
241                                 xmlTextReader = new XmlTextReader (projectFileName);
242                         }
243                         if (xmlValidatingReader != null)
244                                 xmlDocument.Load (xmlValidatingReader);
245                         else if (xmlTextReader != null)
246                                 xmlDocument.Load (xmlTextReader);
247                         else
248                                 throw new Exception ();
249                         xmlElement = xmlDocument.DocumentElement;
250                         if (xmlElement.Name != "Project")
251                                 throw new InvalidProjectFileException ("Invalid root element.");
252                         if (xmlElement.GetAttributeNode ("DefaultTargets") != null)
253                                 defaultTargets = xmlElement.GetAttribute ("DefaultTargets").Split (';');
254                         else
255                                 defaultTargets = new string [0];
256                         
257                         ProcessElements (xmlElement, null);
258                         
259                         isDirty = false;
260                 }
261
262                 public void LoadFromXml (XmlDocument projectXml)
263                 {
264                         fullFileName = "";
265                         xmlDocument = projectXml;
266                         xmlElement = xmlDocument.DocumentElement;
267                         if (xmlElement.Name != "Project")
268                                 throw new InvalidProjectFileException ("Invalid root element.");
269                         if (xmlElement.GetAttributeNode ("DefaultTargets") != null)
270                                 defaultTargets = xmlElement.GetAttribute ("DefaultTargets").Split (';');
271                         else
272                                 defaultTargets = new string [0];
273                         
274                         ProcessElements (xmlElement, null);
275                         
276                         isDirty = false;
277                 }
278
279                 public void MarkProjectAsDirty ()
280                 {
281                         isDirty = true;
282                 }
283
284                 public void RemoveAllItemGroups ()
285                 {
286                 }
287
288                 public void RemoveAllItemsGroupsByCondition (string condition)
289                 {
290                 }
291
292                 public void RemoveAllPropertyGroups ()
293                 {
294                 }
295
296                 public void RemoveAllPropertyGroupsByCondition (string condition)
297                 {
298                 }
299
300                 public void RemoveItem (BuildItem itemToRemove)
301                 {
302                 }
303
304                 public void RemoveItemGroup (BuildItemGroup itemGroupToRemove)
305                 {
306                 }
307
308                 public void RemoveItemsByName (string itemName)
309                 {
310                 }
311
312                 public void RemovePropertyGroup (BuildPropertyGroup propertyGroupToRemove)
313                 {
314                         
315                 }
316
317                 public void ResetBuildStatus ()
318                 {
319                 }
320
321                 public void SaveToFile (string projectFileName)
322                 {
323                         xmlDocument.Save (projectFileName);
324                 }
325
326                 public void SaveToFile (string projectFileName,
327                                         ProjectFileEncoding encoding)
328                 {
329                         SaveToFile (projectFileName);
330                 }
331
332                 public void SaveToTextWriter (TextWriter outTextWriter)
333                 {
334                         xmlDocument.Save (outTextWriter);
335                 }
336
337                 public void SetImportedProperty (string propertyName,
338                                                  string propertyValue,
339                                                  string condition,
340                                                  Project importProject)
341                 {
342                 }
343
344                 public void SetImportedPropertyAt (string propertyName,
345                                                    string propertyValue,
346                                                    string condition,
347                                                    Project importedProject,
348                                                    PropertyPosition position)
349                 {
350                 }
351
352                 public void SetProjectExtensions (string id, string xmlText)
353                 {
354                 }
355
356                 public void SetProperty (string propertyName,
357                                          string propertyValue,
358                                          string condition)
359                 {
360                 }
361
362                 public void SetPropertyAt (string propertyName,
363                                            string propertyValue,
364                                            string condition,
365                                            PropertyPosition position)
366                 {
367                 }
368
369                 public void Unload ()
370                 {
371                 }
372                 
373                 private void ProcessElements (XmlElement rootElement, ImportedProject ip)
374                 {
375                         foreach (XmlNode xn in rootElement.ChildNodes) {
376                                 if (xn is XmlElement) {
377                                         XmlElement xe = (XmlElement) xn;
378                                         switch (xe.Name) {
379                                         case "ProjectExtensions":
380                                                 AddProjectExtensions (xe);
381                                                 break;
382                                         case "Warning":
383                                         case "Message":
384                                         case "Error":
385                                                 AddMessage (xe);
386                                                 break;
387                                         case "Target":
388                                                 AddTarget (xe, ip);
389                                                 break;
390                                         case "UsingTask":
391                                                 AddUsingTask (xe, ip);
392                                                 break;
393                                         case "Import":
394                                                 AddImport (xe, ip);
395                                                 break;
396                                         case "ItemGroup":
397                                                 AddItemGroup (xe);
398                                                 break;
399                                         case "PropertyGroup":
400                                                 AddPropertyGroup (xe);
401                                                 break;
402                                         case  "Choose":
403                                                 AddChoose (xe);
404                                                 break;
405                                         default:
406                                                 throw new InvalidProjectFileException ("Invalid element in project file.");
407                                         }
408                                 }
409                         }
410                 }
411                 
412                 private void AddProjectExtensions (XmlElement xmlElement)
413                 {
414                         if (xmlElement == null)
415                                 throw new ArgumentNullException ("xmlElement");
416                 }
417                 
418                 private void AddMessage (XmlElement xmlElement)
419                 {
420                         if (xmlElement == null)
421                                 throw new ArgumentNullException ("xmlElement");
422                 }
423                 
424                 private void AddTarget (XmlElement xmlElement, ImportedProject importedProject)
425                 {
426                         if (xmlElement == null)
427                                 throw new ArgumentNullException ("xmlElement");
428                         Target target = targets.AddNewTarget (xmlElement.GetAttribute ("Name"));
429                         target.BindToXml (xmlElement);
430                         if (importedProject == null) {
431                                 target.IsImported = false;
432                                 if (firstTargetName == null)
433                                         firstTargetName = target.Name;
434                         } else
435                                 target.IsImported = true;
436                 }
437                 
438                 private void AddUsingTask (XmlElement xmlElement, ImportedProject importedProject)
439                 {
440                         if (xmlElement == null)
441                                 throw new ArgumentNullException ("xmlElement");
442                                 
443                         if (xmlElement.GetAttribute ("TaskName") == String.Empty)
444                                 throw new InvalidProjectFileException ("TaskName attribute must be specified.");
445
446                         usingTaskElements.Add (xmlElement);
447
448                         AssemblyLoadInfo loadInfo = null;
449                         string filename = null;
450                         string name = null;
451                         string taskName = xmlElement.GetAttribute ("TaskName");
452                         
453                         if (xmlElement.GetAttribute ("AssemblyName") != String.Empty) {
454                                 name  = xmlElement.GetAttribute ("AssemblyName");
455                                 loadInfo  = new AssemblyLoadInfo (name, taskName);
456                                 taskDatabase.RegisterTask (taskName, loadInfo);
457                         } else if (xmlElement.GetAttribute ("AssemblyFile") != String.Empty) {
458                                 filename = xmlElement.GetAttribute ("AssemblyFile");
459                                 if (Path.IsPathRooted (filename) == false) {
460                                         if (importedProject == null)
461                                                 filename = Path.Combine (Path.GetDirectoryName (fullFileName), filename);
462                                         else
463                                                 filename = Path.Combine (Path.GetDirectoryName (importedProject.FullFileName), filename);
464                                 }
465                                 loadInfo  = new AssemblyLoadInfo (LoadInfoType.AssemblyFilename, filename, null, null, null, null, taskName);
466                                 taskDatabase.RegisterTask (taskName, loadInfo);
467                         } else
468                                 throw new InvalidProjectFileException ("AssemblyName or AssemblyFile attribute must be specified.");
469                 }
470                 
471                 private void AddImport (XmlElement xmlElement, ImportedProject importingProject)
472                 {
473                         if (xmlElement == null)
474                                 throw new ArgumentNullException ("xmlElement");
475                         
476                         string importedFile;
477                         Expression importedFileExpr;
478                         ImportedProject ImportedProject;
479
480                         importedFileExpr = new Expression (this, xmlElement.GetAttribute ("Project"));
481                         importedFile = (string) importedFileExpr.ToNonArray (typeof (string));
482                         
483                         if (importedFile == String.Empty)
484                                 throw new InvalidProjectFileException ("Project attribute must be specified.");
485                         
486                         if (Path.IsPathRooted (importedFile) == false) {
487                                 if (importingProject == null)
488                                         importedFile = Path.Combine (Path.GetDirectoryName (fullFileName), importedFile);
489                                 else
490                                         importedFile = Path.Combine (Path.GetDirectoryName (importingProject.FullFileName), importedFile);
491                         }
492                         
493                         ImportedProject importedProject = new ImportedProject ();
494                         try {
495                                 importedProject.Load (importedFile);
496                                 ProcessElements (importedProject.XmlDocument.DocumentElement, importedProject);
497                         }
498                         catch (Exception ex) {
499                                 Console.WriteLine (ex);
500                         }
501                 }
502                 
503                 private void AddItemGroup (XmlElement xmlElement)
504                 {
505                         if (xmlElement == null)
506                                 throw new ArgumentNullException ("xmlElement");
507                         BuildItemGroup big = new BuildItemGroup (this);
508                         big.BindToXml (xmlElement);
509                         itemGroups.Add (big);
510                 }
511                 
512                 private void AddPropertyGroup (XmlElement xmlElement)
513                 {
514                         if (xmlElement == null)
515                                 throw new ArgumentNullException ("xmlElement");
516                         BuildPropertyGroup bpg = new BuildPropertyGroup (true, this);
517                         bpg.BindToXml (xmlElement);
518                         propertyGroups.Add (bpg);
519                 }
520                 
521                 private void AddChoose (XmlElement xmlElement)
522                 {
523                         if (xmlElement == null)
524                                 throw new ArgumentNullException ("xmlElement");
525                 }
526                 
527                 private static void ValidationCallBack (object sender, ValidationEventArgs e)
528                 {
529                         Console.WriteLine ("Validation Error: {0}", e.Message);
530                 }
531                 
532                 public bool BuildEnabled {
533                         get {
534                                 return buildEnabled;
535                         }
536                         set {
537                                 buildEnabled = value;
538                         }
539                 }
540
541                 public ProjectFileEncoding CurrentProjectFileEncoding {
542                         get {
543                                 if (currentEncoding == Encoding.UTF8)
544                                         return ProjectFileEncoding.Utf8;
545                                 if (currentEncoding == Encoding.Unicode)
546                                         return ProjectFileEncoding.Unicode;
547                                 if (currentEncoding == Encoding.GetEncoding ("ANSI"))
548                                         return ProjectFileEncoding.Ansi;
549                                 throw new Exception ();
550                         }
551                 }
552
553                 public string DefaultTargets {
554                         get { return xmlElement.GetAttribute ("DefaultTargets"); }
555                         set {
556                                 xmlElement.SetAttribute ("DefaultTargets",value);
557                                 defaultTargets = value.Split (';');
558                         }
559                 }
560
561                 public BuildItemGroup EvaluatedItems {
562                         get { return evaluatedItems; }
563                 }
564
565                 public BuildItemGroup EvaluatedItemsIgnoringCondition {
566                         get { return evaluatedItemsIgnoringCondition; }
567                 }
568                 
569                 internal IDictionary EvaluatedItemsByName {
570                         get { return evaluatedItemsByName; }
571                 }
572                 
573                 internal IDictionary EvaluatedItemsByNameIgnoringCondition {
574                         get { return evaluatedItemsByNameIgnoringCondition; }
575                 }
576
577                 public BuildPropertyGroup EvaluatedProperties {
578                         get { return evaluatedProperties; }
579                 }
580
581                 public string FullFileName {
582                         get { return fullFileName; }
583                         set { fullFileName = value; }
584                 }
585
586                 public BuildPropertyGroup GlobalProperties {
587                         get { return globalProperties; }
588                         set {
589                                 globalProperties = value;
590                                 foreach (BuildProperty bp in globalProperties)
591                                         evaluatedProperties.AddFromExistingProperty (bp);
592                         }
593                 }
594
595                 public bool IsDirty {
596                         get { return isDirty; }
597                 }
598
599                 public bool IsValidated {
600                         get { return isValidated; }
601                         set { isValidated = value; }
602                 }
603
604                 public BuildItemGroupCollection ItemGroups {
605                         get { return itemGroups; }
606                 }
607
608                 public Engine ParentEngine {
609                         get { return parentEngine; }
610                 }
611
612                 public BuildPropertyGroupCollection PropertyGroups {
613                         get { return propertyGroups; }
614                 }
615
616                 public string SchemaFile {
617                         get { return schemaFile; }
618                         set { schemaFile = value; }
619                 }
620
621                 public TargetCollection Targets {
622                         get { return targets; }
623                 }
624
625                 public DateTime TimeOfLastDirty {
626                         get { return timeOfLastDirty; }
627                 }
628
629                 public XmlDocument Xml {
630                         get { return xmlDocument; }
631                 }
632                 
633                 internal TaskDatabase TaskDatabase {
634                         get { return taskDatabase; }
635                 }
636                 
637                 internal BuildPropertyGroup EnvironmentProperties {
638                         set {
639                                 environmentProperties = value;
640                                 foreach (BuildProperty bp in environmentProperties)
641                                         evaluatedProperties.AddFromExistingProperty (bp);
642                         }
643                 }
644                 
645                 internal BuildPropertyGroup ReservedProperties {
646                         set {
647                                 reservedProperties = value;
648                                 foreach (BuildProperty bp in reservedProperties)
649                                         evaluatedProperties.AddFromExistingProperty (bp);
650                         }
651                 }
652         }
653 }