using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Text; | |
using Microsoft.Build.Framework; | |
using System.Xaml; | |
using Microsoft.Build.Framework.XamlTypes; | |
using Microsoft.Build.Utilities; | |
namespace NaCl.Build.CPPTasks | |
{ | |
class XamlParser | |
{ | |
public XamlParser(string path) | |
{ | |
// load and store properties from xaml file | |
m_parsedBuildRule = (Rule)XamlServices.Load(path); | |
// NOTE: | |
// There are MSBuild classes which support command line building, | |
// argument switch encapsulation and more. Code within VCToolTask, | |
// a hidden interface, uses some these classes to generate command line | |
// switches given project settings. As the VCToolTask class is a hidden | |
// class and the MSBuild documentation is very sparse, we are going | |
// with a small custom solution. For reference see the | |
// Microsoft.Build.Tasks.Xaml, Microsoft.Build.Framework.XamlTypes namespaces. | |
// move the properties to a property name keyed dictionary for faster lookup | |
ToolProperties = new Dictionary<string, PropertyWrapper>(); | |
ToolProperties = m_parsedBuildRule.Properties.ToDictionary(x => x.Name, x => (new PropertyWrapper(x.GetType(), x))); | |
InitFunctionMap(); | |
} | |
private void InitFunctionMap() | |
{ | |
m_typeFunctionMap = new Dictionary<Type, Action<CommandLineBuilder, BaseProperty, string>> | |
{ | |
{ typeof(StringListProperty), GenerateArgumentStringList }, | |
{ typeof(StringProperty), GenerateArgumentString }, | |
{ typeof(IntProperty), GenerateArgumentString }, | |
{ typeof(BoolProperty), GenerateArgumentBool }, | |
{ typeof(EnumProperty), GenerateArgumentEnum } | |
}; | |
} | |
public string Parse(ITaskItem taskItem) | |
{ | |
CommandLineBuilder builder = new CommandLineBuilder(); | |
foreach (string name in taskItem.MetadataNames) | |
{ | |
string value = taskItem.GetMetadata(name); | |
AppendArgumentForProperty(builder, name, value); | |
} | |
string result = builder.ToString(); | |
result = result.Replace('\\', '/'); // posix paths | |
return result; | |
} | |
private string AppendArgumentForProperty(CommandLineBuilder builder, string name, string value) | |
{ | |
PropertyWrapper current; | |
ToolProperties.TryGetValue(name, out current); | |
if (current != null && value.Length > 0) | |
{ | |
// call appropriate function for type | |
m_typeFunctionMap[current.PropertyType](builder, current.Property, value); | |
} | |
return string.Empty; | |
} | |
private void GenerateArgumentEnum(CommandLineBuilder builder, BaseProperty property, string value) | |
{ | |
var result = ((EnumProperty)property).AdmissibleValues.Find(x => (x.Name == value)); | |
if (result != null) | |
{ | |
builder.AppendSwitchUnquotedIfNotNull(m_parsedBuildRule.SwitchPrefix, result.Switch); | |
} | |
} | |
// helper for string-based properties | |
private void AppendStringValue(CommandLineBuilder builder, BaseProperty property, string subtype, string value) | |
{ | |
value = value.Trim(); | |
// could cache this SubType test off in property wrapper or somewhere if performance were an issue | |
if (subtype == "file" || subtype == "folder") | |
{ | |
builder.AppendSwitchIfNotNull(m_parsedBuildRule.SwitchPrefix + property.Switch + property.Separator, value); | |
} | |
else | |
{ | |
builder.AppendSwitchUnquotedIfNotNull(m_parsedBuildRule.SwitchPrefix + property.Switch + property.Separator, value); | |
} | |
} | |
private void GenerateArgumentStringList(CommandLineBuilder builder, BaseProperty property, string value) | |
{ | |
string [] arguments = value.Split(';'); | |
foreach (string argument in arguments) | |
{ | |
if (argument.Length > 0) | |
{ | |
StringListProperty casted = (StringListProperty)property; | |
AppendStringValue(builder, property, casted.Subtype, argument); | |
} | |
} | |
} | |
private void GenerateArgumentString(CommandLineBuilder builder, BaseProperty property, string value) | |
{ | |
// could cache this SubType test off in property wrapper or somewhere if performance were an issue | |
StringProperty casted = (StringProperty)property; | |
AppendStringValue(builder, property, casted.Subtype, value); | |
} | |
private void GenerateArgumentBool(CommandLineBuilder builder, BaseProperty property, string value) | |
{ | |
if (value == "true") | |
{ | |
builder.AppendSwitchUnquotedIfNotNull(m_parsedBuildRule.SwitchPrefix, property.Switch); | |
} | |
else if (value == "false" && ((BoolProperty)property).ReverseSwitch != null) | |
{ | |
builder.AppendSwitchUnquotedIfNotNull(m_parsedBuildRule.SwitchPrefix, ((BoolProperty)property).ReverseSwitch); | |
} | |
} | |
// to store off MSBuild property type and allow faster searching on them | |
private class PropertyWrapper | |
{ | |
public PropertyWrapper(Type newType, BaseProperty newProperty) | |
{ | |
PropertyType = newType; | |
Property = newProperty; | |
} | |
public Type PropertyType { get; set; } | |
public BaseProperty Property { get; set; } | |
} // class | |
private Rule m_parsedBuildRule; | |
private Dictionary<string, PropertyWrapper> ToolProperties { get; set; } | |
// function mapping for easy property function calling | |
private Dictionary<Type, Action<CommandLineBuilder, BaseProperty, string>> m_typeFunctionMap; | |
} // XamlParser | |
} // namespace NaCl.Build.CPPTasks | |