| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| using System; |
| using System.Collections.Generic; |
| using System.Linq; |
| using System.Text; |
| using System.IO; |
| |
| using Microsoft.Build.Framework; |
| using System.Xaml; |
| using Microsoft.Build.Framework.XamlTypes; |
| using Microsoft.Build.Utilities; |
| |
| namespace NaCl.Build.CPPTasks |
| { |
| public 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), GenerateArgumentInt }, |
| { typeof(BoolProperty), GenerateArgumentBool }, |
| { typeof(EnumProperty), GenerateArgumentEnum } |
| }; |
| } |
| |
| public string Parse(ITaskItem taskItem, bool fullOutputName) |
| { |
| CommandLineBuilder builder = new CommandLineBuilder(); |
| |
| foreach (string name in taskItem.MetadataNames) |
| { |
| string value = taskItem.GetMetadata(name); |
| if (fullOutputName && name == "ObjectFileName") |
| { |
| if ((File.GetAttributes(value) & FileAttributes.Directory) != 0) |
| { |
| value = Path.Combine(value, Path.GetFileName(taskItem.ItemSpec)); |
| value = Path.ChangeExtension(value, ".obj"); |
| } |
| } |
| 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 |
| string switchName = m_parsedBuildRule.SwitchPrefix + property.Switch; |
| switchName += property.Separator; |
| if (subtype == "file" || subtype == "folder") |
| { |
| // for switches that contains files or folders we need quoting |
| builder.AppendSwitchIfNotNull(switchName, value); |
| } |
| else if (!string.IsNullOrEmpty(property.Switch)) |
| { |
| builder.AppendSwitchUnquotedIfNotNull(switchName, value); |
| } |
| else if (!string.IsNullOrEmpty(value)) |
| { |
| // for non-switches such as AdditionalOptions we just append the value |
| builder.AppendTextUnquoted(" " + 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 GenerateArgumentInt(CommandLineBuilder builder, BaseProperty property, string value) |
| { |
| // Currently we only have one Int property and it doesn't correspond to a |
| // command line arguemnt (ProcessorNumber) so we ignore it here. |
| } |
| |
| 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 |
| |