[emscripten] Add code size test script

And a second code size test for testing dead code elimination.

Produces output like this:

      Type , File                        ,     Size ,     Gzip ,   Brotli
     Debug , emdawnwebgpu_init_only.js   ,   103861 ,    28197 ,    24169
   Release , emdawnwebgpu_init_only.js   ,     9038 ,     4090 ,     3627
MinSizeRel , emdawnwebgpu_init_only.js   ,     9038 ,     4090 ,     3629
     Debug , emdawnwebgpu_init_only.wasm ,  2573142 ,   569846 ,   433061
   Release , emdawnwebgpu_init_only.wasm ,    24031 ,     9468 ,     7908
MinSizeRel , emdawnwebgpu_init_only.wasm ,    22738 ,     9290 ,     7892
     Debug , emdawnwebgpu_link_test.js   ,   169877 ,    37549 ,    31579
   Release , emdawnwebgpu_link_test.js   ,    30874 ,    10954 ,     9572
MinSizeRel , emdawnwebgpu_link_test.js   ,    30874 ,    10960 ,     9577
     Debug , emdawnwebgpu_link_test.wasm ,  2710280 ,   581164 ,   436289
   Release , emdawnwebgpu_link_test.wasm ,    40268 ,    14751 ,    12048
MinSizeRel , emdawnwebgpu_link_test.wasm ,    38676 ,    14623 ,    12119

Bug: 377760848
Change-Id: I3d882df0fcb6c6819f9b0205e59d0b9c8ba1930d
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/255560
Reviewed-by: Loko Kung <lokokung@google.com>
Commit-Queue: Kai Ninomiya <kainino@chromium.org>
diff --git a/.github/workflows/package-emdawnwebgpu.sh b/.github/workflows/package-emdawnwebgpu.sh
index b8974f3..9c6f69b 100755
--- a/.github/workflows/package-emdawnwebgpu.sh
+++ b/.github/workflows/package-emdawnwebgpu.sh
@@ -54,6 +54,9 @@
 # Switch the build type (in-place to save time), rebuild the link test (this
 # time with Closure, which verifies the linked JS to some extent), and build the
 # final package (which is not actually affected by build type).
+# TODO: If we have Ninja (from depot_tools), we could use -G'Ninja Multi-Config'
+# to do multiple build types more cleanly.
+# https://cmake.org/cmake/help/latest/generator/Ninja%20Multi-Config.html
 cmake -S=. -B=out/wasm -DCMAKE_BUILD_TYPE=Release
 make -j4 -C out/wasm emdawnwebgpu_pkg emdawnwebgpu_link_test
 
diff --git a/src/emdawnwebgpu/CMakeLists.txt b/src/emdawnwebgpu/CMakeLists.txt
index 033fb6e..21c8ce6 100644
--- a/src/emdawnwebgpu/CMakeLists.txt
+++ b/src/emdawnwebgpu/CMakeLists.txt
@@ -336,6 +336,7 @@
                 "-sJSPI=1"
         )
 
+        # A "sample" that makes real (bogus) API calls to serve as a basic code size test.
         DawnJSONGenerator(
             TARGET "emdawnwebgpu_link_test_cpp"
             PRINT_NAME "emdawnwebgpu LinkTest.cpp"
@@ -347,7 +348,6 @@
         # The test is just that this links, not that it runs (it will just
         # crash), so just build to .js. Since it has a main() function, this is
         # the same as building to .html, but skipping the .html file.
-        # This makes real (bogus) API calls to serve as a basic code size test.
         set_target_properties(emdawnwebgpu_link_test PROPERTIES
             SUFFIX ".js")
         target_link_libraries(
@@ -356,5 +356,19 @@
                 emdawnwebgpu_test_linkopts
                 emdawnwebgpu_c
         )
+
+        # A "sample" that just creates a device. This is used to check the code size for the
+        # elimination of unused library code.
+        add_executable(emdawnwebgpu_init_only_sample
+            "InitOnlySample.cpp"
+        )
+        set_target_properties(emdawnwebgpu_init_only_sample PROPERTIES
+            SUFFIX ".js")
+        target_link_libraries(
+            emdawnwebgpu_init_only_sample
+            PUBLIC
+                emdawnwebgpu_test_linkopts
+                emdawnwebgpu_cpp
+        )
     endif()
 endif()
diff --git a/src/emdawnwebgpu/InitOnlySample.cpp b/src/emdawnwebgpu/InitOnlySample.cpp
new file mode 100644
index 0000000..4288d22
--- /dev/null
+++ b/src/emdawnwebgpu/InitOnlySample.cpp
@@ -0,0 +1,49 @@
+// Copyright 2025 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Standalone sample that only initializes a device, to test
+// dead-code-elimination of the rest of the library.
+
+#include <webgpu/webgpu_cpp.h>
+
+#include <utility>
+
+int main() {
+    auto instance = wgpu::CreateInstance();
+
+    wgpu::Adapter adapter = nullptr;
+    instance.WaitAny(
+        instance.RequestAdapter(nullptr, wgpu::CallbackMode::AllowSpontaneous,
+                                [&adapter](wgpu::RequestAdapterStatus, wgpu::Adapter a,
+                                           wgpu::StringView) { adapter = std::move(a); }),
+        UINT64_MAX);
+
+    instance.WaitAny(
+        adapter.RequestDevice(nullptr, wgpu::CallbackMode::AllowSpontaneous,
+                              [](wgpu::RequestDeviceStatus, wgpu::Device, wgpu::StringView) {}),
+        UINT64_MAX);
+}
diff --git a/src/emdawnwebgpu/test_code_size.sh b/src/emdawnwebgpu/test_code_size.sh
new file mode 100755
index 0000000..5423c0b
--- /dev/null
+++ b/src/emdawnwebgpu/test_code_size.sh
@@ -0,0 +1,69 @@
+#!/bin/bash
+
+# Copyright 2025 The Dawn & Tint Authors
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice, this
+#    list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+#    this list of conditions and the following disclaimer in the documentation
+#    and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its
+#    contributors may be used to endorse or promote products derived from
+#    this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Script for manual use to quickly test the code size of various targets for use when doing code
+# size optimization work. Outputs CSV to stdout (and logging to stderr), so can be used like so:
+#
+# $ ./src/emdawnwebgpu/test_code_size.sh | tee before.txt
+
+set -euo pipefail
+
+script_dir=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
+cd "$script_dir/../.."
+
+targets=(emdawnwebgpu_init_only emdawnwebgpu_link_test)
+parts=(js wasm)
+types=(Debug Release MinSizeRel)
+
+echo -e '#### Compiling (output on stderr)... ####\n' >&2
+
+outdir=out/emdawnwebgpu_code_size
+mkdir -p "$outdir"
+cd "$outdir"
+
+# Use the Ninja Multi-Config target because CMake configuration takes a long time.
+../../third_party/emsdk/upstream/emscripten/emcmake cmake ../.. -G'Ninja Multi-Config' \
+    -DCMAKE_CONFIGURATION_TYPES="$(IFS=';' ; echo "${types[*]}")" 1>&2
+for type in "${types[@]}" ; do
+    cmake --build . --config="$type" --target "${targets[@]}" 1>&2
+done
+
+echo -e '\n#### Compilation done. Printing CSV results to stdout. ####\n' >&2
+
+fmt_string='%10s , %-27s , %8s , %8s , %8s\n'
+printf "$fmt_string" "Type" "File" "Size" "Gzip" "Brotli"
+for target in "${targets[@]}" ; do
+    for part in "${parts[@]}" ; do
+        for type in "${types[@]}" ; do
+            filename="$target.$part"
+            filepath="$type/$target.$part"
+            printf "$fmt_string" "$type" "$filename" "$(wc -c < $filepath)" "$(gzip -9 < $filepath | wc -c)" "$(brotli < $filepath | wc -c)"
+        done
+    done
+done