Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[wasm] Introduce <InvariantTimezone> build flag #87284

Merged
merged 10 commits into from
Jul 1, 2023
Merged
Show file tree
Hide file tree
Changes from 6 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
12 changes: 12 additions & 0 deletions docs/design/features/timezone-invariant-mode.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Timezone Invariant Mode

Author: [Pavel Savara](https://github.com/pavelsavara)

It's currently only available for Browser OS
Copy link
Member

Choose a reason for hiding this comment

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

Maybe specifically call out that it strips the tz data from the binary


- you enable it in project file:
```xml
<PropertyGroup>
<InvariantTimezone>true</InvariantTimezone>
</PropertyGroup>
```
1 change: 1 addition & 0 deletions eng/testing/scenarios/BuildWasmAppsJobsList.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Wasm.Build.Tests.ConfigSrcTests
Wasm.Build.Tests.IcuShardingTests
Wasm.Build.Tests.HybridGlobalizationTests
Wasm.Build.Tests.InvariantGlobalizationTests
Wasm.Build.Tests.InvariantTimezoneTests
Wasm.Build.Tests.MainWithArgsTests
Wasm.Build.Tests.NativeBuildTests
Wasm.Build.Tests.NativeLibraryTests
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ Copyright (c) .NET Foundation. All rights reserved.

<!-- Runtime feature defaults to trim unnecessary code -->
<InvariantGlobalization Condition="'$(InvariantGlobalization)' == ''">false</InvariantGlobalization>
<InvariantTimezone Condition="'$(BlazorEnableTimeZoneSupport)' == 'false'">true</InvariantTimezone>
pavelsavara marked this conversation as resolved.
Show resolved Hide resolved
<InvariantTimezone Condition="'$(InvariantTimezone)' == ''">false</InvariantTimezone>
<EventSourceSupport Condition="'$(EventSourceSupport)' == ''">false</EventSourceSupport>
<UseSystemResourceKeys Condition="'$(UseSystemResourceKeys)' == ''">true</UseSystemResourceKeys>
<EnableUnsafeUTF7Encoding Condition="'$(EnableUnsafeUTF7Encoding)' == ''">false</EnableUnsafeUTF7Encoding>
Expand Down
20 changes: 20 additions & 0 deletions src/mono/sample/wasm/browser-advanced/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,26 @@ public partial class Test
public static int Main(string[] args)
{
Console.WriteLine ("Hello, World!");

var start = DateTime.UtcNow;
var timezonesCount = TimeZoneInfo.GetSystemTimeZones().Count;
var end = DateTime.UtcNow;
Console.WriteLine($"Found {timezonesCount} timezones in the TZ database in {end-start}");

TimeZoneInfo utc = TimeZoneInfo.FindSystemTimeZoneById("UTC");
Console.WriteLine($"{utc.DisplayName} BaseUtcOffset is {utc.BaseUtcOffset}");

try
{
TimeZoneInfo tst = TimeZoneInfo.FindSystemTimeZoneById("Asia/Tokyo");
Console.WriteLine($"{tst.DisplayName} BaseUtcOffset is {tst.BaseUtcOffset}");
}
catch (TimeZoneNotFoundException tznfe)
{
Console.WriteLine($"Could not find Asia/Tokyo: {tznfe.Message}");
}


return 0;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<PropertyGroup>
<EnableAggressiveTrimming>true</EnableAggressiveTrimming>
<PublishTrimmed>true</PublishTrimmed>
<InvariantTimezone>true</InvariantTimezone>
<WasmEnableLegacyJsInterop>false</WasmEnableLegacyJsInterop>
<WasmEnableWebcil>false</WasmEnableWebcil>
<WasmEmitSymbolMap>true</WasmEmitSymbolMap>
Expand Down
34 changes: 28 additions & 6 deletions src/mono/wasi/Wasi.Build.Tests/WasiTemplateTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,19 +74,19 @@ public void ConsoleBuildThenPublish(string config)
UseCache: false));
}

public static TheoryData<string, bool> TestDataForConsolePublishAndRun()
public static TheoryData<string, bool, bool> TestDataForConsolePublishAndRun()
{
var data = new TheoryData<string, bool>();
data.Add("Debug", false);
data.Add("Debug", true);
data.Add("Release", false); // Release relinks by default
var data = new TheoryData<string, bool, bool>();
data.Add("Debug", false, false);
data.Add("Debug", true, true);
data.Add("Release", false, false); // Release relinks by default
return data;
}

[ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
[ActiveIssue("https://github.com/dotnet/runtime/issues/82515", TestPlatforms.Windows)]
[MemberData(nameof(TestDataForConsolePublishAndRun))]
public void ConsolePublishAndRunForSingleFileBundle(string config, bool relinking)
public void ConsolePublishAndRunForSingleFileBundle(string config, bool relinking, bool invariantTimezone)
{
string id = $"{config}_{Path.GetRandomFileName()}";
string projectFile = CreateWasmTemplateProject(id, "wasiconsole");
Expand All @@ -96,6 +96,8 @@ public void ConsolePublishAndRunForSingleFileBundle(string config, bool relinkin
string extraProperties = "<WasmSingleFileBundle>true</WasmSingleFileBundle>";
if (relinking)
extraProperties += "<WasmBuildNative>true</WasmBuildNative>";
if (invariantTimezone)
extraProperties += "<InvariantTimezone>true</InvariantTimezone>";

AddItemsPropertiesToProject(projectFile, extraProperties);

Expand Down Expand Up @@ -125,6 +127,15 @@ public void ConsolePublishAndRunForSingleFileBundle(string config, bool relinkin
Assert.Contains("args[0] = x", res.Output);
Assert.Contains("args[1] = y", res.Output);
Assert.Contains("args[2] = z", res.Output);
if(invariantTimezone)
{
Assert.Contains("Could not find Asia/Tokyo", res.Output);
}
else
{
Assert.Contains("Asia/Tokyo BaseUtcOffset is 09:00:00", res.Output);
}

}

private static readonly string s_simpleMainWithArgs = """
Expand All @@ -133,6 +144,17 @@ public void ConsolePublishAndRunForSingleFileBundle(string config, bool relinkin
Console.WriteLine("Hello, Wasi Console!");
for (int i = 0; i < args.Length; i ++)
Console.WriteLine($"args[{i}] = {args[i]}");

try
{
TimeZoneInfo tst = TimeZoneInfo.FindSystemTimeZoneById(""Asia/Tokyo"");
Console.WriteLine($""{tst.DisplayName} BaseUtcOffset is {tst.BaseUtcOffset}"");
}
catch (TimeZoneNotFoundException tznfe)
{
Console.WriteLine($""Could not find Asia/Tokyo: {tznfe.Message}"");
}

return 42;
""";
}
3 changes: 2 additions & 1 deletion src/mono/wasi/build/WasiApp.Native.targets
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,9 @@

<ItemGroup>
<_WasmCommonCFlags Include="-DGEN_PINVOKE=1" />
<_WasmCommonCFlags Condition="'$(WasmSingleFileBundle)' == 'true'" Include="-DWASM_SINGLE_FILE=1" />
<_WasmCommonCFlags Condition="'$(WasmSingleFileBundle)' == 'true'" Include="-DWASM_SINGLE_FILE=1" />
<_WasmCommonCFlags Condition="'$(InvariantGlobalization)' == 'true'" Include="-DINVARIANT_GLOBALIZATION=1" />
<_WasmCommonCFlags Condition="'$(InvariantTimezone)' == 'true'" Include="-DINVARIANT_TIMEZONE=1" />

<!-- Adding optimization flag at the top, so it gets precedence -->
<!--<_EmccCFlags Include="$(EmccCompileOptimizationFlag)" />-->
Expand Down
1 change: 1 addition & 0 deletions src/mono/wasi/build/WasiApp.targets
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
- $(WasmProfilers) - Profilers to use
- $(AOTProfilePath) - profile data file to be used for profile-guided optimization
- $(InvariantGlobalization) - Whenever to disable ICU. Defaults to false.
- $(InvariantTimezone) - Whenever to disable Timezone database. Defaults to false.

- $(WasmResolveAssembliesBeforeBuild) - Resolve the assembly dependencies. Defaults to false
- $(WasmAssemblySearchPaths) - used for resolving assembly dependencies
Expand Down
4 changes: 4 additions & 0 deletions src/mono/wasi/runtime/driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ int32_t monoeg_g_hasenv(const char *variable);
void mono_free (void*);
int32_t mini_parse_debug_option (const char *option);
char *mono_method_get_full_name (MonoMethod *method);
#ifndef INVARIANT_TIMEZONE
pavelsavara marked this conversation as resolved.
Show resolved Hide resolved
extern void mono_register_timezones_bundle (void);
#endif /* INVARIANT_TIMEZONE */
#ifdef WASM_SINGLE_FILE
extern void mono_register_assemblies_bundle (void);
#ifndef INVARIANT_GLOBALIZATION
Expand Down Expand Up @@ -439,7 +441,9 @@ mono_wasm_load_runtime (const char *unused, int debug_level)

mini_parse_debug_option ("top-runtime-invoke-unhandled");

#ifndef INVARIANT_TIMEZONE
mono_register_timezones_bundle ();
#endif /* INVARIANT_TIMEZONE */
#ifdef WASM_SINGLE_FILE
mono_register_assemblies_bundle ();
#endif
Expand Down
2 changes: 1 addition & 1 deletion src/mono/wasi/wasi.proj
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
</Target>

<UsingTask TaskName="EmitBundleObjectFiles" AssemblyFile="$(MonoTargetsTasksAssemblyPath)" />
<Target Name="GenerateTimezonesArchive" Returns="@(_WasmArchivedTimezones)">
<Target Name="GenerateTimezonesArchive" Returns="@(_WasmArchivedTimezones)" Condition="'$(InvariantTimezone)' != 'true'">
<PropertyGroup>
<_WasmTimezonesPath>$([MSBuild]::NormalizePath('$(PkgSystem_Runtime_TimeZoneData)', 'contentFiles', 'any', 'any', 'data'))</_WasmTimezonesPath>
<_WasmTimezonesBundleObjectFile>wasm-bundled-timezones.o</_WasmTimezonesBundleObjectFile>
Expand Down
21 changes: 1 addition & 20 deletions src/mono/wasm/Wasm.Build.Tests/InvariantGlobalizationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,29 +54,10 @@ private void TestInvariantGlobalization(BuildArgs buildArgs, bool? invariantGlob
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),
InitProject: () => File.Copy(Path.Combine(BuildEnvironment.TestAssetsPath, "Wasm.Buid.Tests.Programs", "InvariantGlobalization.cs"), Path.Combine(_projectDir!, "Program.cs")),
DotnetWasmFromRuntimePack: dotnetWasmFromRuntimePack,
GlobalizationMode: invariantGlobalization == true ? GlobalizationMode.Invariant : null));

Expand Down
73 changes: 73 additions & 0 deletions src/mono/wasm/Wasm.Build.Tests/InvariantTimezoneTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// 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 Xunit;
using Xunit.Abstractions;

#nullable enable

namespace Wasm.Build.Tests
{
public class InvariantTimezoneTests : BuildTestBase
{
public InvariantTimezoneTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext)
: base(output, buildContext)
{
}

public static IEnumerable<object?[]> InvariantTimezoneTestData(bool aot, RunHost host)
=> ConfigWithAOTData(aot)
.Multiply(
new object?[] { null },
new object?[] { false },
new object?[] { true })
.WithRunHosts(host)
.UnwrapItemsAsArrays();

[Theory]
[MemberData(nameof(InvariantTimezoneTestData), parameters: new object[] { /*aot*/ false, RunHost.All })]
[MemberData(nameof(InvariantTimezoneTestData), parameters: new object[] { /*aot*/ true, RunHost.All })]
public void AOT_InvariantTimezone(BuildArgs buildArgs, bool? invariantTimezone, RunHost host, string id)
=> TestInvariantTimezone(buildArgs, invariantTimezone, host, id);

[Theory]
[MemberData(nameof(InvariantTimezoneTestData), parameters: new object[] { /*aot*/ false, RunHost.All })]
public void RelinkingWithoutAOT(BuildArgs buildArgs, bool? invariantTimezone, RunHost host, string id)
=> TestInvariantTimezone(buildArgs, invariantTimezone, host, id,
extraProperties: "<WasmBuildNative>true</WasmBuildNative>",
dotnetWasmFromRuntimePack: false);

private void TestInvariantTimezone(BuildArgs buildArgs, bool? invariantTimezone,
RunHost host, string id, string extraProperties="", bool? dotnetWasmFromRuntimePack=null)
{
string projectName = $"invariant_{invariantTimezone?.ToString() ?? "unset"}";
pavelsavara marked this conversation as resolved.
Show resolved Hide resolved
if (invariantTimezone != null)
extraProperties = $"{extraProperties}<InvariantTimezone>{invariantTimezone}</InvariantTimezone>";

buildArgs = buildArgs with { ProjectName = projectName };
buildArgs = ExpandBuildArgs(buildArgs, extraProperties);

if (dotnetWasmFromRuntimePack == null)
dotnetWasmFromRuntimePack = !(buildArgs.AOT || buildArgs.Config == "Release");

BuildProject(buildArgs,
id: id,
new BuildProjectOptions(
InitProject: () => File.Copy(Path.Combine(BuildEnvironment.TestAssetsPath, "Wasm.Buid.Tests.Programs", "InvariantTimezone.cs"), Path.Combine(_projectDir!, "Program.cs")),
DotnetWasmFromRuntimePack: dotnetWasmFromRuntimePack));

string output = RunAndTestWasmApp(buildArgs, expectedExitCode: 42, host: host, id: id);
Assert.Contains("UTC BaseUtcOffset is 0", output);
if (invariantTimezone == true)
{
Assert.Contains("Could not find Asia/Tokyo", output);
}
else
{
Assert.Contains("Asia/Tokyo BaseUtcOffset is 09:00:00", output);
}
}
}
}
1 change: 1 addition & 0 deletions src/mono/wasm/build/WasmApp.Native.targets
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@
<_EmccCFlags Include="-DENABLE_AOT=1" Condition="'$(_WasmShouldAOT)' == 'true'" />
<_EmccCFlags Include="-DDRIVER_GEN=1" Condition="'$(_WasmShouldAOT)' == 'true'" />
<_EmccCFlags Include="-DINVARIANT_GLOBALIZATION=1" Condition="'$(InvariantGlobalization)' == 'true'" />
<_EmccCFlags Include="-DINVARIANT_TIMEZONE=1" Condition="'$(InvariantTimezone)' == 'true'" />
<_EmccCFlags Include="-DLINK_ICALLS=1" Condition="'$(WasmLinkIcalls)' == 'true'" />
<_EmccCFlags Include="-DENABLE_AOT_PROFILER=1" Condition="$(WasmProfilers.Contains('aot'))" />
<_EmccCFlags Include="-DENABLE_BROWSER_PROFILER=1" Condition="$(WasmProfilers.Contains('browser'))" />
Expand Down
1 change: 1 addition & 0 deletions src/mono/wasm/build/WasmApp.targets
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
- $(WasmProfilers) - Profilers to use
- $(AOTProfilePath) - profile data file to be used for profile-guided optimization
- $(InvariantGlobalization) - Whenever to disable ICU. Defaults to false.
- $(InvariantTimezone) - Whenever to disable Timezone database. Defaults to false.
pavelsavara marked this conversation as resolved.
Show resolved Hide resolved
- $(HybridGlobalization) - Whenever to enable reduced ICU + native platform functions. Defaults to false and can be set only for InvariantGlobalization=false, WasmIncludeFullIcuData=false and empty WasmIcuDataFileName.

- $(WasmResolveAssembliesBeforeBuild) - Resolve the assembly dependencies. Defaults to false
Expand Down
4 changes: 4 additions & 0 deletions src/mono/wasm/runtime/driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ int monoeg_g_setenv(const char *variable, const char *value, int overwrite);
int32_t mini_parse_debug_option (const char *option);
char *mono_method_get_full_name (MonoMethod *method);

#ifndef INVARIANT_TIMEZONE
extern void mono_register_timezones_bundle (void);
#endif /* INVARIANT_TIMEZONE */
extern void mono_wasm_set_entrypoint_breakpoint (const char* assembly_name, int method_token);
static void mono_wasm_init_finalizer_thread (void);

Expand Down Expand Up @@ -473,7 +475,9 @@ mono_wasm_load_runtime (const char *unused, int debug_level)

mini_parse_debug_option ("top-runtime-invoke-unhandled");

#ifndef INVARIANT_TIMEZONE
mono_register_timezones_bundle ();
#endif /* INVARIANT_TIMEZONE */
mono_dl_fallback_register (wasm_dl_load, wasm_dl_symbol, NULL, NULL);
mono_wasm_install_get_native_to_interp_tramp (get_native_to_interp);

Expand Down
2 changes: 1 addition & 1 deletion src/mono/wasm/runtime/es6/dotnet.es6.lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ linked_functions = [...linked_functions,
"mono_wasm_install_js_worker_interop",
"mono_wasm_uninstall_js_worker_interop",
]
#endif

if (ENABLE_AOT_PROFILER) {
linked_functions = [...linked_functions,
Expand All @@ -153,7 +154,6 @@ if (ENABLE_AOT_PROFILER) {
]
}

#endif
if (!DISABLE_LEGACY_JS_INTEROP) {
linked_functions = [...linked_functions,
"mono_wasm_invoke_js_with_args_ref",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
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;
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System;

// https://github.com/dotnet/runtime/blob/main/docs/design/features/timezone-invariant-mode.md

var timezonesCount = TimeZoneInfo.GetSystemTimeZones().Count;
Console.WriteLine($"Found {timezonesCount} timezones in the TZ database");

TimeZoneInfo utc = TimeZoneInfo.FindSystemTimeZoneById("UTC");
Console.WriteLine($"{utc.DisplayName} BaseUtcOffset is {utc.BaseUtcOffset}");

try
{
TimeZoneInfo tst = TimeZoneInfo.FindSystemTimeZoneById("Asia/Tokyo");
Console.WriteLine($"{tst.DisplayName} BaseUtcOffset is {tst.BaseUtcOffset}");
}
catch (TimeZoneNotFoundException tznfe)
{
Console.WriteLine($"Could not find Asia/Tokyo: {tznfe.Message}");
}

return 42;
2 changes: 1 addition & 1 deletion src/mono/wasm/wasm.proj
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
</Target>

<UsingTask TaskName="EmitBundleSourceFiles" AssemblyFile="$(MonoTargetsTasksAssemblyPath)" />
<Target Name="GenerateTimezonesArchive" Returns="@(_WasmArchivedTimezones)">
<Target Name="GenerateTimezonesArchive" Returns="@(_WasmArchivedTimezones)" Condition="'$(InvariantTimezone)' != 'true'">
<PropertyGroup>
<_WasmTimezonesPath>$([MSBuild]::NormalizePath('$(PkgSystem_Runtime_TimeZoneData)', 'contentFiles', 'any', 'any', 'data'))</_WasmTimezonesPath>
<_WasmTimezonesBundleSourceFile>wasm-bundled-timezones.c</_WasmTimezonesBundleSourceFile>
Expand Down