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
39 changes: 30 additions & 9 deletions docs/guide/build-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@

Build and publish related options are configured in `.csproj` file via MSBuild properties.

| Property | Default | Description |
|-----------------------------|------------|----------------------------------------------------------------------------------------------------------------------------------|
| BootsharpName | bootsharp | Name of the generated JavaScript module. |
| BootsharpEmbedBinaries | true | Whether to embed binaries to the JavaScript module file. |
| BootsharpBundleCommand | npx rollup | The command to bundle generated JavaScrip solution. |
| BootsharpPublishDirectory | /bin | Directory to publish generated JavaScript module. |
| BootsharpTypesDirectory | /types | Directory to publish type declarations. |
| BootsharpBinariesDirectory | /bin | Directory to publish binaries when `EmbedBinaries` disabled. |
| BootsharpPackageDirectory | / | Directory to publish `package.json` file. |
| Property | Default | Description |
|----------------------------|------------|--------------------------------------------------------------|
| BootsharpName | bootsharp | Name of the generated JavaScript module. |
| BootsharpEmbedBinaries | true | Whether to embed binaries to the JavaScript module file. |
| BootsharpBundleCommand | npx rollup | The command to bundle generated JavaScrip solution. |
| BootsharpPublishDirectory | /bin | Directory to publish generated JavaScript module. |
| BootsharpTypesDirectory | /types | Directory to publish type declarations. |
| BootsharpBinariesDirectory | /bin | Directory to publish binaries when `EmbedBinaries` disabled. |
| BootsharpPackageDirectory | / | Directory to publish `package.json` file. |

Below is an example configuration, which will make Bootsharp name compiled module "backend" (instead of the default "bootsharp"), publish the module under solution directory root (instead of "/bin") and disable binaries embedding in favor of publishing them under "public/bin" directory one level above the solution root:

Expand All @@ -32,3 +32,24 @@ Below is an example configuration, which will make Bootsharp name compiled modul

</Project>
```

## Globalization

By default, Bootsharp disables .NET globalization on WASM. This keeps the published output smaller, but culture-specific formatting and culture construction will use invariant mode.

To enable globalization, explicitly disable invariant globalization in your project file:

```xml
<PropertyGroup>
<InvariantGlobalization>false</InvariantGlobalization>
</PropertyGroup>
```

When invariant globalization is disabled, Bootsharp will automatically include the ICU files emitted by the .NET WASM build and configure the runtime accordingly. This works for both embedded and sideloaded binaries.

Bootsharp supports the following globalization modes:

| Mode | How to enable | Behavior |
|---------|----------------------------------------------------------------------|-------------------------------------------------------------------------------------------|
| Sharded | Didable `InvariantGlobalization` | Publishes the default sharded ICU files (`icudt_*.dat`). |
| Full | Didable `InvariantGlobalization` and enable `WasmIncludeFullIcuData` | Publishes the full ICU data file (`icudt.dat`) and supports many cultures in one runtime. |
1 change: 1 addition & 0 deletions src/cs/Bootsharp.Publish.Test/Pack/PackTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ protected override void AddAssembly (string assemblyName, params MockSource[] so
EntryAssemblyName = "System.Runtime.dll",
BuildEngine = Engine,
EmbedBinaries = false,
Globalization = false,
Threading = false,
LLVM = false,
Debug = false
Expand Down
20 changes: 20 additions & 0 deletions src/cs/Bootsharp.Publish.Test/Pack/ResourceTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,24 @@ public void WhenDebugDisabledDebugArtifactsNotIncluded ()
DoesNotContain("""{ name: "Foo.pdb", content: undefined }""");
DoesNotContain("""{ name: "dotnet.native.js.symbols", content: undefined }""");
}

[Fact]
public void WhenGlobalizationEnabledIcuIncluded ()
{
Task.Globalization = true;
AddAssembly("Foo.dll");
Project.WriteFile("icudt.dat", "MockIcuContent");
Execute();
Contains("""{ name: "icudt.dat", content: undefined }""");
}

[Fact]
public void WhenGlobalizationDisabledIcuNotIncluded ()
{
Task.Globalization = false;
AddAssembly("Foo.dll");
Project.WriteFile("icudt.dat", "MockIcuContent");
Execute();
DoesNotContain("""{ name: "icudt.dat", content: undefined }""");
}
}
3 changes: 2 additions & 1 deletion src/cs/Bootsharp.Publish/Pack/BootsharpPack.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public sealed class BootsharpPack : Microsoft.Build.Utilities.Task
public required string InspectedDirectory { get; set; }
public required string EntryAssemblyName { get; set; }
public required bool EmbedBinaries { get; set; }
public required bool Globalization { get; set; }
public required bool Threading { get; set; }
public required bool LLVM { get; set; }
public required bool Debug { get; set; }
Expand Down Expand Up @@ -64,7 +65,7 @@ private void GenerateDeclarations (Preferences prefs, SolutionInspection inspect

private void GenerateResources (SolutionInspection inspection)
{
var generator = new ResourceGenerator(EntryAssemblyName, EmbedBinaries, Debug);
var generator = new ResourceGenerator(EntryAssemblyName, EmbedBinaries, Debug, Globalization);
var content = generator.Generate(BuildDirectory, DebugDirectory);
File.WriteAllText(Path.Combine(BuildDirectory, "resources.g.js"), content);
}
Expand Down
11 changes: 10 additions & 1 deletion src/cs/Bootsharp.Publish/Pack/ResourceGenerator.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
namespace Bootsharp.Publish;

internal sealed class ResourceGenerator (string entryAssemblyName, bool embed, bool debug)
internal sealed class ResourceGenerator (string entryAssemblyName, bool embed, bool debug, bool g11n)
{
private readonly List<string> assemblies = [];
private readonly List<string> symbols = [];
private readonly List<string> pdb = [];
private readonly List<string> icu = [];
private string wasm = null!;

public string Generate (string buildDir, string debugDir)
{
foreach (var path in Directory.GetFiles(buildDir, "*.wasm").Order())
if (path.EndsWith("dotnet.native.wasm")) wasm = BuildBin(path);
else assemblies.Add(BuildBin(path));
if (g11n)
{
foreach (var path in Directory.GetFiles(buildDir, "*.dat").Order())
icu.Add(BuildBin(path));
}
if (debug)
{
foreach (var path in Directory.GetFiles(debugDir, "*.symbols").Order())
Expand All @@ -26,6 +32,9 @@ public string Generate (string buildDir, string debugDir)
assemblies: [
{{JoinLines(assemblies, 2, ",\n")}}
],
icu: [
{{JoinLines(icu, 2, ",\n")}}
],
symbols: [
{{JoinLines(symbols, 2, ",\n")}}
],
Expand Down
42 changes: 22 additions & 20 deletions src/cs/Bootsharp/Build/Bootsharp.props
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@
<WasmEnableLegacyJsInterop>false</WasmEnableLegacyJsInterop>
<WasmEnableSIMD>true</WasmEnableSIMD>

<!-- These inflate bundle size and require additional wiring on the JS side — not supported for now. -->
<InvariantTimezone>true</InvariantTimezone>
<InvariantGlobalization>true</InvariantGlobalization>
<!-- These significantly reduce bundle size and RAM usage — enable by default. -->
<InvariantTimezone Condition="'$(InvariantTimezone)' == ''">true</InvariantTimezone>
<InvariantGlobalization Condition="'$(InvariantGlobalization)' == ''">true</InvariantGlobalization>
<PredefinedCulturesOnly Condition="'$(PredefinedCulturesOnly)' == ''">true</PredefinedCulturesOnly>

<!-- User Preferences -->
<!-- Name of the generated JavaScript module; 'bootsharp' by default. -->
Expand All @@ -36,12 +37,16 @@
<BsLlvm>!$(BsDebug)</BsLlvm>
</PropertyGroup>

<PropertyGroup Condition="$(BsLlvm)">
<!-- NativeAOT-LLVM specific knobs. -->
<DotNetJsApi>true</DotNetJsApi>
<EmccFlags>$(EmccFlags) -O3</EmccFlags>
<UsingBrowserRuntimeWorkload>false</UsingBrowserRuntimeWorkload>
<PropertyGroup Condition="$(BsDebug)">
<!-- Various knobs that improve debugging and compile times. -->
<WasmRunWasmOpt>false</WasmRunWasmOpt>
<WasmNativeStrip>false</WasmNativeStrip>
<WasmEmitSourceMap>true</WasmEmitSourceMap>
<WasmNativeDebugSymbols>true</WasmNativeDebugSymbols>
<NativeDebugSymbols>true</NativeDebugSymbols>
</PropertyGroup>

<PropertyGroup Condition="!$(BsDebug)">
<!-- Various knobs that reduce the generated bundle size or improve performance. -->
<!-- https://learn.microsoft.com/en-us/aspnet/core/blazor/performance?#minimize-app-download-size -->
<!-- https://raw.githubusercontent.com/dotnet/runtime/main/docs/workflow/trimming/feature-switches.md -->
Expand Down Expand Up @@ -71,31 +76,28 @@
<WasmNativeDebugSymbols>false</WasmNativeDebugSymbols>
<NativeDebugSymbols>false</NativeDebugSymbols>
<JsonSerializerIsReflectionEnabledByDefault>false</JsonSerializerIsReflectionEnabledByDefault>
<PredefinedCulturesOnly>true</PredefinedCulturesOnly>
<MetricsSupport>false</MetricsSupport>
<DisableDependencyInjectionDynamicEngine>true</DisableDependencyInjectionDynamicEngine>
<NullabilityInfoContextSupport>false</NullabilityInfoContextSupport>
<DynamicCodeSupport>false</DynamicCodeSupport>
<XmlResolverIsNetworkingEnabledByDefault>false</XmlResolverIsNetworkingEnabledByDefault>
</PropertyGroup>

<PropertyGroup Condition="!$(BsLlvm)">
<!-- Various knobs that improve debugging and compile times. -->
<WasmRunWasmOpt>false</WasmRunWasmOpt>
<WasmNativeStrip>false</WasmNativeStrip>
<WasmEmitSourceMap>true</WasmEmitSourceMap>
<WasmNativeDebugSymbols>true</WasmNativeDebugSymbols>
<NativeDebugSymbols>true</NativeDebugSymbols>
<PropertyGroup Condition="$(BsLlvm)">
<!-- NativeAOT-LLVM specific knobs. -->
<DotNetJsApi>true</DotNetJsApi>
<EmccFlags>$(EmccFlags) -O3</EmccFlags>
<UsingBrowserRuntimeWorkload>false</UsingBrowserRuntimeWorkload>
</PropertyGroup>

<!-- Include NativeAOT-LLVM properties when enabled. -->
<Import Project="$(MSBuildThisFileDirectory)../llvm/microsoft.dotnet.ilcompiler.llvm/build/Microsoft.DotNet.ILCompiler.LLVM.props"
Condition="$(BsLlvm)"/>

<ItemGroup>
<AssemblyAttribute Include="System.Runtime.Versioning.SupportedOSPlatform">
<_Parameter1>browser</_Parameter1> <!-- Enable browser target for the entry assembly. -->
</AssemblyAttribute>
</ItemGroup>

<!-- Include NativeAOT-LLVM properties when enabled. -->
<Import Project="$(MSBuildThisFileDirectory)../llvm/microsoft.dotnet.ilcompiler.llvm/build/Microsoft.DotNet.ILCompiler.LLVM.props"
Condition="$(BsLlvm)"/>

</Project>
8 changes: 6 additions & 2 deletions src/cs/Bootsharp/Build/Bootsharp.targets
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,8 @@
<BootsharpTypesDirectory Condition="'$(BootsharpTypesDirectory)' == ''">$(BootsharpPublishDirectory)/types</BootsharpTypesDirectory>
<BootsharpBinariesDirectory Condition="'$(BootsharpBinariesDirectory)' == ''">$(BootsharpPublishDirectory)/bin</BootsharpBinariesDirectory>
<BootsharpPackageDirectory Condition="'$(BootsharpPackageDirectory)' == ''">$(BootsharpPublishDirectory)</BootsharpPackageDirectory>
<BsThreading Condition="'$(WasmEnableThreads)' == 'true'">true</BsThreading>
<BsThreading Condition="'$(WasmEnableThreads)' != 'true'">false</BsThreading>
<BsGlobalization>$([System.String]::Equals('$(InvariantGlobalization)', 'false'))</BsGlobalization>
<BsThreading>$([System.String]::Equals('$(WasmEnableThreads)', 'true'))</BsThreading>
<BootsharpEmbedBinaries Condition="$(BsThreading)">false</BootsharpEmbedBinaries>
<BsBundleMapsArg Condition="$(BsDebug) And $(BootsharpEmbedBinaries)">--sourcemap</BsBundleMapsArg>
<BootsharpBundleCommand Condition="'$(BootsharpBundleCommand)' == ''">npx --yes rollup index.js -o index.mjs -f es -e process,module,fs/promises --output.inlineDynamicImports $(BsBundleMapsArg)</BootsharpBundleCommand>
Expand Down Expand Up @@ -123,6 +123,7 @@
InspectedDirectory="$(OutputPath)"
EntryAssemblyName="$(BsEntryAssembly)"
EmbedBinaries="$(BootsharpEmbedBinaries)"
Globalization="$(BsGlobalization)"
Threading="$(BsThreading)"
LLVM="$(BsLlvm)"
Debug="$(BsDebug)"/>
Expand All @@ -137,6 +138,7 @@
<BsModuleFiles Include="$(BsBuildDir)/*.mjs"/>
<BsWasmFiles Include="$(BsBuildDir)/*.wasm"/>
<BsTypeFiles Include="$(BsBuildDir)/*.d.ts"/>
<BsIcuFiles Include="$(BsBuildDir)/*.dat"/>
<BsDotNetFile Include="$(BsBuildDir)/dotnet.js"/>
<BsRuntimeFile Include="$(BsBuildDir)/dotnet.runtime.js"/>
<BsNativeFile Include="$(BsBuildDir)/dotnet.native.js"/>
Expand All @@ -151,6 +153,8 @@
DestinationFolder="$(BootsharpBinariesDirectory)"/>
<Copy Condition="!$(BootsharpEmbedBinaries)" SourceFiles="@(BsNativeFile)"
DestinationFolder="$(BootsharpBinariesDirectory)"/>
<Copy Condition="!$(BootsharpEmbedBinaries)" SourceFiles="@(BsIcuFiles)"
DestinationFolder="$(BootsharpBinariesDirectory)"/>
<Copy Condition="$(BsThreading)" SourceFiles="@(BsWorkerFile)"
DestinationFolder="$(BootsharpBinariesDirectory)"/>
<Copy SourceFiles="@(BsTypeFiles)"
Expand Down
2 changes: 1 addition & 1 deletion src/cs/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project>

<PropertyGroup>
<Version>0.8.0-alpha.69</Version>
<Version>0.8.0-alpha.74</Version>
<Authors>Elringus</Authors>
<PackageTags>javascript typescript ts js wasm node deno bun interop codegen</PackageTags>
<PackageProjectUrl>https://bootsharp.com</PackageProjectUrl>
Expand Down
38 changes: 16 additions & 22 deletions src/js/src/config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { RuntimeConfig, WasmAsset, ModuleAsset, AssemblyAsset, PdbAsset, SymbolsAsset, getRuntime, getNative } from "./modules";
import { RuntimeConfig, Asset, WasmAsset, ModuleAsset, AssemblyAsset, IcuAsset, PdbAsset, SymbolsAsset, getRuntime, getNative } from "./modules";
import { BinaryResource, BootResources } from "./resources";
import { decodeBase64 } from "./decoder";

Expand All @@ -8,13 +8,14 @@ import { decodeBase64 } from "./decoder";
export async function buildConfig(resources: BootResources, root?: string): Promise<RuntimeConfig> {
const embed = root == null;
const mt = !embed && (await import("./dotnet.g")).mt;
const [wasm, native, runtime, assemblies, symbols, pdb] = await Promise.all([
resolveWasm(),
const [wasm, native, runtime, assemblies, icu, symbols, pdb] = await Promise.all([
resolveAsset<WasmAsset>(resources.wasm),
resolveModule("dotnet.native.js", embed ? getNative : undefined),
resolveModule("dotnet.runtime.js", embed ? getRuntime : undefined),
Promise.all(resources.assemblies.map(resolveAssembly)),
Promise.all(resources.assemblies.map(resolveAsset<AssemblyAsset>)),
Promise.all(resources.icu.map(resolveAsset<IcuAsset>)),
Promise.all(resources.symbols.map(resolveSymbols)),
Promise.all(resources.pdb.map(resolvePdb))
Promise.all(resources.pdb.map(resolveAsset<PdbAsset>))
]);
return {
resources: {
Expand All @@ -24,17 +25,18 @@ export async function buildConfig(resources: BootResources, root?: string): Prom
jsModuleWorker: mt ? [await resolveModule("dotnet.native.worker.mjs")] : undefined,
assembly: assemblies,
wasmSymbols: symbols,
pdb: pdb
pdb: pdb,
icu: icu
},
mainAssemblyName: resources.entryAssemblyName,
globalizationMode: resolveGlobalizationMode(),
debugLevel: resources.symbols.length > 0 ? -1 : undefined
};

async function resolveWasm(): Promise<WasmAsset> {
return {
name: resources.wasm.name,
buffer: await resolveBuffer(resources.wasm)
};
function resolveGlobalizationMode(): RuntimeConfig["globalizationMode"] {
if (resources.icu.length === 0) return <never>"invariant";
if (resources.icu.some(res => res.name === "icudt.dat")) return <never>"all";
return <never>"sharded";
}

async function resolveModule(name: string, embed?: () => Promise<unknown>): Promise<ModuleAsset> {
Expand All @@ -44,24 +46,16 @@ export async function buildConfig(resources: BootResources, root?: string): Prom
};
}

async function resolveAssembly(res: BinaryResource): Promise<AssemblyAsset> {
return {
name: res.name,
virtualPath: res.name,
buffer: await resolveBuffer(res)
};
}

async function resolvePdb(res: BinaryResource): Promise<PdbAsset> {
return {
async function resolveAsset<T extends Asset>(res: BinaryResource): Promise<T> {
return <T><Asset>{
name: res.name,
virtualPath: res.name,
buffer: await resolveBuffer(res)
};
}

async function resolveSymbols(res: BinaryResource): Promise<SymbolsAsset> {
// Use buffer similar to the other assets once https://github.com/dotnet/runtime/pull/127087 is merged.
// Use 'resolveAsset<SymbolsAsset>()' once https://github.com/dotnet/runtime/pull/127087 is merged.
const txt = new TextDecoder("utf-8").decode(await resolveBuffer(res));
return {
name: res.name,
Expand Down
2 changes: 1 addition & 1 deletion src/js/src/dotnet.g.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ interface Assets {
coreVfs?: VfsAsset[];
vfs?: VfsAsset[];
}
type Asset = {
export type Asset = {
/**
* this should be absolute url to the asset
*/
Expand Down
2 changes: 2 additions & 0 deletions src/js/src/modules.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import type { ModuleAPI, MonoConfig } from "./dotnet.g.d.ts";
export type { Asset } from "./dotnet.g.d.ts";

export type * from "./dotnet.g.d.ts";
export type RuntimeConfig = MonoConfig;
export type RuntimeResources = NonNullable<RuntimeConfig["resources"]>;
export type WasmAsset = NonNullable<RuntimeResources["wasmNative"]>[number];
export type ModuleAsset = NonNullable<RuntimeResources["jsModuleNative"]>[number];
export type AssemblyAsset = NonNullable<RuntimeResources["assembly"]>[number];
export type IcuAsset = NonNullable<RuntimeResources["icu"]>[number];
export type PdbAsset = NonNullable<RuntimeResources["pdb"]>[number];
export type SymbolsAsset = NonNullable<RuntimeResources["wasmSymbols"]>[number];

Expand Down
2 changes: 2 additions & 0 deletions src/js/src/resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ export type BootResources = {
readonly wasm: BinaryResource;
/** Compiled .NET assemblies. */
readonly assemblies: BinaryResource[];
/** Globalization data. */
readonly icu: BinaryResource[];
/** WASM debug symbols. */
readonly symbols: BinaryResource[];
/** PDB debug artifacts. */
Expand Down
Loading
Loading