4 // Rolf Bjarne Kvinge (rolf@xamarin.com)
5 // Atsushi Enomoto (atsushi@xamarin.com)
7 // Copyright (C) 2011,2013 Xamarin Inc.
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:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
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.
31 using Microsoft.Build.Construction;
32 using Microsoft.Build.Internal;
33 using System.Collections.Generic;
34 using System.Reflection;
37 namespace Microsoft.Build.Evaluation
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
45 internal ProjectProperty (Project project) // hide default ctor
50 string evaluated_value; // see UpdateEvaluatedValue().
51 public string EvaluatedValue {
52 get { return evaluated_value; }
55 public abstract bool IsEnvironmentProperty { get; }
57 public abstract bool IsGlobalProperty { get; }
60 public abstract bool IsImported { get; }
62 public abstract bool IsReservedProperty { get; }
64 internal virtual bool IsWellKnownProperty {
68 public abstract string Name { get; }
70 public abstract ProjectProperty Predecessor { get; }
72 public Project Project { get; private set; }
74 public abstract string UnevaluatedValue { get; set; }
76 public abstract ProjectPropertyElement Xml { get; }
78 internal void UpdateEvaluatedValue ()
80 evaluated_value = Project.ExpandString (UnevaluatedValue);
84 // copy from MS.Build.Engine/BuildProperty.cs
85 internal enum PropertyType {
92 internal abstract class BaseProjectProperty : ProjectProperty
94 public BaseProjectProperty (Project project, PropertyType propertyType, string name)
97 property_type = propertyType;
99 predecessor = project.Properties.FirstOrDefault (p => p.Name == name);
100 if (predecessor != null)
101 project.RemoveProperty (predecessor);
104 PropertyType property_type;
106 readonly string name;
107 public override string Name {
111 public override bool IsEnvironmentProperty {
112 get { return property_type == PropertyType.Environment; }
114 public override bool IsGlobalProperty {
115 get { return property_type == PropertyType.Global; }
117 public override bool IsImported {
119 throw new NotImplementedException ();
122 public override bool IsReservedProperty {
123 get { return property_type == PropertyType.Reserved; }
125 readonly ProjectProperty predecessor;
126 public override ProjectProperty Predecessor {
127 get { return predecessor; }
131 internal class XmlProjectProperty : BaseProjectProperty
133 public XmlProjectProperty (Project project, ProjectPropertyElement xml, PropertyType propertyType)
134 : base (project, propertyType, xml.Name)
137 UpdateEvaluatedValue ();
140 ProjectPropertyElement xml;
142 public override string UnevaluatedValue {
143 get { return xml.Value; }
144 set { xml.Value = value; }
146 public override ProjectPropertyElement Xml {
151 internal class EnvironmentProjectProperty : BaseProjectProperty
153 public static IEnumerable<EnvironmentProjectProperty> GetWellKnownProperties (Project project)
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);
164 static string extensions_path;
165 internal static string DefaultExtensionsPath {
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);
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")));
179 return extensions_path;
183 public EnvironmentProjectProperty (Project project, string name, string value, bool wellknown = false)
184 : base (project, PropertyType.Environment, name)
187 UpdateEvaluatedValue ();
188 this.wellknown = wellknown;
191 readonly string value;
192 readonly bool wellknown;
194 internal override bool IsWellKnownProperty {
195 get { return wellknown; }
198 // It can override possible another environment vairable property BUT never gives Predecessor.
199 public override ProjectProperty Predecessor {
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)); }
207 public override ProjectPropertyElement Xml {
212 internal class GlobalProjectProperty : BaseProjectProperty
214 public GlobalProjectProperty (Project project, string name, string value)
215 : base (project, PropertyType.Global, name)
218 UpdateEvaluatedValue ();
221 readonly string value;
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)); }
227 public override ProjectPropertyElement Xml {
232 internal class ManuallyAddedProjectProperty : BaseProjectProperty
234 public ManuallyAddedProjectProperty (Project project, string name, string value)
235 : base (project, PropertyType.Normal, name)
237 this.UnevaluatedValue = value;
240 public override string UnevaluatedValue { get; set; }
242 public override ProjectPropertyElement Xml {
247 internal class ReservedProjectProperty : BaseProjectProperty
249 // seealso http://msdn.microsoft.com/en-us/library/ms164309.aspx
250 public static IEnumerable<ReservedProjectProperty> GetReservedProperties (Toolset toolset, Project project)
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 ()));
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);
277 public ReservedProjectProperty (Project project, string name, Func<string> value)
278 : base (project, PropertyType.Reserved, name)
283 // make sure it does not give access to any possible attempted overrrides.
284 public override ProjectProperty Predecessor {
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)); }
294 public override ProjectPropertyElement Xml {