[wasm] Wasm.Build.Tests - some cleanup to prepare for Wasi.Build.Tests, and wasi...
authorAnkit Jain <radical@gmail.com>
Mon, 13 Feb 2023 22:42:35 +0000 (17:42 -0500)
committerGitHub <noreply@github.com>
Mon, 13 Feb 2023 22:42:35 +0000 (17:42 -0500)
* [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

23 files changed:
src/libraries/sendtohelix-wasm.targets
src/libraries/sendtohelix.proj
src/mono/wasm/Wasm.Build.Tests/Common/BuildAndRunAttribute.cs [moved from src/mono/wasm/Wasm.Build.Tests/BuildAndRunAttribute.cs with 100% similarity]
src/mono/wasm/Wasm.Build.Tests/Common/BuildEnvironment.cs [moved from src/mono/wasm/Wasm.Build.Tests/BuildEnvironment.cs with 100% similarity]
src/mono/wasm/Wasm.Build.Tests/Common/CommandResult.cs [moved from src/mono/wasm/Wasm.Build.Tests/CommandResult.cs with 100% similarity]
src/mono/wasm/Wasm.Build.Tests/Common/DotNetCommand.cs [moved from src/mono/wasm/Wasm.Build.Tests/DotNetCommand.cs with 100% similarity]
src/mono/wasm/Wasm.Build.Tests/Common/EnvironmentVariables.cs [moved from src/mono/wasm/Wasm.Build.Tests/EnvironmentVariables.cs with 84% similarity]
src/mono/wasm/Wasm.Build.Tests/Common/HelperExtensions.cs [moved from src/mono/wasm/Wasm.Build.Tests/HelperExtensions.cs with 100% similarity]
src/mono/wasm/Wasm.Build.Tests/Common/ProcessExtensions.cs [moved from src/mono/wasm/Wasm.Build.Tests/ProcessExtensions.cs with 100% similarity]
src/mono/wasm/Wasm.Build.Tests/Common/RunCommand.cs [moved from src/mono/wasm/Wasm.Build.Tests/RunCommand.cs with 100% similarity]
src/mono/wasm/Wasm.Build.Tests/Common/RunHost.cs [moved from src/mono/wasm/Wasm.Build.Tests/RunHost.cs with 100% similarity]
src/mono/wasm/Wasm.Build.Tests/Common/SharedBuildPerTestClassFixture.cs [moved from src/mono/wasm/Wasm.Build.Tests/SharedBuildPerTestClassFixture.cs with 100% similarity]
src/mono/wasm/Wasm.Build.Tests/Common/ToolCommand.cs [moved from src/mono/wasm/Wasm.Build.Tests/ToolCommand.cs with 100% similarity]
src/mono/wasm/Wasm.Build.Tests/Common/Utils.cs [moved from src/mono/wasm/Wasm.Build.Tests/Utils.cs with 100% similarity]
src/mono/wasm/Wasm.Build.Tests/Wasm.Build.Tests.csproj
src/mono/wasm/Wasm.Build.Tests/data/RunScriptTemplate.cmd
src/mono/wasm/Wasm.Build.Tests/data/RunScriptTemplate.sh
src/mono/wasm/build/WasmApp.Native.targets
src/mono/wasm/build/WasmApp.targets
src/tasks/WasmAppBuilder/ManagedToNativeGenerator.cs
src/tasks/WasmAppBuilder/WasmAppBuilder.cs
src/tasks/WasmAppBuilder/WasmAppBuilderBaseTask.cs [new file with mode: 0644]
src/tasks/WasmAppBuilder/WasmLoadAssembliesAndReferences.cs

index 0e9db2b..3065462 100644 (file)
@@ -55,6 +55,9 @@
     <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 &quot;BUILT_NUGETS_PATH=%HELIX_CORRELATION_PAYLOAD%/built-nugets&quot;" />
     <HelixPreCommand Condition="'$(WindowsShell)' != 'true'" Include="export &quot;BUILT_NUGETS_PATH=$HELIX_CORRELATION_PAYLOAD/built-nugets&quot;" />
+
+    <HelixPreCommand Condition="'$(WindowsShell)' == 'true'" Include="set &quot;SDK_DIR_NAME=$(SdkForWorkloadTestingDirName)&quot;" />
+    <HelixPreCommand Condition="'$(WindowsShell)' != 'true'" Include="export &quot;SDK_DIR_NAME=$(SdkForWorkloadTestingDirName)&quot;" />
   </ItemGroup>
 
   <PropertyGroup>
index a5d1ae7..980c63e 100644 (file)
@@ -90,7 +90,7 @@
       <_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>
@@ -18,6 +18,7 @@ namespace Wasm.Build.Tests
         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");
     }
 }
index 0ef3f19..1f2f1d2 100644 (file)
     <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)" />
@@ -70,6 +75,9 @@
       <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)" />
 
index 23e3dbf..8ce4e7a 100644 (file)
@@ -6,9 +6,6 @@ setlocal enabledelayedexpansion
 [[SetCommandsEcho]]
 
 set EXECUTION_DIR=%~dp0
-if [%3] NEQ [] (
-    set SCENARIO=%3
-)
 
 if [%HELIX_WORKITEM_UPLOAD_ROOT%] == [] (
     set "XHARNESS_OUT=%EXECUTION_DIR%xharness-output"
@@ -16,48 +13,6 @@ if [%HELIX_WORKITEM_UPLOAD_ROOT%] == [] (
     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%"
 )
@@ -66,12 +21,6 @@ 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%
 
 set TEST_LOG_PATH=%XHARNESS_OUT%\logs
 
@@ -98,10 +47,8 @@ exit /b %EXIT_CODE%
 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] (
@@ -111,10 +58,10 @@ 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%"
index 70187b9..6920f6d 100644 (file)
@@ -1,13 +1,12 @@
 #!/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"
@@ -15,67 +14,19 @@ else
        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
 
@@ -87,10 +38,10 @@ function set_env_vars()
 
     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
index 81cde58..112e861 100644 (file)
@@ -1,7 +1,7 @@
 <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>
index 41318e5..6b68828 100644 (file)
@@ -1,7 +1,7 @@
 <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:
index 9b04d02..081803b 100644 (file)
@@ -11,6 +11,8 @@ using System.Text;
 using Microsoft.Build.Framework;
 using Microsoft.Build.Utilities;
 
+namespace Microsoft.WebAssembly.Build.Tasks;
+
 public class ManagedToNativeGenerator : Task
 {
     [Required]
index f0c6749..3b39af3 100644 (file)
@@ -13,38 +13,16 @@ using System.Text.Json.Serialization;
 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; }
@@ -64,16 +42,6 @@ public class WasmAppBuilder : Task
     // </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")]
@@ -150,20 +118,7 @@ public class WasmAppBuilder : Task
         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.");
@@ -425,77 +380,6 @@ public class WasmAppBuilder : Task
         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;
@@ -540,26 +424,4 @@ public class WasmAppBuilder : Task
             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}");
-        }
-    }
 }
diff --git a/src/tasks/WasmAppBuilder/WasmAppBuilderBaseTask.cs b/src/tasks/WasmAppBuilder/WasmAppBuilderBaseTask.cs
new file mode 100644 (file)
index 0000000..942a446
--- /dev/null
@@ -0,0 +1,166 @@
+// 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}");
+        }
+    }
+}
index e124835..b1082fc 100644 (file)
@@ -10,6 +10,8 @@ using System.Reflection;
 using Microsoft.Build.Framework;
 using Microsoft.Build.Utilities;
 
+namespace Microsoft.WebAssembly.Build.Tasks;
+
 public class WasmLoadAssembliesAndReferences : Task
 {
     [Required]