|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#nullable disable
using System.Xml;
using System.Xml.Linq;
using Microsoft.DotNet.Cli.ToolPackage.ToolConfigurationDeserialization;
using Microsoft.Extensions.EnvironmentAbstractions;
using NuGet.Packaging.Core;
namespace Microsoft.DotNet.Cli.ToolPackage;
internal static class ToolConfigurationDeserializer
{
// The supported tool configuration schema version.
// This should match the schema version in the GenerateToolsSettingsFile task from the SDK.
private const int SupportedVersion = 2;
public static ToolConfiguration Deserialize(string pathToXml, IFileSystem fileSystem = null)
{
fileSystem ??= new FileSystemWrapper();
DotNetCliTool dotNetCliTool;
try
{
XDocument doc;
using (var stream = fileSystem.File.OpenRead(pathToXml))
{
doc = XDocument.Load(stream);
}
var root = doc.Root;
dotNetCliTool = new DotNetCliTool
{
Version = (string)root.Attribute("Version"),
Commands = root.Element("Commands")?
.Elements("Command")
.Select(e => new DotNetCliToolCommand
{
Name = (string)e.Attribute("Name"),
EntryPoint = (string)e.Attribute("EntryPoint"),
Runner = (string)e.Attribute("Runner"),
})
.ToArray(),
RuntimeIdentifierPackages = root.Element("RuntimeIdentifierPackages")?
.Elements("RuntimeIdentifierPackage")
.Select(e => new DotNetCliToolRuntimeIdentifierPackage
{
RuntimeIdentifier = (string)e.Attribute("RuntimeIdentifier"),
Id = (string)e.Attribute("Id"),
})
.ToArray(),
};
}
catch (XmlException ex)
{
throw new ToolConfigurationException(
string.Format(
CliStrings.ToolSettingsInvalidXml,
ex.Message),
ex);
}
catch (Exception ex) when (ex is IOException || ex is UnauthorizedAccessException)
{
throw new ToolConfigurationException(
string.Format(
CliStrings.FailedToRetrieveToolConfiguration,
ex.Message),
ex);
}
List<string> warnings = GenerateWarningAccordingToVersionAttribute(dotNetCliTool);
if (dotNetCliTool.Commands.Length != 1)
{
throw new ToolConfigurationException(CliStrings.ToolSettingsMoreThanOneCommand);
}
var runner = dotNetCliTool.Commands[0].Runner;
if (!string.IsNullOrEmpty(runner) && runner != "dotnet" && runner != "executable")
{
throw new ToolConfigurationException(
string.Format(
CliStrings.ToolSettingsUnsupportedRunner,
dotNetCliTool.Commands[0].Name,
dotNetCliTool.Commands[0].Runner));
}
var ridSpecificPackages = dotNetCliTool.RuntimeIdentifierPackages?.ToDictionary(p => p.RuntimeIdentifier, p => new PackageIdentity(p.Id, null))
.AsReadOnly();
// Also error out if there are no RID-specific packages and the runner is empty
if (string.IsNullOrEmpty(runner) && (ridSpecificPackages == null || !ridSpecificPackages.Any()))
{
throw new ToolConfigurationException(
string.Format(
CliStrings.ToolSettingsUnsupportedRunner,
dotNetCliTool.Commands[0].Name,
dotNetCliTool.Commands[0].Runner));
}
return new ToolConfiguration(
dotNetCliTool.Commands[0].Name,
dotNetCliTool.Commands[0].EntryPoint,
dotNetCliTool.Commands[0].Runner,
ridSpecificPackages: ridSpecificPackages,
warnings: warnings);
}
private static List<string> GenerateWarningAccordingToVersionAttribute(DotNetCliTool dotNetCliTool)
{
List<string> warnings = [];
if (string.IsNullOrWhiteSpace(dotNetCliTool.Version))
{
warnings.Add(CliStrings.FormatVersionIsMissing);
}
else
{
if (!int.TryParse(dotNetCliTool.Version, out int version))
{
warnings.Add(CliStrings.FormatVersionIsMalformed);
}
else
{
if (version > SupportedVersion)
{
warnings.Add(CliStrings.FormatVersionIsHigher);
}
}
}
return warnings;
}
}
|