Skip to content

Commit

Permalink
[wasm/wasi] Consolidate build targets (dotnet#95775)
Browse files Browse the repository at this point in the history
* [wasm/wasi] Consolidate build targets

Current state of build files:
```
    wasm: WasmApp.props, WasmApp.targets, WasmApp.Native.targets
    wasi: WasiApp.props, WasiApp.targets, WasiApp.Native.targets
``

The wasm, and wasi build have lot of shared code but that is not
representative in the actual files, since the wasi targets started life
as a quick-copy-comment-out-bits of the wasm targets.

This commit consolidates these into:
```
    common: WasmApp.Common.props, WasmApp.Common.targets
    wasm : WasmApp.props, WasmApp.targets
    wasi : WasiApp.props, WasiApp.targets
```

 ## `WasmApp.Common.{props,targets}`

This has all the common parts of the build for browser-wasm, and wasi,
and includes bits from `WasmApp.{props,targets}`, and
`WasmApp.Native.{props,targets}`.

- The top level target remains the same - `WasmBuildApp`.
- There are a few "public" targets that can be hooked into:
    - `PrepareInputsForWasmBuild`
    - `WasmGenerateAppBundle`
    - `PrepareForWasmBuildNative`
    - `WasmLinkDotNet`

- all these public targets have corresponding `*DependsOn` properties
  which can be used for extending the build

note: this commit does not add a public target for AOT, but it might be
added in future.

 ## WasmApp.{props,targets}

This is for `browser-wasm` projects. The file might be renamed in
future.

 ## WasiApp.{props,targets}

This is for `wasi-wasm` projects. `ILStrip` becomes usable as a feature
for `wasi-wasm` because of this consolidation.

* [wasi] WBT: Use TestOutputWrapper

* [wasm] WBT: Track target name changes

* [wasi] WBT: Add new tests

* cleanup

* cleanup

* address feedback

* address feedback
  • Loading branch information
radical authored Dec 11, 2023
1 parent 46bf1af commit a128c15
Show file tree
Hide file tree
Showing 26 changed files with 1,945 additions and 2,156 deletions.
3 changes: 2 additions & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@
<WasmtimeDir Condition="'$(WasmtimeDir)' == '' and '$(WASMTIME_PATH)' != '' and Exists($(WASMTIME_PATH))">$(WASMTIME_PATH)</WasmtimeDir>
<WasmtimeDir Condition="'$(WasmtimeDir)' == ''">$([MSBuild]::NormalizeDirectory($(ArtifactsObjDir), 'wasmtime'))</WasmtimeDir>
<InstallWasmtimeForTests Condition="'$(InstallWasmtimeForTests)' == '' and !Exists($(WasmtimeDir))">true</InstallWasmtimeForTests>
<WasmCommonTargetsPath>$([MSBuild]::NormalizeDirectory($(MonoProjectRoot), 'wasm', 'build'))</WasmCommonTargetsPath>
</PropertyGroup>

<PropertyGroup Label="CalculatePortableBuild">
Expand Down Expand Up @@ -331,7 +332,7 @@

<!-- this property is used by the SDK to pull in mono-based runtime packs -->
<UseMonoRuntime Condition="'$(UseMonoRuntime)' == '' and '$(RuntimeFlavor)' == 'Mono'">true</UseMonoRuntime>

<!-- For enabling the use of XUnitLogChecker in coreclr and libraries test runs. -->
<!-- TODO: Enable XUnitLogChecker for NativeAOT tests https://github.com/dotnet/runtime/issues/94722 -->
<IsXUnitLogCheckerSupported Condition="'$(IsXUnitLogCheckerSupported)' == ''">false</IsXUnitLogCheckerSupported>
Expand Down
5 changes: 4 additions & 1 deletion eng/testing/scenarios/BuildWasiAppsJobsList.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
Wasi.Build.Tests.InvariantTests
Wasi.Build.Tests.WasiTemplateTests
Wasi.Build.Tests.ILStripTests
Wasi.Build.Tests.SdkMissingTests
Wasi.Build.Tests.RuntimeConfigTests
Wasi.Build.Tests.WasiTemplateTests
2 changes: 2 additions & 0 deletions eng/testing/tests.wasm.targets
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@
<BundleFiles Include="$(RuntimeConfigFilePath)" TargetDir="publish" />

<BundleFiles Include="$(WasmSharedPath)data\aot-tests\*" TargetDir="publish" />
<!-- FIXME: what would be the correct place to do this? -->
<BundleFiles Include="$(MonoProjectRoot)src\mono\wasm\build\WasmApp.Common*" TargetDir="publish" />
</ItemGroup>

<ItemGroup Condition="'$(DebuggerSupport)' == 'true'">
Expand Down
4 changes: 3 additions & 1 deletion src/libraries/sendtohelix-wasi.targets
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@
<EnableDefaultBuildHelixWorkItems>false</EnableDefaultBuildHelixWorkItems>

<WASI_SDK_PATH Condition="'$(WASI_SDK_PATH)' == ''">$(RepoRoot)src\mono\wasi\wasi-sdk\</WASI_SDK_PATH>
<WasmBuildTargetsDir>$([MSBuild]::NormalizeDirectory('$(RepoRoot)', 'src', 'mono', 'wasi', 'build'))</WasmBuildTargetsDir>
<WasiBuildTargetsDir>$([MSBuild]::NormalizeDirectory('$(RepoRoot)', 'src', 'mono', 'wasi', 'build'))</WasiBuildTargetsDir>
<WasmBuildTargetsDir>$([MSBuild]::NormalizeDirectory('$(RepoRoot)', 'src', 'mono', 'wasm', 'build'))</WasmBuildTargetsDir>
<WasmSharedDir>$([MSBuild]::NormalizeDirectory('$(RepoRoot)', 'src', 'mono', 'wasm', 'shared'))</WasmSharedDir>
<WasiSdkDirForHelixPayload>$(HelixDependenciesStagingPath)\wasi-sdk</WasiSdkDirForHelixPayload>
<WasmtimeDirForHelixPayload>$(HelixDependenciesStagingPath)\wasmtime</WasmtimeDirForHelixPayload>
Expand Down Expand Up @@ -123,6 +124,7 @@

<HelixCorrelationPayload Include="$(MicrosoftNetCoreAppRuntimePackDir)" Destination="build/microsoft.netcore.app.runtime.wasi-wasm" />

<HelixCorrelationPayload Include="$(WasiBuildTargetsDir)" Destination="build/wasm" />
<HelixCorrelationPayload Include="$(WasmBuildTargetsDir)" Destination="build/wasm" />
<HelixCorrelationPayload Include="$(WasmAppBuilderDir)" Destination="build/WasmAppBuilder" />
<HelixCorrelationPayload Include="$(MonoAOTCompilerDir)" Destination="build/MonoAOTCompiler" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,13 @@
<PackageFile Include="Sdk\Sdk.props" TargetPath="Sdk" />
<PackageFile Include="$(RepoRoot)\src\mono\wasm\build\ILLink.Substitutions.*.xml" TargetPath="Sdk" />
<PackageFile Include="$(RepoRoot)\src\mono\wasm\build\ILLink.Descriptors.*.xml" TargetPath="Sdk" />

<PackageFile Include="$(RepoRoot)\src\mono\wasm\build\WasmApp.props" TargetPath="Sdk" />
<PackageFile Include="$(RepoRoot)\src\mono\wasm\build\WasmApp.targets" TargetPath="Sdk" />
<PackageFile Include="$(RepoRoot)\src\mono\wasm\build\WasmApp.Native.*" TargetPath="Sdk" />

<PackageFile Include="$(RepoRoot)\src\mono\wasm\build\WasmApp.Common.props" TargetPath="Sdk" />
<PackageFile Include="$(RepoRoot)\src\mono\wasm\build\WasmApp.Common.targets" TargetPath="Sdk" />

<PackageFile Include="$(RepoRoot)\src\mono\wasm\build\EmSdkRepo.Defaults.props" TargetPath="Sdk" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@
<PackageFile Include="Sdk\Sdk.props" TargetPath="Sdk" />
<PackageFile Include="$(RepoRoot)\src\mono\wasi\build\WasiApp.props" TargetPath="Sdk" />
<PackageFile Include="$(RepoRoot)\src\mono\wasi\build\WasiApp.targets" TargetPath="Sdk" />
<PackageFile Include="$(RepoRoot)\src\mono\wasi\build\WasiApp.Native.*" TargetPath="Sdk" />

<PackageFile Include="$(RepoRoot)\src\mono\wasm\build\WasmApp.Common.props" TargetPath="Sdk" />
<PackageFile Include="$(RepoRoot)\src\mono\wasm\build\WasmApp.Common.targets" TargetPath="Sdk" />

<PackageFile Include="$(RepoRoot)\src\mono\wasi\build\WasiSdk.Defaults.props" TargetPath="Sdk" />
</ItemGroup>

Expand Down
7 changes: 3 additions & 4 deletions src/mono/wasi/Wasi.Build.Tests/BuildTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public BuildTestBase(ITestOutputHelper output, SharedBuildPerTestClassFixture bu
{
_testIdx = Interlocked.Increment(ref s_testCounter);
_buildContext = buildContext;
_testOutput = output;
_testOutput = new TestOutputWrapper(output);
_logPath = s_buildEnv.LogRootPath; // FIXME:
}

Expand Down Expand Up @@ -603,8 +603,6 @@ void LogData(string label, string? message)
}
outputBuilder.AppendLine($"{label} {message}");
}
if (EnvironmentVariables.ShowBuildOutput)
Console.WriteLine($"{label} {message}");
}
}

Expand Down Expand Up @@ -703,6 +701,7 @@ public static int Main()
.MultiplyWithSingleArgs(true, false) /*propertyValue*/
.MultiplyWithSingleArgs(true, false) /*aot*/
.UnwrapItemsAsArrays();

protected CommandResult RunWithoutBuild(string config, string id)
{
string runArgs = $"run --no-build -c {config}";
Expand All @@ -721,7 +720,7 @@ protected CommandResult RunWithoutBuild(string config, string id)
return res;
}
}

public record BuildArgs(string ProjectName,
string Config,
bool AOT,
Expand Down
128 changes: 128 additions & 0 deletions src/mono/wasi/Wasi.Build.Tests/ILStripTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.IO;
using Xunit;
using Xunit.Abstractions;
using Wasm.Build.Tests;

#nullable enable

namespace Wasi.Build.Tests;

public class ILStripTests : BuildTestBase
{
public ILStripTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext)
: base(output, buildContext)
{
}

[Theory]
[InlineData("", /*expectILStripping*/ true, /*singleFileBundle*/false)] // Default case
[InlineData("", /*expectILStripping*/ true, /*singleFileBundle*/true)] // Default case
[InlineData("false", /*expectILStripping*/ false, /*singleFileBundle*/false)] // the opposite of the default case
[InlineData("false", /*expectILStripping*/ false, /*singleFileBundle*/true)] // the opposite of the default case
public void WasmStripILAfterAOT_TestDefaultAndOverride(string stripILAfterAOT, bool expectILStripping, bool singleFileBundle)
{
string config = "Release";
string id = $"{config}_{GetRandomId()}";
string projectFile = CreateWasmTemplateProject(id, "wasiconsole");
string projectName = Path.GetFileNameWithoutExtension(projectFile);

string extraProperties = "<RunAOTCompilation>true</RunAOTCompilation>";
if (singleFileBundle)
extraProperties += "<WasmSingleFileBundle>true</WasmSingleFileBundle>";
if (!string.IsNullOrEmpty(stripILAfterAOT))
extraProperties += $"<WasmStripILAfterAOT>{stripILAfterAOT}</WasmStripILAfterAOT>";
AddItemsPropertiesToProject(projectFile, extraProperties);

var buildArgs = new BuildArgs(projectName, config, AOT: true, ProjectFileContents: id, ExtraBuildArgs: null);
buildArgs = ExpandBuildArgs(buildArgs);

BuildProject(buildArgs,
id: id,
new BuildProjectOptions(
DotnetWasmFromRuntimePack: false,
CreateProject: false,
Publish: true,
TargetFramework: BuildTestBase.DefaultTargetFramework,
UseCache: false));

string runArgs = $"run --no-silent --no-build -c {config}";
new RunCommand(s_buildEnv, _testOutput, label: id)
.WithWorkingDirectory(_projectDir!)
.ExecuteWithCapturedOutput(runArgs)
.EnsureSuccessful();

string frameworkDir = singleFileBundle ? "" : Path.Combine(_projectDir!, "bin", config, DefaultTargetFramework, "wasi-wasm", "AppBundle", "managed");
string objBuildDir = Path.Combine(_projectDir!, "obj", config, DefaultTargetFramework, "wasi-wasm", "wasm", "for-publish");
TestWasmStripILAfterAOTOutput(objBuildDir, frameworkDir, expectILStripping, singleFileBundle, _testOutput);
}

private static void TestWasmStripILAfterAOTOutput(string objBuildDir, string appBundleFrameworkDir, bool expectILStripping, bool singleFileBundle, ITestOutputHelper testOutput)
{
string origAssemblyDir = Path.Combine(objBuildDir, "aot-in");
string strippedAssemblyDir = Path.Combine(objBuildDir, "stripped");
Assert.True(Directory.Exists(origAssemblyDir), $"Could not find the original AOT input assemblies dir: {origAssemblyDir}");
if (expectILStripping)
Assert.True(Directory.Exists(strippedAssemblyDir), $"Could not find the stripped assemblies dir: {strippedAssemblyDir}");
else
Assert.False(Directory.Exists(strippedAssemblyDir), $"Expected {strippedAssemblyDir} to not exist");

string assemblyToExamine = "System.Private.CoreLib.dll";
string originalAssembly = Path.Combine(objBuildDir, origAssemblyDir, assemblyToExamine);
string strippedAssembly = Path.Combine(objBuildDir, strippedAssemblyDir, assemblyToExamine);
string includedAssembly = Path.Combine(appBundleFrameworkDir, assemblyToExamine);

Assert.True(File.Exists(originalAssembly), $"Expected {nameof(originalAssembly)} {originalAssembly} to exist");
if (!singleFileBundle)
Assert.True(File.Exists(includedAssembly), $"Expected {nameof(includedAssembly)} {includedAssembly} to exist");
if (expectILStripping)
Assert.True(File.Exists(strippedAssembly), $"Expected {nameof(strippedAssembly)} {strippedAssembly} to exist");
else
Assert.False(File.Exists(strippedAssembly), $"Expected {strippedAssembly} to not exist");

string compressedOriginalAssembly = Utils.GZipCompress(originalAssembly);
string? compressedIncludedAssembly = null;
FileInfo compressedOriginalAssembly_fi = new FileInfo(compressedOriginalAssembly);
FileInfo? compressedincludedAssembly_fi = null;

testOutput.WriteLine ($"compressedOriginalAssembly_fi: {compressedOriginalAssembly_fi.Length}, {compressedOriginalAssembly}");
if (!singleFileBundle)
{
compressedIncludedAssembly = Utils.GZipCompress(includedAssembly)!;
compressedincludedAssembly_fi = new FileInfo(compressedIncludedAssembly);
testOutput.WriteLine ($"compressedincludedAssembly_fi: {compressedincludedAssembly_fi.Length}, {compressedIncludedAssembly}");
}

if (expectILStripping)
{
string compressedStrippedAssembly = Utils.GZipCompress(strippedAssembly);
FileInfo compressedStrippedAssembly_fi = new FileInfo(compressedStrippedAssembly);
testOutput.WriteLine ($"compressedStrippedAssembly_fi: {compressedStrippedAssembly_fi.Length}, {compressedStrippedAssembly}");

// original > stripped assembly
Assert.True(compressedOriginalAssembly_fi.Length > compressedStrippedAssembly_fi.Length,
$"Expected original assembly ({compressedOriginalAssembly}) size ({compressedOriginalAssembly_fi.Length}) " +
$"to be bigger than the stripped assembly ({compressedStrippedAssembly}) size ({compressedStrippedAssembly_fi.Length})");

if (!singleFileBundle)
{
// included == stripped assembly
Assert.True(compressedincludedAssembly_fi!.Length == compressedStrippedAssembly_fi.Length,
$"Expected included assembly ({compressedIncludedAssembly}) size ({compressedincludedAssembly_fi.Length}) " +
$"to be the same as the stripped assembly ({compressedStrippedAssembly}) size ({compressedStrippedAssembly_fi.Length})");
}
}
else
{
if (!singleFileBundle)
{
// original == included assembly
Assert.True(compressedincludedAssembly_fi!.Length == compressedOriginalAssembly_fi.Length,
$"Expected included assembly ({compressedIncludedAssembly}) size ({compressedincludedAssembly_fi.Length}) " +
$"to be the same as the original assembly ({compressedOriginalAssembly}) size ({compressedOriginalAssembly_fi.Length})");
}
}
}
}
2 changes: 0 additions & 2 deletions src/mono/wasi/Wasi.Build.Tests/InvariantTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@

using System;
using System.IO;
using System.Runtime.InteropServices;
using Xunit;
using Xunit.Abstractions;
using Xunit.Sdk;
using Wasm.Build.Tests;

#nullable enable
Expand Down
52 changes: 52 additions & 0 deletions src/mono/wasi/Wasi.Build.Tests/RuntimeConfigTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.IO;
using Xunit;
using Xunit.Abstractions;
using Wasm.Build.Tests;

#nullable enable

namespace Wasi.Build.Tests;

public class RuntimeConfigTests : BuildTestBase
{
public RuntimeConfigTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext)
: base(output, buildContext)
{
}

[Theory]
[ActiveIssue("https://github.com/dotnet/runtime/issues/95345")]
[InlineData(false)]
[InlineData(true)]
public void MissingRuntimeConfigTemplateJson(bool singleFileBundle)
{
string config = "Release";
string id = $"{config}_{GetRandomId()}";
string projectFile = CreateWasmTemplateProject(id, "wasiconsole");
string projectName = Path.GetFileNameWithoutExtension(projectFile);

File.Delete(Path.Combine(_projectDir!, "runtimeconfig.template.json"));

var buildArgs = new BuildArgs(projectName, config, AOT: true, ProjectFileContents: id, ExtraBuildArgs: null);
buildArgs = ExpandBuildArgs(buildArgs);
AddItemsPropertiesToProject(projectFile, singleFileBundle ? "<WasmSingleFileBundle>true</WasmSingleFileBundle>" : "");

BuildProject(buildArgs,
id: id,
new BuildProjectOptions(
DotnetWasmFromRuntimePack: false,
CreateProject: false,
Publish: true,
TargetFramework: BuildTestBase.DefaultTargetFramework,
UseCache: false));

string runArgs = $"run --no-silent --no-build -c {config}";
new RunCommand(s_buildEnv, _testOutput, label: id)
.WithWorkingDirectory(_projectDir!)
.ExecuteWithCapturedOutput(runArgs)
.EnsureSuccessful();
}
}
82 changes: 82 additions & 0 deletions src/mono/wasi/Wasi.Build.Tests/SdkMissingTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// 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.IO;
using System.Runtime.InteropServices;
using Xunit;
using Xunit.Abstractions;
using Xunit.Sdk;
using Wasm.Build.Tests;
using System.Collections.Generic;

#nullable enable

namespace Wasi.Build.Tests;

public class SdkMissingTests : BuildTestBase
{
public SdkMissingTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext)
: base(output, buildContext)
{
}

public static TheoryData<string, string, bool> TestDataForNativeBuildFails(string extraProperties)
=> new TheoryData<string, string, bool>
{
{ "Debug", extraProperties, false },
{ "Debug", extraProperties, true },
{ "Release", extraProperties, false },
{ "Release", extraProperties, true }
};

[Theory]
[MemberData(nameof(TestDataForNativeBuildFails), "<WasmSingleFileBundle>true</WasmSingleFileBundle>")]
[MemberData(nameof(TestDataForNativeBuildFails), "<InvariantGlobalization>true</InvariantGlobalization>")]
public void NativeBuildOrPublishFails(string config, string extraProperties, bool publish)
{
string output = BuildWithInvalidSdkPath(config, extraProperties, publish, expectSuccess: false);
Assert.Contains("SDK is required for building native files.", output);
}

[Theory]
[InlineData("Debug", "<RunAOTCompilation>true</RunAOTCompilation>", false)]
[InlineData("Release", "<RunAOTCompilation>true</RunAOTCompilation>", true)]
public void AOTFailsOnlyOnPublish(string config, string extraProperties, bool publish)
{
string output = BuildWithInvalidSdkPath(config, extraProperties, publish, expectSuccess: !publish);
if (publish)
Assert.Contains("SDK is required for AOT'ing assemblies", output);
else
Assert.DoesNotContain("SDK is required", output);
}

[Theory]
[InlineData("Debug")]
[InlineData("Release")]
public void SimpleBuildDoesNotFail(string config)
=> BuildWithInvalidSdkPath(config, "", publish: false, expectSuccess: true);

private string BuildWithInvalidSdkPath(string config, string extraProperties, bool publish, bool expectSuccess)
{
string id = $"{config}_{GetRandomId()}";
string projectFile = CreateWasmTemplateProject(id, "wasiconsole");
string projectName = Path.GetFileNameWithoutExtension(projectFile);

var buildArgs = new BuildArgs(projectName, config, /*aot*/ true, id, null);
buildArgs = ExpandBuildArgs(buildArgs);
AddItemsPropertiesToProject(projectFile, extraProperties);

(_, string output) = BuildProject(buildArgs,
id: id,
new BuildProjectOptions(
DotnetWasmFromRuntimePack: true,
CreateProject: false,
Publish: publish,
TargetFramework: BuildTestBase.DefaultTargetFramework,
ExtraBuildEnvironmentVariables: new Dictionary<string, string> { { "WASI_SDK_PATH", "x" } },
ExpectSuccess: expectSuccess));

return output;
}
}
1 change: 0 additions & 1 deletion src/mono/wasi/Wasi.Build.Tests/WasiTemplateTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

using System;
using System.IO;
using System.Runtime.InteropServices;
using Xunit;
using Xunit.Abstractions;
using Xunit.Sdk;
Expand Down
Loading

0 comments on commit a128c15

Please sign in to comment.