public void BuildThenPublishWithAOT(BuildArgs buildArgs, RunHost host, string id) { string projectName = $"build_publish_{buildArgs.Config}"; buildArgs = buildArgs with { ProjectName = projectName }; buildArgs = ExpandBuildArgs(buildArgs, extraProperties: "<_WasmDevel>true</_WasmDevel>"); // no relinking for build bool relinked = false; (_, string output) = BuildProject(buildArgs, id, new BuildProjectOptions( InitProject: () => File.WriteAllText(Path.Combine(_projectDir !, "Program.cs"), s_mainReturns42), DotnetWasmFromRuntimePack: !relinked, CreateProject: true, Publish: false, Label: "first_build")); BuildPaths paths = GetBuildPaths(buildArgs); var pathsDict = GetFilesTable(buildArgs, paths, unchanged: false); string mainDll = $"{buildArgs.ProjectName}.dll"; var firstBuildStat = StatFiles(pathsDict.Select(kvp => kvp.Value.fullPath)); Assert.False(firstBuildStat["pinvoke.o"].Exists); Assert.False(firstBuildStat[$"{mainDll}.bc"].Exists); CheckOutputForNativeBuild(expectAOT: false, expectRelinking: relinked, buildArgs, output); Run(expectAOT: false); if (!_buildContext.TryGetBuildFor(buildArgs, out BuildProduct? product)) { throw new XunitException($"Test bug: could not get the build product in the cache"); } File.Move(product !.LogFile, Path.ChangeExtension(product.LogFile !, ".first.binlog")); _testOutput.WriteLine($"{Environment.NewLine}Publishing with no changes ..{Environment.NewLine}"); Console.WriteLine($"{Environment.NewLine}Publishing with no changes ..{Environment.NewLine}"); // relink by default for Release+publish (_, output) = BuildProject(buildArgs, id: id, new BuildProjectOptions( DotnetWasmFromRuntimePack: false, CreateProject: false, Publish: true, UseCache: false, Label: "first_publish")); var publishStat = StatFiles(pathsDict.Select(kvp => kvp.Value.fullPath)); Assert.True(publishStat["pinvoke.o"].Exists); Assert.True(publishStat[$"{mainDll}.bc"].Exists); CheckOutputForNativeBuild(expectAOT: true, expectRelinking: false, buildArgs, output); CompareStat(firstBuildStat, publishStat, pathsDict.Values); Run(expectAOT: true); // second build (_, output) = BuildProject(buildArgs, id: id, new BuildProjectOptions( InitProject: () => File.WriteAllText(Path.Combine(_projectDir !, "Program.cs"), s_mainReturns42), DotnetWasmFromRuntimePack: !relinked, CreateProject: true, Publish: false, Label: "second_build")); var secondBuildStat = StatFiles(pathsDict.Select(kvp => kvp.Value.fullPath)); // no relinking, or AOT CheckOutputForNativeBuild(expectAOT: false, expectRelinking: false, buildArgs, output); // no native files changed pathsDict.UpdateTo(unchanged: true); CompareStat(publishStat, secondBuildStat, pathsDict.Values); void Run(bool expectAOT) => RunAndTestWasmApp( buildArgs with { AOT = expectAOT }, buildDir: _projectDir, expectedExitCode: 42, host: host, id: id); } void CheckOutputForNativeBuild(bool expectAOT, bool expectRelinking, BuildArgs buildArgs, string buildOutput) { AssertSubstring($"{buildArgs.ProjectName}.dll -> {buildArgs.ProjectName}.dll.bc", buildOutput, expectAOT); AssertSubstring($"{buildArgs.ProjectName}.dll.bc -> {buildArgs.ProjectName}.dll.o", buildOutput, expectAOT); AssertSubstring("pinvoke.c -> pinvoke.o", buildOutput, expectRelinking || expectAOT); } }
public void Bug49588_RegressionTest_NativeRelinking(BuildArgs buildArgs, RunHost host, string id) => TestMain("bug49588_native_relinking", s_bug49588_ProgramCS, buildArgs, host, id, extraProperties: "<WasmBuildNative>true</WasmBuildNative>", dotnetWasmFromRuntimePack: false);
public void SimpleNativeBuild(BuildArgs buildArgs, RunHost host, string id) => NativeBuild("simple_native_build", s_mainReturns42, buildArgs, host, id);
private void NativeBuild(string projectNamePrefix, string projectContents, BuildArgs buildArgs, RunHost host, string id) { string projectName = $"{projectNamePrefix}_{buildArgs.Config}_{buildArgs.AOT}"; buildArgs = buildArgs with { ProjectName = projectName }; buildArgs = ExpandBuildArgs(buildArgs, extraProperties: "<WasmBuildNative>true</WasmBuildNative>"); BuildProject(buildArgs, initProject: () => File.WriteAllText(Path.Combine(_projectDir !, "Program.cs"), projectContents), dotnetWasmFromRuntimePack: false, id: id); RunAndTestWasmApp(buildArgs, buildDir: _projectDir, expectedExitCode: 42, test: output => {}, host: host, id: id); }
public void OptimizationFlagChange(BuildArgs buildArgs, string cflags, string ldflags, RunHost host, string id) { // force _WasmDevel=false, so we don't get -O0 buildArgs = buildArgs with { ProjectName = $"rebuild_flags_{buildArgs.Config}", ExtraBuildArgs = "/p:_WasmDevel=false" }; (buildArgs, BuildPaths paths) = FirstNativeBuild(s_mainReturns42, nativeRelink: true, invariant: false, buildArgs, id); string mainAssembly = $"{buildArgs.ProjectName}.dll"; var pathsDict = GetFilesTable(buildArgs, paths, unchanged: false); pathsDict.UpdateTo(unchanged: true, mainAssembly, "icall-table.h", "pinvoke-table.h", "driver-gen.c"); if (cflags.Length == 0) { pathsDict.UpdateTo(unchanged: true, "pinvoke.o", "corebindings.o", "driver.o"); } pathsDict.Remove(mainAssembly); if (buildArgs.AOT) { // link optimization flag change affects .bc->.o files too, but // it might result in only *some* files being *changed, // so, don't check for those // Link optimization flag is set to Compile optimization flag, if unset // so, it affects .bc files too! foreach (string key in pathsDict.Keys.ToArray()) { if (key.EndsWith(".dll.bc", StringComparison.Ordinal) || key.EndsWith(".dll.o", StringComparison.Ordinal)) { pathsDict.Remove(key); } } } var originalStat = StatFiles(pathsDict.Select(kvp => kvp.Value.fullPath)); // Rebuild string output = Rebuild(nativeRelink: true, invariant: false, buildArgs, id, extraBuildArgs: $" {cflags} {ldflags}", verbosity: "normal"); var newStat = StatFiles(pathsDict.Select(kvp => kvp.Value.fullPath)); CompareStat(originalStat, newStat, pathsDict.Values); string runOutput = RunAndTestWasmApp(buildArgs, buildDir: _projectDir, expectedExitCode: 42, host: host, id: id); AssertSubstring($"Found statically linked AOT module '{Path.GetFileNameWithoutExtension(mainAssembly)}'", runOutput, contains: buildArgs.AOT); } }
protected string RunAndTestWasmApp(BuildArgs buildArgs, RunHost host, string id, Action <string>?test = null, string?buildDir = null, int expectedExitCode = 0, string?args = null, Dictionary <string, string>?envVars = null, string targetFramework = DefaultTargetFramework) { buildDir ??= _projectDir; envVars ??= new(); envVars["XHARNESS_DISABLE_COLORED_OUTPUT"] = "true"; if (buildArgs.AOT) { envVars["MONO_LOG_LEVEL"] = "debug"; envVars["MONO_LOG_MASK"] = "aot"; } if (s_buildEnv.EnvVars != null) { foreach (var kvp in s_buildEnv.EnvVars) { envVars[kvp.Key] = kvp.Value; } } string bundleDir = Path.Combine(GetBinDir(baseDir: buildDir, config: buildArgs.Config, targetFramework: targetFramework), "AppBundle"); (string testCommand, string extraXHarnessArgs) = host switch { RunHost.V8 => ("wasm test", "--js-file=test-main.js --engine=V8 -v trace"), RunHost.NodeJS => ("wasm test", "--js-file=test-main.js --engine=NodeJS -v trace"), _ => ("wasm test-browser", $"-v trace -b {host}") }; string testLogPath = Path.Combine(_logPath, host.ToString()); string output = RunWithXHarness( testCommand, testLogPath, buildArgs.ProjectName, bundleDir, _testOutput, envVars: envVars, expectedAppExitCode: expectedExitCode, extraXHarnessArgs: extraXHarnessArgs, appArgs: args); if (buildArgs.AOT) { Assert.Contains("AOT: image 'System.Private.CoreLib' found.", output); Assert.Contains($"AOT: image '{buildArgs.ProjectName}' found.", output); } else { Assert.DoesNotContain("AOT: image 'System.Private.CoreLib' found.", output); Assert.DoesNotContain($"AOT: image '{buildArgs.ProjectName}' found.", output); } if (test != null) { test(output); } return(output); }
public void NoOpRebuildForNativeBuilds(BuildArgs buildArgs, bool nativeRelink, bool invariant, RunHost host, string id) { buildArgs = buildArgs with { ProjectName = $"rebuild_noop_{buildArgs.Config}" }; (buildArgs, BuildPaths paths) = FirstNativeBuild(s_mainReturns42, nativeRelink: nativeRelink, invariant: invariant, buildArgs, id); var pathsDict = GetFilesTable(buildArgs, paths, unchanged: true); var originalStat = StatFiles(pathsDict.Select(kvp => kvp.Value.fullPath)); Rebuild(nativeRelink, invariant, buildArgs, id); var newStat = StatFiles(pathsDict.Select(kvp => kvp.Value.fullPath)); CompareStat(originalStat, newStat, pathsDict.Values); RunAndTestWasmApp(buildArgs, buildDir: _projectDir, expectedExitCode: 42, host: host, id: id); }
public void Bug49588_RegressionTest_AOT(BuildArgs buildArgs, RunHost host, string id) => TestMain("bug49588_aot", s_bug49588_ProgramCS, buildArgs, host, id);
public void RelinkingWithoutAOT(BuildArgs buildArgs, bool?invariantGlobalization, RunHost host, string id) => TestInvariantGlobalization(buildArgs, invariantGlobalization, host, id, extraProperties: "<WasmBuildNative>true</WasmBuildNative>", dotnetWasmFromRuntimePack: false);
private void TestInvariantGlobalization(BuildArgs buildArgs, bool?invariantGlobalization, RunHost host, string id, string extraProperties = "", bool?dotnetWasmFromRuntimePack = null) { string projectName = $"invariant_{invariantGlobalization?.ToString() ?? "unset"}"; if (invariantGlobalization != null) { extraProperties = $"{extraProperties}<InvariantGlobalization>{invariantGlobalization}</InvariantGlobalization>"; } buildArgs = buildArgs with { ProjectName = projectName }; buildArgs = ExpandBuildArgs(buildArgs, extraProperties); if (dotnetWasmFromRuntimePack == null) { dotnetWasmFromRuntimePack = !(buildArgs.AOT || buildArgs.Config == "Release"); } string programText = @" using System; using System.Globalization; // https://github.com/dotnet/runtime/blob/main/docs/design/features/globalization-invariant-mode.md#cultures-and-culture-data try { CultureInfo culture = new (""es-ES"", false); Console.WriteLine($""es-ES: Is Invariant LCID: {culture.LCID == CultureInfo.InvariantCulture.LCID}, NativeName: {culture.NativeName}""); } catch (CultureNotFoundException cnfe) { Console.WriteLine($""Could not create es-ES culture: {cnfe.Message}""); } Console.WriteLine($""CurrentCulture.NativeName: {CultureInfo.CurrentCulture.NativeName}""); return 42; "; BuildProject(buildArgs, id: id, new BuildProjectOptions( InitProject: () => File.WriteAllText(Path.Combine(_projectDir !, "Program.cs"), programText), DotnetWasmFromRuntimePack: dotnetWasmFromRuntimePack, HasIcudt: invariantGlobalization == null || invariantGlobalization.Value == false)); if (invariantGlobalization == true) { string output = RunAndTestWasmApp(buildArgs, expectedExitCode: 42, host: host, id: id); Assert.Contains("Could not create es-ES culture", output); Assert.Contains("CurrentCulture.NativeName: Invariant Language (Invariant Country)", output); } else { string output = RunAndTestWasmApp(buildArgs, expectedExitCode: 42, host: host, id: id); Assert.Contains("es-ES: Is Invariant LCID: False, NativeName: es (ES)", output); // ignoring the last line of the output which prints the current culture } } }
public void AOT_InvariantGlobalization(BuildArgs buildArgs, bool?invariantGlobalization, RunHost host, string id) => TestInvariantGlobalization(buildArgs, invariantGlobalization, host, id);
public static IEnumerable <object?[]> InvariantGlobalizationTestData(bool aot, RunHost host) => ConfigWithAOTData(aot) .Multiply( new object?[] { null }, new object?[] { false }, new object?[] { true }) .WithRunHosts(host) .UnwrapItemsAsArrays();
public static IEnumerable <object?[]> SatelliteAssemblyTestData(bool aot, bool relinking, RunHost host) => ConfigWithAOTData(aot) .Multiply( new object?[] { relinking, "es-ES", "got: hola" }, new object?[] { relinking, null, "got: hello" }, new object?[] { relinking, "ja-JP", "got: \u3053\u3093\u306B\u3061\u306F" }) .WithRunHosts(host) .UnwrapItemsAsArrays();
public static IEnumerable <object?[]> MainMethodTestData(bool aot, RunHost host) => ConfigWithAOTData(aot) .WithRunHosts(host) .UnwrapItemsAsArrays();
public static IEnumerable <object?[]> MainWithArgsTestData(bool aot, RunHost host) => ConfigWithAOTData(aot).Multiply( new object?[] { new object?[] { "abc", "foobar" } }, new object?[] { new object?[0] } ).WithRunHosts(host).UnwrapItemsAsArrays();
public void TopLevelMain(BuildArgs buildArgs, RunHost host, string id) => TestMain("top_level", @"System.Console.WriteLine(""Hello, World!""); return await System.Threading.Tasks.Task.FromResult(42);", buildArgs, host, id);
public void TopLevelWithArgs(BuildArgs buildArgs, string[] args, RunHost host, string id) => TestMainWithArgs("top_level_args", @"##CODE## return await System.Threading.Tasks.Task.FromResult(42 + count);", buildArgs, args, host, id);
public static IEnumerable <IEnumerable <object?> > WithRunHosts(this IEnumerable <IEnumerable <object?> > data, RunHost hosts) { IEnumerable <object?> hostsEnumerable = hosts.Enumerate(); if (hosts == RunHost.None) { return(data.Select(d => d.Append((object?)Path.GetRandomFileName()))); } return(data.SelectMany(d => { string runId = Path.GetRandomFileName(); return hostsEnumerable.Select(o => d.Append((object?)o) .Append((object?)runId)); })); }
public void ExtraEmccFlagsSetButNoRealChange(BuildArgs buildArgs, string extraCFlags, string extraLDFlags, RunHost host, string id) { buildArgs = buildArgs with { ProjectName = $"rebuild_flags_{buildArgs.Config}" }; (buildArgs, BuildPaths paths) = FirstNativeBuild(s_mainReturns42, nativeRelink: true, invariant: false, buildArgs, id); var pathsDict = GetFilesTable(buildArgs, paths, unchanged: true); if (extraLDFlags.Length > 0) { pathsDict.UpdateTo(unchanged: false, "dotnet.wasm", "dotnet.js"); } var originalStat = StatFiles(pathsDict.Select(kvp => kvp.Value.fullPath)); // Rebuild string mainAssembly = $"{buildArgs.ProjectName}.dll"; string extraBuildArgs = $" {extraCFlags} {extraLDFlags}"; string output = Rebuild(nativeRelink: true, invariant: false, buildArgs, id, extraBuildArgs: extraBuildArgs, verbosity: "normal"); var newStat = StatFiles(pathsDict.Select(kvp => kvp.Value.fullPath)); CompareStat(originalStat, newStat, pathsDict.Values); // cflags: pinvoke get's compiled, but doesn't overwrite pinvoke.o // and thus doesn't cause relinking AssertSubstring("pinvoke.c -> pinvoke.o", output, contains: extraCFlags.Length > 0); // ldflags: link step args change, so it should trigger relink AssertSubstring("Linking with emcc", output, contains: extraLDFlags.Length > 0); if (buildArgs.AOT) { // ExtraEmccLDFlags does not affect .bc files Assert.DoesNotContain("Compiling assembly bitcode files", output); } string runOutput = RunAndTestWasmApp(buildArgs, buildDir: _projectDir, expectedExitCode: 42, host: host, id: id); AssertSubstring($"Found statically linked AOT module '{Path.GetFileNameWithoutExtension(mainAssembly)}'", runOutput, contains: buildArgs.AOT); }