Skip to content

Commit e1c4ece

Browse files
[wasm][build] Build and test stdlib for WebAssembly
This patch adds a new build products to build and test the Swift stdlib for WebAssembly. This adds WebAssembly specific stdlib product instead of adding the new target in `stdlib-deployment-targets` because unlike darwin platforms Wasm target has quite different stdlib configuration from host target and there is no way to specify different stdlib configuration for each target in `stdlib-deployment-targets` for now. This patch adds 1 new dependency `wasi-libc` and 3 new build products: 1. `WASILibc` - wasi-libc is a libc implementation for WebAssembly System Interface (WASI). This product is required to build the compiler runtimes and Swift stdlib. 2. `WasmLLVMRuntimeLibs` - This product builds `./runtimes` directory in llvm-project, which contains compiler-rt, libcxx and libcxxabi. This product is required to build Swift stdlib. 3. `WasmStdlib` - This product builds standalone Swift stdlib for WebAssembly. (Similar to `MinimalStdlib` product, but configured for WebAssembly target)
1 parent bfb36e4 commit e1c4ece

File tree

8 files changed

+379
-5
lines changed

8 files changed

+379
-5
lines changed

utils/build-presets.ini

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -923,6 +923,12 @@ mixin-preset=
923923
mixin_lightweight_assertions,no-stdlib-asserts
924924
buildbot_linux_base
925925

926+
[preset: buildbot_linux_crosscompile_wasm]
927+
mixin-preset=buildbot_linux
928+
929+
llvm-targets-to-build=X86;ARM;AArch64;WebAssembly
930+
build-wasm-stdlib
931+
926932
[preset: buildbot_linux,no_assertions]
927933
mixin-preset=buildbot_linux_base
928934

@@ -944,6 +950,7 @@ skip-test-libicu
944950
skip-test-indexstore-db
945951
skip-test-sourcekit-lsp
946952
skip-test-swiftdocc
953+
skip-test-wasm-stdlib
947954

948955
# Linux package with out test
949956
[preset: buildbot_linux,no_test]

utils/build_swift/build_swift/driver_arguments.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -750,6 +750,9 @@ def create_argument_parser():
750750
option(['--build-minimal-stdlib'], toggle_true('build_minimalstdlib'),
751751
help='build the \'minimal\' freestanding stdlib variant into a '
752752
'separate build directory ')
753+
option(['--build-wasm-stdlib'], toggle_true('build_wasmstdlib'),
754+
help='build the stdlib for WebAssembly target into a'
755+
'separate build directory ')
753756

754757
option('--xctest', toggle_true('build_xctest'),
755758
help='build xctest')
@@ -1251,6 +1254,8 @@ def create_argument_parser():
12511254
help='skip testing swift_inspect')
12521255
option('--skip-test-swiftdocc', toggle_false('test_swiftdocc'),
12531256
help='skip testing swift-docc')
1257+
option('--skip-test-wasm-stdlib', toggle_false('test_wasmstdlib'),
1258+
help='skip testing stdlib for WebAssembly')
12541259

12551260
# -------------------------------------------------------------------------
12561261
in_group('Build settings specific for LLVM')

utils/build_swift/tests/expected_options.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@
128128
'build_watchos_device': False,
129129
'build_watchos_simulator': False,
130130
'build_xctest': False,
131+
'build_wasmstdlib': False,
131132
'cmake_c_launcher': None,
132133
'cmake_cxx_launcher': None,
133134
'clang_compiler_version': None,
@@ -293,6 +294,7 @@
293294
'test_swiftevolve': False,
294295
'test_swiftdocc': False,
295296
'test_toolchainbenchmarks': False,
297+
'test_wasmstdlib': True,
296298
'tvos': False,
297299
'tvos_all': False,
298300
'validation_test': None,
@@ -544,6 +546,7 @@ class BuildScriptImplOption(_BaseOption):
544546
SetTrueOption('--swiftevolve', dest='build_swiftevolve'),
545547
SetTrueOption('--swiftdocc', dest='build_swiftdocc'),
546548
SetTrueOption('--build-minimal-stdlib', dest='build_minimalstdlib'),
549+
SetTrueOption('--build-wasm-stdlib', dest='build_wasmstdlib'),
547550
SetTrueOption('-B', dest='benchmark'),
548551
SetTrueOption('-S', dest='skip_build'),
549552
SetTrueOption('-b', dest='build_llbuild'),
@@ -710,6 +713,8 @@ class BuildScriptImplOption(_BaseOption):
710713
dest='test_toolchainbenchmarks'),
711714
DisableOption('--skip-test-swift-inspect',
712715
dest='test_swift_inspect'),
716+
DisableOption('--skip-test-wasm-stdlib',
717+
dest='test_wasmstdlib'),
713718
DisableOption('--skip-build-clang-tools-extra',
714719
dest='build_clang_tools_extra'),
715720
DisableOption('--skip-build-libxml2', dest='build_libxml2'),

utils/swift_build_support/swift_build_support/build_script_invocation.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -666,6 +666,12 @@ def compute_product_pipelines(self):
666666
is_enabled=self.args.install_swiftdocc)
667667
builder.add_product(products.MinimalStdlib,
668668
is_enabled=self.args.build_minimalstdlib)
669+
builder.add_product(products.WASILibc,
670+
is_enabled=self.args.build_wasmstdlib)
671+
builder.add_product(products.WasmLLVMRuntimeLibs,
672+
is_enabled=self.args.build_wasmstdlib)
673+
builder.add_product(products.WasmStdlib,
674+
is_enabled=self.args.build_wasmstdlib)
669675

670676
# Keep SwiftDriver at last.
671677
# swift-driver's integration with the build scripts is not fully

utils/swift_build_support/swift_build_support/products/__init__.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@
3838
from .swiftpm import SwiftPM
3939
from .swiftsyntax import SwiftSyntax
4040
from .tsan_libdispatch import TSanLibDispatch
41+
from .wasisysroot import WASILibc, WasmLLVMRuntimeLibs
42+
from .wasmstdlib import WasmStdlib
4143
from .xctest import XCTest
4244
from .zlib import Zlib
4345

@@ -71,5 +73,8 @@
7173
'Benchmarks',
7274
'TSanLibDispatch',
7375
'SwiftDocC',
74-
'SwiftDocCRender'
76+
'SwiftDocCRender',
77+
'WASILibc',
78+
'WasmLLVMRuntimeLibs',
79+
'WasmStdlib'
7580
]
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
# swift_build_support/products/wasisysroot.py -------------------*- python -*-
2+
#
3+
# This source file is part of the Swift.org open source project
4+
#
5+
# Copyright (c) 2023 Apple Inc. and the Swift project authors
6+
# Licensed under Apache License v2.0 with Runtime Library Exception
7+
#
8+
# See https://swift.org/LICENSE.txt for license information
9+
# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
#
11+
# ----------------------------------------------------------------------------
12+
13+
import multiprocessing
14+
import os
15+
16+
from . import cmake_product
17+
from . import llvm
18+
from . import product
19+
from .. import shell
20+
21+
22+
class WASILibc(product.Product):
23+
@classmethod
24+
def product_source_name(cls):
25+
return "wasi-libc"
26+
27+
@classmethod
28+
def is_build_script_impl_product(cls):
29+
return False
30+
31+
@classmethod
32+
def is_before_build_script_impl_product(cls):
33+
return False
34+
35+
def should_build(self, host_target):
36+
# wasi-libc should always be built if standard library is being built for
37+
# WebAssembly.
38+
return self.args.build_wasmstdlib
39+
40+
def should_test(self, host_target):
41+
return False
42+
43+
def should_install(self, host_target):
44+
return False
45+
46+
def build(self, host_target):
47+
build_root = os.path.dirname(self.build_dir)
48+
llvm_build_dir = os.path.join('..', build_root, '%s-%s' % ('llvm', host_target))
49+
build_jobs = self.args.build_jobs or multiprocessing.cpu_count()
50+
51+
shell.call([
52+
'make', 'install',
53+
'-j', str(build_jobs),
54+
'-C', self.source_dir,
55+
'OBJDIR=' + os.path.join(self.build_dir, 'obj'),
56+
'SYSROOT=' + WASILibc.sysroot_build_path(build_root, host_target),
57+
'INSTALL_DIR=' + WASILibc.sysroot_install_path(build_root),
58+
'CC=' + os.path.join(llvm_build_dir, 'bin', 'clang'),
59+
'AR=' + os.path.join(llvm_build_dir, 'bin', 'llvm-ar'),
60+
'NM=' + os.path.join(llvm_build_dir, 'bin', 'llvm-nm'),
61+
])
62+
63+
@classmethod
64+
def get_dependencies(cls):
65+
return [llvm.LLVM]
66+
67+
@classmethod
68+
def sysroot_build_path(cls, build_root, host_target):
69+
"""
70+
Returns the path to the sysroot build directory, which contains only the
71+
artifacts of wasi-libc (Not including the artifacts of LLVM runtimes).
72+
"""
73+
return os.path.join(build_root,
74+
'%s-%s' % (cls.product_name(), host_target), 'sysroot')
75+
76+
@classmethod
77+
def sysroot_install_path(cls, build_root):
78+
"""
79+
Returns the path to the sysroot install directory, which contains artifacts
80+
of wasi-libc and LLVM runtimes.
81+
"""
82+
return os.path.join(build_root, 'wasi-sysroot')
83+
84+
85+
class WasmLLVMRuntimeLibs(cmake_product.CMakeProduct):
86+
@classmethod
87+
def product_source_name(cls):
88+
return os.path.join("llvm-project", "runtimes")
89+
90+
@classmethod
91+
def is_build_script_impl_product(cls):
92+
return False
93+
94+
@classmethod
95+
def is_before_build_script_impl_product(cls):
96+
return False
97+
98+
def should_build(self, host_target):
99+
# LLVM runtime libraries should always be built for WebAssembly
100+
# if standard library is being built for WebAssembly.
101+
return self.args.build_wasmstdlib
102+
103+
def should_test(self, host_target):
104+
return False
105+
106+
def should_install(self, host_target):
107+
return False
108+
109+
def build(self, host_target):
110+
build_root = os.path.dirname(self.build_dir)
111+
llvm_build_dir = os.path.join('..', build_root, '%s-%s' % ('llvm', host_target))
112+
113+
self.cmake_options.define('CMAKE_SYSROOT:PATH',
114+
WASILibc.sysroot_build_path(build_root, host_target))
115+
self.cmake_options.define('LLVM_ENABLE_RUNTIMES:STRING',
116+
'libcxx;libcxxabi;compiler-rt')
117+
self.cmake_options.define('LIBCXX_LIBDIR_SUFFIX:STRING', '/wasm32-wasi')
118+
self.cmake_options.define('LIBCXXABI_LIBDIR_SUFFIX:STRING', '/wasm32-wasi')
119+
self.cmake_options.define('CMAKE_STAGING_PREFIX:PATH', '/')
120+
121+
self.cmake_options.define('COMPILER_RT_DEFAULT_TARGET_ARCH:STRING', 'wasm32')
122+
self.cmake_options.define('COMPILER_RT_DEFAULT_TARGET_ONLY:BOOL', 'TRUE')
123+
self.cmake_options.define('COMPILER_RT_BAREMETAL_BUILD:BOOL', 'TRUE')
124+
self.cmake_options.define('COMPILER_RT_BUILD_XRAY:BOOL', 'FALSE')
125+
self.cmake_options.define('COMPILER_RT_INCLUDE_TESTS:BOOL', 'FALSE')
126+
self.cmake_options.define('COMPILER_RT_HAS_FPIC_FLAG:BOOL', 'FALSE')
127+
self.cmake_options.define('COMPILER_RT_EXCLUDE_ATOMIC_BUILTIN:BOOL', 'FALSE')
128+
self.cmake_options.define('COMPILER_RT_OS_DIR:STRING', 'wasi')
129+
130+
self.cmake_options.define('CMAKE_C_COMPILER_WORKS:BOOL', 'TRUE')
131+
self.cmake_options.define('CMAKE_CXX_COMPILER_WORKS:BOOL', 'TRUE')
132+
133+
self.cmake_options.define('CMAKE_SYSTEM_NAME:STRING', 'WASI')
134+
self.cmake_options.define('CMAKE_SYSTEM_PROCESSOR:STRING', 'wasm32')
135+
self.cmake_options.define('CMAKE_AR:FILEPATH',
136+
os.path.join(llvm_build_dir, 'bin', 'llvm-ar'))
137+
self.cmake_options.define('CMAKE_RANLIB:FILEPATH',
138+
os.path.join(llvm_build_dir, 'bin', 'llvm-ranlib'))
139+
self.cmake_options.define('CMAKE_C_COMPILER:FILEPATH',
140+
os.path.join(llvm_build_dir, 'bin', 'clang'))
141+
self.cmake_options.define('CMAKE_CXX_COMPILER:STRING',
142+
os.path.join(llvm_build_dir, 'bin', 'clang++'))
143+
# Explicitly disable exceptions even though it's usually implicitly disabled by
144+
# LIBCXX_ENABLE_EXCEPTIONS because the CMake feature check fails to detect
145+
# -fno-exceptions support in clang due to missing compiler-rt while configuring
146+
# as mono project.
147+
self.cmake_options.define('CMAKE_CXX_FLAGS:STRING', '-fno-exceptions')
148+
149+
self.cmake_options.define('CMAKE_C_COMPILER_TARGET:STRING', 'wasm32-wasi')
150+
self.cmake_options.define('CMAKE_CXX_COMPILER_TARGET:STRING', 'wasm32-wasi')
151+
152+
self.cmake_options.define('CXX_SUPPORTS_CXX11:BOOL', 'TRUE')
153+
154+
self.cmake_options.define('LIBCXX_ENABLE_THREADS:BOOL', 'FALSE')
155+
self.cmake_options.define('LIBCXX_HAS_PTHREAD_API:BOOL', 'FALSE')
156+
self.cmake_options.define('LIBCXX_HAS_EXTERNAL_THREAD_API:BOOL', 'FALSE')
157+
self.cmake_options.define('LIBCXX_BUILD_EXTERNAL_THREAD_LIBRARY:BOOL', 'FALSE')
158+
self.cmake_options.define('LIBCXX_HAS_WIN32_THREAD_API:BOOL', 'FALSE')
159+
self.cmake_options.define('LIBCXX_ENABLE_SHARED:BOOL', 'FALSE')
160+
self.cmake_options.define('LIBCXX_ENABLE_EXPERIMENTAL_LIBRARY:BOOL', 'FALSE')
161+
self.cmake_options.define('LIBCXX_ENABLE_EXCEPTIONS:BOOL', 'FALSE')
162+
self.cmake_options.define('LIBCXX_ENABLE_FILESYSTEM:BOOL', 'FALSE')
163+
self.cmake_options.define('LIBCXX_CXX_ABI', 'libcxxabi')
164+
self.cmake_options.define('LIBCXX_HAS_MUSL_LIBC:BOOL', 'TRUE')
165+
166+
self.cmake_options.define('LIBCXX_ABI_VERSION', '2')
167+
self.cmake_options.define('LIBCXXABI_ENABLE_EXCEPTIONS:BOOL', 'FALSE')
168+
self.cmake_options.define('LIBCXXABI_ENABLE_SHARED:BOOL', 'FALSE')
169+
self.cmake_options.define('LIBCXXABI_SILENT_TERMINATE:BOOL', 'TRUE')
170+
self.cmake_options.define('LIBCXXABI_ENABLE_THREADS:BOOL', 'FALSE')
171+
self.cmake_options.define('LIBCXXABI_HAS_PTHREAD_API:BOOL', 'FALSE')
172+
self.cmake_options.define('LIBCXXABI_HAS_EXTERNAL_THREAD_API:BOOL', 'FALSE')
173+
self.cmake_options.define('LIBCXXABI_BUILD_EXTERNAL_THREAD_LIBRARY:BOOL',
174+
'FALSE')
175+
self.cmake_options.define('LIBCXXABI_HAS_WIN32_THREAD_API:BOOL', 'FALSE')
176+
self.cmake_options.define('LIBCXXABI_ENABLE_PIC:BOOL', 'FALSE')
177+
self.cmake_options.define('UNIX:BOOL', 'TRUE')
178+
179+
self.build_with_cmake([], self.args.build_variant, [],
180+
prefer_just_built_toolchain=True)
181+
self.install_with_cmake(
182+
["install"], WASILibc.sysroot_install_path(build_root))
183+
184+
@classmethod
185+
def get_dependencies(cls):
186+
return [WASILibc, llvm.LLVM]

0 commit comments

Comments
 (0)