couple of tokenization fixes with more tests.
[mono.git] / mcs / class / Microsoft.Build / Microsoft.Build.Evaluation / ProjectProperty.cs
1 // ProjectProperty.cs
2 //
3 // Author:
4 //   Rolf Bjarne Kvinge (rolf@xamarin.com)
5 //   Atsushi Enomoto (atsushi@xamarin.com)
6 //
7 // Copyright (C) 2011,2013 Xamarin Inc.
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
29 using System;
30 using System.Linq;
31 using Microsoft.Build.Construction;
32 using Microsoft.Build.Internal;
33 using System.Collections.Generic;
34 using System.Reflection;
35 using System.IO;
36
37 namespace Microsoft.Build.Evaluation
38 {
39         // In .NET 4.0 MSDN says it is non-abstract, but some of those
40         // members are abstract and had been there since 4.0.
41         // I take this as doc bug, as non-abstract to abstract is a
42         // breaking change and I'd rather believe API designer's sanity.
43         public abstract class ProjectProperty
44         {
45                 internal ProjectProperty (Project project) // hide default ctor
46                 {
47                         Project = project;
48                 }
49
50                 string evaluated_value; // see UpdateEvaluatedValue().
51                 public string EvaluatedValue {
52                         get { return evaluated_value; }
53                 }
54
55                 public abstract bool IsEnvironmentProperty { get; }
56
57                 public abstract bool IsGlobalProperty { get; }
58
59                 [MonoTODO]
60                 public abstract bool IsImported { get; }
61
62                 public abstract bool IsReservedProperty { get; }
63
64                 internal virtual bool IsWellKnownProperty {
65                         get { return false; }
66                 }
67
68                 public abstract string Name { get; }
69
70                 public abstract ProjectProperty Predecessor { get; }
71
72                 public Project Project { get; private set; }
73
74                 public abstract string UnevaluatedValue { get; set; }
75
76                 public abstract ProjectPropertyElement Xml { get; }
77                 
78                 internal void UpdateEvaluatedValue ()
79                 {
80                         evaluated_value = Project.ExpandString (UnevaluatedValue);
81                 }
82         }
83
84         // copy from MS.Build.Engine/BuildProperty.cs
85         internal enum PropertyType {
86                 Reserved,
87                 Global,
88                 Normal,
89                 Environment
90         }
91         
92         internal abstract class BaseProjectProperty : ProjectProperty
93         {
94                 public BaseProjectProperty (Project project, PropertyType propertyType, string name)
95                         : base (project)
96                 {
97                         property_type = propertyType;
98                         this.name = name;
99                         predecessor = project.Properties.FirstOrDefault (p => p.Name == name);
100                         if (predecessor != null)
101                                 project.RemoveProperty (predecessor);
102                 }
103                 
104                 PropertyType property_type;
105                 
106                 readonly string name;
107                 public override string Name {
108                         get { return name; }
109                 }
110                 
111                 public override bool IsEnvironmentProperty {
112                         get { return property_type == PropertyType.Environment; }
113                 }
114                 public override bool IsGlobalProperty {
115                         get { return property_type == PropertyType.Global; }
116                 }
117                 public override bool IsImported {
118                         get {
119                                 throw new NotImplementedException ();
120                         }
121                 }
122                 public override bool IsReservedProperty {
123                         get { return property_type == PropertyType.Reserved; }
124                 }
125                 readonly ProjectProperty predecessor; 
126                 public override ProjectProperty Predecessor {
127                         get { return predecessor; }
128                 }
129         }
130         
131         internal class XmlProjectProperty : BaseProjectProperty
132         {
133                 public XmlProjectProperty (Project project, ProjectPropertyElement xml, PropertyType propertyType)
134                         : base (project, propertyType, xml.Name)
135                 {
136                         this.xml = xml;
137                         UpdateEvaluatedValue ();
138                 }
139                 
140                 ProjectPropertyElement xml;
141                 
142                 public override string UnevaluatedValue {
143                         get { return xml.Value; }
144                         set { xml.Value = value; }
145                 }
146                 public override ProjectPropertyElement Xml {
147                         get { return xml; }
148                 }
149         }
150         
151         internal class EnvironmentProjectProperty : BaseProjectProperty
152         {
153                 public static IEnumerable<EnvironmentProjectProperty> GetWellKnownProperties (Project project)
154                 {
155                         Func<string,string,EnvironmentProjectProperty> create = (name, value) => new EnvironmentProjectProperty (project, name, value, true);
156                         var ext = Environment.GetEnvironmentVariable ("MSBuildExtensionsPath") ?? DefaultExtensionsPath;
157                         yield return create ("MSBuildExtensionsPath", ext);
158                         var ext32 = Environment.GetEnvironmentVariable ("MSBuildExtensionsPath32") ?? DefaultExtensionsPath;
159                         yield return create ("MSBuildExtensionsPath32", ext32);
160                         var ext64 = Environment.GetEnvironmentVariable ("MSBuildExtensionsPath64") ?? DefaultExtensionsPath;
161                         yield return create ("MSBuildExtensionsPath64", ext64);
162                 }
163
164                 static string extensions_path;
165                 internal static string DefaultExtensionsPath {
166                         get {
167                                 if (extensions_path == null) {
168                                         // NOTE: code from mcs/tools/gacutil/driver.cs
169                                         PropertyInfo gac = typeof (System.Environment).GetProperty (
170                                                         "GacPath", BindingFlags.Static | BindingFlags.NonPublic);
171
172                                         if (gac != null) {
173                                                 MethodInfo get_gac = gac.GetGetMethod (true);
174                                                 string gac_path = (string) get_gac.Invoke (null, null);
175                                                 extensions_path = Path.GetFullPath (Path.Combine (
176                                                                         gac_path, Path.Combine ("..", "xbuild")));
177                                         }
178                                 }
179                                 return extensions_path;
180                         }
181                 }
182                 
183                 public EnvironmentProjectProperty (Project project, string name, string value, bool wellknown = false)
184                         : base (project, PropertyType.Environment, name)
185                 {
186                         this.value = value;
187                         UpdateEvaluatedValue ();
188                         this.wellknown = wellknown;
189                 }
190                 
191                 readonly string value;
192                 readonly bool wellknown;
193
194                 internal override bool IsWellKnownProperty {
195                         get { return wellknown; }
196                 }
197
198                 // It can override possible another environment vairable property BUT never gives Predecessor.
199                 public override ProjectProperty Predecessor {
200                         get { return null; }
201                 }
202                 
203                 public override string UnevaluatedValue {
204                         get { return value; }
205                         set { throw new InvalidOperationException (string.Format ("You cannot change value of environment property '{0}'.", Name)); }
206                 }
207                 public override ProjectPropertyElement Xml {
208                         get { return null; }
209                 }
210         }
211         
212         internal class GlobalProjectProperty : BaseProjectProperty
213         {
214                 public GlobalProjectProperty (Project project, string name, string value)
215                         : base (project, PropertyType.Global, name)
216                 {
217                         this.value = value;
218                         UpdateEvaluatedValue ();
219                 }
220                 
221                 readonly string value;
222                 
223                 public override string UnevaluatedValue {
224                         get { return value; }
225                         set { throw new InvalidOperationException (string.Format ("You cannot change value of global property '{0}'.", Name)); }
226                 }
227                 public override ProjectPropertyElement Xml {
228                         get { return null; }
229                 }
230         }
231         
232         internal class ManuallyAddedProjectProperty : BaseProjectProperty
233         {
234                 public ManuallyAddedProjectProperty (Project project, string name, string value)
235                         : base (project, PropertyType.Normal, name)
236                 {
237                         this.UnevaluatedValue = value;
238                 }
239                 
240                 public override string UnevaluatedValue { get; set; }
241                 
242                 public override ProjectPropertyElement Xml {
243                         get { return null; }
244                 }
245         }
246         
247         internal class ReservedProjectProperty : BaseProjectProperty
248         {
249                 // seealso http://msdn.microsoft.com/en-us/library/ms164309.aspx
250                 public static IEnumerable<ReservedProjectProperty> GetReservedProperties (Toolset toolset, Project project)
251                 {
252                         Func<string,Func<string>,ReservedProjectProperty> create = (name, value) => new ReservedProjectProperty (project, name, value);
253                         yield return create ("MSBuildBinPath", () => toolset.ToolsPath);
254                         // FIXME: add MSBuildLastTaskResult
255                         // FIXME: add MSBuildNodeCount
256                         // FIXME: add MSBuildProgramFiles32
257                         yield return create ("MSBuildProjectDefaultTargets", () => project.Xml.DefaultTargets);
258                         yield return create ("MSBuildProjectDirectory", () => project.DirectoryPath + Path.DirectorySeparatorChar);
259                         // FIXME: add MSBuildProjectDirectoryNoRoot
260                         yield return create ("MSBuildProjectExtension", () => Path.GetExtension (project.FullPath));
261                         yield return create ("MSBuildProjectFile", () => Path.GetFileName (project.FullPath));
262                         yield return create ("MSBuildProjectFullPath", () => project.FullPath);
263                         yield return create ("MSBuildProjectName", () => Path.GetFileNameWithoutExtension (project.FullPath));
264                         // FIXME: add MSBuildStartupDirectory
265                         yield return create ("MSBuildThisFile", () => Path.GetFileName (project.GetEvaluationTimeThisFile ()));
266                         yield return create ("MSBuildThisFileFullPath", () => project.GetEvaluationTimeThisFile ());
267                         yield return create ("MSBuildThisFileName", () => Path.GetFileNameWithoutExtension (project.GetEvaluationTimeThisFile ()));
268                         yield return create ("MSBuildThisFileExtension", () => Path.GetExtension (project.GetEvaluationTimeThisFile ()));
269
270                         yield return create ("MSBuildThisFileDirectory", () => Path.GetDirectoryName (project.GetEvaluationTimeThisFileDirectory ()));
271                         yield return create ("MSBuildThisFileDirectoryNoRoot", () => {
272                                 string dir = project.GetEvaluationTimeThisFileDirectory () + Path.DirectorySeparatorChar;
273                                 return dir.Substring (Path.GetPathRoot (dir).Length);
274                                 });
275                 }
276                 
277                 public ReservedProjectProperty (Project project, string name, Func<string> value)
278                         : base (project, PropertyType.Reserved, name)
279                 {
280                         this.value = value;
281                 }
282
283                 // make sure it does not give access to any possible attempted overrrides.
284                 public override ProjectProperty Predecessor {
285                         get { return null; }
286                 }
287
288                 readonly Func<string> value;
289                 public override string UnevaluatedValue {
290                         get { return value (); }
291                         set { throw new InvalidOperationException (string.Format ("You cannot change value of reserved property '{0}'.", Name)); }
292                 }
293                 
294                 public override ProjectPropertyElement Xml {
295                         get { return null; }
296                 }
297         }
298 }