Altered expression parser to hand-written one (which was kind of anticipated) for...
[mono.git] / mcs / class / Microsoft.Build / Microsoft.Build.Evaluation / Project.cs
1 //
2 // Project.cs
3 //
4 // Author:
5 //   Leszek Ciesielski (skolima@gmail.com)
6 //   Rolf Bjarne Kvinge (rolf@xamarin.com)
7 //   Atsushi Enomoto (atsushi@xamarin.com)
8 //
9 // (C) 2011 Leszek Ciesielski
10 // Copyright (C) 2011,2013 Xamarin Inc. (http://www.xamarin.com)
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32 using System;
33 using System.Collections.Generic;
34 using System.Diagnostics;
35 using System.IO;
36 using System.Linq;
37 using System.Text;
38 using System.Xml;
39 using Microsoft.Build.Construction;
40 using Microsoft.Build.Internal;
41 using Microsoft.Build.Execution;
42 using Microsoft.Build.Framework;
43 using Microsoft.Build.Logging;
44 using System.Collections;
45
46 namespace Microsoft.Build.Evaluation
47 {
48         [DebuggerDisplay ("{FullPath} EffectiveToolsVersion={ToolsVersion} #GlobalProperties="
49         + "{data.globalProperties.Count} #Properties={data.Properties.Count} #ItemTypes="
50         + "{data.ItemTypes.Count} #ItemDefinitions={data.ItemDefinitions.Count} #Items="
51         + "{data.Items.Count} #Targets={data.Targets.Count}")]
52         public class Project
53         {
54                 public Project (XmlReader xml)
55                         : this (ProjectRootElement.Create (xml))
56                 {
57                 }
58
59                 public Project (XmlReader xml, IDictionary<string, string> globalProperties,
60                                               string toolsVersion)
61                         : this (ProjectRootElement.Create (xml), globalProperties, toolsVersion)
62                 {
63                 }
64
65                 public Project (XmlReader xml, IDictionary<string, string> globalProperties,
66                                               string toolsVersion, ProjectCollection projectCollection)
67                         : this (ProjectRootElement.Create (xml), globalProperties, toolsVersion, projectCollection)
68                 {
69                 }
70
71                 public Project (XmlReader xml, IDictionary<string, string> globalProperties,
72                                               string toolsVersion, ProjectCollection projectCollection,
73                                               ProjectLoadSettings loadSettings)
74                         : this (ProjectRootElement.Create (xml), globalProperties, toolsVersion, projectCollection, loadSettings)
75                 {
76                 }
77
78                 public Project (ProjectRootElement xml) : this (xml, null, null)
79                 {
80                 }
81
82                 public Project (ProjectRootElement xml, IDictionary<string, string> globalProperties,
83                                               string toolsVersion)
84                         : this (xml, globalProperties, toolsVersion, ProjectCollection.GlobalProjectCollection)
85                 {
86                 }
87
88                 public Project (ProjectRootElement xml, IDictionary<string, string> globalProperties,
89                                               string toolsVersion, ProjectCollection projectCollection)
90                         : this (xml, globalProperties, toolsVersion, projectCollection, ProjectLoadSettings.Default)
91                 {
92                 }
93
94                 public Project (ProjectRootElement xml, IDictionary<string, string> globalProperties,
95                                               string toolsVersion, ProjectCollection projectCollection,
96                                               ProjectLoadSettings loadSettings)
97                 {
98                         if (projectCollection == null)
99                                 throw new ArgumentNullException ("projectCollection");
100                         this.Xml = xml;
101                         this.GlobalProperties = globalProperties ?? new Dictionary<string, string> ();
102                         this.ToolsVersion = toolsVersion;
103                         this.ProjectCollection = projectCollection;
104                         this.load_settings = loadSettings;
105
106                         Initialize ();
107                 }
108
109                 public Project (string projectFile)
110                         : this (projectFile, null, null)
111                 {
112                 }
113
114                 public Project (string projectFile, IDictionary<string, string> globalProperties,
115                                 string toolsVersion)
116                 : this (projectFile, globalProperties, toolsVersion, ProjectCollection.GlobalProjectCollection, ProjectLoadSettings.Default)
117                 {
118                 }
119
120                 public Project (string projectFile, IDictionary<string, string> globalProperties,
121                                 string toolsVersion, ProjectCollection projectCollection)
122                 : this (projectFile, globalProperties, toolsVersion, projectCollection, ProjectLoadSettings.Default)
123                 {
124                 }
125
126                 public Project (string projectFile, IDictionary<string, string> globalProperties,
127                                 string toolsVersion, ProjectCollection projectCollection,
128                                 ProjectLoadSettings loadSettings)
129                         : this (ProjectRootElement.Create (projectFile), globalProperties, toolsVersion, projectCollection, loadSettings)
130                 {
131                 }
132
133                 ProjectLoadSettings load_settings;
134
135                 public IDictionary<string, string> GlobalProperties { get; private set; }
136
137                 public ProjectCollection ProjectCollection { get; private set; }
138
139                 public string ToolsVersion { get; private set; }
140
141                 public ProjectRootElement Xml { get; private set; }
142
143                 string dir_path;
144                 Dictionary<string, ProjectItemDefinition> item_definitions;
145                 List<ResolvedImport> raw_imports;
146                 List<ProjectItem> raw_items;
147                 List<ProjectItem> all_evaluated_items;
148                 List<string> item_types;
149                 List<ProjectProperty> properties;
150                 Dictionary<string, ProjectTargetInstance> targets;
151
152                 void Initialize ()
153                 {
154                         dir_path = Directory.GetCurrentDirectory ();
155                         raw_imports = new List<ResolvedImport> ();
156                         item_definitions = new Dictionary<string, ProjectItemDefinition> ();
157                         item_types = new List<string> ();
158                         properties = new List<ProjectProperty> ();
159                         targets = new Dictionary<string, ProjectTargetInstance> ();
160                         raw_items = new List<ProjectItem> ();
161                         
162                         ProcessXml ();
163                 }
164                 
165                 void ProcessXml ()
166                 {
167                         foreach (DictionaryEntry p in Environment.GetEnvironmentVariables ())
168                                 this.properties.Add (new EnvironmentProjectProperty (this, (string)p.Key, (string)p.Value));
169                         foreach (var p in GlobalProperties)
170                                 this.properties.Add (new GlobalProjectProperty (this, p.Key, p.Value));
171
172                         all_evaluated_items = new List<ProjectItem> ();
173                         foreach (var child in Xml.Children) {
174                                 var pge = child as ProjectPropertyGroupElement;
175                                 if (pge != null)
176                                         foreach (var p in pge.Properties)
177                                                 this.properties.Add (new XmlProjectProperty (this, p, PropertyType.Normal));
178                                 var ige = child as ProjectItemGroupElement;
179                                 if (ige != null) {
180                                         foreach (var p in ige.Items) {
181                                                 var item = new ProjectItem (this, p);
182                                                 this.raw_items.Add (item);
183                                                 if (ShouldInclude (ige.Condition))
184                                                         all_evaluated_items.Add (item);
185                                         }
186                                 }
187                                 var def = child as ProjectItemDefinitionGroupElement;
188                                 if (def != null) {
189                                         foreach (var p in def.ItemDefinitions) {
190                                                 if (ShouldInclude (p.Condition)) {
191                                                         ProjectItemDefinition existing;
192                                                         if (!item_definitions.TryGetValue (p.ItemType, out existing))
193                                                                 item_definitions.Add (p.ItemType, (existing = new ProjectItemDefinition (this, p.ItemType)));
194                                                         existing.AddItems (p);
195                                                 }
196                                         }
197                                 }
198                         }
199                         all_evaluated_items.Sort ((p1, p2) => string.Compare (p1.ItemType, p2.ItemType, StringComparison.OrdinalIgnoreCase));
200                 }
201
202                 public ICollection<ProjectItem> GetItemsIgnoringCondition (string itemType)
203                 {
204                         return new CollectionFromEnumerable<ProjectItem> (raw_items.Where (p => p.ItemType.Equals (itemType, StringComparison.OrdinalIgnoreCase)));
205                 }
206
207                 public void RemoveItems (IEnumerable<ProjectItem> items)
208                 {
209                         var removal = new List<ProjectItem> (items);
210                         foreach (var item in removal) {
211                                 var parent = item.Xml.Parent;
212                                 parent.RemoveChild (item.Xml);
213                                 if (parent.Count == 0)
214                                         parent.Parent.RemoveChild (parent);
215                         }
216                 }
217
218                 static readonly Dictionary<string, string> empty_metadata = new Dictionary<string, string> ();
219
220                 public IList<ProjectItem> AddItem (string itemType, string unevaluatedInclude)
221                 {
222                         return AddItem (itemType, unevaluatedInclude, empty_metadata);
223                 }
224
225                 public IList<ProjectItem> AddItem (string itemType, string unevaluatedInclude,
226                                 IEnumerable<KeyValuePair<string, string>> metadata)
227                 {
228                         // FIXME: needs several check that AddItemFast() does not process (see MSDN for details).
229
230                         return AddItemFast (itemType, unevaluatedInclude, metadata);
231                 }
232
233                 public IList<ProjectItem> AddItemFast (string itemType, string unevaluatedInclude)
234                 {
235                         return AddItemFast (itemType, unevaluatedInclude, empty_metadata);
236                 }
237
238                 public IList<ProjectItem> AddItemFast (string itemType, string unevaluatedInclude,
239                                                                      IEnumerable<KeyValuePair<string, string>> metadata)
240                 {
241                         throw new NotImplementedException ();
242                 }
243
244                 public bool Build ()
245                 {
246                         return Build (Xml.DefaultTargets.Split (';'));
247                 }
248
249                 public bool Build (IEnumerable<ILogger> loggers)
250                 {
251                         return Build (Xml.DefaultTargets.Split (';'), loggers);
252                 }
253
254                 public bool Build (string target)
255                 {
256                         return string.IsNullOrWhiteSpace (target) ? Build () : Build (new string [] {target});
257                 }
258
259                 public bool Build (string[] targets)
260                 {
261                         return Build (targets, new ILogger [0]);
262                 }
263
264                 public bool Build (ILogger logger)
265                 {
266                         return Build (Xml.DefaultTargets.Split (';'), new ILogger [] {logger});
267                 }
268
269                 public bool Build (string[] targets, IEnumerable<ILogger> loggers)
270                 {
271                         return Build (targets, loggers, new ForwardingLoggerRecord [0]);
272                 }
273
274                 public bool Build (IEnumerable<ILogger> loggers, IEnumerable<ForwardingLoggerRecord> remoteLoggers)
275                 {
276                         return Build (Xml.DefaultTargets.Split (';'), loggers, remoteLoggers);
277                 }
278
279                 public bool Build (string target, IEnumerable<ILogger> loggers)
280                 {
281                         return Build (new string [] { target }, loggers);
282                 }
283
284                 public bool Build (string[] targets, IEnumerable<ILogger> loggers, IEnumerable<ForwardingLoggerRecord> remoteLoggers)
285                 {
286                         // Unlike ProjectInstance.Build(), there is no place to fill outputs by targets, so ignore them
287                         // (i.e. we don't use the overload with output).
288                         //
289                         // This does not check FullPath, so don't call GetProjectInstanceForBuild() directly.
290                         return new BuildManager ().GetProjectInstanceForBuildInternal (this).Build (targets, loggers, remoteLoggers);
291                 }
292
293                 public bool Build (string target, IEnumerable<ILogger> loggers, IEnumerable<ForwardingLoggerRecord> remoteLoggers)
294                 {
295                         return Build (new string [] { target }, loggers, remoteLoggers);
296                 }
297
298                 public ProjectInstance CreateProjectInstance ()
299                 {
300                         var ret = new ProjectInstance (Xml, GlobalProperties, ToolsVersion, ProjectCollection);
301                         // FIXME: maybe fill other properties to the result.
302                         return ret;
303                 }
304                 
305                 bool ShouldInclude (string unexpandedValue)
306                 {
307                         return string.IsNullOrWhiteSpace (unexpandedValue) || new ExpressionEvaluator (this).EvaluateAsBoolean (unexpandedValue);
308                 }
309
310                 public string ExpandString (string unexpandedValue)
311                 {
312                         return new ExpressionEvaluator (this).Evaluate (unexpandedValue);
313                 }
314
315                 public static string GetEvaluatedItemIncludeEscaped (ProjectItem item)
316                 {
317                         return ProjectCollection.Escape (item.EvaluatedInclude);
318                 }
319
320                 public static string GetEvaluatedItemIncludeEscaped (ProjectItemDefinition item)
321                 {
322                         throw new NotImplementedException ();
323                 }
324
325                 public ICollection<ProjectItem> GetItems (string itemType)
326                 {
327                         return new CollectionFromEnumerable<ProjectItem> (Items.Where (p => p.ItemType.Equals (itemType, StringComparison.OrdinalIgnoreCase)));
328                 }
329
330                 public ICollection<ProjectItem> GetItemsByEvaluatedInclude (string evaluatedInclude)
331                 {
332                         return new CollectionFromEnumerable<ProjectItem> (Items.Where (p => p.EvaluatedInclude.Equals (evaluatedInclude, StringComparison.OrdinalIgnoreCase)));
333                 }
334
335                 public IEnumerable<ProjectElement> GetLogicalProject ()
336                 {
337                         throw new NotImplementedException ();
338                 }
339
340                 public static string GetMetadataValueEscaped (ProjectMetadata metadatum)
341                 {
342                         return ProjectCollection.Escape (metadatum.EvaluatedValue);
343                 }
344
345                 public static string GetMetadataValueEscaped (ProjectItem item, string name)
346                 {
347                         var md = item.GetMetadata (name);
348                         return md != null ? ProjectCollection.Escape (md.EvaluatedValue) : null;
349                 }
350
351                 public static string GetMetadataValueEscaped (ProjectItemDefinition item, string name)
352                 {
353                         var md = item.Metadata.FirstOrDefault (m => m.Name == name);
354                         return md != null ? ProjectCollection.Escape (md.EvaluatedValue) : null;
355                 }
356
357                 public string GetPropertyValue (string name)
358                 {
359                         var prop = GetProperty (name);
360                         return prop != null ? prop.EvaluatedValue : string.Empty;
361                 }
362
363                 public static string GetPropertyValueEscaped (ProjectProperty property)
364                 {
365                         return property.EvaluatedValue;
366                 }
367
368                 public ProjectProperty GetProperty (string name)
369                 {
370                         return properties.FirstOrDefault (p => p.Name.Equals (name, StringComparison.OrdinalIgnoreCase));
371                 }
372
373                 public void MarkDirty ()
374                 {
375                         throw new NotImplementedException ();
376                 }
377
378                 public void ReevaluateIfNecessary ()
379                 {
380                         throw new NotImplementedException ();
381                 }
382
383                 public bool RemoveGlobalProperty (string name)
384                 {
385                         throw new NotImplementedException ();
386                 }
387
388                 public bool RemoveItem (ProjectItem item)
389                 {
390                         throw new NotImplementedException ();
391                 }
392
393                 public bool RemoveProperty (ProjectProperty property)
394                 {
395                         var removed = properties.FirstOrDefault (p => p.Name == property.Name);
396                         if (removed == null)
397                                 return false;
398                         properties.Remove (removed);
399                         return true;
400                 }
401
402                 public void Save ()
403                 {
404                         Xml.Save ();
405                 }
406
407                 public void Save (TextWriter writer)
408                 {
409                         Xml.Save (writer);
410                 }
411
412                 public void Save (string path)
413                 {
414                         Save (path, Encoding.Default);
415                 }
416
417                 public void Save (Encoding encoding)
418                 {
419                         Save (FullPath, encoding);
420                 }
421
422                 public void Save (string path, Encoding encoding)
423                 {
424                         using (var writer = new StreamWriter (path, false, encoding))
425                                 Save (writer);
426                 }
427
428                 public void SaveLogicalProject (TextWriter writer)
429                 {
430                         throw new NotImplementedException ();
431                 }
432
433                 public bool SetGlobalProperty (string name, string escapedValue)
434                 {
435                         throw new NotImplementedException ();
436                 }
437
438                 public ProjectProperty SetProperty (string name, string unevaluatedValue)
439                 {
440                         var p = new ManuallyAddedProjectProperty (this, name, unevaluatedValue);
441                         properties.Add (p);
442                         return p;
443                 }
444
445                 public ICollection<ProjectMetadata> AllEvaluatedItemDefinitionMetadata {
446                         get { throw new NotImplementedException (); }
447                 }
448
449                 public ICollection<ProjectItem> AllEvaluatedItems {
450                         get { return all_evaluated_items; }
451                 }
452
453                 public ICollection<ProjectProperty> AllEvaluatedProperties {
454                         get { throw new NotImplementedException (); }
455                 }
456
457                 public IDictionary<string, List<string>> ConditionedProperties {
458                         get {
459                                 // this property returns different instances every time.
460                                 var dic = new Dictionary<string, List<string>> ();
461                                 
462                                 // but I dunno HOW this evaluates
463                                 
464                                 throw new NotImplementedException ();
465                         }
466                 }
467
468                 public string DirectoryPath {
469                         get { return dir_path; }
470                 }
471
472                 public bool DisableMarkDirty { get; set; }
473
474                 public int EvaluationCounter {
475                         get { throw new NotImplementedException (); }
476                 }
477
478                 public string FullPath {
479                         get { return Xml.FullPath; }
480                         set { Xml.FullPath = value; }
481                 }
482
483                 public IList<ResolvedImport> Imports {
484                         get { throw new NotImplementedException (); }
485                 }
486
487                 public IList<ResolvedImport> ImportsIncludingDuplicates {
488                         get { return raw_imports; }
489                 }
490
491                 public bool IsBuildEnabled {
492                         get { return ProjectCollection.IsBuildEnabled; }
493                 }
494
495                 public bool IsDirty {
496                         get { throw new NotImplementedException (); }
497                 }
498
499                 public IDictionary<string, ProjectItemDefinition> ItemDefinitions {
500                         get { return item_definitions; }
501                 }
502
503                 public ICollection<ProjectItem> Items {
504                         get {
505                                 var ret = new List<ProjectItem> ();
506                                 foreach (var child in Xml.Children) {
507                                         var ige = child as ProjectItemGroupElement;
508                                         if (ige != null) {
509                                                 foreach (var p in ige.Items) {
510                                                         var item = new ProjectItem (this, p);
511                                                         if (ShouldInclude (ige.Condition))
512                                                                 ret.Add (item);
513                                                 }
514                                         }
515                                 }
516                                 return ret;
517                         }
518                 }
519
520                 public ICollection<ProjectItem> ItemsIgnoringCondition {
521                         get { return raw_items; }
522                 }
523
524                 public ICollection<string> ItemTypes {
525                         get { return new CollectionFromEnumerable<string> (raw_items.Select (i => i.ItemType).Distinct ()); }
526                 }
527
528                 public ICollection<ProjectProperty> Properties {
529                         get { return properties; }
530                 }
531
532                 public bool SkipEvaluation { get; set; }
533
534                 #if NET_4_5
535                 public
536                 #else
537                 internal
538                 #endif
539                 IDictionary<string, ProjectTargetInstance> Targets {
540                         get { return targets; }
541                 }
542         }
543 }