Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions eng/pipelines/mono/templates/workloads-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ jobs:
IntermediateArtifacts/MonoRuntimePacks/Shipping/Microsoft.NET.Runtime.MonoTargets.Sdk*.nupkg
IntermediateArtifacts/MonoRuntimePacks/Shipping/Microsoft.NET.Runtime.MonoAOTCompiler.Task*.nupkg
IntermediateArtifacts/MonoRuntimePacks/Shipping/Microsoft.NET.Runtime.WebAssembly.Sdk*.nupkg
IntermediateArtifacts/MonoRuntimePacks/Shipping/Microsoft.NET.Runtime.WebAssembly.Templates*.nupkg

- task: CopyFiles@2
displayName: Flatten packages
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"description": ".NET WebAssembly build tools",
"packs": [
"Microsoft.NET.Runtime.WebAssembly.Sdk",
"Microsoft.NET.Runtime.WebAssembly.Templates",
"Microsoft.NETCore.App.Runtime.Mono.browser-wasm",
"Microsoft.NETCore.App.Runtime.AOT.Cross.browser-wasm"
],
Expand Down Expand Up @@ -140,6 +141,10 @@
"kind": "Sdk",
"version": "${PackageVersion}"
},
"Microsoft.NET.Runtime.WebAssembly.Templates": {
"kind": "template",
"version": "${PackageVersion}"
},
"Microsoft.NETCore.App.Runtime.Mono.android-arm": {
"kind": "framework",
"version": "${PackageVersion}"
Expand Down
1 change: 1 addition & 0 deletions src/mono/nuget/mono-packages.proj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<ProjectReference Include="Microsoft.NET.Runtime.wasm.Sample.Mono\Microsoft.NET.Runtime.wasm.Sample.Mono.pkgproj" />
<ProjectReference Include="Microsoft.NETCore.BrowserDebugHost.Transport\Microsoft.NETCore.BrowserDebugHost.Transport.pkgproj" />
<ProjectReference Include="Microsoft.NET.Runtime.WebAssembly.Sdk\Microsoft.NET.Runtime.WebAssembly.Sdk.pkgproj" />
<ProjectReference Include="..\wasm\templates\Microsoft.NET.Runtime.WebAssembly.Templates.csproj" />
</ItemGroup>

<ItemGroup Condition="'$(TargetOS)' == 'iOS' or '$(TargetOS)' == 'iOSSimulator'">
Expand Down
20 changes: 19 additions & 1 deletion src/mono/wasm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,25 @@ To build and run the samples with AOT, add `/p:RunAOTCompilation=true` to the ab

Also check [bench](../sample/wasm/browser-bench/README.md) sample to measure mono/wasm runtime performance.

### Upgrading Emscripten
## Templates

The wasm templates, located in the `templates` directory, are templates for `dotnet new`, VS and VS for Mac. They are packaged and distributed as part of the `wasm-tools` workload. We have 2 templates, `wasmbrowser` and `wasmconsole`, for browser and console WebAssembly applications.

For details about using `dotnet new` see the dotnet tool [documentation](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-new).

To test changes in the templates, use `dotnet new -i <path>`.

Example use of the `wasmconsole` template:

> dotnet new wasmconsole
> dotnet publish
> cd bin/Debug/net7.0/browser-wasm/AppBundle
> node main.cjs
mono_wasm_runtime_ready fe00e07a-5519-4dfe-b35a-f867dbaf2e28
Hello World!
Args:

## Upgrading Emscripten

Bumping Emscripten version involves these steps:

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<PackageType>Template</PackageType>
<PackageId>Microsoft.NET.Runtime.WebAssembly.Templates</PackageId>
<Title>WebAssembly Templates</Title>
<Authors>Microsoft</Authors>
<Description>Templates to create WebAssembly projects.</Description>
<PackageTags>dotnet-new;templates</PackageTags>

<TargetFramework>net6.0</TargetFramework>

<IncludeContentInPack>true</IncludeContentInPack>
<IncludeBuildOutput>false</IncludeBuildOutput>
<ContentTargetFolders>content</ContentTargetFolders>
<NoWarn>$(NoWarn);NU5128</NoWarn>
<IsPackable>true</IsPackable>
</PropertyGroup>

<ItemGroup>
<Content Include="templates\**\*" Exclude="templates\**\bin\**;templates\**\obj\**" />
<Compile Remove="**\*" />
</ItemGroup>

<Target Name="CreateManifestResourceNames" />
<Target Name="CoreCompile" />
<Target Name="_SetProductVersion" DependsOnTargets="GetProductVersions" BeforeTargets="Pack">
<PropertyGroup>
<PackageVersion>$(ProductVersion)</PackageVersion>
</PropertyGroup>
</Target>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"$schema": "http://json.schemastore.org/template",
"author": "Microsoft",
"classifications": [ "Web", "WebAssembly", "Browser" ],
"identity": "WebAssembly.Browser",
"name": "WebAssembly Browser App",
"shortName": "wasmbrowser",
"tags": {
"language": "C#",
"type": "project"
}
}
12 changes: 12 additions & 0 deletions src/mono/wasm/templates/templates/browser/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System;
using System.Runtime.CompilerServices;

Console.WriteLine ("Hello, Console!");

public class MyClass {
[MethodImpl(MethodImplOptions.NoInlining)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we still need this NoInlining ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess its purpose is to avoid inlining the method during AOT. Not sure whether anything changed recently in that area though. @lewing, do you know if we need it?

public static string CallMeFromJS()
{
return "Hello, World!";
}
}
17 changes: 17 additions & 0 deletions src/mono/wasm/templates/templates/browser/browser.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetArchitecture>wasm</TargetArchitecture>
<TargetOS>Browser</TargetOS>
<RuntimeIdentifier>browser-wasm</RuntimeIdentifier>
<UseMonoRuntime>true</UseMonoRuntime>
<WasmMainJSPath>main.js</WasmMainJSPath>
<OutputType>Exe</OutputType>
<WasmEnableES6>true</WasmEnableES6>
</PropertyGroup>

<ItemGroup>
<WasmExtraFilesToDeploy Include="index.html" />
<WasmExtraFilesToDeploy Include="main.js" />
</ItemGroup>
</Project>
19 changes: 19 additions & 0 deletions src/mono/wasm/templates/templates/browser/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<!DOCTYPE html>
<!-- Licensed to the .NET Foundation under one or more agreements. -->
<!-- The .NET Foundation licenses this file to you under the MIT license. -->
<html>

<head>
<title>Sample ES6</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="modulepreload" href="./main.js" />
<link rel="modulepreload" href="./dotnet.js" />
</head>

<body>
<span id="out"></span>
<script type='module' src="./main.js"></script>
</body>

</html>
13 changes: 13 additions & 0 deletions src/mono/wasm/templates/templates/browser/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import createDotnetRuntime from './dotnet.js'

try {
const { MONO, BINDING, Module, RuntimeBuildInfo } = await createDotnetRuntime();
const managedMethod = BINDING.bind_static_method("[browser] MyClass:CallMeFromJS");
const text = managedMethod();
document.getElementById("out").innerHTML = `${text}`;

await MONO.mono_run_main("browser.dll", []);
} catch (err) {
console.log(`WASM ERROR ${err}`);
document.getElementById("out").innerHTML = `error: ${err}`;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"$schema": "http://json.schemastore.org/template",
"author": "Microsoft",
"classifications": [ "Web", "WebAssembly", "Console" ],
"identity": "WebAssembly.Console",
"name": "WebAssembly Console App",
"shortName": "wasmconsole",
"tags": {
"language": "C#",
"type": "project"
}
}
9 changes: 9 additions & 0 deletions src/mono/wasm/templates/templates/console/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System;
using System.Threading.Tasks;

Console.WriteLine("Hello World!");

Console.WriteLine("Args:");
for (int i = 0; i < args.Length; i++) {
Console.WriteLine($" args[{i}] = {args[i]}");
}
7 changes: 7 additions & 0 deletions src/mono/wasm/templates/templates/console/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Node/CommonJS console App

Run the published application like:

node main.cjs

in `bin/$(Configuration)/net7.0/browser-wasm/AppBundle` directory.
12 changes: 12 additions & 0 deletions src/mono/wasm/templates/templates/console/console.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetArchitecture>wasm</TargetArchitecture>
<TargetOS>Browser</TargetOS>
<RuntimeIdentifier>browser-wasm</RuntimeIdentifier>
<UseMonoRuntime>true</UseMonoRuntime>
<WasmMainJSPath>main.cjs</WasmMainJSPath>
<OutputType>Exe</OutputType>
<WasmEnableES6>false</WasmEnableES6>
</PropertyGroup>
</Project>
8 changes: 8 additions & 0 deletions src/mono/wasm/templates/templates/console/main.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const createDotnetRuntime = require("./dotnet.js");

async function main() {
const { MONO } = await createDotnetRuntime();
const app_args = process.argv.slice(2);
await MONO.mono_run_main_and_exit("console.dll", app_args);
};
main();
34 changes: 25 additions & 9 deletions src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,8 @@ protected string RunAndTestWasmApp(BuildArgs buildArgs,
string? buildDir = null,
int expectedExitCode = 0,
string? args = null,
Dictionary<string, string>? envVars = null)
Dictionary<string, string>? envVars = null,
string targetFramework = "net6.0")
{
buildDir ??= _projectDir;
envVars ??= new();
Expand All @@ -144,7 +145,7 @@ protected string RunAndTestWasmApp(BuildArgs buildArgs,
envVars[kvp.Key] = kvp.Value;
}

string bundleDir = Path.Combine(GetBinDir(baseDir: buildDir, config: buildArgs.Config), "AppBundle");
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"),
Expand Down Expand Up @@ -341,8 +342,8 @@ protected static BuildArgs ExpandBuildArgs(BuildArgs buildArgs, string extraProp

if (options.ExpectSuccess)
{
string bundleDir = Path.Combine(GetBinDir(config: buildArgs.Config), "AppBundle");
AssertBasicAppBundle(bundleDir, buildArgs.ProjectName, buildArgs.Config, options.HasIcudt, options.DotnetWasmFromRuntimePack ?? !buildArgs.AOT);
string bundleDir = Path.Combine(GetBinDir(config: buildArgs.Config, targetFramework: options.TargetFramework ?? "net6.0"), "AppBundle");
AssertBasicAppBundle(bundleDir, buildArgs.ProjectName, buildArgs.Config, options.MainJS ?? "test-main.js", options.HasV8Script, options.HasIcudt, options.DotnetWasmFromRuntimePack ?? !buildArgs.AOT);
}

if (options.UseCache)
Expand Down Expand Up @@ -371,6 +372,18 @@ public void InitBlazorWasmProjectDir(string id)
File.Copy(Path.Combine(BuildEnvironment.TestDataPath, "Blazor.Directory.Build.targets"), Path.Combine(_projectDir, "Directory.Build.targets"));
}

public string CreateWasmTemplateProject(string id, string template = "wasmbrowser")
{
InitPaths(id);
InitProjectDir(id);
new DotNetCommand(s_buildEnv, useDefaultArgs: false)
.WithWorkingDirectory(_projectDir!)
.ExecuteWithCapturedOutput($"new {template}")
.EnsureSuccessful();

return Path.Combine(_projectDir!, $"{id}.csproj");
}

public string CreateBlazorWasmTemplateProject(string id)
{
InitBlazorWasmProjectDir(id);
Expand Down Expand Up @@ -476,19 +489,19 @@ static void AssertRuntimePackPath(string buildOutput)
throw new XunitException($"Runtime pack path doesn't match.{Environment.NewLine}Expected: {s_buildEnv.RuntimePackDir}{Environment.NewLine}Actual: {actualPath}");
}

protected static void AssertBasicAppBundle(string bundleDir, string projectName, string config, bool hasIcudt=true, bool dotnetWasmFromRuntimePack=true)
protected static void AssertBasicAppBundle(string bundleDir, string projectName, string config, string mainJS, bool hasV8Script, bool hasIcudt=true, bool dotnetWasmFromRuntimePack=true)
{
AssertFilesExist(bundleDir, new []
{
"index.html",
"test-main.js",
mainJS,
"dotnet.timezones.blat",
"dotnet.wasm",
"mono-config.json",
"dotnet.js",
"run-v8.sh"
"dotnet.js"
});

AssertFilesExist(bundleDir, new[] { "run-v8.sh" }, expectToExist: hasV8Script);
AssertFilesExist(bundleDir, new[] { "icudt.dat" }, expectToExist: hasIcudt);

string managedDir = Path.Combine(bundleDir, "managed");
Expand Down Expand Up @@ -866,7 +879,10 @@ public record BuildProjectOptions
bool CreateProject = true,
bool Publish = true,
bool BuildOnlyAfterPublish = true,
bool HasV8Script = true,
string? Verbosity = null,
string? Label = null
string? Label = null,
string? TargetFramework = null,
string? MainJS = null
);
}
Loading