implement ProjectItem and co.
[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<string> item_types;
148                 List<ProjectProperty> properties;
149                 Dictionary<string, ProjectTargetInstance> targets;
150
151                 void Initialize ()
152                 {
153                         dir_path = Directory.GetCurrentDirectory ();
154                         raw_imports = new List<ResolvedImport> ();
155                         item_definitions = new Dictionary<string, ProjectItemDefinition> ();
156                         item_types = new List<string> ();
157                         properties = new List<ProjectProperty> ();
158                         targets = new Dictionary<string, ProjectTargetInstance> ();
159                         raw_items = new List<ProjectItem> ();
160                         
161                         ProcessXml ();
162                 }
163                 
164                 void ProcessXml ()
165                 {
166                         foreach (DictionaryEntry p in Environment.GetEnvironmentVariables ())
167                                 this.properties.Add (new EnvironmentProjectProperty (this, (string)p.Key, (string)p.Value));
168                         foreach (var p in GlobalProperties)
169                                 this.properties.Add (new GlobalProjectProperty (this, p.Key, p.Value));
170                         foreach (var child in Xml.Children) {
171                                 if (child is ProjectPropertyGroupElement)
172                                         foreach (var p in ((ProjectPropertyGroupElement) child).Properties)
173                                                 this.properties.Add (new XmlProjectProperty (this, p, PropertyType.Normal));
174                                 else if (child is ProjectItemGroupElement)
175                                         foreach (var p in ((ProjectItemGroupElement) child).Items)
176                                                 this.raw_items.Add (new ProjectItem (this, p));
177                                 else if (child is ProjectItemDefinitionGroupElement)
178                                         foreach (var p in ((ProjectItemDefinitionGroupElement) child).ItemDefinitions) {
179                                                 ProjectItemDefinition existing;
180                                                 if (!item_definitions.TryGetValue (p.ItemType, out existing))
181                                                         item_definitions.Add (p.ItemType, (existing = new ProjectItemDefinition (this, p.ItemType)));
182                                                 existing.AddItems (p);
183                                         }
184                         }
185                 }
186
187                 public ICollection<ProjectItem> GetItemsIgnoringCondition (string itemType)
188                 {
189                         return new CollectionFromEnumerable<ProjectItem> (raw_items.Where (p => p.ItemType.Equals (itemType, StringComparison.OrdinalIgnoreCase)));
190                 }
191
192                 public void RemoveItems (IEnumerable<ProjectItem> items)
193                 {
194                         var removal = new List<ProjectItem> (items);
195                         foreach (var item in removal) {
196                                 var parent = item.Xml.Parent;
197                                 parent.RemoveChild (item.Xml);
198                                 if (parent.Count == 0)
199                                         parent.Parent.RemoveChild (parent);
200                         }
201                 }
202
203                 static readonly Dictionary<string, string> empty_metadata = new Dictionary<string, string> ();
204
205                 public IList<ProjectItem> AddItem (string itemType, string unevaluatedInclude)
206                 {
207                         return AddItem (itemType, unevaluatedInclude, empty_metadata);
208                 }
209
210                 public IList<ProjectItem> AddItem (string itemType, string unevaluatedInclude,
211                                 IEnumerable<KeyValuePair<string, string>> metadata)
212                 {
213                         // FIXME: needs several check that AddItemFast() does not process (see MSDN for details).
214
215                         return AddItemFast (itemType, unevaluatedInclude, metadata);
216                 }
217
218                 public IList<ProjectItem> AddItemFast (string itemType, string unevaluatedInclude)
219                 {
220                         return AddItemFast (itemType, unevaluatedInclude, empty_metadata);
221                 }
222
223                 public IList<ProjectItem> AddItemFast (string itemType, string unevaluatedInclude,
224                                                                      IEnumerable<KeyValuePair<string, string>> metadata)
225                 {
226                         throw new NotImplementedException ();
227                 }
228
229                 public bool Build ()
230                 {
231                         return Build (Xml.DefaultTargets.Split (';'));
232                 }
233
234                 public bool Build (IEnumerable<ILogger> loggers)
235                 {
236                         return Build (Xml.DefaultTargets.Split (';'), loggers);
237                 }
238
239                 public bool Build (string target)
240                 {
241                         return string.IsNullOrWhiteSpace (target) ? Build () : Build (new string [] {target});
242                 }
243
244                 public bool Build (string[] targets)
245                 {
246                         return Build (targets, new ILogger [0]);
247                 }
248
249                 public bool Build (ILogger logger)
250                 {
251                         return Build (Xml.DefaultTargets.Split (';'), new ILogger [] {logger});
252                 }
253
254                 public bool Build (string[] targets, IEnumerable<ILogger> loggers)
255                 {
256                         return Build (targets, loggers, new ForwardingLoggerRecord [0]);
257                 }
258
259                 public bool Build (IEnumerable<ILogger> loggers, IEnumerable<ForwardingLoggerRecord> remoteLoggers)
260                 {
261                         return Build (Xml.DefaultTargets.Split (';'), loggers, remoteLoggers);
262                 }
263
264                 public bool Build (string target, IEnumerable<ILogger> loggers)
265                 {
266                         return Build (new string [] { target }, loggers);
267                 }
268
269                 public bool Build (string[] targets, IEnumerable<ILogger> loggers, IEnumerable<ForwardingLoggerRecord> remoteLoggers)
270                 {
271                         // Unlike ProjectInstance.Build(), there is no place to fill outputs by targets, so ignore them
272                         // (i.e. we don't use the overload with output).
273                         //
274                         // This does not check FullPath, so don't call GetProjectInstanceForBuild() directly.
275                         return new BuildManager ().GetProjectInstanceForBuildInternal (this).Build (targets, loggers, remoteLoggers);
276                 }
277
278                 public bool Build (string target, IEnumerable<ILogger> loggers, IEnumerable<ForwardingLoggerRecord> remoteLoggers)
279                 {
280                         return Build (new string [] { target }, loggers, remoteLoggers);
281                 }
282
283                 public ProjectInstance CreateProjectInstance ()
284                 {
285                         var ret = new ProjectInstance (Xml, GlobalProperties, ToolsVersion, ProjectCollection);
286                         // FIXME: maybe fill other properties to the result.
287                         return ret;
288                 }
289
290                 public string ExpandString (string unexpandedValue)
291                 {
292                         throw new NotImplementedException ();
293                 }
294
295                 public static string GetEvaluatedItemIncludeEscaped (ProjectItem item)
296                 {
297                         throw new NotImplementedException ();
298                 }
299
300                 public static string GetEvaluatedItemIncludeEscaped (ProjectItemDefinition item)
301                 {
302                         throw new NotImplementedException ();
303                 }
304
305                 public ICollection<ProjectItem> GetItems (string itemType)
306                 {
307                         throw new NotImplementedException ();
308                 }
309
310                 public ICollection<ProjectItem> GetItemsByEvaluatedInclude (string evaluatedInclude)
311                 {
312                         throw new NotImplementedException ();
313                 }
314
315                 public IEnumerable<ProjectElement> GetLogicalProject ()
316                 {
317                         throw new NotImplementedException ();
318                 }
319
320                 public static string GetMetadataValueEscaped (ProjectMetadata metadatum)
321                 {
322                         throw new NotImplementedException ();
323                 }
324
325                 public static string GetMetadataValueEscaped (ProjectItem item, string name)
326                 {
327                         throw new NotImplementedException ();
328                 }
329
330                 public static string GetMetadataValueEscaped (ProjectItemDefinition item, string name)
331                 {
332                         throw new NotImplementedException ();
333                 }
334
335                 public string GetPropertyValue (string name)
336                 {
337                         var prop = GetProperty (name);
338                         return prop != null ? prop.EvaluatedValue : string.Empty;
339                 }
340
341                 public static string GetPropertyValueEscaped (ProjectProperty property)
342                 {
343                         return property.EvaluatedValue;
344                 }
345
346                 public ProjectProperty GetProperty (string name)
347                 {
348                         return properties.FirstOrDefault (p => p.Name == name);
349                 }
350
351                 public void MarkDirty ()
352                 {
353                         throw new NotImplementedException ();
354                 }
355
356                 public void ReevaluateIfNecessary ()
357                 {
358                         throw new NotImplementedException ();
359                 }
360
361                 public bool RemoveGlobalProperty (string name)
362                 {
363                         throw new NotImplementedException ();
364                 }
365
366                 public bool RemoveItem (ProjectItem item)
367                 {
368                         throw new NotImplementedException ();
369                 }
370
371                 public bool RemoveProperty (ProjectProperty property)
372                 {
373                         var removed = properties.FirstOrDefault (p => p.Name == property.Name);
374                         if (removed == null)
375                                 return false;
376                         properties.Remove (removed);
377                         return true;
378                 }
379
380                 public void Save ()
381                 {
382                         Xml.Save ();
383                 }
384
385                 public void Save (TextWriter writer)
386                 {
387                         Xml.Save (writer);
388                 }
389
390                 public void Save (string path)
391                 {
392                         Save (path, Encoding.Default);
393                 }
394
395                 public void Save (Encoding encoding)
396                 {
397                         Save (FullPath, encoding);
398                 }
399
400                 public void Save (string path, Encoding encoding)
401                 {
402                         using (var writer = new StreamWriter (path, false, encoding))
403                                 Save (writer);
404                 }
405
406                 public void SaveLogicalProject (TextWriter writer)
407                 {
408                         throw new NotImplementedException ();
409                 }
410
411                 public bool SetGlobalProperty (string name, string escapedValue)
412                 {
413                         throw new NotImplementedException ();
414                 }
415
416                 public ProjectProperty SetProperty (string name, string unevaluatedValue)
417                 {
418                         var p = new ManuallyAddedProjectProperty (this, name, unevaluatedValue);
419                         properties.Add (p);
420                         return p;
421                 }
422
423                 public ICollection<ProjectMetadata> AllEvaluatedItemDefinitionMetadata { get; private set; }
424
425                 public ICollection<ProjectItem> AllEvaluatedItems { get; private set; }
426
427                 public ICollection<ProjectProperty> AllEvaluatedProperties { get; private set; }
428
429                 public IDictionary<string, List<string>> ConditionedProperties {
430                         get {
431                                 // this property returns different instances every time.
432                                 var dic = new Dictionary<string, List<string>> ();
433                                 
434                                 // but I dunno HOW this evaluates
435                                 
436                                 throw new NotImplementedException ();
437                         }
438                 }
439
440                 public string DirectoryPath {
441                         get { return dir_path; }
442                 }
443
444                 public bool DisableMarkDirty { get; set; }
445
446                 public int EvaluationCounter {
447                         get { throw new NotImplementedException (); }
448                 }
449
450                 public string FullPath {
451                         get { return Xml.FullPath; }
452                         set { Xml.FullPath = value; }
453                 }
454
455                 public IList<ResolvedImport> Imports {
456                         get { throw new NotImplementedException (); }
457                 }
458
459                 public IList<ResolvedImport> ImportsIncludingDuplicates {
460                         get { return raw_imports; }
461                 }
462
463                 public bool IsBuildEnabled {
464                         get { return ProjectCollection.IsBuildEnabled; }
465                 }
466
467                 public bool IsDirty {
468                         get { throw new NotImplementedException (); }
469                 }
470
471                 public IDictionary<string, ProjectItemDefinition> ItemDefinitions {
472                         get { return item_definitions; }
473                 }
474
475                 public ICollection<ProjectItem> Items {
476                         get { throw new NotImplementedException (); }
477                 }
478
479                 public ICollection<ProjectItem> ItemsIgnoringCondition {
480                         get { return raw_items; }
481                 }
482
483                 public ICollection<string> ItemTypes {
484                         get { return item_types; }
485                 }
486
487                 public ICollection<ProjectProperty> Properties {
488                         get { return properties; }
489                 }
490
491                 public bool SkipEvaluation { get; set; }
492
493                 #if NET_4_5
494                 public
495                 #else
496                 internal
497                 #endif
498                 IDictionary<string, ProjectTargetInstance> Targets {
499                         get { return targets; }
500                 }
501         }
502 }