BuildProperty evaluated = new BuildProperty (Name, Value);
// In evaluate phase, properties are not expanded
- Expression exp = new Expression ();
- exp.Parse (Value, ParseOptions.None);
- evaluated.finalValue = (string) exp.ConvertTo (parentProject, typeof (string),
- ExpressionOptions.DoNotExpandItemRefs);
+ evaluated.finalValue = Expression.ParseAs<string> (Value, ParseOptions.None,
+ parentProject, ExpressionOptions.DoNotExpandItemRefs);
parentProject.EvaluatedProperties.AddProperty (evaluated);
}
//
// Expression.cs: Stores references to items or properties.
//
-// Author:
+// Authors:
// Marek Sieradzki (marek.sieradzki@gmail.com)
+// Marek Safar (marek.safar@gmail.com)
//
// (C) 2005 Marek Sieradzki
+// Copyright (c) 2014 Xamarin Inc. (http://www.xamarin.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
ExpressionCollection expressionCollection;
static Regex item_regex;
- static Regex property_regex;
static Regex metadata_regex;
public Expression ()
else
parts = new string [] { expression };
+ // TODO: Too complicated, each part parses only its known part
+ // we should simply do it in one go and avoid all this parts code madness
+
List <ArrayList> p1 = new List <ArrayList> (parts.Length);
List <ArrayList> p2 = new List <ArrayList> (parts.Length);
List <ArrayList> p3 = new List <ArrayList> (parts.Length);
p2 [i] = new ArrayList ();
foreach (object o in p1 [i]) {
if (o is string)
- p2 [i].AddRange (SplitProperties ((string) o));
+ p2 [i].AddRange (ExtractProperties ((string) o));
else
p2 [i].Add (o);
}
return phase2;
}
- ArrayList SplitProperties (string text)
+ //
+ // Parses property syntax
+ //
+ static List<object> ExtractProperties (string text)
{
- ArrayList phase1 = new ArrayList ();
- Match m;
- m = PropertyRegex.Match (text);
+ var phase = new List<object> ();
- while (m.Success) {
- string name = null;
- PropertyReference pr;
-
- name = m.Groups [PropertyRegex.GroupNumberFromName ("name")].Value;
-
- pr = new PropertyReference (name, m.Groups [0].Index, m.Groups [0].Length);
- phase1.Add (pr);
- m = m.NextMatch ();
+ var pos = text.IndexOf ("$(", StringComparison.Ordinal);
+ if (pos < 0) {
+ phase.Add (text);
+ return phase;
}
- ArrayList phase2 = new ArrayList ();
- int last_end = -1;
- int end = text.Length - 1;
+ if (pos != 0) {
+ // Extract any whitespaces before property reference
+ phase.Add (text.Substring (0, pos));
+ }
- foreach (PropertyReference pr in phase1) {
- int a,b;
+ while (pos < text.Length) {
+ pos += 2;
+ int start = pos;
+ int end = 0;
+
+ var ch = text [pos];
+ if ((ch == 'r' || ch == 'R') && text.Substring (pos + 1).StartsWith ("egistry:", StringComparison.OrdinalIgnoreCase)) {
+ pos += 9;
+ ParseRegistryFunction (text, pos);
+ } else {
+ while (char.IsWhiteSpace (ch))
+ ch = text [pos++];
+
+ if (ch == '[') {
+ phase.Add (ParsePropertyFunction (text, ref pos));
+ } else {
+ // TODO: There is something like char index syntax as well: $(aa [10])
+ // text.IndexOf ('[');
+
+ end = text.IndexOf (')', pos) + 1;
+ if (end > 0) {
+ //
+ // Instance string method, $(foo.Substring (0, 3))
+ //
+ var dot = text.IndexOf ('.', pos, end - pos);
+ if (dot > 0) {
+ var name = text.Substring (start, dot - start);
+ ++dot;
+ var res = ParseInvocation (text, ref dot, null, new PropertyReference (name));
+ if (res != null) {
+ phase.Add (res);
+ end = dot;
+ }
+ } else {
+ var name = text.Substring (start, end - start - 1);
+
+ //
+ // Check for wrong syntax e.g $(foo()
+ //
+ var open_parens = name.IndexOf ('(');
+ if (open_parens < 0) {
+ //
+ // Simple property reference $(Foo)
+ //
+ phase.Add (new PropertyReference (name));
+ } else {
+ end = 0;
+ }
+ }
+ }
+
+ if (end == 0) {
+ end = text.Length;
+ start -= 2;
+ phase.Add (text.Substring (start, end - start));
+ }
+
+ pos = end;
+ }
+ }
- a = last_end;
- b = pr.Start;
+ end = text.IndexOf ("$(", pos, StringComparison.Ordinal);
+ if (end < 0)
+ end = text.Length;
- if (b - a - 1 > 0) {
- phase2.Add (text.Substring (a + 1, b - a - 1));
+ if (end - pos > 0)
+ phase.Add (text.Substring (pos, end - pos));
+
+ pos = end;
+ }
+
+ return phase;
+ }
+
+ //
+ // Property function with syntax $([Class]::Method(Parameters))
+ //
+ static MemberInvocationReference ParsePropertyFunction (string text, ref int pos)
+ {
+ int p = text.IndexOf ("]::", pos, StringComparison.Ordinal);
+ if (p < 0)
+ throw new InvalidProjectFileException (string.Format ("Invalid static method invocation syntax '{0}'", text.Substring (pos)));
+
+ var type_name = text.Substring (pos + 1, p - pos - 1);
+ var type = GetTypeForStaticMethod (type_name);
+ if (type == null) {
+ if (type_name.Contains ("."))
+ throw new InvalidProjectFileException (string.Format ("Invalid type '{0}' used in static method invocation", type_name));
+
+ throw new InvalidProjectFileException (string.Format ("'{0}': Static method invocation requires full type name to be used", type_name));
+ }
+
+ pos = p + 3;
+ return ParseInvocation (text, ref pos, type, null);
+ }
+
+ //
+ // Property function with syntax $(Registry:Call)
+ //
+ static void ParseRegistryFunction (string text, int pos)
+ {
+ throw new NotImplementedException ("Registry function");
+ }
+
+ static Type GetTypeForStaticMethod (string typeName)
+ {
+ //
+ // In static property functions, you can use any static method or property of these system classes:
+ //
+ switch (typeName.ToLowerInvariant ()) {
+ case "system.byte":
+ return typeof (byte);
+ case "system.char":
+ return typeof (char);
+ case "system.convert":
+ return typeof (Convert);
+ case "system.datetime":
+ return typeof (DateTime);
+ case "system.decimal":
+ return typeof (decimal);
+ case "system.double":
+ return typeof (double);
+ case "system.enum":
+ return typeof (Enum);
+ case "system.guid":
+ return typeof (Guid);
+ case "system.int16":
+ return typeof (Int16);
+ case "system.int32":
+ return typeof (Int32);
+ case "system.int64":
+ return typeof (Int64);
+ case "system.io.path":
+ return typeof (System.IO.Path);
+ case "system.math":
+ return typeof (Math);
+ case "system.uint16":
+ return typeof (UInt16);
+ case "system.uint32":
+ return typeof (UInt32);
+ case "system.uint64":
+ return typeof (UInt64);
+ case "system.sbyte":
+ return typeof (sbyte);
+ case "system.single":
+ return typeof (float);
+ case "system.string":
+ return typeof (string);
+ case "system.stringcomparer":
+ return typeof (StringComparer);
+ case "system.timespan":
+ return typeof (TimeSpan);
+ case "system.text.regularexpressions.regex":
+ return typeof (System.Text.RegularExpressions.Regex);
+ case "system.version":
+ return typeof (Version);
+ case "microsoft.build.utilities.toollocationhelper":
+ throw new NotImplementedException (typeName);
+ case "msbuild":
+ return typeof (PredefinedPropertyFunctions);
+ case "system.environment":
+ return typeof (System.Environment);
+ case "system.io.directory":
+ return typeof (System.IO.Directory);
+ case "system.io.file":
+ return typeof (System.IO.File);
+ }
+
+ return null;
+ }
+
+ static bool IsMethodAllowed (Type type, string name)
+ {
+ if (type == typeof (System.Environment)) {
+ switch (name.ToLowerInvariant ()) {
+ case "commandline":
+ case "expandenvironmentvariables":
+ case "getenvironmentvariable":
+ case "getenvironmentvariables":
+ case "getfolderpath":
+ case "getlogicaldrives":
+ return true;
}
- last_end = pr.End;
- phase2.Add (pr);
+ return false;
}
- if (last_end < end)
- phase2.Add (text.Substring (last_end + 1, end - last_end));
+ if (type == typeof (System.IO.Directory)) {
+ switch (name.ToLowerInvariant ()) {
+ case "getdirectories":
+ case "getfiles":
+ case "getlastaccesstime":
+ case "getlastwritetime":
+ return true;
+ }
- return phase2;
+ return false;
+ }
+
+ if (type == typeof (System.IO.File)) {
+ switch (name.ToLowerInvariant ()) {
+ case "getcreationtime":
+ case "getattributes":
+ case "getlastaccesstime":
+ case "getlastwritetime":
+ case "readalltext":
+ return true;
+ }
+ }
+
+ return true;
+ }
+
+ static List<string> ParseArguments (string text, ref int pos)
+ {
+ List<string> args = new List<string> ();
+ int parens = 0;
+ bool backticks = false;
+ int start = pos;
+ for (; pos < text.Length; ++pos) {
+ var ch = text [pos];
+
+ if (ch == '`') {
+ backticks = !backticks;
+ continue;
+ }
+
+ if (backticks)
+ continue;
+
+ if (ch == '(') {
+ ++parens;
+ continue;
+ }
+
+ if (ch == ')') {
+ if (parens == 0) {
+ var arg = text.Substring (start, pos - start).Trim ();
+ if (arg.Length > 0)
+ args.Add (arg);
+
+ ++pos;
+ return args;
+ }
+
+ --parens;
+ continue;
+ }
+
+ if (parens != 0)
+ continue;
+
+ if (ch == ',') {
+ args.Add (text.Substring (start, pos - start));
+ start = pos + 1;
+ continue;
+ }
+ }
+
+ // Invalid syntax
+ return null;
+ }
+
+ static MemberInvocationReference ParseInvocation (string text, ref int p, Type type, IReference instance)
+ {
+ var open_parens = text.IndexOf ('(', p);
+ string name;
+ int end;
+ List<string> args;
+
+ //
+ // Is it method or property
+ //
+ if (open_parens > 0) {
+ name = text.Substring (p, open_parens - p);
+
+ //
+ // It can be instance method on static property
+ //
+ if (name.IndexOf ('.') > 0) {
+ var names = name.Split ('.');
+ int i;
+ for (i = 0; i < names.Length - 1; ++i) {
+ instance = new MemberInvocationReference (type, names [i]) {
+ Instance = instance
+ };
+ }
+
+ type = null;
+ name = names [i];
+ }
+ ++open_parens;
+ args = ParseArguments (text, ref open_parens);
+ end = text.IndexOf (')', open_parens);
+ } else {
+ end = text.IndexOf (')', p);
+ if (end < 0)
+ throw new InvalidProjectFileException (string.Format ("Invalid static method invocation syntax '{0}'", text.Substring (p)));
+
+ name = text.Substring (p, end - p);
+ args = null;
+ }
+
+ name = name.TrimEnd ();
+ if (!IsMethodAllowed (type, name))
+ throw new InvalidProjectFileException (string.Format ("The function '{0}' on type '{1}' has not been enabled for execution", name, type.FullName));
+
+ p = end + 1;
+ return new MemberInvocationReference (type, name) {
+ Arguments = args,
+ Instance = instance
+ };
}
ArrayList SplitMetadata (string text)
return item_regex;
}
}
-
- static Regex PropertyRegex {
- get {
- if (property_regex == null)
- property_regex = new Regex (
- @"\$\(\s*"
- + @"(?<name>[_a-zA-Z][_\-0-9a-zA-Z]*)"
- + @"\s*\)");
- return property_regex;
- }
- }
-
+
static Regex MetadataRegex {
get {
if (metadata_regex == null)
using Microsoft.Build.Framework;
namespace Microsoft.Build.BuildEngine {
- internal interface IReference {
+ interface IReference {
string ConvertToString (Project project, ExpressionOptions options);
ITaskItem[] ConvertToITaskItemArray (Project project, ExpressionOptions options);
-
- int Start { get; }
- int End { get; }
}
}
--- /dev/null
+//
+// MemberInvocationReference.cs
+//
+// Authors:
+// Marek Safar <marek.safar@gmail.com>
+//
+// Copyright (C) 2014 Xamarin Inc (http://www.xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Globalization;
+using Microsoft.Build.Framework;
+using System.Text;
+
+namespace Microsoft.Build.BuildEngine
+{
+ class MemberInvocationReference : IReference
+ {
+ Type type;
+ readonly string name;
+
+ static readonly char[] ArgumentTrimChars = new char[] { '\"', '\'', '`' };
+ static readonly object ConversionFailed = new object ();
+
+ public MemberInvocationReference (Type type, string name)
+ {
+ this.type = type;
+ this.name = name;
+ }
+
+ public List<string> Arguments { get; set; }
+
+ public IReference Instance { get; set; }
+
+ public string ConvertToString (Project project, ExpressionOptions options)
+ {
+ return ConvertResult (Invoke (project, options));
+ }
+
+ object Invoke (Project project, ExpressionOptions options)
+ {
+ var flags = BindingFlags.IgnoreCase | BindingFlags.Public;
+ object target;
+
+ if (Instance == null) {
+ target = null;
+ flags |= BindingFlags.Static;
+ } else {
+ var mir = Instance as MemberInvocationReference;
+ if (mir != null) {
+ target = mir.Invoke (project, options);
+ if (target == null) {
+ throw new NotImplementedException ("Instance method on null value");
+ }
+
+ type = target.GetType ();
+ } else {
+ target = Instance.ConvertToString (project, options);
+ type = typeof (string);
+ }
+
+ flags |= BindingFlags.Instance;
+ }
+
+ object[] args;
+ if (Arguments == null) {
+ flags |= BindingFlags.GetProperty;
+ args = null;
+ } else {
+ flags |= BindingFlags.InvokeMethod;
+ ExpandArguments (project, options);
+ args = PrepareMethodArguments (flags);
+ if (args == null)
+ throw new InvalidProjectFileException (string.Format ("Method '{0}({1})' arguments cannot be evaluated'", name, string.Join (", ", Arguments.ToArray ())));
+ }
+
+ object value;
+ try {
+ value = type.InvokeMember (name, flags, null, target, args, CultureInfo.InvariantCulture);
+ } catch (MissingFieldException) {
+ //
+ // It can be field/constant instead of a property
+ //
+ if (args == null && Instance == null) {
+ flags &= ~BindingFlags.GetProperty;
+ flags |= BindingFlags.GetField;
+ value = type.InvokeMember (name, flags, null, null, null, CultureInfo.InvariantCulture);
+ } else {
+ throw;
+ }
+ }
+
+ return value;
+ }
+
+ void ExpandArguments (Project project, ExpressionOptions options)
+ {
+ for (int i = 0; i < Arguments.Count; ++i) {
+ string arg = Arguments [i].Trim ();
+ if (string.Equals (arg, "null", StringComparison.OrdinalIgnoreCase)) {
+ arg = null;
+ } else {
+ arg = Expression.ParseAs<string> (arg, ParseOptions.None,
+ project, options);
+
+ arg = arg.Trim (ArgumentTrimChars);
+ }
+
+ Arguments [i] = arg;
+ }
+ }
+
+ object[] PrepareMethodArguments (BindingFlags flags)
+ {
+ var candidates = type.GetMember (name, MemberTypes.Method, flags);
+ object[] args = null;
+ ParameterInfo[] best = null;
+ foreach (MethodBase candidate in candidates) {
+ var parameters = candidate.GetParameters ();
+ if (parameters.Length != Arguments.Count)
+ continue;
+
+ if (parameters.Length == 0)
+ return new object [0];
+
+ object[] cand_args = null;
+ for (int i = 0; i < parameters.Length; ++i) {
+ var target = ConvertArgument (Arguments [i], parameters [i]);
+ if (target == ConversionFailed) {
+ cand_args = null;
+ break;
+ }
+
+ if (cand_args == null)
+ cand_args = new object[parameters.Length];
+
+ cand_args [i] = target;
+ }
+
+ if (cand_args == null)
+ continue;
+
+ if (args == null) {
+ args = cand_args;
+ best = parameters;
+ continue;
+ }
+
+ if (BetterCandidate (best, parameters) > 1) {
+ args = cand_args;
+ best = parameters;
+ }
+ }
+
+ return args;
+ }
+
+ static object ConvertArgument (object value, ParameterInfo target)
+ {
+ var ptype = target.ParameterType;
+ if (ptype.IsEnum) {
+ var s = value as string;
+ if (s != null)
+ return ConvertToEnum (s, ptype);
+ }
+
+ try {
+ return Convert.ChangeType (value, ptype, CultureInfo.InvariantCulture);
+ } catch {
+ return ConversionFailed;
+ }
+ }
+
+ static object ConvertToEnum (string s, Type type)
+ {
+ var dot = s.IndexOf ('.');
+ if (dot < 0)
+ return ConversionFailed;
+
+ var fn = type.FullName + ".";
+ if (s.StartsWith (fn, StringComparison.Ordinal)) {
+ s = s.Substring (fn.Length);
+ } else if (s.StartsWith (type.Name, StringComparison.Ordinal) && s [type.Name.Length] == '.') {
+ s = s.Substring (type.Name.Length + 1);
+ }
+
+ try {
+ return Enum.Parse (type, s);
+ } catch {
+ return ConversionFailed;
+ }
+ }
+
+ static string ConvertResult (object value)
+ {
+ if (value is string)
+ return (string)value;
+
+ var e = value as IEnumerable;
+ if (e != null) {
+ var sb = new StringBuilder ();
+ foreach (var entry in e) {
+ if (sb.Length > 0)
+ sb.Append (";");
+
+ sb.Append (ConvertResult (entry));
+ }
+
+ return sb.ToString ();
+ }
+
+ return value == null ? "" : value.ToString ();
+ }
+
+ //
+ // Returns better candidate for untyped string values. We can really do only
+ // preference for string over any other types
+ //
+ // 1: a is better
+ // 2: b is better
+ // 0: neither is better
+ //
+ static int BetterCandidate (ParameterInfo[] a, ParameterInfo[] b)
+ {
+ int res = 0;
+ for (int i = 0; i < a.Length; ++i) {
+ var atype = a [i].ParameterType;
+ var btype = b [i].ParameterType;
+
+ if (atype == typeof (string) && btype != atype) {
+ if (res < 2)
+ res = 1;
+ continue;
+ }
+
+ if (btype == typeof (string) && btype != atype) {
+ if (res != 1)
+ res = 2;
+
+ continue;
+ }
+ }
+
+ return res;
+ }
+
+ public ITaskItem[] ConvertToITaskItemArray (Project project, ExpressionOptions options)
+ {
+ throw new NotImplementedException ();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+//
+// PredefinedPropertyFunctions.cs
+//
+// Authors:
+// Marek Safar <marek.safar@gmail.com>
+//
+// Copyright (C) 2014 Xamarin Inc (http://www.xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using Mono.XBuild.Utilities;
+
+namespace Microsoft.Build.BuildEngine
+{
+ static class PredefinedPropertyFunctions
+ {
+ public static double Add (double a, double b)
+ {
+ return a + b;
+ }
+
+ public static long Add (long a, long b)
+ {
+ return a + b;
+ }
+
+ public static double Subtract (double a, double b)
+ {
+ return a - b;
+ }
+
+ public static long Subtract (long a, long b)
+ {
+ return a - b;
+ }
+
+ public static double Multiply (double a, double b)
+ {
+ return a * b;
+ }
+
+ public static long Multiply (long a, long b)
+ {
+ return a * b;
+ }
+
+ public static double Divide (double a, double b)
+ {
+ return a / b;
+ }
+
+ public static long Divide (long a, long b)
+ {
+ return a / b;
+ }
+
+ public static double Modulo (double a, double b)
+ {
+ return a % b;
+ }
+
+ public static long Modulo (long a, long b)
+ {
+ return a % b;
+ }
+
+ public static string Escape (string unescaped)
+ {
+ return MSBuildUtils.Escape (unescaped);
+ }
+
+ public static string Unescape (string escaped)
+ {
+ return MSBuildUtils.Unescape (escaped);
+ }
+
+ public static int BitwiseOr (int first, int second)
+ {
+ return first | second;
+ }
+
+ public static int BitwiseAnd (int first, int second)
+ {
+ return first & second;
+ }
+
+ public static int BitwiseXor (int first, int second)
+ {
+ return first ^ second;
+ }
+
+ public static int BitwiseNot (int first)
+ {
+ return ~first;
+ }
+
+ public static bool DoesTaskHostExist (string theRuntime, string theArchitecture)
+ {
+ // TODO: What is this actually supposed to do?
+ return true;
+ }
+
+ public static string GetDirectoryNameOfFileAbove (string path, string file)
+ {
+ throw new NotImplementedException ("GetDirectoryNameOfFileAbove");
+ }
+
+ public static object GetRegistryValue (string key, string value)
+ {
+ throw new NotImplementedException ("GetRegistryValue");
+ }
+
+ public static object GetRegistryValueFromView (string key, string value, object defaultValue, params object[] views)
+ {
+ throw new NotImplementedException ("GetRegistryValueFromView");
+ }
+
+ public static string MakeRelative (string basePath, string path)
+ {
+ throw new NotImplementedException ("MakeRelative");
+ }
+
+ public static string ValueOrDefault (string value, string defaultValue)
+ {
+ return string.IsNullOrEmpty (value) ? defaultValue : value;
+ }
+ }
+}
\ No newline at end of file
using Microsoft.Build.Utilities;
namespace Microsoft.Build.BuildEngine {
- internal class PropertyReference : IReference {
+ class PropertyReference : IReference {
- string name;
- int start;
- int length;
-
- public PropertyReference (string name, int start, int length)
+ readonly string name;
+
+ public PropertyReference (string name)
{
this.name = name;
- this.start = start;
- this.length = length;
}
-
-
+
// when evaluating items: expand: true
// all other times, expand: true
// so, always true, ignore @options
return bp == null ? String.Empty : bp.Value;
}
- public int Start {
- get { return start; }
- }
-
- public int End {
- get { return start + length - 1; }
- }
-
public override string ToString ()
{
return String.Format ("$({0})", name);
Microsoft.Build.BuildEngine/IReference.cs
Microsoft.Build.BuildEngine/ItemReference.cs
Microsoft.Build.BuildEngine/LogExtensions.cs
+Microsoft.Build.BuildEngine/MemberInvocationReference.cs
Microsoft.Build.BuildEngine/MetadataReference.cs
+Microsoft.Build.BuildEngine/PredefinedPropertyFunctions.cs
Microsoft.Build.BuildEngine/Project.cs
Microsoft.Build.BuildEngine/ProjectLoadSettings.cs
Microsoft.Build.BuildEngine/PropertyPosition.cs
namespace MonoTests.Microsoft.Build.BuildEngine.Various {
[TestFixture]
public class Properties {
- [Test]
- public void TestProperties1 ()
+
+ Project proj;
+
+ [SetUp]
+ public void Setup ()
{
Engine engine = new Engine (Consts.BinPath);
- Project proj = engine.CreateNewProject ();
+ proj = engine.CreateNewProject ();
+ }
+ [Test]
+ public void PropertyReference ()
+ {
string documentString = @"
<Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
<PropertyGroup>
<Config>debug</Config>
<ExpProp>$(Config)-$(Config)</ExpProp>
+ <ExpProp2> $(Config) $(Config) </ExpProp2>
+ <InvProp1>$(Config-$(Config)</InvProp1>
</PropertyGroup>
</Project>
";
Assert.AreEqual (1, proj.PropertyGroups.Count, "A1");
Assert.AreEqual ("debug", proj.GetEvaluatedProperty ("Config"), "A2");
Assert.AreEqual ("debug-debug", proj.GetEvaluatedProperty ("ExpProp"), "A3");
+ Assert.AreEqual (" debug debug ", proj.GetEvaluatedProperty ("ExpProp2"), "A4");
+ Assert.AreEqual ("$(Config-$(Config)", proj.GetEvaluatedProperty ("InvProp1"), "A5");
}
[Test]
[Category ("NotDotNet")]
- public void TestProperties2 ()
+ public void PropertyReference2 ()
{
- Engine engine = new Engine (Consts.BinPath);
- Project proj = engine.CreateNewProject ();
-
string documentString = @"
<Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
<UsingTask TaskName='StringTestTask' AssemblyFile='Test\resources\TestTasks.dll' />
Assert.AreEqual (1, proj.GetEvaluatedItemsByName ("Out").Count, "A1");
Assert.AreEqual ("AB", proj.GetEvaluatedItemsByName ("Out") [0].Include, "A2");
}
+
+ [Test]
+ public void StringInstanceProperties ()
+ {
+ string documentString = @"
+ <Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
+ <PropertyGroup>
+ <Config>debug</Config>
+ <NullValue>null</NullValue>
+ <Prop1>$(Config.Substring(0,3)) </Prop1>
+ <Prop2>$(Config.Length )</Prop2>
+ <Prop3>$(Config.StartsWith ('DE', System.StringComparison.OrdinalIgnoreCase))</Prop3>
+ <Prop4>$(NullValue.StartsWith ('Te', StringComparison.OrdinalIgnoreCase))</Prop4>
+ </PropertyGroup>
+ </Project>
+ ";
+
+ proj.LoadXml (documentString);
+ Assert.AreEqual ("deb ", proj.GetEvaluatedProperty ("Prop1"), "#1");
+ Assert.AreEqual ("5", proj.GetEvaluatedProperty ("Prop2"), "#2");
+ Assert.AreEqual ("True", proj.GetEvaluatedProperty ("Prop3"), "#3");
+ Assert.AreEqual ("False", proj.GetEvaluatedProperty ("Prop4"), "#4");
+ }
+
+ [Test]
+ public void AllowedFrameworkMembers ()
+ {
+ string documentString = @"
+ <Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
+ <PropertyGroup>
+ <Prop1>$([System.Byte]::MaxValue)</Prop1>
+ <Prop2>$([System.math]::Abs (-4.2) )</Prop2>
+ <Prop3>$([System.DateTime]::Today )</Prop3>
+ <Prop4>$([System.Char]::GetNumericValue('3'))</Prop4>
+ <Prop5>$([System.String]::Compare (Null, nUll))</Prop5>
+ <Prop6>$([System.Environment]::GetLogicalDrives ( ))</Prop6>
+ <Prop7>$([System.String]::Concat (`,`, `n`, `,`))</Prop7>
+ </PropertyGroup>
+ </Project>
+ ";
+
+ proj.LoadXml (documentString);
+ Assert.AreEqual ("255", proj.GetEvaluatedProperty ("Prop1"), "#1");
+ Assert.AreEqual ("4.2", proj.GetEvaluatedProperty ("Prop2"), "#2");
+ Assert.AreEqual (DateTime.Today.ToString (), proj.GetEvaluatedProperty ("Prop3"), "#3");
+ Assert.AreEqual ("3", proj.GetEvaluatedProperty ("Prop4"), "#4");
+ Assert.AreEqual ("0", proj.GetEvaluatedProperty ("Prop5"), "#5");
+ Assert.AreEqual (string.Join (";", Environment.GetLogicalDrives ()), proj.GetEvaluatedProperty ("Prop6"), "#6");
+ Assert.AreEqual (",n,", proj.GetEvaluatedProperty ("Prop7"), "#7");
+ }
+
+ [Test]
+ public void InstanceMethodOnStaticProperty ()
+ {
+ string documentString = @"
+ <Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
+ <PropertyGroup>
+ <Prop1>$([System.DateTime]::Now.ToString(""yyyy.MM.dd""))</Prop1>
+ </PropertyGroup>
+ </Project>
+ ";
+
+ proj.LoadXml (documentString);
+ Assert.AreEqual (DateTime.Now.ToString ("yyyy.MM.dd"), proj.GetEvaluatedProperty ("Prop1"), "#1");
+ }
+
+ [Test]
+ public void MSBuildPropertyFunctions ()
+ {
+ string documentString = @"
+ <Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
+ <PropertyGroup>
+ <NumberOne>0.6</NumberOne>
+ <NumberTwo>6</NumberTwo>
+ <Prop1>$([MSBuild]::Add($(NumberOne), $(NumberTwo)))</Prop1>
+ </PropertyGroup>
+ </Project>
+ ";
+
+ proj.LoadXml (documentString);
+ Assert.AreEqual ("6.6", proj.GetEvaluatedProperty ("Prop1"), "#1");
+ }
}
}