SERVER-120523 Get wasm_mozjs_test working on windows (#50448)

GitOrigin-RevId: 4b11c6f56f922317d6d78404b6b7200e4802381d
This commit is contained in:
Andrew Bradshaw 2026-03-26 13:37:29 -07:00 committed by MongoDB Bot
parent edf9d7205e
commit 9f9bac4916
14 changed files with 422 additions and 77 deletions

View File

@ -328,6 +328,11 @@ register_toolchains("@mongo_windows_toolchain//...")
###################### RUST DEPS ######################
bazel_dep(name = "rules_rust", version = "0.68.1")
single_version_override(
module_name = "rules_rust",
patches = ["//bazel/rules_rust:windows_long_path.patch"],
version = "0.68.1",
)
rust = use_extension("@rules_rust//rust:extensions.bzl", "rust")
rust.toolchain(
@ -428,6 +433,19 @@ crate.annotation_select(
"s390x-unknown-linux-gnu",
],
)
crate.annotation_select(
build_script_data = [
"@@windows_libclang//file",
],
build_script_env = {
"LIBCLANG_PATH": "$(execpath @@windows_libclang//file)",
},
crate = "zstd-sys",
repositories = ["crates"],
triples = [
"x86_64-pc-windows-msvc",
],
)
# Update the lock file using "CARGO_BAZEL_REPIN=1 bazel sync --only=crates"
crate.from_specs(

View File

@ -1,4 +1,4 @@
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive", "http_file")
load("//bazel/toolchains/cc/mongo_linux:mongo_toolchain.bzl", "setup_mongo_toolchains")
load("//bazel/toolchains/cc/mongo_linux:mongo_gdb.bzl", "setup_gdb_toolchains")
@ -13,6 +13,17 @@ setup_mongo_toolchains()
setup_gdb_toolchains()
http_file(
name = "windows_libclang",
downloaded_file_path = "libclang.dll",
sha256 = "fd77bdb8098cd82ab918bf50e9b12743e1fc8e524fa64cb90e729fbe9cee9c15",
urls = [
# Implements retry by relisting each url multiple times to be used as a failover.
# TODO(SERVER-86719): Re-implement http_archive to allow sleeping between retries
"https://mdb-build-public.s3.us-east-1.amazonaws.com/windows_libclang/libclang.dll",
] * 5,
)
http_archive(
name = "windows_sasl",
build_file_content = """

File diff suppressed because one or more lines are too long

View File

@ -2325,7 +2325,7 @@ dependencies = [
"toml_datetime",
"toml_parser",
"toml_writer",
"winnow",
"winnow 0.7.15",
]
[[package]]
@ -2339,18 +2339,18 @@ dependencies = [
[[package]]
name = "toml_parser"
version = "1.0.9+spec-1.1.0"
version = "1.0.10+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4"
checksum = "7df25b4befd31c4816df190124375d5a20c6b6921e2cad937316de3fccd63420"
dependencies = [
"winnow",
"winnow 1.0.0",
]
[[package]]
name = "toml_writer"
version = "1.0.6+spec-1.1.0"
version = "1.0.7+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607"
checksum = "f17aaa1c6e3dc22b1da4b6bba97d066e354c7945cac2f7852d4e4e7ca7a6b56d"
[[package]]
name = "tracing"
@ -3615,6 +3615,12 @@ version = "0.7.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945"
[[package]]
name = "winnow"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a90e88e4667264a994d34e6d1ab2d26d398dcdca8b7f52bec8668957517fc7d8"
[[package]]
name = "winx"
version = "0.36.4"
@ -3774,18 +3780,18 @@ dependencies = [
[[package]]
name = "zerocopy"
version = "0.8.42"
version = "0.8.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2578b716f8a7a858b7f02d5bd870c14bf4ddbbcf3a4c05414ba6503640505e3"
checksum = "efbb2a062be311f2ba113ce66f697a4dc589f85e78a4aea276200804cea0ed87"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.42"
version = "0.8.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e6cc098ea4d3bd6246687de65af3f920c430e236bee1e3bf2e441463f08a02f"
checksum = "0e8bc7269b54418e7aeeef514aa68f8690b8c0489a06b0136e5f57c4c5ccab89"
dependencies = [
"proc-macro2",
"quote",

View File

@ -0,0 +1,17 @@
load("@rules_rust//rust:defs.bzl", "rust_library")
package(default_visibility = ["//visibility:public"])
# Allocator shim for linking Rust rlibs into a C++ binary on Windows.
# On Linux, rules_rust provides this automatically. On Windows, MSVC doesn't
# support weak linkage so rules_rust's allocator library is empty. We provide
# strong symbol definitions here since we only link into C++ binaries.
rust_library(
name = "win_allocator_shim",
srcs = ["win_allocator_shim.rs"],
# Use empty allocator libraries to avoid circular dependency - this IS
# the allocator library.
allocator_libraries = "@rules_rust//ffi/rs:empty_allocator_libraries",
edition = "2024",
target_compatible_with = ["@platforms//os:windows"],
)

View File

@ -0,0 +1,52 @@
// Allocator shim for linking Rust rlibs into a C++ binary on Windows.
//
// On Linux, rules_rust provides this via ffi/rs/allocator_library with
// #[linkage = "weak"], but MSVC doesn't support weak linkage. Since we're
// always linking into a C++ binary (never a Rust main), strong symbols are fine.
//
// See: https://github.com/rust-lang/rust/issues/73632
// See: rules_rust/ffi/rs/allocator_library/allocator_library.rs
#![no_std]
#![allow(warnings)]
#![allow(internal_features)]
#![feature(rustc_attrs)]
unsafe extern "C" {
#[rustc_std_internal_symbol]
fn __rdl_alloc(size: usize, align: usize) -> *mut u8;
#[rustc_std_internal_symbol]
fn __rdl_dealloc(ptr: *mut u8, size: usize, align: usize);
#[rustc_std_internal_symbol]
fn __rdl_realloc(ptr: *mut u8, old_size: usize, align: usize, new_size: usize) -> *mut u8;
#[rustc_std_internal_symbol]
fn __rdl_alloc_zeroed(size: usize, align: usize) -> *mut u8;
}
#[rustc_std_internal_symbol]
fn __rust_alloc(size: usize, align: usize) -> *mut u8 {
unsafe { __rdl_alloc(size, align) }
}
#[rustc_std_internal_symbol]
fn __rust_dealloc(ptr: *mut u8, size: usize, align: usize) {
unsafe { __rdl_dealloc(ptr, size, align) }
}
#[rustc_std_internal_symbol]
fn __rust_realloc(ptr: *mut u8, old_size: usize, align: usize, new_size: usize) -> *mut u8 {
unsafe { __rdl_realloc(ptr, old_size, align, new_size) }
}
#[rustc_std_internal_symbol]
fn __rust_alloc_zeroed(size: usize, align: usize) -> *mut u8 {
unsafe { __rdl_alloc_zeroed(size, align) }
}
#[rustc_std_internal_symbol]
static __rust_no_alloc_shim_is_unstable: u8 = 0;
#[rustc_std_internal_symbol]
fn __rust_no_alloc_shim_is_unstable_v2() {}

View File

@ -0,0 +1,18 @@
diff --git rust/private/rust.bzl rust/private/rust.bzl
index d06f5fa71..3eadb42d1 100644
--- rust/private/rust.bzl
+++ rust/private/rust.bzl
@@ -173,6 +173,13 @@ def _rust_library_common(ctx, crate_type):
toolchain = find_toolchain(ctx)
crate_name = compute_crate_name(ctx.workspace_name, ctx.label, toolchain, ctx.attr.crate_name)
+ # These are here for windows long path issues
+ if crate_name == "wasmtime_internal_versioned_export_macros":
+ crate_name = "wivem"
+ if crate_name == "wasmtime_internal_component_macro":
+ crate_name = "wicm"
+ if crate_name == "wasmtime_internal_c_api_macros":
+ crate_name = "wicam"
crate_root = getattr(ctx.file, "crate_root", None)
if not crate_root:

View File

@ -116,3 +116,85 @@ embed_binary_obj = rule(
toolchains = ["@bazel_tools//tools/cpp:toolchain_type"],
fragments = ["cpp"],
)
"""Rule to embed a binary file as a Windows resource using rc.exe.
Generates an .rc file referencing the binary, compiles it to a .res with rc.exe,
and provides CcInfo so dependents can link it via deps.
At runtime, use FindResource/LoadResource/LockResource/SizeofResource to access
the data, or use the helper in embedded_resource.h.
"""
def _embed_binary_rc_impl(ctx):
input_file = ctx.file.src
rc_file = ctx.actions.declare_file(ctx.attr.name + ".rc")
res_file = ctx.outputs.out
# The resource type and name used to look up the data at runtime.
resource_name = ctx.attr.resource_name
# Generate an .rc file that references the binary as RCDATA.
ctx.actions.write(
output = rc_file,
content = '{resource_name} RCDATA "{input_path}"\n'.format(
resource_name = resource_name,
input_path = input_file.path.replace("/", "\\\\"),
),
)
args = ctx.actions.args()
args.add("/nologo")
args.add("/fo" + res_file.path)
args.add(rc_file.path)
ctx.actions.run(
executable = ctx.executable.rc,
arguments = [args],
inputs = [rc_file, input_file],
outputs = [res_file],
mnemonic = "EmbedBinaryRC",
progress_message = "Embedding %s as Windows resource" % input_file.short_path,
)
linker_input = cc_common.create_linker_input(
owner = ctx.label,
user_link_flags = [res_file.path],
additional_inputs = depset([res_file]),
)
linking_context = cc_common.create_linking_context(
linker_inputs = depset(direct = [linker_input]),
)
return [
DefaultInfo(files = depset([res_file])),
CcInfo(linking_context = linking_context),
]
embed_binary_rc = rule(
implementation = _embed_binary_rc_impl,
attrs = {
"src": attr.label(
allow_single_file = True,
mandatory = True,
doc = "Binary file to embed (e.g. .wasm or .cwasm).",
),
"out": attr.output(
mandatory = True,
doc = "Output .res file.",
),
"resource_name": attr.string(
mandatory = True,
doc = "Resource name used in the .rc file and at runtime with FindResource.",
),
"rc": attr.label(
executable = True,
cfg = "exec",
allow_files = True,
default = "@mongo_windows_toolchain//:rc",
doc = "The rc.exe compiler.",
),
},
provides = [CcInfo],
doc = "Embeds a binary file as a Windows resource (.res) using rc.exe.",
)

View File

@ -9,21 +9,22 @@ set -o verbose
# Use the Evergreen temp directory to avoid filling up the disk.
mkdir -p $TMPDIR
if [[ "$OSTYPE" == "cygwin" ]] || [[ "$OSTYPE" == "win32" ]]; then
mkdir -p Z:/bazel_tmp
touch Z:/bazel_tmp/mci_path
mkdir -p Z:/b
touch Z:/b/mci_path
# TODO(SERVER-94605): remove when Windows temp directory is cleared between task runs
if [[ "$PWD" != "$(cat Z:/bazel_tmp/mci_path)" ]]; then
echo "Clearing bazel output root from previous task mci '$(cat Z:/bazel_tmp/mci_path)'"
rm -rf Z:/bazel_tmp/* || true
echo $PWD >Z:/bazel_tmp/mci_path
if [[ "$PWD" != "$(cat Z:/b/mci_path)" ]]; then
echo "Clearing bazel output root from previous task mci '$(cat Z:/b/mci_path)'"
rm -rf Z:/b/* || true
echo $PWD >Z:/b/mci_path
fi
# Z:/ path is necessary to avoid running into MSVC's file length limit,
# see https://jira.mongodb.org/browse/DEVPROD-11126
abs_path=$(cygpath -w "$TMPDIR" | tr '\\' '/')
echo "startup --output_user_root=Z:/bazel_tmp" >.bazelrc.evergreen
echo "common --action_env=TMP=Z:/bazel_tmp" >>.bazelrc.evergreen
echo "common --action_env=TEMP=Z:/bazel_tmp" >>.bazelrc.evergreen
echo "startup --output_user_root=Z:/b" >.bazelrc.evergreen
echo "startup --output_base=Z:/b/b" >.bazelrc.evergreen
echo "common --action_env=TMP=Z:/b" >>.bazelrc.evergreen
echo "common --action_env=TEMP=Z:/b" >>.bazelrc.evergreen
echo "BAZELISK_HOME=${abs_path}/bazelisk_home" >>.bazeliskrc
echo "common --define GIT_COMMIT_HASH=$(git rev-parse HEAD)" >>.bazelrc.git
else

View File

@ -1,9 +1,9 @@
load("@rules_cc//cc:defs.bzl", "cc_binary")
load("//bazel:mongo_src_rules.bzl", "mongo_cc_benchmark", "mongo_cc_binary", "mongo_cc_library", "mongo_cc_unit_test") # @unused mongo_cc_binary mongo_cc_library
load("//bazel/toolchains/cc/mongo_wasm/toolchain:with_wasi_config.bzl", "with_wasi_config")
load("//src/mongo/scripting/mozjs/wasm/engine:linkset.bzl", "cc_linkset")
load("@rules_cc//cc:defs.bzl", "cc_binary")
load("//bazel/wasm_rules:embed_binary.bzl", "embed_binary_obj", "embed_binary_rc")
load("//bazel/wasm_rules:wasm_aot_compile.bzl", "aot_compile_wasm")
load("//bazel/wasm_rules:embed_binary_obj.bzl", "embed_binary_obj")
load("//src/mongo/scripting/mozjs/wasm/engine:linkset.bzl", "cc_linkset")
# Gate WASM module compilation behind --define=build_mozjs_wasm=true.
# Without this flag the test compiles and runs but skips (no .wasm module found).
@ -13,6 +13,7 @@ config_setting(
)
exports_files([
"embedded_wasm_resource.h",
"status.cpp",
])
@ -186,10 +187,18 @@ mongo_cc_library(
"//src/mongo/scripting/mozjs/wasm/bridge:bridge.h",
"//src/mongo/scripting/mozjs/wasm/bridge:wasm_helpers.h",
],
target_compatible_with = select({
"@platforms//cpu:x86_64": [],
"@platforms//cpu:aarch64": [],
"//conditions:default": ["@platforms//:incompatible"],
}),
deps = [
"//src/mongo:base",
"@crates//:wasmtime_c",
],
] + select({
"@platforms//os:linux": [],
"@platforms//os:windows": ["//bazel/rules_rust:win_allocator_shim"],
}),
)
mongo_cc_unit_test(
@ -201,17 +210,24 @@ mongo_cc_unit_test(
"//src/mongo/scripting/mozjs/wasm/engine:engine_test.cpp",
],
auto_header = False,
private_hdrs = [":embedded_wasm_resource.h"],
tags = [
"mongo_unittest_eighth_group",
"mozjs_wasm_tests",
],
target_compatible_with = ["@platforms//os:linux"],
target_compatible_with = select({
"@platforms//cpu:x86_64": [],
"@platforms//cpu:aarch64": [],
"//conditions:default": ["@platforms//:incompatible"],
}),
deps = [
":bridge",
":embed_mozjs_wasm_obj",
"//src/mongo:base",
"@crates//:wasmtime_c",
],
] + select({
"@platforms//os:linux": [":embed_mozjs_wasm_obj"],
"@platforms//os:windows": [":embed_mozjs_wasm_rc"],
}),
)
# ---------------------------------------------------------------------------
@ -231,7 +247,7 @@ aot_compile_wasm(
name = "aot_compile_mozjs_wasm",
src = ":mozjs_wasm_api_data",
out = "mozjs_wasm_api.cwasm",
target_compatible_with = ["@platforms//os:linux"] + select({
target_compatible_with = select({
"@platforms//cpu:x86_64": [],
"@platforms//cpu:aarch64": [],
"//conditions:default": ["@platforms//:incompatible"],
@ -243,5 +259,13 @@ embed_binary_obj(
src = ":aot_compile_mozjs_wasm",
out = "embedded_mozjs_wasm.o",
symbol_prefix = "binary_mozjs_wasm_api_cwasm",
target_compatible_with = ["@platforms//os:linux"],
target_compatible_with = ["//bazel/config:not_windows"],
)
embed_binary_rc(
name = "embed_mozjs_wasm_rc",
src = ":aot_compile_mozjs_wasm",
out = "embedded_mozjs_wasm.res",
resource_name = "MOZJS_WASM_CWASM",
target_compatible_with = ["@platforms//os:windows"],
)

View File

@ -47,6 +47,7 @@
#include "mongo/bson/oid.h"
#include "mongo/bson/timestamp.h"
#include "mongo/platform/decimal128.h"
#include "mongo/scripting/mozjs/wasm/embedded_wasm_resource.h"
#include "mongo/unittest/unittest.h"
#include <cstdint>
@ -55,13 +56,6 @@
#include <string>
#include <vector>
// Symbols produced by objcopy from the AOT-compiled mozjs_wasm_api.cwasm.
// See the embed_mozjs_wasm_obj genrule in BUILD.bazel.
extern "C" {
extern const uint8_t _binary_mozjs_wasm_api_cwasm_start[];
extern const uint8_t _binary_mozjs_wasm_api_cwasm_end[];
}
namespace mongo {
namespace mozjs {
namespace wasm {
@ -72,10 +66,8 @@ namespace {
class WasmMozJSBridgeTest : public unittest::Test {
public:
static void SetUpTestSuite() {
size_t size = static_cast<size_t>(_binary_mozjs_wasm_api_cwasm_end -
_binary_mozjs_wasm_api_cwasm_start);
_s_engineCtx =
WasmEngineContext::createFromPrecompiled(_binary_mozjs_wasm_api_cwasm_start, size);
auto [wasmData, wasmSize] = getEmbeddedWasmResource();
_s_engineCtx = WasmEngineContext::createFromPrecompiled(wasmData, wasmSize);
}
protected:

View File

@ -0,0 +1,73 @@
/**
* Copyright (C) 2026-present MongoDB, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the Server Side Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
#pragma once
#include <cstddef>
#include <cstdint>
#include <utility>
#ifdef _WIN32
#include <windows.h>
#endif
namespace mongo::mozjs::wasm {
#ifdef _WIN32
/**
* Returns {pointer, size} for a Windows resource embedded with embed_binary_rc.
* The resource name must match the resource_name attribute in the BUILD rule.
*/
inline std::pair<const uint8_t*, size_t> getEmbeddedWasmResource() {
HMODULE mod = GetModuleHandleW(nullptr);
HRSRC res = FindResourceW(mod, L"MOZJS_WASM_CWASM", RT_RCDATA);
HGLOBAL handle = LoadResource(mod, res);
const uint8_t* data = static_cast<const uint8_t*>(LockResource(handle));
size_t size = SizeofResource(mod, res);
return {data, size};
}
#else
extern "C" {
extern const uint8_t _binary_mozjs_wasm_api_cwasm_start[];
extern const uint8_t _binary_mozjs_wasm_api_cwasm_end[];
}
inline std::pair<const uint8_t*, size_t> getEmbeddedWasmResource() {
const uint8_t* data = _binary_mozjs_wasm_api_cwasm_start;
size_t size =
static_cast<size_t>(_binary_mozjs_wasm_api_cwasm_end - _binary_mozjs_wasm_api_cwasm_start);
return {data, size};
}
#endif
} // namespace mongo::mozjs::wasm

View File

@ -45,6 +45,7 @@
#include "mongo/bson/oid.h"
#include "mongo/bson/timestamp.h"
#include "mongo/platform/decimal128.h"
#include "mongo/scripting/mozjs/wasm/embedded_wasm_resource.h"
#include "mongo/unittest/unittest.h"
#include <cstdint>
@ -58,13 +59,6 @@
#include <wasmtime/component.hh>
#include <wasmtime/component/val.h>
// Symbols produced by objcopy from the AOT-compiled mozjs_wasm_api.cwasm.
// See the embed_mozjs_wasm_obj genrule in BUILD.bazel.
extern "C" {
extern const uint8_t _binary_mozjs_wasm_api_cwasm_start[];
extern const uint8_t _binary_mozjs_wasm_api_cwasm_end[];
}
namespace wt = wasmtime;
namespace wc = wasmtime::component;
@ -213,8 +207,8 @@ class WasmMozJSTest : public unittest::Test {
public:
// One-time setup: deserialize the AOT pre-compiled component (near-instant).
static void SetUpTestSuite() {
std::vector<uint8_t> cwasmBytes(_binary_mozjs_wasm_api_cwasm_start,
_binary_mozjs_wasm_api_cwasm_end);
auto [wasmData, wasmSize] = getEmbeddedWasmResource();
std::vector<uint8_t> cwasmBytes(wasmData, wasmData + wasmSize);
wt::Config config;
config.wasm_component_model(true);

View File

@ -35,6 +35,19 @@ expand_template(
cc_library(
name = "wasmtime_c",
hdrs = glob(["include/**"]) + [":gen_conf"],
defines = select({
"@platforms//os:windows": [
"WASM_API_EXTERN=",
"WASI_API_EXTERN=",
],
"//conditions:default": [],
}),
linkopts = select({
"@platforms//os:windows": [
"ntdll.lib",
],
"//conditions:default": [],
}),
strip_include_prefix = "include",
deps = [":wasmtime_c_api"],
)