未验证 提交 ac5c33de 编写于 作者: F Fan Yang 提交者: GitHub

Add msbuild task to generate binary runtimeconfig format (#49544)

* Add msbuild task to generate binary runtimeconfig format

* Update property name due to naming conversion.

* Fixed more formatting issue

* Fixed one more naming convention

* Update src/tasks/RuntimeConfigParser/RuntimeConfigParser.cs
Co-authored-by: NRyan Lucia <ryan@luciaonline.net>

* Update src/tasks/RuntimeConfigParser/RuntimeConfigParser.cs
Co-authored-by: NRyan Lucia <ryan@luciaonline.net>

* Update src/tasks/RuntimeConfigParser/RuntimeConfigParser.cs
Co-authored-by: NRyan Lucia <ryan@luciaonline.net>

* Fixed comments

* Update src/tasks/RuntimeConfigParser/RuntimeConfigParser.cs
Co-authored-by: NDan Moseley <danmose@microsoft.com>

* Fix error handling
Co-authored-by: NRyan Lucia <ryan@luciaonline.net>
Co-authored-by: NDan Moseley <danmose@microsoft.com>
上级 49e5d17b
......@@ -70,6 +70,7 @@
<WasmAppBuilderDir>$([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'WasmAppBuilder', 'Debug', '$(NetCoreAppToolCurrent)', 'publish'))</WasmAppBuilderDir>
<WasmBuildTasksDir>$([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'WasmBuildTasks', 'Debug', '$(NetCoreAppToolCurrent)', 'publish'))</WasmBuildTasksDir>
<MonoAOTCompilerDir>$([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'MonoAOTCompiler', 'Debug', '$(NetCoreAppToolCurrent)'))</MonoAOTCompilerDir>
<RuntimeConfigParserDir>$([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'RuntimeConfigParser', 'Debug', '$(NetCoreAppToolCurrent)', 'publish'))</RuntimeConfigParserDir>
<InstallerTasksAssemblyPath Condition="'$(MSBuildRuntimeType)' == 'Core'">$([MSBuild]::NormalizePath('$(ArtifactsBinDir)', 'installer.tasks', 'Debug', '$(NetCoreAppToolCurrent)', 'installer.tasks.dll'))</InstallerTasksAssemblyPath>
<InstallerTasksAssemblyPath Condition="'$(MSBuildRuntimeType)' != 'Core'">$([MSBuild]::NormalizePath('$(ArtifactsBinDir)', 'installer.tasks', 'Debug', 'net461', 'installer.tasks.dll'))</InstallerTasksAssemblyPath>
......@@ -78,6 +79,7 @@
<WasmAppBuilderTasksAssemblyPath>$([MSBuild]::NormalizePath('$(WasmAppBuilderDir)', 'WasmAppBuilder.dll'))</WasmAppBuilderTasksAssemblyPath>
<WasmBuildTasksAssemblyPath>$([MSBuild]::NormalizePath('$(WasmBuildTasksDir)', 'WasmBuildTasks.dll'))</WasmBuildTasksAssemblyPath>
<MonoAOTCompilerTasksAssemblyPath>$([MSBuild]::NormalizePath('$(MonoAOTCompilerDir)', 'MonoAOTCompiler.dll'))</MonoAOTCompilerTasksAssemblyPath>
<RuntimeConfigParserTasksAssemblyPath>$([MSBuild]::NormalizePath('$(RuntimeConfigParserDir)', 'RuntimeConfigParser.dll'))</RuntimeConfigParserTasksAssemblyPath>
</PropertyGroup>
<PropertyGroup Label="CalculateConfiguration">
......
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Reflection.Metadata;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
public class RuntimeConfigParserTask : Task
{
/// <summary>
/// The path to runtimeconfig.json file.
/// </summary>
[Required]
public string RuntimeConfigFile { get; set; } = "";
/// <summary>
/// The path to the output binary file.
/// </summary>
[Required]
public string OutputFile { get; set; } = "";
/// <summary>
/// List of properties reserved for the host.
/// </summary>
public ITaskItem[] ReservedProperties { get; set; } = Array.Empty<ITaskItem>();
public override bool Execute()
{
if (string.IsNullOrEmpty(RuntimeConfigFile))
{
Log.LogError($"'{nameof(RuntimeConfigFile)}' is required.");
}
if (string.IsNullOrEmpty(OutputFile))
{
Log.LogError($"'{nameof(OutputFile)}' is required.");
}
Dictionary<string, string> configProperties = ConvertInputToDictionary(RuntimeConfigFile);
if (ReservedProperties.Length != 0)
{
CheckDuplicateProperties(configProperties, ReservedProperties);
}
var blobBuilder = new BlobBuilder();
ConvertDictionaryToBlob(configProperties, blobBuilder);
using var stream = File.OpenWrite(OutputFile);
blobBuilder.WriteContentTo(stream);
return !Log.HasLoggedErrors;
}
/// Reads a json file from the given path and extracts the "configProperties" key (assumed to be a string to string dictionary)
private Dictionary<string, string> ConvertInputToDictionary(string inputFilePath)
{
var options = new JsonSerializerOptions {
AllowTrailingCommas = true,
ReadCommentHandling = JsonCommentHandling.Skip,
};
var jsonString = File.ReadAllText(inputFilePath);
var parsedJson = JsonSerializer.Deserialize<Root>(jsonString, options);
if (parsedJson == null)
{
throw new ArgumentException("Wasn't able to parse the json file successfully.");
}
return parsedJson.ConfigProperties;
}
/// Just write the dictionary out to a blob as a count followed by
/// a length-prefixed UTF8 encoding of each key and value
private void ConvertDictionaryToBlob(IReadOnlyDictionary<string, string> properties, BlobBuilder builder)
{
int count = properties.Count;
builder.WriteCompressedInteger(count);
foreach (var kvp in properties)
{
builder.WriteSerializedString(kvp.Key);
builder.WriteSerializedString(kvp.Value);
}
}
private void CheckDuplicateProperties(IReadOnlyDictionary<string, string> properties, ITaskItem[] keys)
{
foreach (var key in keys)
{
if (properties.ContainsKey(key.ItemSpec))
{
throw new ArgumentException($"Property '{key}' can't be set by the user!");
}
}
}
}
public class Root
{
// the configProperties key
[JsonPropertyName("configProperties")]
public Dictionary<string, string> ConfigProperties { get; set; } = new Dictionary<string, string>();
// everything other than configProperties
[JsonExtensionData]
public Dictionary<string, object> ExtensionData { get; set; } = new Dictionary<string, object>();
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>$(NetCoreAppToolCurrent)</TargetFramework>
<OutputType>Library</OutputType>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
<Nullable>enable</Nullable>
<NoWarn>$(NoWarn),CA1050</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Build" Version="$(RefOnlyMicrosoftBuildVersion)" />
<PackageReference Include="Microsoft.Build.Framework" Version="$(RefOnlyMicrosoftBuildFrameworkVersion)" />
<PackageReference Include="Microsoft.Build.Tasks.Core" Version="$(RefOnlyMicrosoftBuildTasksCoreVersion)" />
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="$(RefOnlyMicrosoftBuildUtilitiesCoreVersion)" />
<PackageReference Include="System.Reflection.Metadata" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
<Compile Include="RuntimeConfigParser.cs" />
</ItemGroup>
<Target Name="PublishBuilder"
AfterTargets="Build"
DependsOnTargets="Publish" />
<Target Name="GetFilesToPackage" />
</Project>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册