5 // Leszek Ciesielski (skolima@gmail.com)
6 // Rolf Bjarne Kvinge (rolf@xamarin.com)
7 // Atsushi Enomoto (atsushi@xamarin.com)
9 // (C) 2011 Leszek Ciesielski
10 // Copyright (C) 2011,2013 Xamarin Inc. (http://www.xamarin.com)
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:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
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.
33 using System.Collections.Generic;
34 using System.Diagnostics;
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;
46 namespace Microsoft.Build.Evaluation
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}")]
54 public Project (XmlReader xml)
55 : this (ProjectRootElement.Create (xml))
59 public Project (XmlReader xml, IDictionary<string, string> globalProperties,
61 : this (ProjectRootElement.Create (xml), globalProperties, toolsVersion)
65 public Project (XmlReader xml, IDictionary<string, string> globalProperties,
66 string toolsVersion, ProjectCollection projectCollection)
67 : this (ProjectRootElement.Create (xml), globalProperties, toolsVersion, projectCollection)
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)
78 public Project (ProjectRootElement xml) : this (xml, null, null)
82 public Project (ProjectRootElement xml, IDictionary<string, string> globalProperties,
84 : this (xml, globalProperties, toolsVersion, ProjectCollection.GlobalProjectCollection)
88 public Project (ProjectRootElement xml, IDictionary<string, string> globalProperties,
89 string toolsVersion, ProjectCollection projectCollection)
90 : this (xml, globalProperties, toolsVersion, projectCollection, ProjectLoadSettings.Default)
94 public Project (ProjectRootElement xml, IDictionary<string, string> globalProperties,
95 string toolsVersion, ProjectCollection projectCollection,
96 ProjectLoadSettings loadSettings)
98 if (projectCollection == null)
99 throw new ArgumentNullException ("projectCollection");
101 this.GlobalProperties = globalProperties ?? new Dictionary<string, string> ();
102 this.ToolsVersion = toolsVersion;
103 this.ProjectCollection = projectCollection;
104 this.load_settings = loadSettings;
109 public Project (string projectFile)
110 : this (projectFile, null, null)
114 public Project (string projectFile, IDictionary<string, string> globalProperties,
116 : this (projectFile, globalProperties, toolsVersion, ProjectCollection.GlobalProjectCollection, ProjectLoadSettings.Default)
120 public Project (string projectFile, IDictionary<string, string> globalProperties,
121 string toolsVersion, ProjectCollection projectCollection)
122 : this (projectFile, globalProperties, toolsVersion, projectCollection, ProjectLoadSettings.Default)
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)
133 ProjectLoadSettings load_settings;
135 public IDictionary<string, string> GlobalProperties { get; private set; }
137 public ProjectCollection ProjectCollection { get; private set; }
139 public string ToolsVersion { get; private set; }
141 public ProjectRootElement Xml { get; private set; }
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;
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> ();
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));
172 all_evaluated_items = new List<ProjectItem> ();
173 foreach (var child in Xml.Children) {
174 var pge = child as ProjectPropertyGroupElement;
176 foreach (var p in pge.Properties)
177 this.properties.Add (new XmlProjectProperty (this, p, PropertyType.Normal));
178 var ige = child as ProjectItemGroupElement;
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);
187 var def = child as ProjectItemDefinitionGroupElement;
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);
199 all_evaluated_items.Sort ((p1, p2) => string.Compare (p1.ItemType, p2.ItemType, StringComparison.OrdinalIgnoreCase));
202 public ICollection<ProjectItem> GetItemsIgnoringCondition (string itemType)
204 return new CollectionFromEnumerable<ProjectItem> (raw_items.Where (p => p.ItemType.Equals (itemType, StringComparison.OrdinalIgnoreCase)));
207 public void RemoveItems (IEnumerable<ProjectItem> items)
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);
218 static readonly Dictionary<string, string> empty_metadata = new Dictionary<string, string> ();
220 public IList<ProjectItem> AddItem (string itemType, string unevaluatedInclude)
222 return AddItem (itemType, unevaluatedInclude, empty_metadata);
225 public IList<ProjectItem> AddItem (string itemType, string unevaluatedInclude,
226 IEnumerable<KeyValuePair<string, string>> metadata)
228 // FIXME: needs several check that AddItemFast() does not process (see MSDN for details).
230 return AddItemFast (itemType, unevaluatedInclude, metadata);
233 public IList<ProjectItem> AddItemFast (string itemType, string unevaluatedInclude)
235 return AddItemFast (itemType, unevaluatedInclude, empty_metadata);
238 public IList<ProjectItem> AddItemFast (string itemType, string unevaluatedInclude,
239 IEnumerable<KeyValuePair<string, string>> metadata)
241 throw new NotImplementedException ();
246 return Build (Xml.DefaultTargets.Split (';'));
249 public bool Build (IEnumerable<ILogger> loggers)
251 return Build (Xml.DefaultTargets.Split (';'), loggers);
254 public bool Build (string target)
256 return string.IsNullOrWhiteSpace (target) ? Build () : Build (new string [] {target});
259 public bool Build (string[] targets)
261 return Build (targets, new ILogger [0]);
264 public bool Build (ILogger logger)
266 return Build (Xml.DefaultTargets.Split (';'), new ILogger [] {logger});
269 public bool Build (string[] targets, IEnumerable<ILogger> loggers)
271 return Build (targets, loggers, new ForwardingLoggerRecord [0]);
274 public bool Build (IEnumerable<ILogger> loggers, IEnumerable<ForwardingLoggerRecord> remoteLoggers)
276 return Build (Xml.DefaultTargets.Split (';'), loggers, remoteLoggers);
279 public bool Build (string target, IEnumerable<ILogger> loggers)
281 return Build (new string [] { target }, loggers);
284 public bool Build (string[] targets, IEnumerable<ILogger> loggers, IEnumerable<ForwardingLoggerRecord> remoteLoggers)
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).
289 // This does not check FullPath, so don't call GetProjectInstanceForBuild() directly.
290 return new BuildManager ().GetProjectInstanceForBuildInternal (this).Build (targets, loggers, remoteLoggers);
293 public bool Build (string target, IEnumerable<ILogger> loggers, IEnumerable<ForwardingLoggerRecord> remoteLoggers)
295 return Build (new string [] { target }, loggers, remoteLoggers);
298 public ProjectInstance CreateProjectInstance ()
300 var ret = new ProjectInstance (Xml, GlobalProperties, ToolsVersion, ProjectCollection);
301 // FIXME: maybe fill other properties to the result.
305 bool ShouldInclude (string unexpandedValue)
307 return string.IsNullOrWhiteSpace (unexpandedValue) || new ExpressionEvaluator (this).EvaluateAsBoolean (unexpandedValue);
310 public string ExpandString (string unexpandedValue)
312 return new ExpressionEvaluator (this).Evaluate (unexpandedValue);
315 public static string GetEvaluatedItemIncludeEscaped (ProjectItem item)
317 return ProjectCollection.Escape (item.EvaluatedInclude);
320 public static string GetEvaluatedItemIncludeEscaped (ProjectItemDefinition item)
322 throw new NotImplementedException ();
325 public ICollection<ProjectItem> GetItems (string itemType)
327 return new CollectionFromEnumerable<ProjectItem> (Items.Where (p => p.ItemType.Equals (itemType, StringComparison.OrdinalIgnoreCase)));
330 public ICollection<ProjectItem> GetItemsByEvaluatedInclude (string evaluatedInclude)
332 return new CollectionFromEnumerable<ProjectItem> (Items.Where (p => p.EvaluatedInclude.Equals (evaluatedInclude, StringComparison.OrdinalIgnoreCase)));
335 public IEnumerable<ProjectElement> GetLogicalProject ()
337 throw new NotImplementedException ();
340 public static string GetMetadataValueEscaped (ProjectMetadata metadatum)
342 return ProjectCollection.Escape (metadatum.EvaluatedValue);
345 public static string GetMetadataValueEscaped (ProjectItem item, string name)
347 var md = item.GetMetadata (name);
348 return md != null ? ProjectCollection.Escape (md.EvaluatedValue) : null;
351 public static string GetMetadataValueEscaped (ProjectItemDefinition item, string name)
353 var md = item.Metadata.FirstOrDefault (m => m.Name == name);
354 return md != null ? ProjectCollection.Escape (md.EvaluatedValue) : null;
357 public string GetPropertyValue (string name)
359 var prop = GetProperty (name);
360 return prop != null ? prop.EvaluatedValue : string.Empty;
363 public static string GetPropertyValueEscaped (ProjectProperty property)
365 return property.EvaluatedValue;
368 public ProjectProperty GetProperty (string name)
370 return properties.FirstOrDefault (p => p.Name.Equals (name, StringComparison.OrdinalIgnoreCase));
373 public void MarkDirty ()
375 throw new NotImplementedException ();
378 public void ReevaluateIfNecessary ()
380 throw new NotImplementedException ();
383 public bool RemoveGlobalProperty (string name)
385 throw new NotImplementedException ();
388 public bool RemoveItem (ProjectItem item)
390 throw new NotImplementedException ();
393 public bool RemoveProperty (ProjectProperty property)
395 var removed = properties.FirstOrDefault (p => p.Name == property.Name);
398 properties.Remove (removed);
407 public void Save (TextWriter writer)
412 public void Save (string path)
414 Save (path, Encoding.Default);
417 public void Save (Encoding encoding)
419 Save (FullPath, encoding);
422 public void Save (string path, Encoding encoding)
424 using (var writer = new StreamWriter (path, false, encoding))
428 public void SaveLogicalProject (TextWriter writer)
430 throw new NotImplementedException ();
433 public bool SetGlobalProperty (string name, string escapedValue)
435 throw new NotImplementedException ();
438 public ProjectProperty SetProperty (string name, string unevaluatedValue)
440 var p = new ManuallyAddedProjectProperty (this, name, unevaluatedValue);
445 public ICollection<ProjectMetadata> AllEvaluatedItemDefinitionMetadata {
446 get { throw new NotImplementedException (); }
449 public ICollection<ProjectItem> AllEvaluatedItems {
450 get { return all_evaluated_items; }
453 public ICollection<ProjectProperty> AllEvaluatedProperties {
454 get { throw new NotImplementedException (); }
457 public IDictionary<string, List<string>> ConditionedProperties {
459 // this property returns different instances every time.
460 var dic = new Dictionary<string, List<string>> ();
462 // but I dunno HOW this evaluates
464 throw new NotImplementedException ();
468 public string DirectoryPath {
469 get { return dir_path; }
472 public bool DisableMarkDirty { get; set; }
474 public int EvaluationCounter {
475 get { throw new NotImplementedException (); }
478 public string FullPath {
479 get { return Xml.FullPath; }
480 set { Xml.FullPath = value; }
483 public IList<ResolvedImport> Imports {
484 get { throw new NotImplementedException (); }
487 public IList<ResolvedImport> ImportsIncludingDuplicates {
488 get { return raw_imports; }
491 public bool IsBuildEnabled {
492 get { return ProjectCollection.IsBuildEnabled; }
495 public bool IsDirty {
496 get { throw new NotImplementedException (); }
499 public IDictionary<string, ProjectItemDefinition> ItemDefinitions {
500 get { return item_definitions; }
503 public ICollection<ProjectItem> Items {
505 var ret = new List<ProjectItem> ();
506 foreach (var child in Xml.Children) {
507 var ige = child as ProjectItemGroupElement;
509 foreach (var p in ige.Items) {
510 var item = new ProjectItem (this, p);
511 if (ShouldInclude (ige.Condition))
520 public ICollection<ProjectItem> ItemsIgnoringCondition {
521 get { return raw_items; }
524 public ICollection<string> ItemTypes {
525 get { return new CollectionFromEnumerable<string> (raw_items.Select (i => i.ItemType).Distinct ()); }
528 public ICollection<ProjectProperty> Properties {
529 get { return properties; }
532 public bool SkipEvaluation { get; set; }
539 IDictionary<string, ProjectTargetInstance> Targets {
540 get { return targets; }