// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable enable
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Security;
namespace Microsoft.CodeAnalysis.BuildTasks
{
///
/// General utilities.
///
internal static class Utilities
{
private const string MSBuildRoslynFolderName = "Roslyn";
///
/// Copied from msbuild. ItemSpecs are normalized using this method.
///
public static string FixFilePath(string path)
=> string.IsNullOrEmpty(path) || Path.DirectorySeparatorChar == '\\' ? path : path.Replace('\\', '/');
///
/// 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.
///
/// The item that contains the metadata.
/// The name of the metadata.
/// The resulting boolean value.
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");
}
}
///
/// Converts a string to a bool. We consider "true/false", "on/off", and
/// "yes/no" to be valid boolean representations in the XML.
///
/// The string to convert.
/// Boolean true or false, corresponding to the string.
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);
}
}
///
/// Returns true if the string can be successfully converted to a bool,
/// such as "on" or "yes"
///
internal static bool CanConvertStringToBool(string parameterValue) =>
ValidBooleanTrue(parameterValue) || ValidBooleanFalse(parameterValue);
///
/// Returns true if the string represents a valid MSBuild boolean true value,
/// such as "on", "!false", "yes"
///
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;
///
/// Returns true if the string represents a valid MSBuild boolean false value,
/// such as "!on" "off" "no" "!true"
///
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));
}
internal static string? TryGetAssemblyPath(Assembly assembly)
{
if (assembly.GlobalAssemblyCache)
{
return null;
}
if (assembly.CodeBase is { } codebase)
{
var uri = new Uri(codebase);
return uri.IsFile ? uri.LocalPath : assembly.Location;
}
return null;
}
///
/// Generate the full path to the tool that is deployed with our build tasks.
///
internal static string GenerateFullPathToTool(string toolName)
{
var buildTask = typeof(Utilities).GetTypeInfo().Assembly;
var assemblyPath = buildTask.Location;
var assemblyDirectory = Path.GetDirectoryName(assemblyPath);
return RuntimeHostInfo.IsDesktopRuntime
? Path.Combine(assemblyDirectory!, toolName)
: Path.Combine(assemblyDirectory!, "bincore", toolName);
}
}
}