[wasm] Update templates to work for multiple versions (#76124)
authorAnkit Jain <radical@gmail.com>
Thu, 29 Sep 2022 09:44:43 +0000 (05:44 -0400)
committerGitHub <noreply@github.com>
Thu, 29 Sep 2022 09:44:43 +0000 (05:44 -0400)
15 files changed:
eng/Versions.props
src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.net7.Manifest/Microsoft.NET.Workload.Mono.Toolchain.net7.Manifest.pkgproj
src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.net7.Manifest/WorkloadManifest.json.in
src/mono/wasm/Makefile
src/mono/wasm/Wasm.Build.Tests/BrowserRunner.cs
src/mono/wasm/Wasm.Build.Tests/BuildEnvironment.cs
src/mono/wasm/Wasm.Build.Tests/BuildTestBase.cs
src/mono/wasm/Wasm.Build.Tests/WasmTemplateTests.cs
src/mono/wasm/debugger/BrowserDebugHost/DebugProxyHost.cs
src/mono/wasm/host/BrowserHost.cs
src/mono/wasm/host/WebServer.cs
src/mono/wasm/host/WebServerStartup.cs
src/mono/wasm/templates/templates/browser/.template.config/template.json
src/mono/wasm/templates/templates/console/.template.config/template.json
src/tasks/WorkloadBuildTasks/InstallWorkloadFromArtifacts.cs

index cbccaa3..c156052 100644 (file)
@@ -9,6 +9,7 @@
     <SdkBandVersion>8.0.100</SdkBandVersion>
     <PackageVersionNet6>6.0.10</PackageVersionNet6>
     <!-- problematic until NetCoreAppCurrent is net8.0 <PackageVersionNet7>7.0.0</PackageVersionNet7> -->
+    <PackageVersionForTemplates7>7.0.0-rtm.22476.8</PackageVersionForTemplates7>
     <PreReleaseVersionLabel>alpha</PreReleaseVersionLabel>
     <PreReleaseVersionIteration>1</PreReleaseVersionIteration>
     <!-- Set assembly version to align with major and minor version,
index 18360d0..2d1b508 100644 (file)
       <_WorkloadManifestValues Include="WorkloadVersion" Value="$(PackageVersion)" />
       <_WorkloadManifestValues Include="PackageVersionNet7" Value="$(PackageVersionNet7)" Condition="'$(PackageVersionNet7)' != ''" />
       <_WorkloadManifestValues Include="PackageVersionNet7" Value="$(PackageVersion)" Condition="'$(PackageVersionNet7)' == ''" />
+
+      <!-- We need to use a different version for net7 templates, to differentiate from net8 ones -->
+      <_WorkloadManifestValues Include="PackageVersionForTemplates7" Value="$(PackageVersionForTemplates7)" Condition="'$(PackageVersionNet7)' == ''" />
+      <_WorkloadManifestValues Include="PackageVersionForTemplates7" Value="$(PackageVersionNet7)" Condition="'$(PackageVersionNet7)' != ''" />
+
       <_WorkloadManifestValues Include="EmscriptenVersion" Value="$(MicrosoftNETRuntimeEmscriptenVersion)" />
       <_WorkloadManifestValues Include="NetCoreAppCurrent" Value="$(NetCoreAppCurrent)" />
     </ItemGroup>
index de062e6..6ce2373 100644 (file)
     },
     "Microsoft.NET.Runtime.WebAssembly.Templates.net7": {
       "kind": "template",
-      "version": "${PackageVersionNet7}",
+      "version": "${PackageVersionForTemplates7}",
       "alias-to": {
         "any": "Microsoft.NET.Runtime.WebAssembly.Templates"
       }
index 494c7fe..9188f97 100644 (file)
@@ -140,6 +140,8 @@ build-dbg-proxy:
        $(DOTNET) build $(TOP)/src/mono/wasm/debugger/BrowserDebugHost $(MSBUILD_ARGS)
 build-dbg-testsuite:
        $(DOTNET) build $(TOP)/src/mono/wasm/debugger/DebuggerTestSuite $(MSBUILD_ARGS)
+build-app-host:
+       $(DOTNET) build $(TOP)/src/mono/wasm/host $(_MSBUILD_WASM_BUILD_ARGS) $(MSBUILD_ARGS)
 
 patch-deterministic:
        cd emsdk/upstream/emscripten/ && patch -p1 < ../../../runtime/deterministic.diff
index 13a4ff9..718e7f9 100644 (file)
@@ -10,6 +10,7 @@ using System.Text.RegularExpressions;
 using System.Threading.Tasks;
 using Microsoft.Playwright;
 using Wasm.Tests.Internal;
+using Xunit.Abstractions;
 
 namespace Wasm.Build.Tests;
 
@@ -29,6 +30,9 @@ internal class BrowserRunner : IAsyncDisposable
     public Task<CommandResult>? RunTask { get; private set; }
     public IList<string> OutputLines { get; private set; } = new List<string>();
     private TaskCompletionSource<int> _exited = new();
+    private readonly ITestOutputHelper _testOutput;
+
+    public BrowserRunner(ITestOutputHelper testOutput) => _testOutput = testOutput;
 
     // FIXME: options
     public async Task<IPage> RunAsync(ToolCommand cmd, string args, bool headless = true)
@@ -78,7 +82,7 @@ internal class BrowserRunner : IAsyncDisposable
         var url = new Uri(urlAvailable.Task.Result);
         Playwright = await Microsoft.Playwright.Playwright.CreateAsync();
         string[] chromeArgs = new[] { $"--explicitly-allowed-ports={url.Port}" };
-        Console.WriteLine($"Launching chrome ('{s_chromePath.Value}') via playwright with args = {string.Join(',', chromeArgs)}");
+        _testOutput.WriteLine($"Launching chrome ('{s_chromePath.Value}') via playwright with args = {string.Join(',', chromeArgs)}");
         Browser = await Playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions{
             ExecutablePath = s_chromePath.Value,
             Headless = headless,
@@ -99,7 +103,7 @@ internal class BrowserRunner : IAsyncDisposable
         await Task.WhenAny(RunTask!, _exited.Task, Task.Delay(timeout));
         if (_exited.Task.IsCompleted)
         {
-            Console.WriteLine ($"Exited with {await _exited.Task}");
+            _testOutput.WriteLine ($"Exited with {await _exited.Task}");
             return;
         }
 
@@ -114,7 +118,7 @@ internal class BrowserRunner : IAsyncDisposable
         await Task.WhenAny(RunTask!, _exited.Task, Task.Delay(timeout));
         if (RunTask.IsCanceled)
         {
-            Console.WriteLine ($"Exited with {(await RunTask).ExitCode}");
+            _testOutput.WriteLine ($"Exited with {(await RunTask).ExitCode}");
             return;
         }
 
index d8ec1e5..f921b87 100644 (file)
@@ -27,6 +27,7 @@ namespace Wasm.Build.Tests
         public static readonly string           RelativeTestAssetsPath = @"..\testassets\";
         public static readonly string           TestAssetsPath = Path.Combine(AppContext.BaseDirectory, "testassets");
         public static readonly string           TestDataPath = Path.Combine(AppContext.BaseDirectory, "data");
+        public static readonly string           TmpPath = Path.Combine(Path.GetTempPath(), "wasmbuildtests");
 
         private static readonly Dictionary<string, string> s_runtimePackVersions = new();
 
@@ -141,6 +142,10 @@ namespace Wasm.Build.Tests
             {
                 LogRootPath = Environment.CurrentDirectory;
             }
+
+            if (Directory.Exists(TmpPath))
+                Directory.Delete(TmpPath, recursive: true);
+            Directory.CreateDirectory(TmpPath);
         }
 
         // FIXME: error checks
index e34917b..f4a73bd 100644 (file)
@@ -290,13 +290,25 @@ namespace Wasm.Build.Tests
             Directory.CreateDirectory(_logPath);
         }
 
-        protected static void InitProjectDir(string dir)
+        protected static void InitProjectDir(string dir, bool addNuGetSourceForLocalPackages = false)
         {
             Directory.CreateDirectory(dir);
             File.WriteAllText(Path.Combine(dir, "Directory.Build.props"), s_buildEnv.DirectoryBuildPropsContents);
             File.WriteAllText(Path.Combine(dir, "Directory.Build.targets"), s_buildEnv.DirectoryBuildTargetsContents);
 
-            File.Copy(Path.Combine(BuildEnvironment.TestDataPath, NuGetConfigFileNameForDefaultFramework), Path.Combine(dir, "nuget.config"));
+            string targetNuGetConfigPath = Path.Combine(dir, "nuget.config");
+            if (addNuGetSourceForLocalPackages)
+            {
+                File.WriteAllText(targetNuGetConfigPath,
+                                    GetNuGetConfigWithLocalPackagesPath(
+                                                Path.Combine(BuildEnvironment.TestDataPath, NuGetConfigFileNameForDefaultFramework),
+                                                s_buildEnv.BuiltNuGetsPath));
+            }
+            else
+            {
+                File.Copy(Path.Combine(BuildEnvironment.TestDataPath, NuGetConfigFileNameForDefaultFramework),
+                            targetNuGetConfigPath);
+            }
             Directory.CreateDirectory(Path.Combine(dir, ".nuget"));
         }
 
@@ -444,10 +456,10 @@ namespace Wasm.Build.Tests
             return contents.Replace(s_nugetInsertionTag, $@"<add key=""nuget-local"" value=""{localNuGetsPath}"" />");
         }
 
-        public string CreateWasmTemplateProject(string id, string template = "wasmbrowser")
+        public string CreateWasmTemplateProject(string id, string template = "wasmbrowser", string extraArgs = "")
         {
             InitPaths(id);
-            InitProjectDir(id);
+            InitProjectDir(id, addNuGetSourceForLocalPackages: true);
 
             File.WriteAllText(Path.Combine(_projectDir, "Directory.Build.props"), "<Project />");
             File.WriteAllText(Path.Combine(_projectDir, "Directory.Build.targets"),
@@ -464,7 +476,7 @@ namespace Wasm.Build.Tests
 
             new DotNetCommand(s_buildEnv, _testOutput, useDefaultArgs: false)
                     .WithWorkingDirectory(_projectDir!)
-                    .ExecuteWithCapturedOutput($"new {template}")
+                    .ExecuteWithCapturedOutput($"new {template} {extraArgs}")
                     .EnsureSuccessful();
 
             return Path.Combine(_projectDir!, $"{id}.csproj");
index 131ca64..cd1500b 100644 (file)
@@ -184,10 +184,19 @@ namespace Wasm.Build.Tests
         [InlineData("Debug", true)]
         [InlineData("Release", false)]
         [InlineData("Release", true)]
-        public void ConsoleBuildAndRun(string config, bool relinking)
+        public void ConsoleBuildAndRunDefault(string config, bool relinking)
+            => ConsoleBuildAndRun(config, relinking, string.Empty);
+
+        [ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
+        [InlineData("Debug", "-f net7.0")]
+        [InlineData("Debug", "-f net8.0")]
+        public void ConsoleBuildAndRunForSpecificTFM(string config, string extraNewArgs)
+            => ConsoleBuildAndRun(config, false, extraNewArgs);
+
+        private void ConsoleBuildAndRun(string config, bool relinking, string extraNewArgs)
         {
             string id = $"{config}_{Path.GetRandomFileName()}";
-            string projectFile = CreateWasmTemplateProject(id, "wasmconsole");
+            string projectFile = CreateWasmTemplateProject(id, "wasmconsole", extraNewArgs);
             string projectName = Path.GetFileNameWithoutExtension(projectFile);
 
             UpdateProgramCS();
@@ -233,9 +242,9 @@ namespace Wasm.Build.Tests
                 //data.Add(runOutsideProjectDirectory, forConsole, string.Empty);
 
                 data.Add(runOutsideProjectDirectory, forConsole,
-                                $"<OutputPath>{Path.Combine(Path.GetTempPath(), Path.GetRandomFileName())}</OutputPath>");
+                                $"<OutputPath>{Path.Combine(BuildEnvironment.TmpPath, Path.GetRandomFileName())}</OutputPath>");
                 data.Add(runOutsideProjectDirectory, forConsole,
-                                $"<WasmAppDir>{Path.Combine(Path.GetTempPath(), Path.GetRandomFileName())}</WasmAppDir>");
+                                $"<WasmAppDir>{Path.Combine(BuildEnvironment.TmpPath, Path.GetRandomFileName())}</WasmAppDir>");
             }
 
             return data;
@@ -258,13 +267,13 @@ namespace Wasm.Build.Tests
             if (!string.IsNullOrEmpty(extraProperties))
                 AddItemsPropertiesToProject(projectFile, extraProperties: extraProperties);
 
-            string workingDir = runOutsideProjectDirectory ? Path.GetTempPath() : _projectDir!;
+            string workingDir = runOutsideProjectDirectory ? BuildEnvironment.TmpPath : _projectDir!;
 
             {
                 using var runCommand = new RunCommand(s_buildEnv, _testOutput)
                                             .WithWorkingDirectory(workingDir);
 
-                await using var runner = new BrowserRunner();
+                await using var runner = new BrowserRunner(_testOutput);
                 var page = await runner.RunAsync(runCommand, $"run -c {config} --project {projectFile} --forward-console");
                 await runner.WaitForExitMessageAsync(TimeSpan.FromMinutes(2));
                 Assert.Contains("Hello, Browser!", string.Join(Environment.NewLine, runner.OutputLines));
@@ -274,7 +283,7 @@ namespace Wasm.Build.Tests
                 using var runCommand = new RunCommand(s_buildEnv, _testOutput)
                                             .WithWorkingDirectory(workingDir);
 
-                await using var runner = new BrowserRunner();
+                await using var runner = new BrowserRunner(_testOutput);
                 var page = await runner.RunAsync(runCommand, $"run -c {config} --no-build --project {projectFile} --forward-console");
                 await runner.WaitForExitMessageAsync(TimeSpan.FromMinutes(2));
                 Assert.Contains("Hello, Browser!", string.Join(Environment.NewLine, runner.OutputLines));
@@ -292,7 +301,7 @@ namespace Wasm.Build.Tests
             if (!string.IsNullOrEmpty(extraProperties))
                 AddItemsPropertiesToProject(projectFile, extraProperties: extraProperties);
 
-            string workingDir = runOutsideProjectDirectory ? Path.GetTempPath() : _projectDir!;
+            string workingDir = runOutsideProjectDirectory ? BuildEnvironment.TmpPath : _projectDir!;
 
             {
                 string runArgs = $"run -c {config} --project {projectFile}";
@@ -419,7 +428,7 @@ namespace Wasm.Build.Tests
             using var runCommand = new RunCommand(s_buildEnv, _testOutput)
                                         .WithWorkingDirectory(_projectDir!);
 
-            await using var runner = new BrowserRunner();
+            await using var runner = new BrowserRunner(_testOutput);
             var page = await runner.RunAsync(runCommand, $"run -c {config} --no-build");
 
             await page.Locator("text=Counter").ClickAsync();
@@ -431,12 +440,15 @@ namespace Wasm.Build.Tests
             Assert.Equal("Current count: 1", txt);
         }
 
-        [ConditionalFact(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
-        public async Task BrowserTest()
+        [ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
+        [InlineData("")]
+        [InlineData("-f net7.0")]
+        [InlineData("-f net8.0")]
+        public async Task BrowserBuildAndRun(string extraNewArgs)
         {
             string config = "Debug";
             string id = $"browser_{config}_{Path.GetRandomFileName()}";
-            CreateWasmTemplateProject(id, "wasmbrowser");
+            CreateWasmTemplateProject(id, "wasmbrowser", extraNewArgs);
 
             UpdateBrowserMainJs(DefaultTargetFramework);
 
@@ -448,7 +460,7 @@ namespace Wasm.Build.Tests
             using var runCommand = new RunCommand(s_buildEnv, _testOutput)
                                         .WithWorkingDirectory(_projectDir!);
 
-            await using var runner = new BrowserRunner();
+            await using var runner = new BrowserRunner(_testOutput);
             var page = await runner.RunAsync(runCommand, $"run -c {config} --no-build -r browser-wasm --forward-console");
             await runner.WaitForExitMessageAsync(TimeSpan.FromMinutes(2));
             Assert.Contains("Hello, Browser!", string.Join(Environment.NewLine, runner.OutputLines));
index be3095e..b97ffe7 100644 (file)
@@ -1,7 +1,6 @@
 // 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.Runtime.ExceptionServices;
index f546127..a592ca7 100644 (file)
@@ -71,9 +71,9 @@ internal sealed class BrowserHost
                                                debugging: _args.CommonConfig.Debugging);
         runArgsJson.Save(Path.Combine(_args.CommonConfig.AppPath, "runArgs.json"));
 
-        var urls = new string[] { $"http://localhost:{_args.CommonConfig.HostProperties.WebServerPort}", "https://localhost:0" };
-        if (envVars["ASPNETCORE_URLS"] is not null)
-            urls = envVars["ASPNETCORE_URLS"].Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
+        string[] urls = envVars.TryGetValue("ASPNETCORE_URLS", out string? aspnetUrls)
+                            ? aspnetUrls.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries)
+                            : new string[] { $"http://127.0.0.1:{_args.CommonConfig.HostProperties.WebServerPort}", "https://127.0.0.1:0" };
 
         (ServerURLs serverURLs, IWebHost host) = await StartWebServerAsync(_args.CommonConfig.AppPath,
                                                                            _args.ForwardConsoleOutput ?? false,
index aed1948..44d0243 100644 (file)
@@ -1,13 +1,9 @@
 // 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.Linq;
 using System.Threading;
 using System.Threading.Tasks;
 using Microsoft.AspNetCore.Hosting;
-using Microsoft.AspNetCore.Hosting.Server.Features;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Options;
@@ -20,7 +16,7 @@ public class WebServer
 {
     internal static async Task<(ServerURLs, IWebHost)> StartAsync(WebServerOptions options, ILogger logger, CancellationToken token)
     {
-        string[] urls = options.Urls;
+        TaskCompletionSource<ServerURLs> realUrlsAvailableTcs = new();
 
         IWebHostBuilder builder = new WebHostBuilder()
             .UseKestrel()
@@ -43,9 +39,10 @@ public class WebServer
                 }
                 services.AddSingleton(logger);
                 services.AddSingleton(Options.Create(options));
+                services.AddSingleton(realUrlsAvailableTcs);
                 services.AddRouting();
             })
-            .UseUrls(urls);
+            .UseUrls(options.Urls);
 
         if (options.ContentRootPath != null)
             builder.UseContentRoot(options.ContentRootPath);
@@ -53,27 +50,11 @@ public class WebServer
         IWebHost? host = builder.Build();
         await host.StartAsync(token);
 
-        ICollection<string>? addresses = host.ServerFeatures
-                            .Get<IServerAddressesFeature>()?
-                            .Addresses;
+        if (token.CanBeCanceled)
+            token.Register(async () => await host.StopAsync());
 
-        string? ipAddress =
-                        addresses?
-                        .Where(a => a.StartsWith("http:", StringComparison.InvariantCultureIgnoreCase))
-                        .Select(a => new Uri(a))
-                        .Select(uri => uri.ToString())
-                        .FirstOrDefault();
-
-        string? ipAddressSecure =
-                        addresses?
-                        .Where(a => a.StartsWith("https:", StringComparison.OrdinalIgnoreCase))
-                        .Select(a => new Uri(a))
-                        .Select(uri => uri.ToString())
-                        .FirstOrDefault();
-
-        return ipAddress == null || ipAddressSecure == null
-            ? throw new InvalidOperationException("Failed to determine web server's IP address or port")
-            : (new ServerURLs(ipAddress, ipAddressSecure), host);
+        ServerURLs serverUrls = await realUrlsAvailableTcs.Task;
+        return (serverUrls, host);
     }
 
 }
index e09cdd6..64bf9e7 100644 (file)
@@ -5,19 +5,20 @@ using System;
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.IO;
-using System.Net;
+using System.Linq;
 using System.Net.WebSockets;
-using System.Reflection;
 using System.Runtime.InteropServices;
 using System.Threading.Tasks;
 using System.Web;
 using Microsoft.AspNetCore.Builder;
 using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Hosting.Server.Features;
 using Microsoft.AspNetCore.Routing;
 using Microsoft.AspNetCore.StaticFiles;
 using Microsoft.Extensions.FileProviders;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Options;
-using Microsoft.WebAssembly.Diagnostics;
 
 #nullable enable
 
@@ -28,6 +29,7 @@ internal sealed class WebServerStartup
     private readonly IWebHostEnvironment _hostingEnvironment;
     private static readonly object LaunchLock = new object();
     private static string LaunchedDebugProxyUrl = "";
+    private ILogger? _logger;
     public WebServerStartup(IWebHostEnvironment hostingEnvironment) => _hostingEnvironment = hostingEnvironment;
 
     public static int StartDebugProxy(string devToolsHost)
@@ -52,8 +54,13 @@ internal sealed class WebServerStartup
         return generateRandomPort;
     }
 
-    public void Configure(IApplicationBuilder app, IOptions<WebServerOptions> optionsContainer)
+    public void Configure(IApplicationBuilder app,
+                          IOptions<WebServerOptions> optionsContainer,
+                          TaskCompletionSource<ServerURLs> realUrlsAvailableTcs,
+                          ILogger logger,
+                          IHostApplicationLifetime applicationLifetime)
     {
+        _logger = logger;
         var provider = new FileExtensionContentTypeProvider();
         provider.Mappings[".wasm"] = "application/wasm";
         provider.Mappings[".cjs"] = "text/javascript";
@@ -144,5 +151,43 @@ internal sealed class WebServerStartup
                 return Task.CompletedTask;
             });
         });
+
+
+        applicationLifetime.ApplicationStarted.Register(() =>
+        {
+            TaskCompletionSource<ServerURLs> tcs = realUrlsAvailableTcs;
+            try
+            {
+                ICollection<string>? addresses = app.ServerFeatures
+                                                    .Get<IServerAddressesFeature>()
+                                                    ?.Addresses;
+
+                string? ipAddress = null;
+                string? ipAddressSecure = null;
+                if (addresses is not null)
+                {
+                    ipAddress = GetHttpServerAddress(addresses, secure: false);
+                    ipAddressSecure = GetHttpServerAddress(addresses, secure: true);
+                }
+
+                if (ipAddress == null)
+                    tcs.SetException(new InvalidOperationException("Failed to determine web server's IP address or port"));
+                else
+                    tcs.SetResult(new ServerURLs(ipAddress, ipAddressSecure));
+            }
+            catch (Exception ex)
+            {
+                _logger?.LogError($"Failed to get urls for the webserver: {ex}");
+                tcs.TrySetException(ex);
+                throw;
+            }
+
+            static string? GetHttpServerAddress(ICollection<string> addresses, bool secure)
+                => addresses?
+                        .Where(a => a.StartsWith(secure ? "https:" : "http:", StringComparison.InvariantCultureIgnoreCase))
+                        .Select(a => new Uri(a))
+                        .Select(uri => uri.ToString())
+                        .FirstOrDefault();
+        });
     }
 }
index 4d34193..7fc4f15 100644 (file)
@@ -3,7 +3,10 @@
   "author": "Microsoft",
   "classifications": [ "Web", "WebAssembly", "Browser" ],
   "generatorVersions": "[1.0.0.0-*)",
-  "identity": "WebAssembly.Browser",
+  "groupIdentity": "WebAssembly.Browser",
+  "precedence": 8000,
+  "identity": "WebAssembly.Browser.8.0",
+  "description": "WebAssembly Browser App",
   "name": "WebAssembly Browser App",
   "description": "A project template for creating a .NET app that runs on WebAssembly in a browser",
   "shortName": "wasmbrowser",
@@ -21,7 +24,7 @@
         "low": 5000,
         "high": 5300
       },
-           "replaces": "5000"
+      "replaces": "5000"
     },
     "kestrelHttpsPortGenerated": {
       "type": "generated",
         "low": 7000,
         "high": 7300
       },
-           "replaces": "5001"
+      "replaces": "5001"
+    },
+    "framework": {
+      "type": "parameter",
+      "description": "The target framework for the project.",
+      "datatype": "choice",
+      "choices": [
+        {
+          "choice": "net8.0",
+          "description": "Target net8.0",
+          "displayName": ".NET 8.0"
+        }
+      ],
+      "defaultValue": "net8.0",
+      "displayName": "framework"
     }
   }
 }
index de91185..0ea5238 100644 (file)
@@ -2,7 +2,10 @@
   "$schema": "http://json.schemastore.org/template",
   "author": "Microsoft",
   "classifications": [ "Web", "WebAssembly", "Console" ],
-  "identity": "WebAssembly.Console",
+  "groupIdentity": "WebAssembly.Console",
+  "precedence": 8000,
+  "identity": "WebAssembly.Console.8.0",
+  "description": "WebAssembly Console App",
   "name": "WebAssembly Console App",
   "description": "A project template for creating a .NET app that runs on WebAssembly on Node JS or V8",
   "shortName": "wasmconsole",
   "tags": {
     "language": "C#",
     "type": "project"
+  },
+  "symbols": {
+    "framework": {
+      "type": "parameter",
+      "description": "The target framework for the project.",
+      "datatype": "choice",
+      "choices": [
+        {
+          "choice": "net8.0",
+          "description": "Target net8.0",
+          "displayName": ".NET 8.0"
+        }
+      ],
+      "defaultValue": "net8.0",
+      "displayName": "framework"
+    }
   }
 }
index d879358..94e255d 100644 (file)
@@ -97,9 +97,14 @@ namespace Microsoft.Workload.Build.Tasks
                 }
 
                 string cachePath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
+                string lastTargetPath = string.Empty;
                 foreach (InstallWorkloadRequest req in selectedRequests)
                 {
-                    Log.LogMessage(MessageImportance.High, $"** Installing workload {req.WorkloadId} in {req.TargetPath} **");
+                    if (req.TargetPath != lastTargetPath)
+                        Log.LogMessage(MessageImportance.High, $"{Environment.NewLine}** Preparing {req.TargetPath} **");
+                    lastTargetPath = req.TargetPath;
+
+                    Log.LogMessage(MessageImportance.High, $"    - {req.WorkloadId}: Installing workload");
                     if (!req.Validate(Log))
                         return false;
 
@@ -159,11 +164,11 @@ namespace Microsoft.Workload.Build.Tasks
 
                 if (manifestsInstalled.Contains(req.ManifestName))
                 {
-                    Log.LogMessage(MessageImportance.High, $"{Environment.NewLine}** Manifests for workload {req.WorkloadId} are already installed **{Environment.NewLine}");
+                    Log.LogMessage(MessageImportance.High, $"** {req.WorkloadId}: Manifests are already installed **");
                     continue;
                 }
 
-                Log.LogMessage(MessageImportance.High, $"{Environment.NewLine}** Installing manifests for workload {req.WorkloadId} **");
+                Log.LogMessage(MessageImportance.High, $"{Environment.NewLine}** {req.WorkloadId}: Installing manifests **");
                 if (!InstallWorkloadManifest(workload,
                                              req.ManifestName,
                                              req.Version,
@@ -230,13 +235,13 @@ namespace Microsoft.Workload.Build.Tasks
                                                     Path.Combine(req.TargetPath, "dotnet"),
                                                     $"workload install --skip-manifest-update --no-cache --configfile \"{nugetConfigPath}\" {req.WorkloadId}",
                                                     workingDir: Path.GetTempPath(),
-                                                    silent: false,
                                                     logStdErrAsMessage: req.IgnoreErrors,
-                                                    debugMessageImportance: MessageImportance.High);
+                                                    debugMessageImportance: MessageImportance.Normal);
             if (exitCode != 0)
             {
                 if (req.IgnoreErrors)
                 {
+                    Log.LogMessage(MessageImportance.High, output);
                     Log.LogMessage(MessageImportance.High,
                                     $"{Environment.NewLine} ** Ignoring workload installation failure exit code {exitCode}. **{Environment.NewLine}");
                 }
@@ -245,6 +250,7 @@ namespace Microsoft.Workload.Build.Tasks
                     Log.LogError($"workload install failed with exit code {exitCode}: {output}");
                 }
 
+                Log.LogMessage(MessageImportance.Low, $"List of the relevant paths in {req.TargetPath}");
                 foreach (string dir in Directory.EnumerateDirectories(Path.Combine(req.TargetPath, "sdk-manifests"), "*", SearchOption.AllDirectories))
                     Log.LogMessage(MessageImportance.Low, $"\t{Path.Combine(req.TargetPath, "sdk-manifests", dir)}");
 
@@ -257,7 +263,7 @@ namespace Microsoft.Workload.Build.Tasks
 
         private void UpdateAppRef(string sdkPath, string version)
         {
-            Log.LogMessage(MessageImportance.High, $"{Environment.NewLine}** Updating Targeting pack **{Environment.NewLine}");
+            Log.LogMessage(MessageImportance.Normal, $"    - Updating Targeting pack");
 
             string pkgPath = Path.Combine(LocalNuGetsPath, $"Microsoft.NETCore.App.Ref.{version}.nupkg");
             if (!File.Exists(pkgPath))
@@ -291,7 +297,7 @@ namespace Microsoft.Workload.Build.Tasks
 
         private bool InstallWorkloadManifest(ITaskItem workloadId, string name, string version, string sdkDir, string nugetConfigContents, bool stopOnMissing)
         {
-            Log.LogMessage(MessageImportance.High, $"    ** Installing manifest: {name}/{version}");
+            Log.LogMessage(MessageImportance.High, $"    - Installing manifest: {name}/{version}");
 
             // Find any existing directory with the manifest name, ignoring the case
             // Multiple directories for a manifest, differing only in case causes