* [wasm] Extract a base class from WasmAppBuilder, for future use with WasiAppBuilder task
* [wasm] RunScriptTemplate* - cleanup for use in WBT
And introduce a $SDK_DIR_NAME envvar
* [wasm] Wasm.Build.Tests - move some files to Common/ for use with Wasi.Build.Tests
* Address review feedback from @maraf
<IncludeNodePayload Condition="'$(NeedsEMSDKNode)' == 'true' and '$(NeedsEMSDK)' != 'true'">true</IncludeNodePayload>
<UseDotNetCliVersionFromGlobalJson>true</UseDotNetCliVersionFromGlobalJson>
+
+ <SdkForWorkloadTestingDirName Condition="'$(NeedsWorkload)' == 'true' and '$(TestUsingWorkloads)' == 'true'">dotnet-latest</SdkForWorkloadTestingDirName>
+ <SdkForWorkloadTestingDirName Condition="'$(NeedsWorkload)' == 'true' and '$(TestUsingWorkloads)' != 'true'">dotnet-none</SdkForWorkloadTestingDirName>
</PropertyGroup>
<Import Project="$(TestArchiveRoot)**\*.helix.targets" />
<HelixPreCommand Condition="'$(WindowsShell)' == 'true'" Include="set "BUILT_NUGETS_PATH=%HELIX_CORRELATION_PAYLOAD%/built-nugets"" />
<HelixPreCommand Condition="'$(WindowsShell)' != 'true'" Include="export "BUILT_NUGETS_PATH=$HELIX_CORRELATION_PAYLOAD/built-nugets"" />
+
+ <HelixPreCommand Condition="'$(WindowsShell)' == 'true'" Include="set "SDK_DIR_NAME=$(SdkForWorkloadTestingDirName)"" />
+ <HelixPreCommand Condition="'$(WindowsShell)' != 'true'" Include="export "SDK_DIR_NAME=$(SdkForWorkloadTestingDirName)"" />
</ItemGroup>
<PropertyGroup>
<_TestUsingCrossProductValues Include="@(_TestUsingCrossProductValuesTemp)">
<Webcil>%(_TestUsingWebcilValues.Identity)</Webcil>
</_TestUsingCrossProductValues>
-
+
<_BuildWasmAppsProjectsToBuild Include="$(PerScenarioProjectFile)">
<AdditionalProperties>$(_PropertiesToPass);Scenario=BuildWasmApps;TestArchiveRuntimeFile=$(TestArchiveRuntimeFile);TestUsingWorkloads=%(_TestUsingCrossProductValues.Workloads);TestUsingWebcil=%(_TestUsingCrossProductValues.Webcil)</AdditionalProperties>
<AdditionalProperties Condition="'$(NeedsToBuildWasmAppsOnHelix)' != ''">%(_BuildWasmAppsProjectsToBuild.AdditionalProperties);NeedsToBuildWasmAppsOnHelix=$(NeedsToBuildWasmAppsOnHelix)</AdditionalProperties>
internal static readonly string? BuiltNuGetsPath = Environment.GetEnvironmentVariable("BUILT_NUGETS_PATH");
internal static readonly string? BrowserPathForTests = Environment.GetEnvironmentVariable("BROWSER_PATH_FOR_TESTS");
internal static readonly bool ShowBuildOutput = Environment.GetEnvironmentVariable("SHOW_BUILD_OUTPUT") is not null;
- internal static readonly bool UseWebcil = Environment.GetEnvironmentVariable("USE_WEBCIL_FOR_TESTS") is "true";
+ internal static readonly bool UseWebcil = Environment.GetEnvironmentVariable("USE_WEBCIL_FOR_TESTS") is "true";
+ internal static readonly string? SdkDirName = Environment.GetEnvironmentVariable("SDK_DIR_NAME");
}
}
<Error Condition="'$(TestUsingWorkloads)' == 'true' and '$(PackageVersionForWorkloadManifests)' == ''"
Text="%24(PackageVersionForWorkloadManifests) is not set. PackageVersion=$(PackageVersion)." />
+ <PropertyGroup>
+ <_SdkWithWorkloadForTestingDirName>$([System.IO.Path]::GetDirectoryName($(SdkWithWorkloadForTestingPath)))</_SdkWithWorkloadForTestingDirName>
+ <_SdkWithWorkloadForTestingDirName>$([System.IO.Path]::GetFilename($(_SdkWithWorkloadForTestingDirName)))</_SdkWithWorkloadForTestingDirName>
+ </PropertyGroup>
+
<ItemGroup Condition="'$(TestUsingWorkloads)' == 'true'">
<RunScriptCommands Condition="'$(OS)' != 'Windows_NT'" Include="export WORKLOAD_PACKS_VER=$(PackageVersionForWorkloadManifests)" />
<RunScriptCommands Condition="'$(OS)' == 'Windows_NT'" Include="set WORKLOAD_PACKS_VER=$(PackageVersionForWorkloadManifests)" />
<RunScriptCommands Condition="'$(OS)' != 'Windows_NT'" Include="export TEST_USING_WORKLOADS=$(TestUsingWorkloads)" />
<RunScriptCommands Condition="'$(OS)' == 'Windows_NT'" Include="set TEST_USING_WORKLOADS=$(TestUsingWorkloads)" />
+ <RunScriptCommands Condition="'$(OS)' != 'Windows_NT'" Include="export SDK_DIR_NAME=$(_SdkWithWorkloadForTestingDirName)" />
+ <RunScriptCommands Condition="'$(OS)' == 'Windows_NT'" Include="set SDK_DIR_NAME=$(_SdkWithWorkloadForTestingDirName)" />
+
<RunScriptCommands Condition="'$(OS)' != 'Windows_NT'" Include="export TEST_USING_WEBCIL=$(TestUsingWebcil)" />
<RunScriptCommands Condition="'$(OS)' == 'Windows_NT'" Include="set TEST_USING_WEBCIL=$(TestUsingWebcil)" />
[[SetCommandsEcho]]
set EXECUTION_DIR=%~dp0
-if [%3] NEQ [] (
- set SCENARIO=%3
-)
if [%HELIX_WORKITEM_UPLOAD_ROOT%] == [] (
set "XHARNESS_OUT=%EXECUTION_DIR%xharness-output"
set "XHARNESS_OUT=%HELIX_WORKITEM_UPLOAD_ROOT%\xharness-output"
)
-if [%XHARNESS_CLI_PATH%] NEQ [] (
- :: When running in CI, we only have the .NET runtime available
- :: We need to call the XHarness CLI DLL directly via dotnet exec
- set HARNESS_RUNNER=dotnet.exe exec "%XHARNESS_CLI_PATH%"
-) else (
- set HARNESS_RUNNER=dotnet.exe xharness
-)
-
-if [%XHARNESS_COMMAND%] == [] (
- if /I [%SCENARIO%]==[WasmTestOnBrowser] (
- set XHARNESS_COMMAND=test-browser
- ) else (
- set XHARNESS_COMMAND=test
- )
-)
-
-if /I [%XHARNESS_COMMAND%] == [test] (
- if [%JS_ENGINE%] == [] (
- if /I [%SCENARIO%] == [WasmTestOnNodeJS] (
- set "JS_ENGINE=--engine^=NodeJS"
- ) else (
- set "JS_ENGINE=--engine^=V8"
- )
- )
-
- if [%MAIN_JS%] == [] (
- set "MAIN_JS=--js-file^=test-main.js"
- )
-
- if [%JS_ENGINE_ARGS%] == [] (
- set "JS_ENGINE_ARGS=--engine-arg^=--stack-trace-limit^=1000"
- )
-) else (
- if [%BROWSER_PATH%] == [] if not [%HELIX_CORRELATION_PAYLOAD%] == [] (
- set "BROWSER_PATH=--browser-path^=%HELIX_CORRELATION_PAYLOAD%\chrome-win\chrome.exe"
- )
-)
-
-if [%XHARNESS_ARGS%] == [] (
- set "XHARNESS_ARGS=%JS_ENGINE% %JS_ENGINE_ARGS% %BROWSER_PATH% %MAIN_JS%"
-)
-
if [%PREPEND_PATH%] NEQ [] (
set "PATH=%PREPEND_PATH%;%PATH%"
)
echo SCENARIO=%SCENARIO%
echo XHARNESS_OUT=%XHARNESS_OUT%
echo XHARNESS_CLI_PATH=%XHARNESS_CLI_PATH%
-echo HARNESS_RUNNER=%HARNESS_RUNNER%
-echo XHARNESS_COMMAND=%XHARNESS_COMMAND%
-echo MAIN_JS=%MAIN_JS%
-echo JS_ENGINE=%JS_ENGINE%
-echo JS_ENGINE_ARGS=%JS_ENGINE_ARGS%
-echo XHARNESS_ARGS=%XHARNESS_ARGS%
set TEST_LOG_PATH=%XHARNESS_OUT%\logs
REM Functions
:SetEnvVars
if [%TEST_USING_WORKLOADS%] == [true] (
- set _DIR_NAME=dotnet-net7+latest
set SDK_HAS_WORKLOAD_INSTALLED=true
) else (
- set _DIR_NAME=dotnet-none
set SDK_HAS_WORKLOAD_INSTALLED=false
)
if [%TEST_USING_WEBCIL%] == [true] (
)
if [%HELIX_CORRELATION_PAYLOAD%] NEQ [] (
- robocopy /mt /np /nfl /NDL /nc /e %BASE_DIR%\%_DIR_NAME% %EXECUTION_DIR%\%_DIR_NAME%
- set _SDK_DIR=%EXECUTION_DIR%\%_DIR_NAME%
+ robocopy /mt /np /nfl /NDL /nc /e %BASE_DIR%\%SDK_DIR_NAME% %EXECUTION_DIR%\%SDK_DIR_NAME%
+ set _SDK_DIR=%EXECUTION_DIR%\%SDK_DIR_NAME%
) else (
- set _SDK_DIR=%BASE_DIR%\%_DIR_NAME%
+ set _SDK_DIR=%BASE_DIR%\%SDK_DIR_NAME%
)
set "PATH=%_SDK_DIR%;%PATH%"
#!/usr/bin/env bash
-# SetCommands defined in eng\testing\tests.wasm.targets
+set
+echo "------------------------ start -------------------"
+
[[SetCommands]]
[[SetCommandsEcho]]
EXECUTION_DIR=$(dirname $0)
-if [[ -n "$3" ]]; then
- SCENARIO=$3
-fi
if [[ -z "$HELIX_WORKITEM_UPLOAD_ROOT" ]]; then
XHARNESS_OUT="$EXECUTION_DIR/xharness-output"
XHARNESS_OUT="$HELIX_WORKITEM_UPLOAD_ROOT/xharness-output"
fi
-if [[ -n "$XHARNESS_CLI_PATH" ]]; then
- # When running in CI, we only have the .NET runtime available
- # We need to call the XHarness CLI DLL directly via dotnet exec
- HARNESS_RUNNER="dotnet exec $XHARNESS_CLI_PATH"
-else
- HARNESS_RUNNER="dotnet xharness"
-fi
-
-if [[ -z "$XHARNESS_COMMAND" ]]; then
- if [[ "$SCENARIO" == "WasmTestOnBrowser" || "$SCENARIO" == "wasmtestonbrowser" ]]; then
- XHARNESS_COMMAND="test-browser"
- else
- XHARNESS_COMMAND="test"
- fi
-fi
-
-if [[ "$XHARNESS_COMMAND" == "test" ]]; then
- if [[ -z "$JS_ENGINE" ]]; then
- if [[ "$SCENARIO" == "WasmTestOnNodeJS" || "$SCENARIO" == "wasmtestonnodejs" ]]; then
- JS_ENGINE="--engine=NodeJS"
- else
- JS_ENGINE="--engine=V8"
- fi
- fi
-
- if [[ -z "$MAIN_JS" ]]; then
- MAIN_JS="--js-file=test-main.js"
- fi
-
- if [[ -z "$JS_ENGINE_ARGS" ]]; then
- JS_ENGINE_ARGS="--engine-arg=--stack-trace-limit=1000"
- fi
-fi
-
-if [[ -z "$XHARNESS_ARGS" ]]; then
- XHARNESS_ARGS="$JS_ENGINE $JS_ENGINE_ARGS $MAIN_JS"
-fi
-
if [[ -n "$PREPEND_PATH" ]]; then
export PATH=$PREPEND_PATH:$PATH
fi
echo EXECUTION_DIR=$EXECUTION_DIR
-echo SCENARIO=$SCENARIO
echo XHARNESS_OUT=$XHARNESS_OUT
echo XHARNESS_CLI_PATH=$XHARNESS_CLI_PATH
-echo HARNESS_RUNNER=$HARNESS_RUNNER
-echo XHARNESS_COMMAND=$XHARNESS_COMMAND
-echo MAIN_JS=$MAIN_JS
-echo JS_ENGINE=$JS_ENGINE
-echo JS_ENGINE_ARGS=$JS_ENGINE_ARGS
-echo XHARNESS_ARGS=$XHARNESS_ARGS
function set_env_vars()
{
- local _DIR_NAME=
if [ "x$TEST_USING_WORKLOADS" = "xtrue" ]; then
- _DIR_NAME=dotnet-net7+latest
export SDK_HAS_WORKLOAD_INSTALLED=true
else
- _DIR_NAME=dotnet-none
export SDK_HAS_WORKLOAD_INSTALLED=false
fi
local _SDK_DIR=
if [[ -n "$HELIX_WORKITEM_UPLOAD_ROOT" ]]; then
- cp -r $BASE_DIR/$_DIR_NAME $EXECUTION_DIR
- _SDK_DIR=$EXECUTION_DIR/$_DIR_NAME
+ cp -r $BASE_DIR/$SDK_DIR_NAME $EXECUTION_DIR
+ _SDK_DIR=$EXECUTION_DIR/$SDK_DIR_NAME
else
- _SDK_DIR=$BASE_DIR/$_DIR_NAME
+ _SDK_DIR=$BASE_DIR/$SDK_DIR_NAME
fi
export PATH=$_SDK_DIR:$PATH
<Project>
<!-- not really meant to be used w/o WasmApp.targets -->
- <UsingTask TaskName="ManagedToNativeGenerator" AssemblyFile="$(WasmAppBuilderTasksAssemblyPath)" />
+ <UsingTask TaskName="Microsoft.WebAssembly.Build.Tasks.ManagedToNativeGenerator" AssemblyFile="$(WasmAppBuilderTasksAssemblyPath)" />
<UsingTask TaskName="Microsoft.WebAssembly.Build.Tasks.EmccCompile" AssemblyFile="$(WasmAppBuilderTasksAssemblyPath)" />
<PropertyGroup>
<Project>
- <UsingTask TaskName="WasmAppBuilder" AssemblyFile="$(WasmAppBuilderTasksAssemblyPath)" />
- <UsingTask TaskName="WasmLoadAssembliesAndReferences" AssemblyFile="$(WasmAppBuilderTasksAssemblyPath)" />
- <UsingTask TaskName="WasmCalculateInitialHeapSize" AssemblyFile="$(WasmAppBuilderTasksAssemblyPath)" />
+ <UsingTask TaskName="Microsoft.WebAssembly.Build.Tasks.WasmAppBuilder" AssemblyFile="$(WasmAppBuilderTasksAssemblyPath)" />
+ <UsingTask TaskName="Microsoft.WebAssembly.Build.Tasks.WasmLoadAssembliesAndReferences" AssemblyFile="$(WasmAppBuilderTasksAssemblyPath)" />
+ <UsingTask TaskName="Microsoft.WebAssembly.Build.Tasks.WasmCalculateInitialHeapSize" AssemblyFile="$(WasmAppBuilderTasksAssemblyPath)" />
<!--
Required public items/properties:
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
+namespace Microsoft.WebAssembly.Build.Tasks;
+
public class ManagedToNativeGenerator : Task
{
[Required]
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
-public class WasmAppBuilder : Task
-{
- [NotNull]
- [Required]
- public string? AppDir { get; set; }
+namespace Microsoft.WebAssembly.Build.Tasks;
+public class WasmAppBuilder : WasmAppBuilderBaseTask
+{
[NotNull]
[Required]
public string? MainJS { get; set; }
- [Required]
- public string[] Assemblies { get; set; } = Array.Empty<string>();
-
- [NotNull]
- [Required]
- public ITaskItem[]? NativeAssets { get; set; }
-
- private readonly List<string> _fileWrites = new();
-
- [Output]
- public string[]? FileWrites => _fileWrites.ToArray();
-
- // full list of ICU data files we produce can be found here:
- // https://github.com/dotnet/icu/tree/maint/maint-67/icu-filters
- public string? IcuDataFileName { get; set; }
-
- public int DebugLevel { get; set; }
- public ITaskItem[]? SatelliteAssemblies { get; set; }
public ITaskItem[]? FilesToIncludeInFileSystem { get; set; }
public ITaskItem[]? RemoteSources { get; set; }
- public bool InvariantGlobalization { get; set; }
- public ITaskItem[]? ExtraFilesToDeploy { get; set; }
public string? MainHTMLPath { get; set; }
public bool IncludeThreadsWorker {get; set; }
public int PThreadPoolSize {get; set; }
// </summary>
public ITaskItem[]? ExtraConfig { get; set; }
- public string? DefaultHostConfig { get; set; }
-
- [Required, NotNull]
- public string? MainAssemblyName { get; set; }
-
- [Required]
- public ITaskItem[] HostConfigs { get; set; } = Array.Empty<ITaskItem>();
-
- public ITaskItem[] RuntimeArgsForHost { get; set; } = Array.Empty<ITaskItem>();
-
private sealed class WasmAppConfig
{
[JsonPropertyName("mainAssemblyName")]
public bool LoadRemote { get; set; }
}
- public override bool Execute ()
- {
- try
- {
- return ExecuteInternal();
- }
- catch (LogAsErrorException laee)
- {
- Log.LogError(laee.Message);
- return false;
- }
- }
-
- private bool ExecuteInternal ()
+ protected override bool ExecuteInternal()
{
if (!File.Exists(MainJS))
throw new LogAsErrorException($"File MainJS='{MainJS}' doesn't exist.");
return !Log.HasLoggedErrors;
}
- private void UpdateRuntimeConfigJson()
- {
- string[] matchingAssemblies = Assemblies.Where(asm => Path.GetFileName(asm) == MainAssemblyName).ToArray();
- if (matchingAssemblies.Length == 0)
- throw new LogAsErrorException($"Could not find main assembly named {MainAssemblyName} in the list of assemblies");
-
- if (matchingAssemblies.Length > 1)
- throw new LogAsErrorException($"Found more than one assembly matching the main assembly name {MainAssemblyName}: {string.Join(",", matchingAssemblies)}");
-
- string runtimeConfigPath = Path.ChangeExtension(matchingAssemblies[0], ".runtimeconfig.json");
- if (!File.Exists(runtimeConfigPath))
- {
- Log.LogMessage(MessageImportance.Low, $"Could not find {runtimeConfigPath}. Ignoring.");
- return;
- }
-
- var rootNode = JsonNode.Parse(File.ReadAllText(runtimeConfigPath),
- new JsonNodeOptions { PropertyNameCaseInsensitive = true });
- if (rootNode == null)
- throw new LogAsErrorException($"Failed to parse {runtimeConfigPath}");
-
- JsonObject? rootObject = rootNode.AsObject();
- if (!rootObject.TryGetPropertyValue("runtimeOptions", out JsonNode? runtimeOptionsNode)
- || !(runtimeOptionsNode is JsonObject runtimeOptionsObject))
- {
- throw new LogAsErrorException($"Could not find node named 'runtimeOptions' in {runtimeConfigPath}");
- }
-
- JsonObject wasmHostProperties = runtimeOptionsObject.GetOrCreate<JsonObject>("wasmHostProperties", () => new JsonObject());
- JsonArray runtimeArgsArray = wasmHostProperties.GetOrCreate<JsonArray>("runtimeArgs", () => new JsonArray());
- JsonArray perHostConfigs = wasmHostProperties.GetOrCreate<JsonArray>("perHostConfig", () => new JsonArray());
-
- if (string.IsNullOrEmpty(DefaultHostConfig) && HostConfigs.Length > 0)
- DefaultHostConfig = HostConfigs[0].ItemSpec;
-
- if (!string.IsNullOrEmpty(DefaultHostConfig))
- wasmHostProperties["defaultConfig"] = DefaultHostConfig;
-
- wasmHostProperties["mainAssembly"] = MainAssemblyName;
-
- foreach (JsonValue? rarg in RuntimeArgsForHost.Select(ri => JsonValue.Create(ri.ItemSpec)))
- {
- if (rarg is not null)
- runtimeArgsArray.Add(rarg);
- }
-
- foreach (ITaskItem hostConfigItem in HostConfigs)
- {
- var hostConfigObject = new JsonObject();
-
- string name = hostConfigItem.ItemSpec;
- string host = hostConfigItem.GetMetadata("host");
- if (string.IsNullOrEmpty(host))
- throw new LogAsErrorException($"BUG: Could not find required metadata 'host' for host config named '{name}'");
-
- hostConfigObject.Add("name", name);
- foreach (KeyValuePair<string, string> kvp in hostConfigItem.CloneCustomMetadata().Cast<KeyValuePair<string, string>>())
- hostConfigObject.Add(kvp.Key, kvp.Value);
-
- perHostConfigs.Add(hostConfigObject);
- }
-
- string dstPath = Path.Combine(AppDir!, Path.GetFileName(runtimeConfigPath));
- using FileStream? fs = File.OpenWrite(dstPath);
- using var writer = new Utf8JsonWriter(fs, new JsonWriterOptions { Indented = true });
- rootObject.WriteTo(writer);
- _fileWrites.Add(dstPath);
-
- Log.LogMessage(MessageImportance.Low, $"Generated {dstPath} from {runtimeConfigPath}");
- }
-
private bool TryParseExtraConfigValue(ITaskItem extraItem, out object? valueObject)
{
valueObject = null;
return false;
}
}
-
- private bool FileCopyChecked(string src, string dst, string label)
- {
- if (!File.Exists(src))
- {
- Log.LogError($"{label} file '{src}' not found");
- return false;
- }
-
- Log.LogMessage(MessageImportance.Low, $"Copying file from '{src}' to '{dst}'");
- try
- {
- File.Copy(src, dst, true);
- _fileWrites.Add(dst);
-
- return true;
- }
- catch (Exception ex) when (ex is IOException or UnauthorizedAccessException)
- {
- throw new LogAsErrorException($"{label} Failed to copy {src} to {dst} because {ex.Message}");
- }
- }
}
--- /dev/null
+// 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.Diagnostics.CodeAnalysis;
+using System.IO;
+using System.Linq;
+using System.Text.Json;
+using System.Text.Json.Nodes;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+
+namespace Microsoft.WebAssembly.Build.Tasks;
+
+public abstract class WasmAppBuilderBaseTask : Task
+{
+ [NotNull]
+ [Required]
+ public string? AppDir { get; set; }
+
+ [Required]
+ public string[] Assemblies { get; set; } = Array.Empty<string>();
+
+ [NotNull]
+ [Required]
+ public ITaskItem[] NativeAssets { get; set; } = Array.Empty<ITaskItem>();
+
+ protected readonly List<string> _fileWrites = new();
+
+ [Output]
+ public string[]? FileWrites => _fileWrites.ToArray();
+
+ // full list of ICU data files we produce can be found here:
+ // https://github.com/dotnet/icu/tree/maint/maint-67/icu-filters
+ public string? IcuDataFileName { get; set; }
+
+ public int DebugLevel { get; set; }
+ public ITaskItem[] SatelliteAssemblies { get; set; } = Array.Empty<ITaskItem>();
+ public bool InvariantGlobalization { get; set; }
+ public ITaskItem[] ExtraFilesToDeploy { get; set; } = Array.Empty<ITaskItem>();
+
+ public string? DefaultHostConfig { get; set; }
+
+ [Required, NotNull]
+ public string? MainAssemblyName { get; set; }
+
+ [Required]
+ public ITaskItem[] HostConfigs { get; set; } = Array.Empty<ITaskItem>();
+
+ public ITaskItem[] RuntimeArgsForHost { get; set; } = Array.Empty<ITaskItem>();
+
+ public override bool Execute()
+ {
+ try
+ {
+ return ExecuteInternal();
+ }
+ catch (LogAsErrorException laee)
+ {
+ Log.LogError(laee.Message);
+ return false;
+ }
+ }
+
+ protected abstract bool ExecuteInternal();
+
+ protected virtual void UpdateRuntimeConfigJson()
+ {
+ string[] matchingAssemblies = Assemblies.Where(asm => Path.GetFileName(asm) == MainAssemblyName).ToArray();
+ if (matchingAssemblies.Length == 0)
+ throw new LogAsErrorException($"Could not find main assembly named {MainAssemblyName} in the list of assemblies");
+
+ if (matchingAssemblies.Length > 1)
+ throw new LogAsErrorException($"Found more than one assembly matching the main assembly name {MainAssemblyName}: {string.Join(",", matchingAssemblies)}");
+
+ string runtimeConfigPath = Path.ChangeExtension(matchingAssemblies[0], ".runtimeconfig.json");
+ if (!File.Exists(runtimeConfigPath))
+ {
+ Log.LogMessage(MessageImportance.Low, $"Could not find {runtimeConfigPath}. Ignoring.");
+ return;
+ }
+
+ var rootNode = JsonNode.Parse(File.ReadAllText(runtimeConfigPath),
+ new JsonNodeOptions { PropertyNameCaseInsensitive = true });
+ if (rootNode == null)
+ throw new LogAsErrorException($"Failed to parse {runtimeConfigPath}");
+
+ JsonObject? rootObject = rootNode.AsObject();
+ if (!rootObject.TryGetPropertyValue("runtimeOptions", out JsonNode? runtimeOptionsNode)
+ || !(runtimeOptionsNode is JsonObject runtimeOptionsObject))
+ {
+ throw new LogAsErrorException($"Could not find node named 'runtimeOptions' in {runtimeConfigPath}");
+ }
+
+ JsonObject wasmHostProperties = runtimeOptionsObject.GetOrCreate<JsonObject>("wasmHostProperties", () => new JsonObject());
+ JsonArray runtimeArgsArray = wasmHostProperties.GetOrCreate<JsonArray>("runtimeArgs", () => new JsonArray());
+ JsonArray perHostConfigs = wasmHostProperties.GetOrCreate<JsonArray>("perHostConfig", () => new JsonArray());
+
+ if (string.IsNullOrEmpty(DefaultHostConfig) && HostConfigs.Length > 0)
+ DefaultHostConfig = HostConfigs[0].ItemSpec;
+
+ if (!string.IsNullOrEmpty(DefaultHostConfig))
+ wasmHostProperties["defaultConfig"] = DefaultHostConfig;
+
+ wasmHostProperties["mainAssembly"] = MainAssemblyName;
+
+ foreach (JsonValue? rarg in RuntimeArgsForHost.Select(ri => JsonValue.Create(ri.ItemSpec)))
+ {
+ if (rarg is not null)
+ runtimeArgsArray.Add(rarg);
+ }
+
+ foreach (ITaskItem hostConfigItem in HostConfigs)
+ {
+ var hostConfigObject = new JsonObject();
+
+ string name = hostConfigItem.ItemSpec;
+ string host = hostConfigItem.GetMetadata("host");
+ if (string.IsNullOrEmpty(host))
+ throw new LogAsErrorException($"BUG: Could not find required metadata 'host' for host config named '{name}'");
+
+ hostConfigObject.Add("name", name);
+ foreach (KeyValuePair<string, string> kvp in hostConfigItem.CloneCustomMetadata().Cast<KeyValuePair<string, string>>())
+ hostConfigObject.Add(kvp.Key, kvp.Value);
+
+ perHostConfigs.Add(hostConfigObject);
+ }
+
+ AddToRuntimeConfig(wasmHostProperties: wasmHostProperties, runtimeArgsArray: runtimeArgsArray, perHostConfigs: perHostConfigs);
+
+ string dstPath = Path.Combine(AppDir!, Path.GetFileName(runtimeConfigPath));
+ using FileStream? fs = File.OpenWrite(dstPath);
+ using var writer = new Utf8JsonWriter(fs, new JsonWriterOptions { Indented = true });
+ rootObject.WriteTo(writer);
+ _fileWrites.Add(dstPath);
+
+ Log.LogMessage(MessageImportance.Low, $"Generated {dstPath} from {runtimeConfigPath}");
+ }
+
+ protected virtual void AddToRuntimeConfig(JsonObject wasmHostProperties, JsonArray runtimeArgsArray, JsonArray perHostConfigs)
+ {
+ }
+
+ protected bool FileCopyChecked(string src, string dst, string label)
+ {
+ if (!File.Exists(src))
+ {
+ Log.LogError($"{label} file '{src}' not found");
+ return false;
+ }
+
+ Log.LogMessage(MessageImportance.Low, $"Copying file from '{src}' to '{dst}'");
+ try
+ {
+ File.Copy(src, dst, true);
+ _fileWrites.Add(dst);
+
+ return true;
+ }
+ catch (Exception ex) when (ex is IOException or UnauthorizedAccessException)
+ {
+ throw new LogAsErrorException($"{label} Failed to copy {src} to {dst} because {ex.Message}");
+ }
+ }
+}
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
+namespace Microsoft.WebAssembly.Build.Tasks;
+
public class WasmLoadAssembliesAndReferences : Task
{
[Required]