Imported Upstream version 3.6.0

Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
This commit is contained in:
Jo Shields
2014-08-13 10:39:27 +01:00
commit a575963da9
50588 changed files with 8155799 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,209 @@
//
// BuildNodeManager.cs
//
// Author:
// Atsushi Enomoto (atsushi@xamarin.com)
//
// Copyright (C) 2013 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.Generic;
using System.Linq;
using Microsoft.Build.Execution;
using Microsoft.Build.Framework;
using System.Threading.Tasks;
using System.Threading;
using System.Collections.Concurrent;
namespace Microsoft.Build.Internal
{
class BuildNodeManager
{
public BuildNodeManager (BuildManager buildManager)
{
BuildManager = buildManager;
new Thread (RunLoop).Start ();
}
~BuildNodeManager ()
{
run_loop = false;
queue_wait_handle.Set ();
}
public BuildManager BuildManager { get; private set; }
List<BuildNode> in_proc_nodes = new List<BuildNode> ();
List<BuildNode> out_proc_nodes = new List<BuildNode> ();
AutoResetEvent queue_wait_handle = new AutoResetEvent (false);
ConcurrentQueue<BuildSubmission> queued_builds = new ConcurrentQueue<BuildSubmission> ();
// FIXME: currently it is not in use but it should be stored somewhere for cancellation.
Dictionary<BuildSubmission,Task> ongoing_builds = new Dictionary<BuildSubmission, Task> ();
bool run_loop = true;
readonly TaskFactory<BuildResult> task_factory = new TaskFactory<BuildResult> ();
internal TaskFactory<BuildResult> ThreadTaskFactory {
get { return task_factory; }
}
void RunLoop ()
{
while (run_loop) {
try {
if (queued_builds.Count == 0)
queue_wait_handle.WaitOne ();
if (!run_loop)
break;
BuildSubmission build;
if (!queued_builds.TryDequeue (out build))
continue;
StartOneBuild (build);
} catch (Exception ex) {
// FIXME: I guess INodeLogger should be used instead.
Console.Error.WriteLine ("Uncaught build node exception occured");
Console.Error.WriteLine (ex);
}
}
}
public void Stop ()
{
run_loop = false;
queue_wait_handle.Set ();
}
public void ResetCaches ()
{
in_proc_nodes.Clear ();
out_proc_nodes.Clear ();
}
public void Enqueue (BuildSubmission build)
{
queued_builds.Enqueue (build);
queue_wait_handle.Set ();
}
void StartOneBuild (BuildSubmission build)
{
var node = TakeNode (build);
// FIXME: Task (non-generic) here causes NotImplementedException in somewhere in Interlocked. It does not make sense.
ongoing_builds [build] = task_factory.StartNew (node.ExecuteBuild);
//new Thread (() => { node.ExecuteBuild (); }).Start ();
}
void EndOneBuild (BuildNode node)
{
var task = ongoing_builds [node.Build];
ongoing_builds [node.Build] = null;
node.Release ();
}
// FIXME: take max nodes into account here, and get throttling working.
BuildNode TakeNode (BuildSubmission build)
{
var host = BuildManager.OngoingBuildParameters.HostServices;
NodeAffinity affinity;
if (host == null)
affinity = NodeAffinity.Any;
else
affinity = host.GetNodeAffinity (build.BuildRequest.ProjectFullPath);
BuildNode n = GetReusableNode (affinity);
if (n != null)
n.Assign (build);
else {
n = new BuildNode (this, affinity == NodeAffinity.Any ? NodeAffinity.InProc : affinity);
n.Assign (build);
if (n.Affinity == NodeAffinity.InProc)
in_proc_nodes.Add (n);
else
out_proc_nodes.Add (n);
}
return n;
}
BuildNode GetReusableNode (NodeAffinity affinity)
{
if (!BuildManager.OngoingBuildParameters.EnableNodeReuse)
return null;
if (affinity != NodeAffinity.OutOfProc)
foreach (var n in in_proc_nodes)
if (n.IsAvailable && (n.Affinity & affinity) != 0)
return n;
if (affinity != NodeAffinity.InProc)
foreach (var n in out_proc_nodes)
if (n.IsAvailable && (n.Affinity & affinity) != 0)
return n;
return null;
}
internal class BuildNode
{
static Random rnd = new Random ();
public BuildNode (BuildNodeManager manager, NodeAffinity affinity)
{
Manager = manager;
Affinity = affinity;
Id = rnd.Next ();
}
public bool IsAvailable { get; private set; }
public int Id { get; private set; }
public BuildNodeManager Manager { get; set; }
public NodeAffinity Affinity { get; private set; }
public BuildSubmission Build { get; private set; }
public void Assign (BuildSubmission build)
{
IsAvailable = false;
Build = build;
}
public void Release ()
{
Build = null;
IsAvailable = true;
}
public BuildResult ExecuteBuild ()
{
BuildResult result;
try {
// FIXME: depending on NodeAffinity, build it through another MSBuild process.
if (Affinity == NodeAffinity.OutOfProc)
throw new NotImplementedException ();
result = Build.InternalExecute ();
} catch (Exception ex) {
// FIXME: I guess INodeLogger should be used instead.
Console.Error.WriteLine ("Uncaught build node exception occured");
Console.Error.WriteLine (ex);
result = null;
} finally {
Manager.EndOneBuild (this);
}
return result;
}
}
}
}

View File

@@ -0,0 +1,172 @@
//
// BuildTaskFactory.cs
//
// Author:
// Atsushi Enomoto (atsushi@xamarin.com)
//
// Copyright (C) 2013 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.Generic;
using System.Linq;
using Microsoft.Build.Framework;
using System.Reflection;
using Microsoft.Build.Execution;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Construction;
using System.IO;
using System.Xml;
namespace Microsoft.Build.Internal
{
class BuildTaskDatabase
{
const string default_tasks_file = "Microsoft.Common.tasks";
static readonly Dictionary<string,BuildTaskDatabase> default_factory = new Dictionary<string, BuildTaskDatabase> ();
public static BuildTaskDatabase GetDefaultTaskDatabase (Toolset toolset)
{
if (toolset == null)
throw new ArgumentNullException ("toolset");
BuildTaskDatabase defaults;
if (!default_factory.TryGetValue (toolset.ToolsVersion, out defaults)) {
defaults = new BuildTaskDatabase (toolset);
}
return defaults;
}
// for 'default' tasks.
BuildTaskDatabase (Toolset toolset)
{
ProjectRootElement root;
using (var xml = XmlReader.Create (Path.Combine (toolset.ToolsPath, default_tasks_file)))
root = ProjectRootElement.Create (xml);
LoadUsingTasks (null, root.UsingTasks);
}
public BuildTaskDatabase (IBuildEngine engine, ProjectInstance projectInstance)
{
this.engine = engine;
LoadUsingTasks (projectInstance, projectInstance.UsingTasks);
}
internal class TaskDescription
{
public TaskAssembly TaskAssembly { get; set; }
public string Name { get; set; }
public Type TaskFactoryType { get; set; }
public Type TaskType { get; set; }
public IDictionary<string, TaskPropertyInfo> TaskFactoryParameters { get; set; }
public string TaskBody { get; set; }
public bool IsMatch (string name)
{
int ridx = Name.LastIndexOf ('.');
int tidx = name.IndexOf ('.');
return string.Equals (Name, name, StringComparison.OrdinalIgnoreCase) ||
tidx < 0 && ridx > 0 && string.Equals (Name.Substring (ridx + 1), name, StringComparison.OrdinalIgnoreCase);
}
}
internal class TaskAssembly
{
public string AssemblyName { get; set; }
public string AssemblyFile { get; set; }
public Assembly LoadedAssembly { get; set; }
}
readonly IBuildEngine engine;
readonly List<TaskAssembly> assemblies = new List<TaskAssembly> ();
readonly List<TaskDescription> task_descs = new List<TaskDescription> ();
public List<TaskDescription> Tasks {
get { return task_descs; }
}
// FIXME: my guess is the tasks does not have to be loaded entirely but only requested tasks must be loaded at invocation time.
void LoadUsingTasks (ProjectInstance projectInstance, IEnumerable<ProjectUsingTaskElement> usingTasks)
{
Func<string,bool> cond = s => projectInstance != null ? projectInstance.EvaluateCondition (s) : Convert.ToBoolean (s);
Func<string,string> expand = s => projectInstance != null ? projectInstance.ExpandString (s) : s;
foreach (var ut in usingTasks) {
var aName = expand (ut.AssemblyName);
var aFile = expand (ut.AssemblyFile);
if (string.IsNullOrEmpty (aName) && string.IsNullOrEmpty (aFile)) {
var errorNoAssembly = string.Format ("Task '{0}' does not specify either of AssemblyName or AssemblyFile.", ut.TaskName);
engine.LogWarningEvent (new BuildWarningEventArgs (null, null, projectInstance.FullPath, ut.Location.Line, ut.Location.Column, 0, 0, errorNoAssembly, null, null));
continue;
}
var ta = assemblies.FirstOrDefault (a => a.AssemblyFile.Equals (aFile, StringComparison.OrdinalIgnoreCase) || a.AssemblyName.Equals (aName, StringComparison.OrdinalIgnoreCase));
if (ta == null) {
var path = Path.GetDirectoryName (string.IsNullOrEmpty (ut.Location.File) ? projectInstance.FullPath : ut.Location.File);
ta = new TaskAssembly () { AssemblyName = aName, AssemblyFile = aFile };
try {
ta.LoadedAssembly = !string.IsNullOrEmpty (ta.AssemblyName) ? Assembly.Load (ta.AssemblyName) : Assembly.LoadFile (Path.Combine (path, ta.AssemblyFile));
} catch {
var errorNotLoaded = string.Format ("For task '{0}' Specified assembly '{1}' was not found", ut.TaskName, string.IsNullOrEmpty (ta.AssemblyName) ? Path.Combine (path, ta.AssemblyFile) : ta.AssemblyName);
engine.LogWarningEvent (new BuildWarningEventArgs (null, null, projectInstance.FullPath, ut.Location.Line, ut.Location.Column, 0, 0, errorNotLoaded, null, null));
continue;
}
assemblies.Add (ta);
}
var pg = ut.ParameterGroup == null ? null : ut.ParameterGroup.Parameters.Select (p => new TaskPropertyInfo (p.Name, Type.GetType (p.ParameterType), cond (p.Output), cond (p.Required)))
.ToDictionary (p => p.Name);
Type type = null;
string error = null;
TaskDescription task = new TaskDescription () {
TaskAssembly = ta,
Name = ut.TaskName,
TaskFactoryParameters = pg,
TaskBody = ut.TaskBody != null && cond (ut.TaskBody.Condition) ? ut.TaskBody.TaskBody : null,
};
if (string.IsNullOrEmpty (ut.TaskFactory)) {
type = LoadTypeFrom (ta.LoadedAssembly, ut.TaskName, ut.TaskName);
if (type == null)
error = string.Format ("For task '{0}' Specified type '{1}' was not found in assembly '{2}'", ut.TaskName, ut.TaskName, ta.LoadedAssembly.FullName);
else
task.TaskType = type;
} else {
type = LoadTypeFrom (ta.LoadedAssembly, ut.TaskName, ut.TaskFactory);
if (type == null)
error = string.Format ("For task '{0}' Specified factory type '{1}' was not found in assembly '{2}'", ut.TaskName, ut.TaskFactory, ta.LoadedAssembly.FullName);
else
task.TaskFactoryType = type;
}
if (error != null)
engine.LogWarningEvent (new BuildWarningEventArgs (null, null, projectInstance.FullPath, ut.Location.Line, ut.Location.Column, 0, 0, error, null, null));
else
task_descs.Add (task);
}
}
Type LoadTypeFrom (Assembly a, string taskName, string possiblyShortTypeName)
{
Type type = a.GetType (possiblyShortTypeName, false, true);
if (possiblyShortTypeName.IndexOf ('.') < 0)
type = a.GetTypes ().FirstOrDefault (t => t.Name == possiblyShortTypeName);
return type;
}
}
}

View File

@@ -0,0 +1,81 @@
// BuildTaskFactory.cs
//
// Author:
// Atsushi Enomoto (atsushi@xamarin.com)
//
// Copyright (C) 2013 Xamarin Inc.
//
// 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.Threading;
using System.Threading.Tasks;
using System.Linq;
using Microsoft.Build.Framework;
using Microsoft.Build.Internal;
using System.Collections.Generic;
using Microsoft.Build.Execution;
namespace Microsoft.Build.Internal
{
class BuildTaskFactory
{
public BuildTaskFactory (BuildTaskDatabase builtInDatabase, BuildTaskDatabase perProjectDatabase)
{
this.built_in_database = builtInDatabase;
this.per_project_database = perProjectDatabase;
}
readonly BuildTaskDatabase built_in_database, per_project_database;
readonly List<ITaskFactory> task_factories = new List<ITaskFactory> ();
public void ResetCaches ()
{
task_factories.Clear ();
}
public ITask CreateTask (string name, IDictionary<string,string> factoryIdentityParameters, IBuildEngine engine)
{
Func<BuildTaskDatabase.TaskDescription,bool> fn = t => t.IsMatch (name);
var td = per_project_database.Tasks.FirstOrDefault (fn) ?? built_in_database.Tasks.FirstOrDefault (fn);
if (td == null)
throw new InvalidOperationException (string.Format ("Task '{0}' could not be found", name));
if (td.TaskFactoryType != null) {
var tf = task_factories.FirstOrDefault (f => f.GetType () == td.TaskFactoryType);
if (tf == null) {
tf = (ITaskFactory) Activator.CreateInstance (td.TaskFactoryType);
#if NET_4_5
var tf2 = tf as ITaskFactory2;
if (tf2 != null)
tf2.Initialize (name, factoryIdentityParameters, td.TaskFactoryParameters, td.TaskBody, engine);
else
#endif
tf.Initialize (name, td.TaskFactoryParameters, td.TaskBody, engine);
task_factories.Add (tf);
}
return tf.CreateTask (engine);
}
else
return (ITask) Activator.CreateInstance (td.TaskType);
}
}
}

View File

@@ -0,0 +1,104 @@
//
// CollectionFromEnumerable.cs
//
// Author:
// Leszek Ciesielski (skolima@gmail.com)
//
// (C) 2011 Leszek Ciesielski
//
// 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.Generic;
using System.Linq;
using System.Text;
namespace Microsoft.Build.Internal
{
internal class CollectionFromEnumerable<T> : ICollection<T>
{
IEnumerable<T> backingEnumerable;
public CollectionFromEnumerable (IEnumerable<T> enumerable)
{
backingEnumerable = enumerable;
}
public void Add (T item)
{
throw new InvalidOperationException ("This collection is read-only.");
}
public void Clear ()
{
throw new InvalidOperationException ("This collection is read-only.");
}
public bool Contains (T item)
{
List<T> backingList = backingEnumerable as List<T>;
if (backingList != null)
return backingList.Contains (item);
return backingEnumerable.Contains (item);
}
public void CopyTo (T[] array, int arrayIndex)
{
List<T> backingList = backingEnumerable as List<T>;
if (backingList != null) {
backingList.CopyTo (array, arrayIndex);
return;
}
int i = arrayIndex;
foreach (var item in backingEnumerable) {
array[i++] = item;
}
}
public int Count {
get {
var backingList = backingEnumerable as List<T>;
if(backingList == null)
backingEnumerable = backingList = new List<T> (backingEnumerable);
return backingList.Count;
}
}
public bool IsReadOnly {
get { return true; }
}
public bool Remove (T item)
{
throw new InvalidOperationException ("This collection is read-only.");
}
public IEnumerator<T> GetEnumerator ()
{
return backingEnumerable.GetEnumerator ();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator ()
{
return backingEnumerable.GetEnumerator ();
}
}
}

View File

@@ -0,0 +1,244 @@
//
// ExpressionConstructs.cs
//
// Author:
// Atsushi Enomoto (atsushi@xamarin.com)
//
// Copyright (C) 2013 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.Linq;
namespace Microsoft.Build.Internal.Expressions
{
class Locatable
{
public ILocation Location { get; set; }
}
partial class ExpressionList : ILocation, IEnumerable<Expression>
{
public ExpressionList ()
{
}
public ExpressionList (Expression entry)
{
Add (entry);
}
public int Count {
get { return list.Count; }
}
//public int Line {
// get { return list.Count == 0 ? 0 : list [0].Line; }
//}
public int Column {
get { return list.Count == 0 ? 0 : list [0].Column; }
}
public string File {
get { return list.Count == 0 ? null : list [0].File; }
}
public string ToLocationString ()
{
return list.Count == 0 ? null : list [0].Location.ToLocationString ();
}
public IEnumerator<Expression> GetEnumerator ()
{
return list.GetEnumerator ();
}
IEnumerator IEnumerable.GetEnumerator ()
{
return list.GetEnumerator ();
}
List<Expression> list = new List<Expression> ();
public ExpressionList Add (Expression expr)
{
list.Add (expr);
return this;
}
public ExpressionList Insert (int pos, Expression expr)
{
list.Insert (pos, expr);
return this;
}
public override string ToString ()
{
return string.Join (" ", list.Select (i => i.ToString ()));
}
}
abstract partial class Expression : Locatable, ILocation
{
//public int Line {
// get { return Location.Line; }
//}
public int Column {
get { return Location.Column; }
}
public string File {
get { return Location.File; }
}
public string ToLocationString ()
{
return Location.ToLocationString ();
}
}
enum Operator
{
EQ,
NE,
LT,
LE,
GT,
GE,
And,
Or
}
partial class BinaryExpression : Expression
{
public Operator Operator { get; set; }
public Expression Left { get; set; }
public Expression Right { get; set; }
public override string ExpressionString {
get { return string.Format ("{0} {1} {2}", Left, Operator, Right); }
}
}
partial class BooleanLiteral : Expression
{
public bool Value { get; set; }
public override string ExpressionString {
get { return Value ? "true" : "false"; }
}
}
partial class NotExpression : Expression
{
public Expression Negated { get; set; }
public override string ExpressionString {
get { return string.Format ("!{0}", Negated); }
}
}
partial class PropertyAccessExpression : Expression
{
public PropertyAccess Access { get; set; }
public override string ExpressionString {
get { return Access.ExpressionString; }
}
}
enum PropertyTargetType
{
Object,
Type,
}
class PropertyAccess : Locatable
{
public NameToken Name { get; set; }
public Expression Target { get; set; }
public PropertyTargetType TargetType { get; set; }
public ExpressionList Arguments { get; set; }
public string ExpressionString {
get { return string.Format ("$([{0}][{1}][{2}][{3}])", Target, TargetType, Name, Arguments != null && Arguments.Any () ? string.Join (", ", Arguments.Select (e => e.ExpressionString)) : null); }
}
}
partial class ItemAccessExpression : Expression
{
public ItemApplication Application { get; set; }
public override string ExpressionString {
get { return Application.ExpressionString; }
}
}
class ItemApplication : Locatable
{
public NameToken Name { get; set; }
public ExpressionList Expressions { get; set; }
public string ExpressionString {
get { return string.Format ("@([{0}][{1}])", Name, Expressions != null && Expressions.Any () ? string.Join (" ||| ", Expressions.Select (e => e.ExpressionString)) : null); }
}
}
partial class MetadataAccessExpression : Expression
{
public MetadataAccess Access { get; set; }
public override string ExpressionString {
get { return Access.ExpressionString; }
}
}
class MetadataAccess : Locatable
{
public NameToken Metadata { get; set; }
public NameToken ItemType { get; set; }
public string ExpressionString {
get { return string.Format ("%([{0}].[{1}])", ItemType, Metadata); }
}
}
partial class StringLiteral : Expression
{
public NameToken Value { get; set; }
public override string ExpressionString {
get { return '"' + Value.ToString () + '"'; }
}
}
partial class RawStringLiteral : Expression
{
public NameToken Value { get; set; }
public override string ExpressionString {
get { return "[rawstr] " + Value; }
}
}
partial class FunctionCallExpression : Expression
{
public NameToken Name { get; set; }
public ExpressionList Arguments { get; set; }
public override string ExpressionString {
get { return string.Format ("[func] {0}({1})", Name, string.Join (", ", Arguments.Select (e => e.ExpressionString))); }
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,264 @@
//
// ExpressionParser.jay
//
// Author:
// Atsushi Enomoto (atsushi@xamarin.com)
//
// Copyright (C) 2013 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.Text;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Exceptions;
using Microsoft.Build.Framework;
/*
Pseudo formal syntax for .NET 4.0 expression:
Condition = Expression
Include = Expression*
Expression
BooleanLiteral
TrueLiteral
FalseLiteral
BinaryExpression
Expression "==" Expression
Expression "!=" Expression
Expression ">" Expression
Expression ">=" Expression
Expression "<" Expression
Expression "<=" Expression
Expression "And" Expression
Expression "Or" Expression
UnaryExpression
"!" Expression
PropertyExpression
"$(" PropertyApplication ")"
ItemExpression
"@(" ItemApplication ")"
MetadataBatchingExpression
"%(" MetadataBatchingApplication ")"
StringLiteralOrFunction
StringLiteralOrFunctionName ( "(" FunctionArguments ")" )?
.NET error messages are so detailed which is something like "you forgot '(' after '$' ?" - so
it is likely that the MS tokenizer is hand-written.
*/
namespace Microsoft.Build.Internal.Expressions
{
class ExpressionParser
{
static readonly int yacc_verbose_flag = Environment.GetEnvironmentVariable ("MONO_MSBUILD_PARSER_DEBUG") == "1" ? 1 : 0;
object debug_obj = yacc_verbose_flag == 0 ? null : new yydebug.yyDebugSimple ();
public ExpressionList Parse (string source, ExpressionValidationType validationType)
{
var tokenizer = new ExpressionTokenizer (source, validationType);
return (ExpressionList) yyparse (tokenizer, debug_obj);
}
BinaryExpression Binary (Operator op, object left, object right)
{
return new BinaryExpression () { Operator = op, Left = (Expression) left, Right = (Expression) right, Location = (ILocation) left };
}
%}
%token TRUE_LITERAL
%token FALSE_LITERAL
%token STRING_LITERAL
%token EQ // ==
%token NE // !=
%token GT // >
%token GE // >=
%token LT // <
%token LE // <=
%token AND // AND
%token OR // OR
%token NOT //!
%token DOT //.
%token COMMA //,
%token PROP_OPEN // $(
%token ITEM_OPEN // @(
%token METADATA_OPEN // %(
%token PAREN_OPEN // (
%token PAREN_CLOSE // )
%token BRACE_OPEN // [
%token BRACE_CLOSE // ]
%token COLON2 // ::
%token ARROW // ->
%token NAME
%token ERROR
%start ExpressionList
%%
ExpressionList
: /* empty */
{ $$ = new ExpressionList (); }
| ExpressionList Expression
{ $$ = ((ExpressionList) $1).Add ((Expression) $2); }
;
Expression
: LogicalExpression
;
LogicalExpression
: ComparisonExpression
| LogicalExpression AND LogicalExpression
{ $$ = Binary (Operator.And, $1, $3); }
| LogicalExpression OR LogicalExpression
{ $$ = Binary (Operator.Or, $1, $3); }
;
ComparisonExpression
: UnaryExpression
| UnaryExpression EQ UnaryExpression
{ $$ = Binary (Operator.EQ, $1, $3); }
| UnaryExpression NE UnaryExpression
{ $$ = Binary (Operator.NE, $1, $3); }
| UnaryExpression GT UnaryExpression
{ $$ = Binary (Operator.GT, $1, $3); }
| UnaryExpression GE UnaryExpression
{ $$ = Binary (Operator.GE, $1, $3); }
| UnaryExpression LT UnaryExpression
{ $$ = Binary (Operator.LT, $1, $3); }
| UnaryExpression LE UnaryExpression
{ $$ = Binary (Operator.LE, $1, $3); }
;
UnaryExpression
: PrimaryExpression
| NOT UnaryExpression
{ $$ = new NotExpression () { Negated = (Expression) $2, Location = (ILocation) $1 }; }
;
PrimaryExpression
: BooleanLiteral
| StringLiteral
| UnaryExpression
| PropertyAccessExpression
| ItemAccessExpression
| MetadataAccessExpression
| RawStringLiteralOrFunction
| ParenthesizedExpression
;
BooleanLiteral
: TRUE_LITERAL
{ $$ = new BooleanLiteral () { Value = true, Location = (ILocation) $1 }; }
| FALSE_LITERAL
{ $$ = new BooleanLiteral () { Value = false, Location = (ILocation) $1 }; }
;
PropertyAccessExpression
: PROP_OPEN PropertyAccess PAREN_CLOSE
{ $$ = new PropertyAccessExpression () { Access = (PropertyAccess) $2, Location = (ILocation) $1 }; }
;
PropertyAccess
: NAME
{ $$ = new PropertyAccess () { Name = (NameToken) $1, TargetType = PropertyTargetType.Object, Location = (NameToken) $1 }; }
| Expression DOT NAME
{ $$ = new PropertyAccess () { Name = (NameToken) $3, Target = (Expression) $1, TargetType = PropertyTargetType.Object, Location = (ILocation) $1 }; }
| BRACE_OPEN QualifiedNameExpression BRACE_CLOSE COLON2 NAME
{ $$ = new PropertyAccess () { Name = (NameToken) $5, Target = (Expression) $2, TargetType = PropertyTargetType.Type, Location = (ILocation) $1 }; }
| BRACE_OPEN QualifiedNameExpression BRACE_CLOSE COLON2 NAME PAREN_OPEN FunctionCallArguments PAREN_CLOSE
{ $$ = new PropertyAccess () { Name = (NameToken) $5, Target = (Expression) $2, TargetType = PropertyTargetType.Type, Arguments = (ExpressionList) $7, Location = (ILocation) $1 }; }
;
QualifiedNameExpression
: QualifiedName
{ $$ = new StringLiteral () { Value = (NameToken) $1, Location = (ILocation) $1 }; }
;
QualifiedName
: NAME
| QualifiedName DOT NAME
{ $$ = new NameToken () { Name = ((NameToken) $1).Name + "." + ((NameToken) $3).Name, Column = ((ILocation) $1).Column }; }
;
ItemAccessExpression
: ITEM_OPEN ItemApplication PAREN_CLOSE
{ $$ = new ItemAccessExpression () { Application = (ItemApplication) $2, Location = (ILocation) $1 }; }
;
// looking a bit messy, but gives different location
ItemApplication
: NAME
{ $$ = new ItemApplication () { Name = (NameToken) $1, Location = (ILocation) $1 }; }
| NAME ARROW ExpressionList
{ $$ = new ItemApplication () { Name = (NameToken) $1, Expressions = (ExpressionList) $3, Location = (ILocation) $1 }; }
;
MetadataAccessExpression
: METADATA_OPEN MetadataAccess PAREN_CLOSE
{ $$ = new MetadataAccessExpression () { Access = (MetadataAccess) $2, Location = (ILocation) $1 }; }
;
// looking a bit messy, but gives different location
MetadataAccess
: NAME
{ $$ = new MetadataAccess () { Metadata = (NameToken) $1, Location = (ILocation) $1 }; }
| NAME DOT NAME
{ $$ = new MetadataAccess () { ItemType = (NameToken) $1, Metadata = (NameToken) $3, Location = (ILocation) $1 }; }
;
StringLiteral
: STRING_LITERAL
{ $$ = new StringLiteral () { Value = (NameToken) $1, Location = (ILocation) $1 }; }
;
RawStringLiteralOrFunction
: NAME
{ $$ = new RawStringLiteral () { Value = (NameToken) $1, Location = (ILocation) $1 }; }
| NAME PAREN_OPEN PAREN_CLOSE
{ $$ = new FunctionCallExpression () { Name = (NameToken) $1, Arguments = new ExpressionList (), Location = (ILocation) $1 }; }
| NAME PAREN_OPEN FunctionCallArguments PAREN_CLOSE
{ $$ = new FunctionCallExpression () { Name = (NameToken) $1, Arguments = (ExpressionList) $3, Location = (ILocation) $1 }; }
;
FunctionCallArguments
: /* empty */
{ $$ = new ExpressionList (); }
| Expression
{ $$ = new ExpressionList ().Add ((Expression) $1); }
| FunctionCallArguments COMMA Expression
{ $$ = ((ExpressionList) $1).Add ((Expression) $3); }
;
ParenthesizedExpression
: PAREN_OPEN Expression PAREN_CLOSE
{ $$ = (Expression) $2; }
;
%%
}

View File

@@ -0,0 +1,271 @@
//
// ExpressionParserManual.cs
//
// Author:
// Atsushi Enomoto (atsushi@xamarin.com)
//
// Copyright (C) 2013 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.Generic;
using System.Linq;
using Microsoft.Build.Exceptions;
namespace Microsoft.Build.Internal.Expressions
{
class ExpressionParserManual
{
// FIXME: we are going to not need ExpressionValidationType for this; always LaxString.
public ExpressionParserManual (string source, ExpressionValidationType validationType)
{
if (source == null)
throw new ArgumentNullException ("source");
this.source = source;
validation_type = validationType;
}
string source;
ExpressionValidationType validation_type;
public ExpressionList Parse ()
{
return Parse (0, source.Length);
}
ExpressionList Parse (int start, int end)
{
if (string.IsNullOrWhiteSpace (source))
return new ExpressionList ();
var ret = new ExpressionList ();
while (start < end) {
int bak = start;
ret.Add (ParseSingle (ref start, end));
SkipSpaces (ref start);
if (bak == start)
throw new Exception ("Parser failed to progress token position: " + source);
}
return ret;
}
static readonly char [] token_starters = "$@%(),".ToCharArray ();
Expression ParseSingle (ref int start, int end)
{
char token = source [start];
switch (token) {
case '$':
case '@':
case '%':
if (start == end || start + 1 == source.Length || source [start + 1] != '(') {
if (validation_type == ExpressionValidationType.StrictBoolean)
throw new InvalidProjectFileException (string.Format ("missing '(' after '{0}' at {1} in \"{2}\"", source [start], start, source));
else
goto default; // treat as raw literal to the section end
}
start += 2;
int last = FindMatchingCloseParen (start, end);
if (last < 0) {
if (validation_type == ExpressionValidationType.StrictBoolean)
throw new InvalidProjectFileException (string.Format ("expression did not have matching ')' since index {0} in \"{1}\"", start, source));
else {
start -= 2;
goto default; // treat as raw literal to the section end
}
}
Expression ret;
if (token == '$')
ret = EvaluatePropertyExpression (start, last);
else if (token == '%')
ret = EvaluateMetadataExpression (start, last);
else
ret = EvaluateItemExpression (start, last);
start = last + 1;
return ret;
// Below (until default) are important only for Condition evaluation
case '(':
if (validation_type == ExpressionValidationType.LaxString)
goto default;
start++;
last = FindMatchingCloseParen (start, end);
if (last < 0) {
if (validation_type == ExpressionValidationType.StrictBoolean)
throw new InvalidProjectFileException (string.Format ("expression did not have matching ')' since index {0} in \"{1}\"", start, source));
else {
start--;
goto default; // treat as raw literal to the section end
}
}
var contents = Parse (start, last).ToArray ();
if (contents.Length > 1)
throw new InvalidProjectFileException (string.Format ("unexpected continuous expression within (){0} in \"{1}\"", contents [1].Column > 0 ? " at " + contents [1].Column : null, source));
return contents.First ();
default:
int idx = source.IndexOfAny (token_starters, start + 1);
string name = idx < 0 ? source.Substring (start, end - start) : source.Substring (start, idx - start);
var val = new NameToken () { Name = name };
ret = new RawStringLiteral () { Value = val };
if (idx >= 0)
start = idx;
else
start = end;
return ret;
}
}
int FindMatchingCloseParen (int start, int end)
{
int n = 0;
for (int i = start; i < end; i++) {
if (source [i] == '(')
n++;
else if (source [i] == ')') {
if (n-- == 0)
return i;
}
}
return -1; // invalid
}
static readonly string spaces = " \t\r\n";
void SkipSpaces (ref int start)
{
while (start < source.Length && spaces.Contains (source [start]))
start++;
}
PropertyAccessExpression EvaluatePropertyExpression (int start, int end)
{
// member access
int dotAt = source.LastIndexOf ('.', end, end - start);
int colonsAt = source.LastIndexOf ("::", end, end - start, StringComparison.Ordinal);
if (dotAt < 0 && colonsAt < 0) {
// property access without member specification
int parenAt = source.IndexOf ('(', start, end - start);
string name = parenAt < 0 ? source.Substring (start, end - start) : source.Substring (start, parenAt - start);
var access = new PropertyAccess () {
Name = new NameToken () { Name = name },
TargetType = PropertyTargetType.Object
};
if (parenAt > 0) { // method arguments
start = parenAt + 1;
access.Arguments = ParseFunctionArguments (ref start, end);
}
return new PropertyAccessExpression () { Access = access };
}
if (colonsAt < 0 || colonsAt < dotAt) {
// property access with member specification
int mstart = dotAt + 1;
int parenAt = source.IndexOf ('(', mstart, end - mstart);
string name = parenAt < 0 ? source.Substring (mstart, end - mstart) : source.Substring (mstart, parenAt - mstart);
var access = new PropertyAccess () {
Name = new NameToken () { Name = name },
TargetType = PropertyTargetType.Object,
Target = dotAt < 0 ? null : Parse (start, dotAt).FirstOrDefault ()
};
if (parenAt > 0) { // method arguments
start = parenAt + 1;
access.Arguments = ParseFunctionArguments (ref start, end);
}
return new PropertyAccessExpression () { Access = access };
} else {
// static type access
string type = source.Substring (start, colonsAt - start);
if (type.Length < 2 || type [0] != '[' || type [type.Length - 1] != ']')
throw new InvalidProjectFileException (string.Format ("Static function call misses appropriate type name surrounded by '[' and ']' at {0} in \"{1}\"", start, source));
type = type.Substring (1, type.Length - 2);
start = colonsAt + 2;
int parenAt = source.IndexOf ('(', start, end - start);
string member = parenAt < 0 ? source.Substring (start, end - start) : source.Substring (start, parenAt - start);
if (member.Length == 0)
throw new InvalidProjectFileException ("Static member name is missing");
var access = new PropertyAccess () {
Name = new NameToken () { Name = member },
TargetType = PropertyTargetType.Type,
Target = new StringLiteral () { Value = new NameToken () { Name = type } }
};
if (parenAt > 0) { // method arguments
start = parenAt + 1;
access.Arguments = ParseFunctionArguments (ref start, end);
}
return new PropertyAccessExpression () { Access = access };
}
}
ExpressionList ParseFunctionArguments (ref int start, int end)
{
var args = new ExpressionList ();
do {
SkipSpaces (ref start);
if (start == source.Length)
throw new InvalidProjectFileException ("unterminated function call arguments.");
if (source [start] == ')')
break;
else if (args.Any ()) {
if (source [start] != ',')
throw new InvalidProjectFileException (string.Format ("invalid function call arguments specification. ',' is expected, got '{0}'", source [start]));
start++;
}
args.Add (ParseSingle (ref start, end));
} while (true);
start++;
return args;
}
ItemAccessExpression EvaluateItemExpression (int start, int end)
{
// using property as context and evaluate
int idx = source.IndexOf ("->", start, StringComparison.Ordinal);
if (idx > 0) {
string name = source.Substring (start, idx - start);
return new ItemAccessExpression () {
Application = new ItemApplication () {
Name = new NameToken () { Name = name },
Expressions = Parse (idx + 2, end)
}
};
} else {
string name = source.Substring (start, end - start);
return new ItemAccessExpression () {
Application = new ItemApplication () { Name = new NameToken () { Name = name } }
};
}
}
MetadataAccessExpression EvaluateMetadataExpression (int start, int end)
{
int idx = source.IndexOf ('.', start, end - start);
string item = idx < 0 ? null : source.Substring (start, idx - start);
string meta = idx < 0 ? source.Substring (start, end - start) : source.Substring (idx + 1, end - idx - 1);
var access = new MetadataAccess () {
ItemType = item == null ? null : new NameToken () { Column = start, Name = item },
Metadata = new NameToken () { Column = idx < 0 ? start : idx + 1, Name = meta }
};
return new MetadataAccessExpression () { Access = access };
}
}
}

View File

@@ -0,0 +1,309 @@
//
// ExpressionTokenizer.cs
//
// Author:
// Atsushi Enomoto (atsushi@xamarin.com)
//
// Copyright (C) 2013 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.Generic;
using Microsoft.Build.Evaluation;
namespace Microsoft.Build.Internal.Expressions
{
enum ExpressionValidationType
{
LaxString,
StrictBoolean,
}
enum TokenizerMode
{
Default,
InsideItemOrProperty,
}
class ExpressionTokenizer : yyParser.yyInput
{
public ExpressionTokenizer (string source, ExpressionValidationType validationType)
{
this.source = source;
current_token_position = -1;
validation_type = validationType;
modes.Push (TokenizerMode.Default);
}
string source;
ExpressionValidationType validation_type;
int current_token;
string error;
int pos, current_token_position;
object token_value;
Stack<TokenizerMode> modes = new Stack<TokenizerMode> ();
TokenizerMode CurrentTokenizerMode {
get { return modes.Peek (); }
}
public bool advance ()
{
if (pos == source.Length)
return false;
error = null;
token_value = null;
while (pos < source.Length) {
if (spaces.IndexOf (source [pos]) >= 0)
pos++;
else
break;
}
if (pos == source.Length)
return false;
current_token_position = pos;
switch (source [pos++]) {
case '.':
TokenForItemPropertyValue (".", Token.DOT);
break;
case ',':
TokenForItemPropertyValue (",", Token.COMMA);
break;
case '[':
TokenForItemPropertyValue ("[", Token.BRACE_OPEN);
break;
case ']':
TokenForItemPropertyValue ("]", Token.BRACE_CLOSE);
break;
case '(':
modes.Push (TokenizerMode.Default);
TokenForItemPropertyValue ("(", Token.PAREN_OPEN);
break;
case ')':
if (modes.Count > 0) {
modes.Pop ();
current_token = Token.PAREN_CLOSE;
} else {
token_value = ")";
current_token = Token.NAME;
}
break;
case '-':
if (pos < source.Length && source [pos] == '>') {
current_token = Token.ARROW;
pos++;
} else
ErrorOnStrictBoolean ("-", "'-' is not followed by '>'.");
break;
case '=':
if (pos < source.Length && source [pos] == '=') {
current_token = Token.EQ;
pos++;
} else
ErrorOnStrictBoolean ("=", "'=' is not followed by '='.");
break;
case ':':
if (pos < source.Length && source [pos] == ':') {
current_token = Token.COLON2;
} else
ErrorOnStrictBoolean (":", "':' is not followed by ':'.");
pos++;
break;
case '!':
if (pos < source.Length && source [pos] == '=') {
pos++;
current_token = Token.NE;
} else
TokenForItemPropertyValue ("!", Token.NOT);
break;
case '>':
if (pos < source.Length && source [pos] == '=') {
pos++;
current_token = Token.GE;
} else
current_token = Token.GT;
break;
case '<':
if (pos < source.Length && source [pos] == '=') {
pos++;
current_token = Token.LE;
} else
current_token = Token.LT;
break;
case '$':
if (pos < source.Length && source [pos] == '(') {
modes.Push (TokenizerMode.InsideItemOrProperty);
current_token = Token.PROP_OPEN;
pos++;
} else
ErrorOnStrictBoolean ("$", "property reference '$' is not followed by '('.");
break;
case '@':
if (pos < source.Length && source [pos] == '(') {
modes.Push (TokenizerMode.InsideItemOrProperty);
current_token = Token.ITEM_OPEN;
pos++;
} else
ErrorOnStrictBoolean ("@", "item reference '@' is not followed by '('.");
break;
case '%':
if (pos < source.Length && source [pos] == '(') {
modes.Push (TokenizerMode.InsideItemOrProperty);
current_token = Token.METADATA_OPEN;
pos++;
} else
ErrorOnStrictBoolean ("%", "metadata reference '%' is not followed by '('.");
break;
case '"':
case '\'':
pos = source.IndexOf (source [pos - 1], pos);
if (pos < 0) {
ErrorOnStrictBoolean ("'", "unterminated string literal");
pos = source.Length;
}
token_value = source.Substring (current_token_position + 1, pos - current_token_position - 1);
current_token = Token.STRING_LITERAL;
pos++;
break;
default:
pos = source.IndexOfAny (token_starter_chars, pos);
if (pos < 0)
pos = source.Length;
var val = source.Substring (current_token_position, pos - current_token_position);
if (val.Equals ("AND", StringComparison.OrdinalIgnoreCase))
current_token = Token.AND;
else if (val.Equals ("OR", StringComparison.OrdinalIgnoreCase))
current_token = Token.OR;
else if (val.Equals ("TRUE", StringComparison.OrdinalIgnoreCase))
current_token = Token.TRUE_LITERAL;
else if (val.Equals ("FALSE", StringComparison.OrdinalIgnoreCase))
current_token = Token.FALSE_LITERAL;
else if (val.Equals ("YES", StringComparison.OrdinalIgnoreCase))
current_token = Token.TRUE_LITERAL;
else if (val.Equals ("NO", StringComparison.OrdinalIgnoreCase))
current_token = Token.FALSE_LITERAL;
else if (val.Equals ("ON", StringComparison.OrdinalIgnoreCase))
current_token = Token.TRUE_LITERAL;
else if (val.Equals ("OFF", StringComparison.OrdinalIgnoreCase))
current_token = Token.FALSE_LITERAL;
else {
current_token = Token.NAME;
token_value = ProjectCollection.Unescape (val);
break;
}
break;
}
return true;
}
string spaces = " \t\r\n";
static readonly char [] token_starter_chars = ".,[]()-=:!><$@%\"' ".ToCharArray ();
void ReadStringLiteral (string source, char c)
{
while (pos < source.Length && source [pos] != c)
pos++;
if (source [pos - 1] != c)
ErrorOnStrictBoolean (c.ToString (), string.Format ("missing string literal terminator [{0}]", c));
else {
current_token = Token.NAME;
token_value = source.Substring (current_token_position + 1, pos - current_token_position - 2);
token_value = ProjectCollection.Unescape ((string) token_value);
}
}
void TokenForItemPropertyValue (string value, int token)
{
if (true)//CurrentTokenizerMode == TokenizerMode.InsideItemOrProperty)
current_token = token;
else {
current_token = Token.NAME;
token_value = value;
}
}
void ErrorOnStrictBoolean (string value, string message)
{
if (validation_type == ExpressionValidationType.StrictBoolean) {
current_token = Token.ERROR;
error = message;
} else {
current_token = Token.NAME;
token_value = value;
}
}
public int token ()
{
return current_token;
}
public object value ()
{
if (current_token == Token.NAME || current_token == Token.STRING_LITERAL)
return new NameToken () { Name = (string) token_value, Column = current_token_position };
else if (error != null)
return new ErrorToken () { Message = error, Column = current_token_position };
else
return new Location () { Column = current_token_position };
}
}
class NameToken : Location
{
public string Name { get; set; }
public override string ToString ()
{
return string.Format ("[NameToken: Value={0}]", Name);
}
}
class ErrorToken : Location
{
public string Message { get; set; }
}
interface ILocation
{
//int Line { get; }
int Column { get; }
string File { get; }
string ToLocationString ();
}
class Location : ILocation
{
//public int Line { get; set; }
public int Column { get; set; }
public string File { get; set; }
public string ToLocationString ()
{
return "at " + Column;
}
}
}

View File

@@ -0,0 +1,60 @@
//
// FilteredEnumerable.cs
//
// Author:
// Leszek Ciesielski (skolima@gmail.com)
//
// (C) 2011 Leszek Ciesielski
//
// 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.Generic;
using System.Linq;
using System.Text;
namespace Microsoft.Build.Internal
{
internal class FilteredEnumerable<T> : IEnumerable<T> where T : class
{
System.Collections.IEnumerable backingEnumerable;
public FilteredEnumerable (System.Collections.IEnumerable enumerable)
{
backingEnumerable = enumerable;
}
public IEnumerator<T> GetEnumerator ()
{
foreach (var item in backingEnumerable) {
var typedItem = item as T;
if (typedItem != null)
yield return typedItem;
}
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator ()
{
return GetEnumerator ();
}
}
}

View File

@@ -0,0 +1,92 @@
//
// ProjectTaskItem.cs
//
// Author:
// Atsushi Enomoto (atsushi@xamarin.com)
//
// Copyright (C) 2013 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.Linq;
using Microsoft.Build.Framework;
using Microsoft.Build.Construction;
using Microsoft.Build.Evaluation;
using System.IO;
namespace Microsoft.Build.Internal
{
class ProjectTaskItem : ITaskItem
{
ProjectItemElement item;
string evaluated_include_part;
public ProjectTaskItem (ProjectItemElement item, string evaluatedIncludePart)
{
this.item = item;
this.evaluated_include_part = WindowsCompatibilityExtensions.FindMatchingPath (evaluatedIncludePart);
}
#region ITaskItem implementation
System.Collections.IDictionary ITaskItem.CloneCustomMetadata ()
{
var ret = new System.Collections.Hashtable ();
foreach (var p in item.Metadata)
ret [p.Name] = p;
return ret;
}
void ITaskItem.CopyMetadataTo (ITaskItem destinationItem)
{
throw new NotImplementedException ();
}
string ITaskItem.GetMetadata (string metadataName)
{
var wk = ProjectCollection.GetWellKnownMetadata (metadataName, evaluated_include_part, Path.GetFullPath, null);
if (wk != null)
return wk;
var mde = item.Metadata.FirstOrDefault (m => m.Name == metadataName);
return mde != null ? mde.Value : string.Empty;
}
void ITaskItem.RemoveMetadata (string metadataName)
{
throw new NotImplementedException ();
}
void ITaskItem.SetMetadata (string metadataName, string metadataValue)
{
throw new NotImplementedException ();
}
string ITaskItem.ItemSpec {
get { return evaluated_include_part; }
set { throw new NotImplementedException (); }
}
int ITaskItem.MetadataCount {
get {
throw new NotImplementedException ();
}
}
System.Collections.ICollection ITaskItem.MetadataNames {
get {
throw new NotImplementedException ();
}
}
#endregion
}
}

View File

@@ -0,0 +1,53 @@
//
// ReverseEnumerable.cs
//
// Author:
// Leszek Ciesielski (skolima@gmail.com)
//
// (C) 2011 Leszek Ciesielski
//
// 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.Collections.Generic;
namespace Microsoft.Build.Internal
{
internal class ReverseEnumerable<T> : IEnumerable<T>
{
LinkedList<T> backingList;
public ReverseEnumerable (LinkedList<T> list)
{
backingList = list;
}
public IEnumerator<T> GetEnumerator ()
{
for (var node = backingList.Last; node != null; node = node.Previous)
yield return node.Value;
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator ()
{
return GetEnumerator ();
}
}
}

View File

@@ -0,0 +1,61 @@
//
// WindowsCompatibilityExtensions.cs
//
// Author:
// Atsushi Enomoto (atsushi@xamarin.com)
//
// (C) 2013 Xamarin Inc.
//
// 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.IO;
using Mono.XBuild.Utilities;
namespace Microsoft.Build.Internal
{
static class WindowsCompatibilityExtensions
{
public static string NormalizeFilePath (string path)
{
if (MSBuildUtils.RunningOnWindows || string.IsNullOrWhiteSpace (path) || File.Exists (path) || Directory.Exists (path))
return path;
return path.Replace ('\\', Path.DirectorySeparatorChar);
}
public static string FindMatchingPath (string path)
{
if (MSBuildUtils.RunningOnWindows || string.IsNullOrWhiteSpace (path) || File.Exists (path) || Directory.Exists (path))
return path;
path = path.Replace ('\\', Path.DirectorySeparatorChar);
var file = Path.GetFileName (path);
var dir = FindMatchingPath (Path.GetDirectoryName (path));
if (Directory.Exists (dir)) {
foreach (FileSystemInfo e in new DirectoryInfo (dir.Length > 0 ? dir : ".").EnumerateFileSystemInfos ()) {
if (e.Name.Equals (file, StringComparison.OrdinalIgnoreCase))
return dir.Length > 0 ? Path.Combine (dir, e.Name) : e.Name;
}
}
// The directory part is still based on case insensitive match.
return dir.Length > 0 ? Path.Combine (dir, file) : file;
}
}
}