[wasm] Introduce <InvariantTimezone> build flag (#87284)
authorPavel Savara <pavel.savara@gmail.com>
Sat, 1 Jul 2023 07:50:21 +0000 (09:50 +0200)
committerGitHub <noreply@github.com>
Sat, 1 Jul 2023 07:50:21 +0000 (09:50 +0200)
19 files changed:
docs/design/features/timezone-invariant-mode.md [new file with mode: 0644]
eng/testing/scenarios/BuildWasmAppsJobsList.txt
src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets
src/mono/sample/wasm/browser-advanced/Program.cs
src/mono/sample/wasm/browser-advanced/Wasm.Advanced.Sample.csproj
src/mono/wasi/Wasi.Build.Tests/WasiTemplateTests.cs
src/mono/wasi/build/WasiApp.Native.targets
src/mono/wasi/build/WasiApp.targets
src/mono/wasi/runtime/driver.c
src/mono/wasi/wasi.proj
src/mono/wasm/Wasm.Build.Tests/InvariantGlobalizationTests.cs
src/mono/wasm/Wasm.Build.Tests/InvariantTimezoneTests.cs [new file with mode: 0644]
src/mono/wasm/build/WasmApp.Native.targets
src/mono/wasm/build/WasmApp.targets
src/mono/wasm/runtime/driver.c
src/mono/wasm/runtime/es6/dotnet.es6.lib.js
src/mono/wasm/testassets/Wasm.Buid.Tests.Programs/InvariantGlobalization.cs [new file with mode: 0644]
src/mono/wasm/testassets/Wasm.Buid.Tests.Programs/InvariantTimezone.cs [new file with mode: 0644]
src/mono/wasm/wasm.proj

diff --git a/docs/design/features/timezone-invariant-mode.md b/docs/design/features/timezone-invariant-mode.md
new file mode 100644 (file)
index 0000000..4fc3069
--- /dev/null
@@ -0,0 +1,16 @@
+# Timezone Invariant Mode
+
+Author: [Pavel Savara](https://github.com/pavelsavara)
+
+It's currently only available for Browser OS.
+The timezone database is not part of the browser environment (as opposed to other operating systems).
+Therefore dotnet bundles the timezone database as binary as part of the runtime.
+That makes download size larger and application startup slower.
+If your application doesn't need to work with time zone information, you could use this feature to make the runtime about 200KB smaller.
+
+You enable it in project file:
+    ```xml
+    <PropertyGroup>
+        <InvariantTimezone>true</InvariantTimezone>
+    </PropertyGroup>
+    ```
index e84e70c..64f6abf 100644 (file)
@@ -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
index 4186a7a..cdcf90d 100644 (file)
@@ -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>
+    <InvariantTimezone Condition="'$(InvariantTimezone)' == ''">false</InvariantTimezone>
     <EventSourceSupport Condition="'$(EventSourceSupport)' == ''">false</EventSourceSupport>
     <UseSystemResourceKeys Condition="'$(UseSystemResourceKeys)' == ''">true</UseSystemResourceKeys>
     <EnableUnsafeUTF7Encoding Condition="'$(EnableUnsafeUTF7Encoding)' == ''">false</EnableUnsafeUTF7Encoding>
index af6ca78..bec9cb3 100644 (file)
@@ -13,6 +13,26 @@ namespace Sample
         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;
         }
 
index 6875be3..fee0941 100644 (file)
@@ -3,6 +3,7 @@
   <PropertyGroup>
     <EnableAggressiveTrimming>true</EnableAggressiveTrimming>
     <PublishTrimmed>true</PublishTrimmed>
+    <InvariantTimezone>true</InvariantTimezone>
     <WasmEnableLegacyJsInterop>false</WasmEnableLegacyJsInterop>
     <WasmEnableWebcil>false</WasmEnableWebcil>
     <WasmEmitSymbolMap>true</WasmEmitSymbolMap>
index cad7521..196d930 100644 (file)
@@ -74,19 +74,19 @@ public class WasiTemplateTests : BuildTestBase
                         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");
@@ -96,6 +96,8 @@ public class WasiTemplateTests : BuildTestBase
         string extraProperties = "<WasmSingleFileBundle>true</WasmSingleFileBundle>";
         if (relinking)
             extraProperties += "<WasmBuildNative>true</WasmBuildNative>";
+        if (invariantTimezone)
+            extraProperties += "<InvariantTimezone>true</InvariantTimezone>";
 
         AddItemsPropertiesToProject(projectFile, extraProperties);
 
@@ -125,6 +127,15 @@ public class WasiTemplateTests : BuildTestBase
         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 = """
@@ -133,6 +144,17 @@ public class WasiTemplateTests : BuildTestBase
         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;
         """;
 }
index e0e43c0..bcc9931 100644 (file)
@@ -63,6 +63,8 @@
       <WasmBuildNative Condition="'$(RunAOTCompilation)' == 'true' and '$(RunAOTCompilationAfterBuild)' == 'true'">true</WasmBuildNative>
 
       <WasmBuildNative Condition="'$(WasmBuildNative)' == '' and '$(WasmEnableSIMD)' == 'true'">true</WasmBuildNative>
+      <WasmBuildNative Condition="'$(WasmBuildNative)' == '' and '$(InvariantTimezone)' == 'true'">true</WasmBuildNative>
+      <WasmBuildNative Condition="'$(WasmBuildNative)' == '' and '$(InvariantGlobalization)' == 'true'">true</WasmBuildNative>
 
       <WasmBuildNative Condition="'$(WasmBuildNative)' == '' and @(NativeFileReference->Count()) > 0" >true</WasmBuildNative>
 
@@ -75,6 +77,8 @@
       <WasmBuildNative Condition="'$(RunAOTCompilation)' == 'true'">true</WasmBuildNative>
       <WasmBuildNative Condition="'$(WasmBuildNative)' == '' and @(NativeFileReference->Count()) > 0" >true</WasmBuildNative>
       <WasmBuildNative Condition="'$(WasmBuildNative)' == '' and '$(WasmEnableSIMD)' == 'true'">true</WasmBuildNative>
+      <WasmBuildNative Condition="'$(WasmBuildNative)' == '' and '$(InvariantTimezone)' == 'true'">true</WasmBuildNative>
+      <WasmBuildNative Condition="'$(WasmBuildNative)' == '' and '$(InvariantGlobalization)' == 'true'">true</WasmBuildNative>
 
       <!-- not aot, not trimmed app, no reason to relink -->
       <WasmBuildNative Condition="'$(WasmBuildNative)' == '' and '$(PublishTrimmed)' != 'true'">false</WasmBuildNative>
 
     <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)" />-->
       <!--<_WasmNativeFileForLinking Include="%(_BitcodeFile.ObjectFile)" />-->
       <!--<_WasmNativeFileForLinking Include="%(_WasmSourceFileToCompile.ObjectFile)" />-->
       <_MonoRuntimeComponentDontLink Include="libmono-component-diagnostics_tracing-static.a" />
+      <_MonoRuntimeComponentDontLink Include="wasm-bundled-timezones.a" Condition="'$(InvariantTimezone)' == 'true'"/>
 
       <_WasmNativeFileForLinking
           Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)*.a"
index c39ec3a..8909c89 100644 (file)
@@ -11,9 +11,9 @@
       Public properties (optional):
       - $(WasmAppDir)        - AppBundle dir (Defaults to `$(OutputPath)\$(Configuration)\AppBundle`)
       - $(WasmMainAssemblyFileName)- Defaults to $(TargetFileName)
-      - $(WasmBuildNative)   - Whenever to build the native executable. Defaults to false.
-      - $(WasmNativeStrip)   - Whenever to strip the native executable. Defaults to true.
-      - $(WasmLinkIcalls)    - Whenever to link out unused icalls. Defaults to $(WasmBuildNative).
+      - $(WasmBuildNative)   - Whether to build the native executable. Defaults to false.
+      - $(WasmNativeStrip)   - Whether to strip the native executable. Defaults to true.
+      - $(WasmLinkIcalls)    - Whether to link out unused icalls. Defaults to $(WasmBuildNative).
       - $(RunAOTCompilation) - Defaults to false.
 
       - $(WasmDebugLevel)
       - $(WasmNativeDebugSymbols) - Build with native debug symbols, useful only with `$(RunAOTCompilation)`, or `$(WasmBuildNative)`
                                     Defaults to true.
       - $(WasmEmitSymbolMap)      - Generates a `dotnet.js.symbols` file with a map of wasm function number to name.
-      - $(WasmDedup)         - Whenever to dedup generic instances when using AOT. Defaults to true.
+      - $(WasmDedup)         - Whether to dedup generic instances when using AOT. Defaults to true.
 
       - $(WasmProfilers)     - Profilers to use
       - $(AOTProfilePath)    - profile data file to be used for profile-guided optimization
-      - $(InvariantGlobalization)       - Whenever to disable ICU. Defaults to false.
+      - $(InvariantGlobalization)       - Whether to disable ICU. Defaults to false.
+      - $(InvariantTimezone)            - Whether to disable Timezone database. Defaults to false.
 
       - $(WasmResolveAssembliesBeforeBuild) - Resolve the assembly dependencies. Defaults to false
       - $(WasmAssemblySearchPaths)          - used for resolving assembly dependencies
     <WasmSingleFileBundle Condition="'$(WasmSingleFileBundle)' == ''">false</WasmSingleFileBundle>
     <WasmBuildNative Condition="'$(WasmBuildNative)' == '' and '$(WasmSingleFileBundle)' == 'true'">true</WasmBuildNative>
     <WasmBuildNative Condition="'$(WasmBuildNative)' == '' and '$(InvariantGlobalization)' == 'true'">true</WasmBuildNative>
+    <WasmBuildNative Condition="'$(WasmBuildNative)' == '' and '$(InvariantTimezone)' == 'true'">true</WasmBuildNative>
 
     <WasiBundleAssemblies Condition="'$(WasiBundleAssemblies)' == ''">true</WasiBundleAssemblies>
     <WasiRunner Condition="'$(WasiRunner)' == ''">wasmtime</WasiRunner>
index d6a51f9..98fbe1f 100644 (file)
@@ -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
 extern void mono_register_timezones_bundle (void);
+#endif /* INVARIANT_TIMEZONE */
 #ifdef WASM_SINGLE_FILE
 extern void mono_register_assemblies_bundle (void);
 #ifndef INVARIANT_GLOBALIZATION
@@ -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
index fbf434c..d601f2c 100644 (file)
@@ -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>
index 41dac50..ea5a045 100644 (file)
@@ -54,29 +54,10 @@ namespace Wasm.Build.Tests
             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));
 
diff --git a/src/mono/wasm/Wasm.Build.Tests/InvariantTimezoneTests.cs b/src/mono/wasm/Wasm.Build.Tests/InvariantTimezoneTests.cs
new file mode 100644 (file)
index 0000000..f89e04d
--- /dev/null
@@ -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"}";
+            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);
+            }
+        }
+    }
+}
index 310d77c..3473661 100644 (file)
       <!-- removing legacy interop needs relink -->
       <WasmBuildNative Condition="'$(WasmBuildNative)' == '' and '$(WasmEnableLegacyJsInterop)' == 'false'" >true</WasmBuildNative>
 
+      <!-- InvariantTimezone and InvariantGlobalization need rebuild -->
+      <WasmBuildNative Condition="'$(WasmBuildNative)' == '' and '$(InvariantTimezone)' == 'true'">true</WasmBuildNative>
+      <WasmBuildNative Condition="'$(WasmBuildNative)' == '' and '$(InvariantGlobalization)' == 'true'">true</WasmBuildNative>
+
       <WasmBuildNative Condition="'$(WasmBuildNative)' == '' and @(NativeFileReference->Count()) > 0" >true</WasmBuildNative>
 
       <WasmBuildNative Condition="'$(WasmBuildNative)' == ''">false</WasmBuildNative>
       <!-- removing legacy interop needs relink -->
       <WasmBuildNative Condition="'$(WasmBuildNative)' == '' and '$(WasmEnableLegacyJsInterop)' == 'false'" >true</WasmBuildNative>
 
+      <!-- InvariantTimezone and InvariantGlobalization need rebuild -->
+      <WasmBuildNative Condition="'$(WasmBuildNative)' == '' and '$(InvariantTimezone)' == 'true'">true</WasmBuildNative>
+      <WasmBuildNative Condition="'$(WasmBuildNative)' == '' and '$(InvariantGlobalization)' == 'true'">true</WasmBuildNative>
+
       <!-- not aot, not trimmed app, no reason to relink -->
       <WasmBuildNative Condition="'$(WasmBuildNative)' == '' and '$(PublishTrimmed)' != 'true'">false</WasmBuildNative>
 
       <_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'))" />
index 68f6877..ba18198 100644 (file)
@@ -10,9 +10,9 @@
       Public properties (optional):
       - $(WasmAppDir)        - AppBundle dir (Defaults to `$(OutputPath)\$(Configuration)\AppBundle`)
       - $(WasmMainAssemblyFileName)- Defaults to $(TargetFileName)
-      - $(WasmBuildNative)   - Whenever to build the native executable. Defaults to false.
-      - $(WasmNativeStrip)   - Whenever to strip the native executable. Defaults to true.
-      - $(WasmLinkIcalls)    - Whenever to link out unused icalls. Defaults to $(WasmBuildNative).
+      - $(WasmBuildNative)   - Whether to build the native executable. Defaults to false.
+      - $(WasmNativeStrip)   - Whether to strip the native executable. Defaults to true.
+      - $(WasmLinkIcalls)    - Whether to link out unused icalls. Defaults to $(WasmBuildNative).
       - $(RunAOTCompilation) - Defaults to false.
 
       - $(WasmDebugLevel)
                                     Defaults to true.
       - $(WasmEmitSymbolMap)      - Generates a `dotnet.native.js.symbols` file with a map of wasm function number to name.
       - $(WasmEmitSourceMap)      - Generates `dotnet.runtime.js.map` and `dotnet.js.map` files with a TypeScript source map.
-      - $(WasmDedup)         - Whenever to dedup generic instances when using AOT. Defaults to true.
+      - $(WasmDedup)         - Whether to dedup generic instances when using AOT. Defaults to true.
 
       - $(WasmProfilers)     - Profilers to use
       - $(AOTProfilePath)    - profile data file to be used for profile-guided optimization
-      - $(InvariantGlobalization)       - Whenever to disable ICU. Defaults to false.
-      - $(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.
+      - $(InvariantGlobalization)       - Whether to disable ICU. Defaults to false.
+      - $(InvariantTimezone)            - Whether to disable Timezone database. Defaults to false.
+      - $(HybridGlobalization)          - Whether 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
       - $(WasmAssemblySearchPaths)          - used for resolving assembly dependencies
index 51cd249..551d1b4 100644 (file)
@@ -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);
 
@@ -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);
 
index cea78ac..8ea0b7d 100644 (file)
@@ -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,
@@ -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",
diff --git a/src/mono/wasm/testassets/Wasm.Buid.Tests.Programs/InvariantGlobalization.cs b/src/mono/wasm/testassets/Wasm.Buid.Tests.Programs/InvariantGlobalization.cs
new file mode 100644 (file)
index 0000000..9237110
--- /dev/null
@@ -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;
diff --git a/src/mono/wasm/testassets/Wasm.Buid.Tests.Programs/InvariantTimezone.cs b/src/mono/wasm/testassets/Wasm.Buid.Tests.Programs/InvariantTimezone.cs
new file mode 100644 (file)
index 0000000..deba28c
--- /dev/null
@@ -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;
index 2842fb1..33441e6 100644 (file)
@@ -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>