From 8e45d5f364ab17842073940dfe11d08e3e154b6b Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Thu, 3 Nov 2022 11:41:51 -0400 Subject: [PATCH] Mono workloads: Fix building non-mono project when `RunAOTCompilation=true` (#77762) This manifested in a case where a non-mono project was passed a mono specific property `RunAOTCompilation=true`, but instead of it getting ignored, the build failed with: ``` /usr/local/share/dotnet/sdk/7.0.200-preview.22521.7/Sdks/Microsoft.NET.Sdk/targets/Microsoft.NET.Sdk.ImportWorkloads.targets(38,5): error NETSDK1147: To build this project, the following workloads must be installed: macos [/private/tmp/c/c.csproj] error NETSDK1147: To install these workloads, run the following command: dotnet workload restore [/private/tmp/c/c.csproj] ``` And this is because the `MonoAOTCompiler.Task` is imported with: https://github.com/dotnet/runtime/blob/82aa87f9cdf9ac20f0f685016648c36247a76ff1/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets.in#L57-L57 - This happens for net6, and net8 projects. Fixes https://github.com/dotnet/runtime/issues/77707 . cc @steveisok @lewing --- .../WorkloadManifest.targets.in | 2 +- .../WorkloadManifest.targets.in | 2 +- .../wasm/Wasm.Build.Tests/HelperExtensions.cs | 3 + .../NonWasmTemplateBuildTests.cs | 132 ++++++++++++++++++ 4 files changed, 137 insertions(+), 2 deletions(-) create mode 100644 src/mono/wasm/Wasm.Build.Tests/NonWasmTemplateBuildTests.cs diff --git a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets.in b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets.in index cd5534f8a111b8..985e0c31cb99a6 100644 --- a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets.in +++ b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets.in @@ -54,7 +54,7 @@ - + diff --git a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.net6.Manifest/WorkloadManifest.targets.in b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.net6.Manifest/WorkloadManifest.targets.in index 375f86564c9151..12f408307622ab 100644 --- a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.net6.Manifest/WorkloadManifest.targets.in +++ b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.net6.Manifest/WorkloadManifest.targets.in @@ -1,6 +1,6 @@ - + diff --git a/src/mono/wasm/Wasm.Build.Tests/HelperExtensions.cs b/src/mono/wasm/Wasm.Build.Tests/HelperExtensions.cs index 7f36db407e2dd0..7690ce61b88052 100644 --- a/src/mono/wasm/Wasm.Build.Tests/HelperExtensions.cs +++ b/src/mono/wasm/Wasm.Build.Tests/HelperExtensions.cs @@ -58,6 +58,9 @@ public static class HelperExtensions => data.SelectMany(row => rowsWithColumnArrays.Select(new_cols => row.Concat(new_cols))); + public static IEnumerable> MultiplyWithSingleArgs(this IEnumerable> data, params object?[] arrayOfArgs) + => data.SelectMany(row => arrayOfArgs.Select(argCol => row.Concat(new[] { argCol }))); + public static object?[] Enumerate(this RunHost host) { if (host == RunHost.None) diff --git a/src/mono/wasm/Wasm.Build.Tests/NonWasmTemplateBuildTests.cs b/src/mono/wasm/Wasm.Build.Tests/NonWasmTemplateBuildTests.cs new file mode 100644 index 00000000000000..22f0fa44c7d1b5 --- /dev/null +++ b/src/mono/wasm/Wasm.Build.Tests/NonWasmTemplateBuildTests.cs @@ -0,0 +1,132 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Xunit; +using Xunit.Abstractions; + +#nullable enable + +namespace Wasm.Build.Tests; + +public class NonWasmTemplateBuildTests : BuildTestBase +{ + public NonWasmTemplateBuildTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext) + : base(output, buildContext) + { + } + + // For building non-wasm project with the sdk installed, we need to + // patch the framework references. But we want to maintain the versions. + // So, copy the reference for latest TFM, and add that back with the + // TFM=DefaultTargetFramework + // + // This is useful for the case when we are on tfm=net7.0, but sdk, and packages + // are really 8.0 . + private const string s_latestTargetFramework = "net8.0"; + private const string s_previousTargetFramework = "net7.0"; + private static string s_directoryBuildTargetsForPreviousTFM = + $$""" + + + + + <_KnownFrameworkReferenceToCopyFrom + Include="@(KnownFrameworkReference)" + Condition="'%(Identity)' == 'Microsoft.NETCore.App' and '%(TargetFramework)' == '{{s_latestTargetFramework}}'" /> + + <_KnownFrameworkReferenceToCopyFrom Update="@(_KnownFrameworkReferenceToCopyFrom)" TargetFramework="{{DefaultTargetFramework}}" /> + + + + + + + + + """; + + private static string s_directoryBuildTargetsForCurrentTFM = ""; + + public static IEnumerable GetTestData() => + new IEnumerable[] + { + new object?[] { "Debug" }, + new object?[] { "Release" } + } + .AsEnumerable() + .MultiplyWithSingleArgs + ( + "", + "/p:RunAOTCompilation=true", + "/p:WasmBuildNative=true" + ) + .MultiplyWithSingleArgs + ( + "net6.0", + s_previousTargetFramework, + s_latestTargetFramework + ) + .UnwrapItemsAsArrays().ToList(); + + [Theory, TestCategory("no-workload")] + [MemberData(nameof(GetTestData))] + public void NonWasmConsoleBuild_WithoutWorkload(string config, string extraBuildArgs, string targetFramework) + => NonWasmConsoleBuild(config, + extraBuildArgs, + targetFramework, + // net6 is sdk would be needed to run the app + shouldRun: targetFramework != "net6.0"); + + + [Theory] + [MemberData(nameof(GetTestData))] + public void NonWasmConsoleBuild_WithWorkload(string config, string extraBuildArgs, string targetFramework) + => NonWasmConsoleBuild(config, + extraBuildArgs, + targetFramework, + // net6 is sdk would be needed to run the app + shouldRun: targetFramework != "net6.0"); + + private void NonWasmConsoleBuild(string config, + string extraBuildArgs, + string targetFramework, + string? directoryBuildTargets = null, + bool shouldRun = true) + { + string id = $"nonwasm_{targetFramework}_{config}_{Path.GetRandomFileName()}"; + InitPaths(id); + InitProjectDir(_projectDir); + + directoryBuildTargets ??= targetFramework == s_previousTargetFramework + ? s_directoryBuildTargetsForPreviousTFM + : s_directoryBuildTargetsForCurrentTFM; + + File.WriteAllText(Path.Combine(_projectDir, "Directory.Build.props"), ""); + File.WriteAllText(Path.Combine(_projectDir, "Directory.Build.targets"), directoryBuildTargets); + + new DotNetCommand(s_buildEnv, _testOutput, useDefaultArgs: false) + .WithWorkingDirectory(_projectDir!) + .ExecuteWithCapturedOutput("new console --no-restore") + .EnsureSuccessful(); + + new DotNetCommand(s_buildEnv, _testOutput, useDefaultArgs: false) + .WithWorkingDirectory(_projectDir!) + .ExecuteWithCapturedOutput($"build -restore -c {config} -bl:{Path.Combine(s_buildEnv.LogRootPath, $"{id}.binlog")} {extraBuildArgs} -f {targetFramework}") + .EnsureSuccessful(); + + if (shouldRun) + { + var result = new DotNetCommand(s_buildEnv, _testOutput, useDefaultArgs: false) + .WithWorkingDirectory(_projectDir!) + .ExecuteWithCapturedOutput($"run -c {config} -f {targetFramework} --no-build") + .EnsureSuccessful(); + + Assert.Contains("Hello, World!", result.Output); + } + } +}