提交 49f6947d 编写于 作者: A Andy Gocke

Add the C# and VB MSBuild tasks to Roslyn

This moves the existing tasks into the source repository for Roslyn, as Roslyn
owns the C# and VB MSBuild tasks.
上级 9de3519f
using System;
using System.Globalization;
using System.Text.RegularExpressions;
namespace Microsoft.CodeAnalysis.BuildTask
{
/// <summary>
/// Functions for dealing with the specially formatted errors returned by
/// build tools.
/// </summary>
/// <remarks>
/// Various tools produce and consume CanonicalErrors in various formats.
///
/// DEVENV Format When Clicking on Items in the Output Window
/// (taken from env\msenv\core\findutil.cpp ParseLocation function)
///
/// v:\dir\file.ext (loc) : msg
/// \\server\share\dir\file.ext(loc):msg
/// url
///
/// loc:
/// (line)
/// (line-line)
/// (line,col)
/// (line,col-col)
/// (line,col,len)
/// (line,col,line,col)
///
/// DevDiv Build Process
/// (taken from tools\devdiv2.def)
///
/// To echo warnings and errors to the build console, the
/// "description block" must be recognized by build. To do this,
/// add a $(ECHO_COMPILING_COMMAND) or $(ECHO_PROCESSING_COMMAND)
/// to the first line of the description block, e.g.
///
/// $(ECHO_COMPILING_CMD) Resgen_$&lt;
///
/// Errors must have the format:
///
/// &lt;text&gt; : error [num]: &lt;msg&gt;
///
/// Warnings must have the format:
///
/// &lt;text&gt; : warning [num]: &lt;msg&gt;
/// </remarks>
/// <owner>JomoF</owner>
internal static class CanonicalError
{
// Defines the main pattern for matching messages.
private static Regex originCategoryCodeTextExpression = new Regex
(
// Beginning of line and any amount of whitespace.
@"^\s*"
// Match a [optional project number prefix 'ddd>'], single letter + colon + remaining filename, or
// string with no colon followed by a colon.
+ @"(((?<ORIGIN>(((\d+>)?[a-zA-Z]?:[^:]*)|([^:]*))):)"
// Origin may also be empty. In this case there's no trailing colon.
+ "|())"
// Match the empty string or a string without a colon that ends with a space
+ "(?<SUBCATEGORY>(()|([^:]*? )))"
// Match 'error' or 'warning'.
+ @"(?<CATEGORY>(error|warning))"
// Match anything starting with a space that's not a colon/space, followed by a colon.
// Error code is optional in which case "error"/"warning" can be followed immediately by a colon.
+ @"( \s*(?<CODE>[^: ]*))?\s*:"
// Whatever's left on this line, including colons.
+ "(?<TEXT>.*)$",
RegexOptions.IgnoreCase
);
// Matches and extracts filename and location from an 'origin' element.
private static Regex filenameLocationFromOrigin = new Regex
(
"^" // Beginning of line
+ @"(\d+>)?" // Optional ddd> project number prefix
+ "(?<FILENAME>.*)" // Match anything.
+ @"\(" // Find a parenthesis.
+ @"(?<LOCATION>[\,,0-9,-]*)" // Match any combination of numbers and ',' and '-'
+ @"\)\s*" // Find the closing paren then any amount of spaces.
+ "$", // End-of-line
RegexOptions.IgnoreCase
);
// Matches location that is a simple number.
private static Regex lineFromLocation = new Regex // Example: line
(
"^" // Beginning of line
+ "(?<LINE>[0-9]*)" // Match any number.
+ "$", // End-of-line
RegexOptions.IgnoreCase
);
// Matches location that is a range of lines.
private static Regex lineLineFromLocation = new Regex // Example: line-line
(
"^" // Beginning of line
+ "(?<LINE>[0-9]*)" // Match any number.
+ "-" // Dash
+ "(?<ENDLINE>[0-9]*)" // Match any number.
+ "$", // End-of-line
RegexOptions.IgnoreCase
);
// Matches location that is a line and column
private static Regex lineColFromLocation = new Regex // Example: line,col
(
"^" // Beginning of line
+ "(?<LINE>[0-9]*)" // Match any number.
+ "," // Comma
+ "(?<COLUMN>[0-9]*)" // Match any number.
+ "$", // End-of-line
RegexOptions.IgnoreCase
);
// Matches location that is a line and column-range
private static Regex lineColColFromLocation = new Regex // Example: line,col-col
(
"^" // Beginning of line
+ "(?<LINE>[0-9]*)" // Match any number.
+ "," // Comma
+ "(?<COLUMN>[0-9]*)" // Match any number.
+ "-" // Dash
+ "(?<ENDCOLUMN>[0-9]*)" // Match any number.
+ "$", // End-of-line
RegexOptions.IgnoreCase
);
// Matches location that is line,col,line,col
private static Regex lineColLineColFromLocation = new Regex // Example: line,col,line,col
(
"^" // Beginning of line
+ "(?<LINE>[0-9]*)" // Match any number.
+ "," // Comma
+ "(?<COLUMN>[0-9]*)" // Match any number.
+ "," // Dash
+ "(?<ENDLINE>[0-9]*)" // Match any number.
+ "," // Dash
+ "(?<ENDCOLUMN>[0-9]*)" // Match any number.
+ "$", // End-of-line
RegexOptions.IgnoreCase
);
/// <summary>
/// Represents the parts of a decomposed canonical message.
/// </summary>
/// <owner>JomoF</owner>
internal sealed class Parts
{
/// <summary>
/// Defines the error category\severity level.
/// </summary>
internal enum Category
{
Warning,
Error
}
/// <summary>
/// Value used for unspecified line and column numbers, which are 1-relative.
/// </summary>
internal const int numberNotSpecified = 0;
/// <summary>
/// Initializes a new instance of the <see cref="Parts"/> class.
/// </summary>
internal Parts()
{
}
/// <summary>
/// Name of the file or tool (not localized)
/// </summary>
internal string origin;
/// <summary>
/// The line number.
/// </summary>
internal int line = Parts.numberNotSpecified;
/// <summary>
/// The column number.
/// </summary>
internal int column = Parts.numberNotSpecified;
/// <summary>
/// The ending line number.
/// </summary>
internal int endLine = Parts.numberNotSpecified;
/// <summary>
/// The ending column number.
/// </summary>
internal int endColumn = Parts.numberNotSpecified;
/// <summary>
/// The category/severity level
/// </summary>
internal Category category;
/// <summary>
/// The sub category (localized)
/// </summary>
internal string subcategory;
/// <summary>
/// The error code (not localized)
/// </summary>
internal string code;
/// <summary>
/// The error message text (localized)
/// </summary>
internal string text;
#if NEVER
internal new string ToString()
{
return String.Format
(
"Origin='{0}'\n"
+"Filename='{1}'\n"
+"Line='{2}'\n"
+"Column='{3}'\n"
+"EndLine='{4}'\n"
+"EndColumn='{5}'\n"
+"Category='{6}'\n"
+"Subcategory='{7}'\n"
+"Text='{8}'\n"
, origin, line, column, endLine, endColumn, category.ToString(), subcategory, code, text
);
}
#endif
}
/// <summary>
/// A small custom int conversion method that treats invalid entries as missing (0). This is done to work around tools
/// that don't fully conform to the canonical message format - we still want to salvage what we can from the message.
/// </summary>
/// <param name="value"></param>
/// <returns>'value' converted to int or 0 if it can't be parsed or is negative</returns>
/// <owner>LukaszG</owner>
private static int ConvertToIntWithDefault(string value)
{
int result;
bool success = int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out result);
if (!success || (result < 0))
{
result = CanonicalError.Parts.numberNotSpecified;
}
return result;
}
/// <summary>
/// Decompose an error or warning message into constituent parts. If the message isn't in the canonical form, return null.
/// </summary>
/// <remarks>This method is thread-safe, because the Regex class is thread-safe (per MSDN).</remarks>
/// <owner>JomoF</owner>
/// <param name="message"></param>
/// <returns>Decomposed canonical message, or null.</returns>
internal static Parts Parse(string message)
{
// An unusually long string causes pathologically slow Regex back-tracking.
// To avoid that, only scan the first 400 characters. That's enough for
// the longest possible prefix: MAX_PATH, plus a huge subcategory string, and an error location.
// After the regex is done, we can append the overflow.
string messageOverflow = String.Empty;
if (message.Length > 400)
{
messageOverflow = message.Substring(400);
message = message.Substring(0, 400);
}
// If a tool has a large amount of output that isn't an error or warning (eg., "dir /s %hugetree%")
// the regex below is slow. It's faster to pre-scan for "warning" and "error"
// and bail out if neither are present.
if (message.IndexOf("warning", StringComparison.OrdinalIgnoreCase) == -1 &&
message.IndexOf("error", StringComparison.OrdinalIgnoreCase) == -1)
{
return null;
}
Parts parsedMessage = new Parts();
// First, split the message into three parts--Origin, Category, Code, Text.
// Example,
// Main.cs(17,20):Command line warning CS0168: The variable 'foo' is declared but never used
// -------------- ------------ ------- ------ ----------------------------------------------
// Origin SubCategory Cat. Code Text
//
// To accomodate absolute filenames in Origin, tolerate a colon in the second position
// as long as its preceded by a letter.
//
// Localization Note:
// Even in foreign-language versions of tools, the category field needs to be in English.
// Also, if origin is a tool name, then that needs to be in English.
//
// Here's an example from the Japanese version of CL.EXE:
// cl : ???? ??? warning D4024 : ?????????? 'AssemblyInfo.cs' ?????????????????? ???????????
//
// Here's an example from the Japanese version of LINK.EXE:
// AssemblyInfo.cpp : fatal error LNK1106: ???????????? ??????????????: 0x6580 ??????????
//
Match match = originCategoryCodeTextExpression.Match(message);
if (!match.Success)
{
// If no match here, then this message is not an error or warning.
return null;
}
string origin = match.Groups["ORIGIN"].Value.Trim();
string category = match.Groups["CATEGORY"].Value.Trim();
parsedMessage.code = match.Groups["CODE"].Value.Trim();
parsedMessage.text = (match.Groups["TEXT"].Value + messageOverflow).Trim();
parsedMessage.subcategory = match.Groups["SUBCATEGORY"].Value.Trim();
// Next, see if category is something that is recognized.
if (0 == String.Compare(category, "error", StringComparison.OrdinalIgnoreCase))
{
parsedMessage.category = Parts.Category.Error;
}
else if (0 == String.Compare(category, "warning", StringComparison.OrdinalIgnoreCase))
{
parsedMessage.category = Parts.Category.Warning;
}
else
{
// Not an error\warning message.
return null;
}
// Origin is not a simple file, but it still could be of the form,
// foo.cpp(location)
match = filenameLocationFromOrigin.Match(origin);
if (match.Success)
{
// The origin is in the form,
// foo.cpp(location)
// Assume the filename exists, but don't verify it. What else could it be?
string location = match.Groups["LOCATION"].Value.Trim();
parsedMessage.origin = match.Groups["FILENAME"].Value.Trim();
// Now, take apart the location. It can be one of these:
// loc:
// (line)
// (line-line)
// (line,col)
// (line,col-col)
// (line,col,len)
// (line,col,line,col)
if (location.Length > 0)
{
match = lineFromLocation.Match(location);
if (match.Success)
{
parsedMessage.line = ConvertToIntWithDefault(match.Groups["LINE"].Value.Trim());
}
else
{
match = lineLineFromLocation.Match(location);
if (match.Success)
{
parsedMessage.line = ConvertToIntWithDefault(match.Groups["LINE"].Value.Trim());
parsedMessage.endLine = ConvertToIntWithDefault(match.Groups["ENDLINE"].Value.Trim());
}
else
{
match = lineColFromLocation.Match(location);
if (match.Success)
{
parsedMessage.line = ConvertToIntWithDefault(match.Groups["LINE"].Value.Trim());
parsedMessage.column = ConvertToIntWithDefault(match.Groups["COLUMN"].Value.Trim());
}
else
{
match = lineColColFromLocation.Match(location);
if (match.Success)
{
parsedMessage.line = ConvertToIntWithDefault(match.Groups["LINE"].Value.Trim());
parsedMessage.column = ConvertToIntWithDefault(match.Groups["COLUMN"].Value.Trim());
parsedMessage.endColumn = ConvertToIntWithDefault(match.Groups["ENDCOLUMN"].Value.Trim());
}
else
{
match = lineColLineColFromLocation.Match(location);
if (match.Success)
{
parsedMessage.line = ConvertToIntWithDefault(match.Groups["LINE"].Value.Trim());
parsedMessage.column = ConvertToIntWithDefault(match.Groups["COLUMN"].Value.Trim());
parsedMessage.endLine = ConvertToIntWithDefault(match.Groups["ENDLINE"].Value.Trim());
parsedMessage.endColumn = ConvertToIntWithDefault(match.Groups["ENDCOLUMN"].Value.Trim());
}
}
}
}
}
}
}
else
{
// The origin does not fit the filename(location) pattern.
parsedMessage.origin = origin;
}
return parsedMessage;
}
}
}
using System;
using System.Diagnostics;
using System.Globalization;
using System.Text;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
namespace Microsoft.CodeAnalysis.BuildTask
{
/// <summary>
/// CommandLineBuilder derived class for specialized logic specific to MSBuild tasks
/// </summary>
public class CommandLineBuilderExtension : CommandLineBuilder
{
/// <summary>
/// Set a boolean switch iff its value exists and its value is 'true'.
/// </summary>
internal void AppendWhenTrue
(
string switchName,
PropertyDictionary bag,
string parameterName
)
{
object obj = bag[parameterName];
// If the switch isn't set, don't add it to the command line.
if (obj != null)
{
bool value = (bool)obj;
if (value)
{
AppendSwitch(switchName);
}
}
}
/// <summary>
/// Set a boolean switch only if its value exists.
/// </summary>
internal void AppendPlusOrMinusSwitch
(
string switchName,
PropertyDictionary bag,
string parameterName
)
{
object obj = bag[parameterName];
// If the switch isn't set, don't add it to the command line.
if (obj != null)
{
bool value = (bool)obj;
// Do not quote - or + as they are part of the switch
AppendSwitchUnquotedIfNotNull(switchName, (value ? "+" : "-"));
}
}
/// <summary>
/// Set a switch if its value exists by choosing from the input choices
/// </summary>
internal void AppendByChoiceSwitch
(
string switchName,
PropertyDictionary bag,
string parameterName,
string choice1,
string choice2
)
{
object obj = bag[parameterName];
// If the switch isn't set, don't add it to the command line.
if (obj != null)
{
bool value = (bool)obj;
AppendSwitchUnquotedIfNotNull(switchName, (value ? choice1 : choice2));
}
}
/// <summary>
/// Set an integer switch only if its value exists.
/// </summary>
internal void AppendSwitchWithInteger
(
string switchName,
PropertyDictionary bag,
string parameterName
)
{
object obj = bag[parameterName];
// If the switch isn't set, don't add it to the command line.
if (obj != null)
{
int value = (int)obj;
AppendSwitchIfNotNull(switchName, value.ToString(CultureInfo.InvariantCulture));
}
}
/// <summary>
/// Adds an aliased switch, used for ResGen:
/// /reference:Foo=System.Xml.dll
/// </summary>
internal void AppendSwitchAliased(string switchName, string alias, string parameter)
{
AppendSwitchUnquotedIfNotNull(switchName, alias + "=");
AppendTextWithQuoting(parameter);
}
/// <summary>
/// Adds a nested switch, used by SGen.exe. For example:
/// /compiler:"/keyfile:\"c:\some folder\myfile.snk\""
/// </summary>
internal void AppendNestedSwitch(string outerSwitchName, string innerSwitchName, string parameter)
{
string quotedParameter = GetQuotedText(parameter);
AppendSwitchIfNotNull(outerSwitchName, innerSwitchName + quotedParameter);
}
/// <summary>
/// Returns a quoted string appropriate for appending to a command line.
/// </summary>
/// <remarks>
/// Escapes any double quotes in the string.
/// </remarks>
protected string GetQuotedText(string unquotedText)
{
StringBuilder quotedText = new StringBuilder();
AppendQuotedTextToBuffer(quotedText, unquotedText);
return quotedText.ToString();
}
/// <summary>
/// Appends a command-line switch that takes a compound string parameter. The parameter is built up from the item-spec and
/// the specified attributes. The switch is appended as many times as there are parameters given.
/// </summary>
internal void AppendSwitchIfNotNull
(
string switchName,
ITaskItem[] parameters,
string[] attributes
)
{
AppendSwitchIfNotNull(switchName, parameters, attributes, null /* treatAsFlag */);
}
/// <summary>
/// Append a switch if 'parameter' is not null.
/// Split on the characters provided.
/// </summary>
internal void AppendSwitchWithSplitting(string switchName, string parameter, string delimiter, params char[] splitOn)
{
if (parameter != null)
{
string[] splits = parameter.Split(splitOn, /* omitEmptyEntries */ StringSplitOptions.RemoveEmptyEntries);
string[] splitAndTrimmed = new string[splits.Length];
for (int i = 0; i < splits.Length; ++i)
{
splitAndTrimmed[i] = splits[i].Trim();
}
AppendSwitchIfNotNull(switchName, splitAndTrimmed, delimiter);
}
}
/// <summary>
/// Returns true if the parameter is empty in spirits,
/// even if it contains the seperators and white space only
/// Split on the characters provided.
/// </summary>
internal static bool IsParameterEmpty(string parameter, params char[] splitOn)
{
if (parameter != null)
{
string[] splits = parameter.Split(splitOn, /* omitEmptyEntries */ StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < splits.Length; ++i)
{
if (!String.IsNullOrEmpty(splits[i].Trim()))
{
return false;
}
}
}
return true;
}
/// <summary>
/// Designed to handle the /link and /embed swithes:
///
/// /embed[resource]:&lt;filename>[,&lt;name>[,Private]]
/// /link[resource]:&lt;filename>[,&lt;name>[,Private]]
///
/// Where the last flag--Private--is either present or not present
/// depending on whether the ITaskItem has a Private="True" attribue.
/// </summary>
internal void AppendSwitchIfNotNull
(
string switchName,
ITaskItem[] parameters,
string[] metadataNames,
bool[] treatAsFlags // May be null. In this case no metadata are treated as flags.
)
{
Debug.Assert(treatAsFlags == null
|| (metadataNames.Length == treatAsFlags.Length),
"metadataNames and treatAsFlags should have the same length.");
if (parameters != null)
{
foreach (ITaskItem parameter in parameters)
{
AppendSwitchIfNotNull(switchName, parameter.ItemSpec);
if (metadataNames != null)
{
for (int i = 0; i < metadataNames.Length; ++i)
{
string metadataValue = parameter.GetMetadata(metadataNames[i]);
if ((metadataValue != null) && (metadataValue.Length > 0))
{
// Treat attribute as a boolean flag?
if (treatAsFlags == null || treatAsFlags[i] == false)
{
// Not a boolean flag.
CommandLine.Append(',');
AppendTextWithQuoting(metadataValue);
}
else
{
// A boolean flag.
bool flagSet = false;
flagSet = Utilities.TryConvertItemMetadataToBool(parameter, metadataNames[i]);
if (flagSet)
{
CommandLine.Append(',');
AppendTextWithQuoting(metadataNames[i]);
}
}
}
else
{
if (treatAsFlags == null || treatAsFlags[i] == false)
{
// If the caller of this method asked us to add metadata
// A, B, and C, and metadata A doesn't exist on the item,
// then it doesn't make sense to check for B and C. Because
// since these metadata are just being appended on the
// command-line switch with comma-separation, you can't pass
// in the B metadata unless you've also passed in the A
// metadata. Otherwise the tool's command-line parser will
// get totally confused.
// This only applies to non-flag attributes.
break;
}
}
}
}
}
}
}
}
}
using System;
using System.Text;
using Microsoft.Build.Framework;
using Microsoft.Build.Tasks.Hosting;
using Microsoft.Build.Utilities;
namespace Microsoft.CodeAnalysis.BuildTask
{
/// <summary>
/// This class defines the "Csc" XMake task, which enables building assemblies from C#
/// source files by invoking the C# compiler. This is the new Roslyn XMake task,
/// meaning that the code is compiled by using the Roslyn compiler server, rather
/// than csc.exe. The two should be functionally identical, but the compiler server
/// should be significantly faster with larger projects and have a smaller memory
/// footprint.
/// </summary>
public class Csc : ManagedCompiler
{
bool useHostCompilerIfAvailable = false;
#region Properties
// Please keep these alphabetized. These are the parameters specific to Csc. The
// ones shared between Vbc and Csc are defined in ManagedCompiler.cs, which is
// the base class.
public bool AllowUnsafeBlocks
{
set { _store["AllowUnsafeBlocks"] = value; }
get { return _store.GetOrDefault("AllowUnsafeBlocks", false); }
}
public string ApplicationConfiguration
{
set { _store["ApplicationConfiguration"] = value; }
get { return (string)_store["ApplicationConfiguration"]; }
}
public string BaseAddress
{
set { _store["BaseAddress"] = value; }
get { return (string)_store["BaseAddress"]; }
}
public bool CheckForOverflowUnderflow
{
set { _store["CheckForOverflowUnderflow"] = value; }
get { return _store.GetOrDefault("CheckForOverflowUnderflow", false); }
}
public string DocumentationFile
{
set { _store["DocumentationFile"] = value; }
get { return (string)_store["DocumentationFile"]; }
}
public string DisabledWarnings
{
set { _store["DisabledWarnings"] = value; }
get { return (string)_store["DisabledWarnings"]; }
}
public bool ErrorEndLocation
{
set { _store["ErrorEndLocation"] = value; }
get { return _store.GetOrDefault("ErrorEndLocation", false); }
}
public string ErrorReport
{
set { _store["ErrorReport"] = value; }
get { return (string)_store["ErrorReport"]; }
}
public bool GenerateFullPaths
{
set { _store["GenerateFullPaths"] = value; }
get { return _store.GetOrDefault("GenerateFullPaths", false); }
}
public string LangVersion
{
set { _store["LangVersion"] = value; }
get { return (string)_store["LangVersion"]; }
}
public string ModuleAssemblyName
{
set { _store["ModuleAssemblyName"] = value; }
get { return (string)_store["ModuleAssemblyName"]; }
}
public bool NoStandardLib
{
set { _store["NoStandardLib"] = value; }
get { return _store.GetOrDefault("NoStandardLib", false); }
}
public string PdbFile
{
set { _store["PdbFile"] = value; }
get { return (string)_store["PdbFile"]; }
}
/// <summary>
/// Name of the language passed to "/preferreduilang" compiler option.
/// </summary>
/// <remarks>
/// If set to null, "/preferreduilang" option is omitted, and csc.exe uses its default setting.
/// Otherwise, the value is passed to "/preferreduilang" as is.
/// </remarks>
public string PreferredUILang
{
set { _store["PreferredUILang"] = value; }
get { return (string)_store["PreferredUILang"]; }
}
public string VsSessionGuid
{
set { _store["VsSessionGuid"] = value; }
get { return (string)_store["VsSessionGuid"]; }
}
public bool UseHostCompilerIfAvailable
{
set { this.useHostCompilerIfAvailable = value; }
get { return this.useHostCompilerIfAvailable; }
}
public int WarningLevel
{
set { _store["WarningLevel"] = value; }
get { return _store.GetOrDefault("WarningLevel", 4); }
}
public string WarningsAsErrors
{
set { _store["WarningsAsErrors"] = value; }
get { return (string)_store["WarningsAsErrors"]; }
}
public string WarningsNotAsErrors
{
set { _store["WarningsNotAsErrors"] = value; }
get { return (string)_store["WarningsNotAsErrors"]; }
}
#endregion
#region Tool Members
/// <summary>
/// Return the name of the tool to execute.
/// </summary>
override protected string ToolName
{
get
{
return "csc2.exe";
}
}
/// <summary>
/// Return the path to the tool to execute.
/// </summary>
override protected string GenerateFullPathToTool()
{
string pathToTool = ToolLocationHelper.GetPathToBuildToolsFile(ToolName, ToolLocationHelper.CurrentToolsVersion);
if (null == pathToTool)
{
pathToTool = ToolLocationHelper.GetPathToDotNetFrameworkFile(ToolName, TargetDotNetFrameworkVersion.VersionLatest);
if (null == pathToTool)
{
Log.LogErrorWithCodeFromResources("General.FrameworksFileNotFound", ToolName, ToolLocationHelper.GetDotNetFrameworkVersionFolderPrefix(TargetDotNetFrameworkVersion.VersionLatest));
}
}
return pathToTool;
}
/// <summary>
/// Fills the provided CommandLineBuilderExtension with those switches and other information that can go into a response file.
/// </summary>
override protected internal void AddResponseFileCommands(CommandLineBuilderExtension commandLine)
{
commandLine.AppendSwitchIfNotNull("/lib:", this.AdditionalLibPaths, ",");
commandLine.AppendPlusOrMinusSwitch("/unsafe", this._store, "AllowUnsafeBlocks");
commandLine.AppendPlusOrMinusSwitch("/checked", this._store, "CheckForOverflowUnderflow");
commandLine.AppendSwitchWithSplitting("/nowarn:", this.DisabledWarnings, ",", ';', ',');
commandLine.AppendWhenTrue("/fullpaths", this._store, "GenerateFullPaths");
commandLine.AppendSwitchIfNotNull("/langversion:", this.LangVersion);
commandLine.AppendSwitchIfNotNull("/moduleassemblyname:", this.ModuleAssemblyName);
commandLine.AppendSwitchIfNotNull("/pdb:", this.PdbFile);
commandLine.AppendPlusOrMinusSwitch("/nostdlib", this._store, "NoStandardLib");
commandLine.AppendSwitchIfNotNull("/platform:", this.PlatformWith32BitPreference);
commandLine.AppendSwitchIfNotNull("/errorreport:", this.ErrorReport);
commandLine.AppendSwitchWithInteger("/warn:", this._store, "WarningLevel");
commandLine.AppendSwitchIfNotNull("/doc:", this.DocumentationFile);
commandLine.AppendSwitchIfNotNull("/baseaddress:", this.BaseAddress);
commandLine.AppendSwitchUnquotedIfNotNull("/define:", this.GetDefineConstantsSwitch(this.DefineConstants));
commandLine.AppendSwitchIfNotNull("/win32res:", this.Win32Resource);
commandLine.AppendSwitchIfNotNull("/main:", this.MainEntryPoint);
commandLine.AppendSwitchIfNotNull("/appconfig:", this.ApplicationConfiguration);
commandLine.AppendWhenTrue("/errorendlocation", this._store, "ErrorEndLocation");
commandLine.AppendSwitchIfNotNull("/preferreduilang:", this.PreferredUILang);
commandLine.AppendPlusOrMinusSwitch("/highentropyva", this._store, "HighEntropyVA");
// If not design time build and the globalSessionGuid property was set then add a -globalsessionguid:<guid>
bool designTime = false;
if (this.HostObject != null)
{
var csHost = this.HostObject as ICscHostObject;
designTime = csHost.IsDesignTime();
}
if (!designTime)
{
if (!string.IsNullOrWhiteSpace(this.VsSessionGuid))
{
commandLine.AppendSwitchIfNotNull("/sqmsessionguid:", this.VsSessionGuid);
}
}
this.AddReferencesToCommandLine(commandLine);
base.AddResponseFileCommands(commandLine);
// This should come after the "TreatWarningsAsErrors" flag is processed (in managedcompiler.cs).
// Because if TreatWarningsAsErrors=false, then we'll have a /warnaserror- on the command-line,
// and then any specific warnings that should be treated as errors should be specified with
// /warnaserror+:<list> after the /warnaserror- switch. The order of the switches on the command-line
// does matter.
//
// Note that
// /warnaserror+
// is just shorthand for:
// /warnaserror+:<all possible warnings>
//
// Similarly,
// /warnaserror-
// is just shorthand for:
// /warnaserror-:<all possible warnings>
commandLine.AppendSwitchWithSplitting("/warnaserror+:", this.WarningsAsErrors, ",", ';', ',');
commandLine.AppendSwitchWithSplitting("/warnaserror-:", this.WarningsNotAsErrors, ",", ';', ',');
// It's a good idea for the response file to be the very last switch passed, just
// from a predictability perspective. It also solves the problem that a dogfooder
// ran into, which is described in an email thread attached to bug VSWhidbey 146883.
// See also bugs 177762 and 118307 for additional bugs related to response file position.
if (this.ResponseFiles != null)
{
foreach (ITaskItem response in this.ResponseFiles)
{
commandLine.AppendSwitchIfNotNull("@", response.ItemSpec);
}
}
}
#endregion
/// <summary>
/// The C# compiler (starting with Whidbey) supports assembly aliasing for references.
/// See spec at http://devdiv/spectool/Documents/Whidbey/VCSharp/Design%20Time/M3%20DCRs/DCR%20Assembly%20aliases.doc.
/// This method handles the necessary work of looking at the "Aliases" attribute on
/// the incoming "References" items, and making sure to generate the correct
/// command-line on csc.exe. The syntax for aliasing a reference is:
/// csc.exe /reference:Foo=System.Xml.dll
///
/// The "Aliases" attribute on the "References" items is actually a comma-separated
/// list of aliases, and if any of the aliases specified is the string "global",
/// then we add that reference to the command-line without an alias.
/// </summary>
/// <param name="commandLine"></param>
/// <owner>RGoel</owner>
void AddReferencesToCommandLine
(
CommandLineBuilderExtension commandLine
)
{
// If there were no references passed in, don't add any /reference: switches
// on the command-line.
if ((this.References == null) || (this.References.Length == 0))
{
return;
}
// Loop through all the references passed in. We'll be adding separate
// /reference: switches for each reference, and in some cases even multiple
// /reference: switches per reference.
foreach (ITaskItem reference in this.References)
{
// See if there was an "Alias" attribute on the reference.
string aliasString = reference.GetMetadata("Aliases");
string switchName = "/reference:";
bool embed = Utilities.TryConvertItemMetadataToBool(reference,
"EmbedInteropTypes");
if (embed == true)
{
switchName = "/link:";
}
if ((aliasString == null) || (aliasString.Length == 0))
{
// If there was no "Alias" attribute, just add this as a global reference.
commandLine.AppendSwitchIfNotNull(switchName, reference.ItemSpec);
}
else
{
// If there was an "Alias" attribute, it contains a comma-separated list
// of aliases to use for this reference. For each one of those aliases,
// we're going to add a separate /reference: switch to the csc.exe
// command-line
string[] aliases = aliasString.Split(',');
foreach (string alias in aliases)
{
// Trim whitespace.
string trimmedAlias = alias.Trim();
if (alias.Length == 0)
{
continue;
}
// The alias should be a valid C# identifier. Therefore it cannot
// contain comma, space, semicolon, or double-quote. Let's check for
// the existence of those characters right here, and bail immediately
// if any are present. There are a whole bunch of other characters
// that are not allowed in a C# identifier, but we'll just let csc.exe
// error out on those. The ones we're checking for here are the ones
// that could seriously screw up the command-line parsing or could
// allow parameter injection.
if (trimmedAlias.IndexOfAny(new char[] { ',', ' ', ';', '"' }) != -1)
{
throw Utilities.GetLocalizedArgumentException(
ErrorString.Csc_AssemblyAliasContainsIllegalCharacters,
reference.ItemSpec,
trimmedAlias);
}
// The alias called "global" is special. It means that we don't
// give it an alias on the command-line.
if (String.Compare("global", trimmedAlias, StringComparison.OrdinalIgnoreCase) == 0)
{
commandLine.AppendSwitchIfNotNull(switchName, reference.ItemSpec);
}
else
{
// We have a valid (and explicit) alias for this reference. Add
// it to the command-line using the syntax:
// /reference:Foo=System.Xml.dll
commandLine.AppendSwitchAliased(switchName, trimmedAlias, reference.ItemSpec);
}
}
}
}
}
/// <summary>
/// Old VS projects had some pretty messed-up looking values for the
/// "DefineConstants" property. It worked fine in the IDE, because it
/// effectively munged up the string so that it ended up being valid for
/// the compiler. We do the equivalent munging here now.
///
/// Basically, we take the incoming string, and split it on comma/semicolon/space.
/// Then we look at the resulting list of strings, and remove any that are
/// illegal identifiers, and pass the remaining ones through to the compiler.
///
/// Note that CSharp does support assigning a value to the constants ... in
/// other words, a constant is either defined or not defined ... it can't have
/// an actual value.
/// </summary>
internal string GetDefineConstantsSwitch(string originalDefineConstants)
{
if (originalDefineConstants == null)
{
return null;
}
StringBuilder finalDefineConstants = new StringBuilder();
// Split the incoming string on comma/semicolon/space.
string[] allIdentifiers = originalDefineConstants.Split(new char[] { ',', ';', ' ' });
// Loop through all the parts, and for the ones that are legal C# identifiers,
// add them to the outgoing string.
foreach (string singleIdentifier in allIdentifiers)
{
if (CSharp.SyntaxFacts.IsValidIdentifier(singleIdentifier))
{
// Separate them with a semicolon if there's something already in
// the outgoing string.
if (finalDefineConstants.Length > 0)
{
finalDefineConstants.Append(";");
}
finalDefineConstants.Append(singleIdentifier);
}
else if (singleIdentifier.Length > 0)
{
Log.LogWarningWithCodeFromResources("Csc.InvalidParameterWarning", "/define:", singleIdentifier);
}
}
if (finalDefineConstants.Length > 0)
{
return finalDefineConstants.ToString();
}
else
{
// We wouldn't want to pass in an empty /define: switch on the csc.exe command-line.
return null;
}
}
}
}
\ No newline at end of file
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.0
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Microsoft.CodeAnalysis.BuildTask {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class ErrorString {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal ErrorString() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.CodeAnalysis.BuildTask.ErrorString", typeof(ErrorString).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to MSB3053: The assembly alias &quot;{1}&quot; on reference &quot;{0}&quot; contains illegal characters..
/// </summary>
internal static string Csc_AssemblyAliasContainsIllegalCharacters {
get {
return ResourceManager.GetString("Csc_AssemblyAliasContainsIllegalCharacters", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to MSB3051: The parameter to the compiler is invalid. {0}.
/// </summary>
internal static string Csc_InvalidParameter {
get {
return ResourceManager.GetString("Csc_InvalidParameter", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to MSB3052: The parameter to the compiler is invalid, &apos;{0}{1}&apos; will be ignored..
/// </summary>
internal static string Csc_InvalidParameterWarning {
get {
return ResourceManager.GetString("Csc_InvalidParameterWarning", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to The string &quot;{0}&quot; cannot be converted to a boolean (true/false) value..
/// </summary>
internal static string General_CannotConvertStringToBool {
get {
return ResourceManager.GetString("General_CannotConvertStringToBool", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Item &quot;{0}&quot; has attribute &quot;{1}&quot; with value &quot;{2}&quot; that could not be converted to &quot;{3}&quot;..
/// </summary>
internal static string General_InvalidAttributeMetadata {
get {
return ResourceManager.GetString("General_InvalidAttributeMetadata", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to &quot;{1}&quot; is an invalid value for the &quot;{0}&quot; parameter..
/// </summary>
internal static string Vbc_ParameterHasInvalidValue {
get {
return ResourceManager.GetString("Vbc_ParameterHasInvalidValue", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to MSB3402: There was an error creating the pdb file &quot;{0}&quot;. {1}.
/// </summary>
internal static string Vbc_RenamePDB {
get {
return ResourceManager.GetString("Vbc_RenamePDB", resourceCulture);
}
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Csc_AssemblyAliasContainsIllegalCharacters" xml:space="preserve">
<value>MSB3053: The assembly alias "{1}" on reference "{0}" contains illegal characters.</value>
<comment>{StrBegin="MSB3053: "}</comment>
</data>
<data name="Csc_InvalidParameter" xml:space="preserve">
<value>MSB3051: The parameter to the compiler is invalid. {0}</value>
<comment>{StrBegin="MSB3051: "}</comment>
</data>
<data name="Csc_InvalidParameterWarning" xml:space="preserve">
<value>MSB3052: The parameter to the compiler is invalid, '{0}{1}' will be ignored.</value>
<comment>{StrBegin="MSB3052: "}</comment>
</data>
<data name="General_CannotConvertStringToBool" xml:space="preserve">
<value>The string "{0}" cannot be converted to a boolean (true/false) value.</value>
</data>
<data name="General_InvalidAttributeMetadata" xml:space="preserve">
<value>Item "{0}" has attribute "{1}" with value "{2}" that could not be converted to "{3}".</value>
</data>
<data name="Vbc_ParameterHasInvalidValue" xml:space="preserve">
<value>"{1}" is an invalid value for the "{0}" parameter.</value>
</data>
<data name="Vbc_RenamePDB" xml:space="preserve">
<value>MSB3402: There was an error creating the pdb file "{0}". {1}</value>
<comment>{StrBegin="MSB3402: "}</comment>
</data>
</root>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -->
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ImportGroup Label="Settings">
<Import Project="..\..\..\Tools\Microsoft.CodeAnalysis.Toolset.Open\Targets\VSL.Settings.targets" />
<Import Project="..\..\..\..\build\VSL.Settings.Closed.targets" />
</ImportGroup>
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{D874349C-8BB3-4BDC-8535-2D52CCCA1198}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Microsoft.CodeAnalysis.BuildTask</RootNamespace>
<AssemblyName>Microsoft.Build.Tasks.Roslyn</AssemblyName>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' " />
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' " />
<ItemGroup>
<Reference Include="Microsoft.Build.Framework, Version=$(VisualStudioReferenceAssemblyVersion), Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<Reference Include="Microsoft.Build.Tasks.$(MSBuildAssemblyNameFragment), Version=$(VisualStudioReferenceAssemblyVersion), Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<Reference Include="Microsoft.Build.Utilities.$(MSBuildAssemblyNameFragment), Version=$(VisualStudioReferenceAssemblyVersion), Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<Reference Include="System" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\CSharp\Portable\Parser\CharacterInfo.cs">
<Link>CharacterInfo.cs</Link>
</Compile>
<Compile Include="CanonicalError.cs" />
<Compile Include="CommandLineBuilderExtension.cs" />
<Compile Include="Csc.cs" />
<Compile Include="ErrorString.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>ErrorString.resx</DependentUpon>
</Compile>
<Compile Include="ManagedCompiler.cs" />
<Compile Include="PropertyDictionary.cs" />
<Compile Include="Utilities.cs" />
<Compile Include="Vbc.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="ErrorString.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>ErrorString.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ImportGroup Label="Targets">
<Import Project="..\..\..\Tools\Microsoft.CodeAnalysis.Toolset.Open\Targets\VSL.Imports.targets" />
<Import Project="..\..\..\..\build\VSL.Imports.Closed.targets" />
</ImportGroup>
</Project>
\ No newline at end of file
此差异已折叠。
using System.Collections.Generic;
namespace Microsoft.CodeAnalysis.BuildTask
{
internal class PropertyDictionary : Dictionary<string, object>
{
public T GetOrDefault<T>(string name, T @default)
{
object value;
if (this.TryGetValue(name, out value))
{
return (T)value;
}
return @default;
}
public new object this[string name]
{
get
{
object value;
return this.TryGetValue(name, out value)
? value : null;
}
set { this[name] = value; }
}
}
}
using System;
using System.Globalization;
using System.IO;
using System.Security;
using Microsoft.Build.Framework;
namespace Microsoft.CodeAnalysis.BuildTask
{
/// <summary>
/// General utilities.
/// </summary>
internal static class Utilities
{
/// <summary>
/// Convert a task item metadata to bool. Throw an exception if the string is badly formed and can't
/// be converted.
///
/// If the metadata is not found, then set metadataFound to false and then return false.
/// </summary>
/// <param name="item">The item that contains the metadata.</param>
/// <param name="itemMetadataName">The name of the metadata.</param>
/// <returns>The resulting boolean value.</returns>
internal static bool TryConvertItemMetadataToBool(ITaskItem item, string itemMetadataName)
{
string metadataValue = item.GetMetadata(itemMetadataName);
if (metadataValue == null || metadataValue.Length == 0)
{
return false;
}
try
{
return ConvertStringToBool(metadataValue);
}
catch (System.ArgumentException e)
{
throw Utilities.GetLocalizedArgumentException(
e,
ErrorString.General_InvalidAttributeMetadata,
item.ItemSpec, itemMetadataName, metadataValue, "bool");
}
}
/// <summary>
/// Converts a string to a bool. We consider "true/false", "on/off", and
/// "yes/no" to be valid boolean representations in the XML.
/// </summary>
/// <param name="parameterValue">The string to convert.</param>
/// <returns>Boolean true or false, corresponding to the string.</returns>
internal static bool ConvertStringToBool(string parameterValue)
{
if (ValidBooleanTrue(parameterValue))
{
return true;
}
else if (ValidBooleanFalse(parameterValue))
{
return false;
}
else
{
// Unsupported boolean representation.
throw Utilities.GetLocalizedArgumentException(
ErrorString.General_CannotConvertStringToBool,
parameterValue);
}
}
/// <summary>
/// Returns true if the string can be successfully converted to a bool,
/// such as "on" or "yes"
/// </summary>
internal static bool CanConvertStringToBool(string parameterValue) =>
ValidBooleanTrue(parameterValue) || ValidBooleanFalse(parameterValue);
/// <summary>
/// Returns true if the string represents a valid MSBuild boolean true value,
/// such as "on", "!false", "yes"
/// </summary>
private static bool ValidBooleanTrue(string parameterValue) =>
String.Compare(parameterValue, "true", StringComparison.OrdinalIgnoreCase) == 0 ||
String.Compare(parameterValue, "on", StringComparison.OrdinalIgnoreCase) == 0 ||
String.Compare(parameterValue, "yes", StringComparison.OrdinalIgnoreCase) == 0 ||
String.Compare(parameterValue, "!false", StringComparison.OrdinalIgnoreCase) == 0 ||
String.Compare(parameterValue, "!off", StringComparison.OrdinalIgnoreCase) == 0 ||
String.Compare(parameterValue, "!no", StringComparison.OrdinalIgnoreCase) == 0;
/// <summary>
/// Returns true if the string represents a valid MSBuild boolean false value,
/// such as "!on" "off" "no" "!true"
/// </summary>
private static bool ValidBooleanFalse(string parameterValue) =>
String.Compare(parameterValue, "false", StringComparison.OrdinalIgnoreCase) == 0 ||
String.Compare(parameterValue, "off", StringComparison.OrdinalIgnoreCase) == 0 ||
String.Compare(parameterValue, "no", StringComparison.OrdinalIgnoreCase) == 0 ||
String.Compare(parameterValue, "!true", StringComparison.OrdinalIgnoreCase) == 0 ||
String.Compare(parameterValue, "!on", StringComparison.OrdinalIgnoreCase) == 0 ||
String.Compare(parameterValue, "!yes", StringComparison.OrdinalIgnoreCase) == 0;
internal static string GetFullPathNoThrow(string path)
{
try
{
path = Path.GetFullPath(path);
}
catch (Exception e) when (IsIoRelatedException(e)) { }
return path;
}
internal static void DeleteNoThrow(string path)
{
try
{
File.Delete(path);
}
catch (Exception e) when (IsIoRelatedException(e)) { }
}
internal static bool IsIoRelatedException(Exception e) =>
e is UnauthorizedAccessException ||
e is NotSupportedException ||
(e is ArgumentException && !(e is ArgumentNullException)) ||
e is SecurityException ||
e is IOException;
internal static Exception GetLocalizedArgumentException(Exception e,
string errorString,
params object[] args)
{
return new ArgumentException(string.Format(CultureInfo.CurrentCulture, errorString, args), e);
}
internal static Exception GetLocalizedArgumentException(string errorString,
params object[] args)
{
return new ArgumentException(string.Format(CultureInfo.CurrentCulture, errorString, args));
}
}
}
此差异已折叠。
<?xml version="1.0" encoding="utf-8"?>
<packages />
......@@ -321,7 +321,7 @@ Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "SharedCollections", "Compil
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CSharpWinRTTest", "Compilers\CSharp\Test\WinRT\CSharpWinRTTest.csproj", "{FCFA8808-A1B6-48CC-A1EA-0B8CA8AEDA8E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pdb2Xml", "Tools\Source\Pdb2Xml\Pdb2Xml.csproj", "{CF450DCE-D12B-4A11-8D2D-A7A125372C48}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MSBuildTask", "Compilers\Core\MSBuildTask\MSBuildTask.csproj", "{D874349C-8BB3-4BDC-8535-2D52CCCA1198}"
EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
......@@ -1862,6 +1862,18 @@ Global
{CF450DCE-D12B-4A11-8D2D-A7A125372C48}.Release|ARM.Build.0 = Release|Any CPU
{CF450DCE-D12B-4A11-8D2D-A7A125372C48}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{CF450DCE-D12B-4A11-8D2D-A7A125372C48}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{D874349C-8BB3-4BDC-8535-2D52CCCA1198}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D874349C-8BB3-4BDC-8535-2D52CCCA1198}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D874349C-8BB3-4BDC-8535-2D52CCCA1198}.Debug|ARM.ActiveCfg = Debug|Any CPU
{D874349C-8BB3-4BDC-8535-2D52CCCA1198}.Debug|ARM.Build.0 = Debug|Any CPU
{D874349C-8BB3-4BDC-8535-2D52CCCA1198}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{D874349C-8BB3-4BDC-8535-2D52CCCA1198}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{D874349C-8BB3-4BDC-8535-2D52CCCA1198}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D874349C-8BB3-4BDC-8535-2D52CCCA1198}.Release|Any CPU.Build.0 = Release|Any CPU
{D874349C-8BB3-4BDC-8535-2D52CCCA1198}.Release|ARM.ActiveCfg = Release|Any CPU
{D874349C-8BB3-4BDC-8535-2D52CCCA1198}.Release|ARM.Build.0 = Release|Any CPU
{D874349C-8BB3-4BDC-8535-2D52CCCA1198}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{D874349C-8BB3-4BDC-8535-2D52CCCA1198}.Release|Mixed Platforms.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
......@@ -2011,6 +2023,7 @@ Global
{D0BC9BE7-24F6-40CA-8DC6-FCB93BD44B34} = {A41D1B99-F489-4C43-BBDF-96D61B19A6B9}
{C1930979-C824-496B-A630-70F5369A636F} = {A41D1B99-F489-4C43-BBDF-96D61B19A6B9}
{FCFA8808-A1B6-48CC-A1EA-0B8CA8AEDA8E} = {32A48625-F0AD-419D-828B-A50BDABA38EA}
{D874349C-8BB3-4BDC-8535-2D52CCCA1198} = {A41D1B99-F489-4C43-BBDF-96D61B19A6B9}
{CF450DCE-D12B-4A11-8D2D-A7A125372C48} = {64BDF58B-41BA-A19E-0D34-B5FA598403B6}
EndGlobalSection
EndGlobal
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册