Skip to content

utils/build-script: add--test-with-wasm-runtime option #83573

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

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
2 changes: 2 additions & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,8 @@ set(SWIFT_LIT_ENVIRONMENT "" CACHE STRING "Environment to use for lit invocation

option(SWIFT_TEST_USE_LEAKS "Run Swift stdlib tests under leaks" FALSE)

option(SWIFT_TEST_WASM_RUNTIME "Wasm runtime used for running Wasm tests" "wasmkit")

function(setup_lit_args ARGS_VAR_OUT tested_sdk test_results_dir resource_dir_override)
set(swift_lit_args_result)

Expand Down
3 changes: 2 additions & 1 deletion test/lit.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -2093,7 +2093,8 @@ elif kIsWASI:
config.swift_test_options, config.swift_frontend_test_options])
subst_target_swift_frontend_mock_sdk = config.target_swift_frontend
subst_target_swift_frontend_mock_sdk_after = ""
config.target_run = os.path.join(config.swift_utils, 'wasm-run.py')
config.target_run = f"{os.path.join(config.swift_utils, 'wasm-run.py')} " \
f"-r {config.wasm_runtime}"
config.target_env_prefix = 'WASM_RUN_CHILD_'

if 'interpret' in lit_config.params:
Expand Down
13 changes: 8 additions & 5 deletions test/lit.site.cfg.in
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,17 @@ config.exe_linker_flags = r'''@CMAKE_EXE_LINKER_FLAGS@'''
# --- Darwin ---
config.darwin_xcrun_toolchain = "@SWIFT_DARWIN_XCRUN_TOOLCHAIN@"

# --- android ---
config.darwin_enable_maccatalyst = "@SWIFT_ENABLE_MACCATALYST@" == "TRUE"
config.darwin_maccatalyst_build_flavor = "@BUILD_FLAVOR@"
config.darwin_osx_variant_suffix = "@DEFAULT_OSX_VARIANT_SUFFIX@"

# --- Android ---
config.android_ndk_path = "@SWIFT_ANDROID_NDK_PATH@"
config.android_api_level = "@SWIFT_ANDROID_API_LEVEL@"

# --- Wasm ---
config.wasm_runtime = "@SWIFT_TEST_WASM_RUNTIME@"

# --- Windows ---
msvc_runtime_flags = {
'MultiThreaded': 'MT',
Expand All @@ -69,10 +76,6 @@ msvc_runtime_flags = {
config.swift_stdlib_msvc_runtime = \
msvc_runtime_flags["@SWIFT_STDLIB_MSVC_RUNTIME_LIBRARY@"]

config.darwin_enable_maccatalyst = "@SWIFT_ENABLE_MACCATALYST@" == "TRUE"
config.darwin_maccatalyst_build_flavor = "@BUILD_FLAVOR@"
config.darwin_osx_variant_suffix = "@DEFAULT_OSX_VARIANT_SUFFIX@"

# If we are not testing against the host compiler, we are testing against the
# just built compiler. Add that as a feature so we can conditionally mark tests
# as requiring a just_built_compiler.
Expand Down
5 changes: 5 additions & 0 deletions utils/build_swift/build_swift/driver_arguments.py
Original file line number Diff line number Diff line change
Expand Up @@ -1183,6 +1183,11 @@ def create_argument_parser():
'Then re-builds the TSan runtime (compiler-rt) using this '
'freshly-built Clang and runs the TSan libdispatch tests.')

option('--test-with-wasm-runtime', store, metavar='WASM_RUNTIME',
choices=['wasmkit', 'nodejs'], default='wasmkit',
help='Wasm runtime to use when running tests. Available choices: '
'`wasmkit` or `nodejs`')

option('--skip-test-osx', toggle_false('test_osx'),
help='skip testing Swift stdlibs for Mac OS X')
option('--skip-test-linux', toggle_false('test_linux'),
Expand Down
2 changes: 2 additions & 0 deletions utils/build_swift/tests/expected_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,7 @@
'test_swiftdocc': False,
'test_toolchainbenchmarks': False,
'test_wasmstdlib': False,
'test_with_wasm_runtime': 'wasmkit',
'tvos': False,
'tvos_all': False,
'validation_test': None,
Expand Down Expand Up @@ -806,6 +807,7 @@ class BuildScriptImplOption(_BaseOption):
choices=['armv7', 'aarch64', 'x86_64']),
ChoicesOption('--foundation-tests-build-type',
dest='foundation_tests_build_variant', choices=['Debug', 'Release']),
ChoicesOption('--test-with-wasm-runtime', choices=['wasmkit', 'nodejs']),

StrOption('--android-api-level'),
StrOption('--build-args'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def build(self, host_target):
shutil.copy(bin_path, dest_bin_path)

@classmethod
def cli_file_path(cls, build_dir):
def cli_file_path(cls, build_dir) -> str:
return os.path.join(build_dir, 'bin', 'wasmkit')


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
# ----------------------------------------------------------------------------

import os
import shutil
import sys

from typing import NoReturn

from . import cmake_product
from . import llvm
Expand Down Expand Up @@ -206,31 +210,52 @@ def _build_stdlib(self, host_target, target_triple, llvm_cmake_dir):
self.cmake_options.define('SWIFT_DRIVER_TEST_OPTIONS:STRING',
' ' + ' '.join(test_driver_options))

self.cmake_options.define('SWIFT_TEST_WASM_RUNTIME', self.args.test_with_wasm_runtime)

# Configure with WebAssembly target variant, and build with just-built toolchain
self.build_with_cmake([], self._build_variant, [],
prefer_native_toolchain=not self.args.build_runtime_with_host_compiler)

def add_extra_cmake_options(self):
self.cmake_options.define('SWIFT_THREADING_PACKAGE:STRING', 'none')

def _wasm_runtime_lookup_failed(self) -> NoReturn:
print("wasmstdlib testing: Wasm runtime not found")
sys.exit(1)
Comment on lines +223 to +224
Copy link
Member

Choose a reason for hiding this comment

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

indent


def infer_wasm_runtime(self, host_target) -> str:
build_root = os.path.dirname(self.build_dir)
if self.args.test_with_wasm_runtime == 'wasmkit':
wasmkit_build_path = os.path.join(
build_root, '%s-%s' % ('wasmkit', host_target))
return wasmkit.WasmKit.cli_file_path(wasmkit_build_path)
elif self.args.test_with_wasm_runtime == 'nodejs':
result = shutil.which('node')
if result:
return result
Comment on lines +232 to +235
Copy link
Member

Choose a reason for hiding this comment

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

Do we need to add it to PATH again? isn't it already in PATH?

else:
self._wasm_runtime_lookup_failed()
else:
self._wasm_runtime_lookup_failed()


def test(self, host_target):
self._test(host_target, 'wasm32-wasip1')

def _test(self, host_target, target_triple):
build_root = os.path.dirname(self.build_dir)
bin_paths = [
os.path.join(self._host_swift_build_dir(host_target), 'bin'),
os.path.join(self._host_llvm_build_dir(host_target), 'bin'),
os.environ['PATH']
]
wasmkit_build_path = os.path.join(
build_root, '%s-%s' % ('wasmkit', host_target))
wasmkit_bin_path = wasmkit.WasmKit.cli_file_path(wasmkit_build_path)
if not os.path.exists(wasmkit_bin_path) or not self.should_test_executable():

wasm_runtime_bin_path = self.infer_wasm_runtime(host_target)
print(f"wasm_runtime_bin_path is {wasm_runtime_bin_path}")
Copy link
Member

Choose a reason for hiding this comment

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

debug code?

if not os.path.exists(wasm_runtime_bin_path) or not self.should_test_executable():
test_target = "check-swift-only_non_executable-wasi-wasm32-custom"
else:
test_target = "check-swift-wasi-wasm32-custom"
bin_paths = [os.path.dirname(wasmkit_bin_path)] + bin_paths
bin_paths = [os.path.dirname(wasm_runtime_bin_path)] + bin_paths

env = {
'PATH': os.path.pathsep.join(bin_paths),
Expand Down
21 changes: 15 additions & 6 deletions utils/wasm-run.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,16 @@ def run(self, args):
subprocess.check_call(command)

def invocation(self, args):
command = ["wasmkit", "run"]
envs = collect_wasm_env()
for key in envs:
command.append("--env")
command.append(f"{key}={envs[key]}")
command.append("--")
if args.runtime == 'nodejs':
command = [os.path.join(os.path.dirname(__file__), 'wasm', 'node-wasi-runner')]
Copy link
Member

@kateinoigakukun kateinoigakukun Aug 7, 2025

Choose a reason for hiding this comment

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

You need to forward env vars from collect_wasm_env and command line args as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Doesn't Node.js WASI inherit env vars by default? And args are passed below in command.extend(args)

Copy link
Member

@kateinoigakukun kateinoigakukun Aug 7, 2025

Choose a reason for hiding this comment

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

oops, I missed the command args.

We need to set env vars prefixed with WASM_RUN_CHILD_ from host to guest while stripping the prefix. You can find it in collect_wasm_env.

else:
command = ["wasmkit", "run"]
envs = collect_wasm_env()
for key in envs:
command.append("--env")
command.append(f"{key}={envs[key]}")
command.append("--")

command.extend(args.command)
return command

Expand All @@ -42,6 +46,11 @@ def main():
parser.add_argument('-n', '--dry-run', action='store_true', dest='dry_run',
help="print the commands that would have been run, but"
" don't actually run them")
parser.add_argument('-r', '--runtime', metavar='WASM_RUNTIME',
choices=['wasmkit', 'nodejs'], default='wasmkit',
help='Wasm runtime to use when running tests. Available choices: '
'`wasmkit` or `nodejs`')

parser.add_argument('command', nargs=argparse.REMAINDER,
help='the command to run', metavar='command...')

Expand Down