-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Add wasm-bindgen support #23493
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
base: main
Are you sure you want to change the base?
Add wasm-bindgen support #23493
Changes from all commits
e9bf8a0
135405f
e5b67a7
6732eec
1708fdf
5f3feae
8ff4580
44d0f55
836fbe6
2debab9
ea4eab0
d7faa6b
cb281cb
2843e36
26cd774
cfc9b11
c2dfc91
4196290
8c08d8e
50e8795
09992ae
126f551
c779002
8e5fd0a
feaf9be
50b600f
50c4fe1
24be666
fe3db01
6d1c5ff
9c92122
3be75f2
4e8aab6
20839d5
95c89b5
65fca2a
e40e789
45c8c8f
11855f5
24d681b
2ef1305
dfd65cf
fe99e55
1c74e70
79a4af7
9627ee3
cba2609
d56b37f
64f0dad
e146085
44d3059
1b72851
afebddc
80d0b67
3cd3b31
68eb9b2
2b1c672
d229aaf
e55a8a1
bc3a3f0
bf9d095
6029dd7
c082ea1
78b0a55
1925b05
b009b61
2b591ee
4b27565
a739374
5cc0091
094743a
ebd53fe
076abcb
dc705f4
06cf81a
f19ae01
b804a48
298d746
7898c9d
389f901
aa7bcc2
3f38afd
2ae5656
067c6c8
4661535
13b5ce7
aa92019
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| [build] | ||
| target = "wasm32-unknown-emscripten" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| [build] | ||
| target = "wasm32-unknown-emscripten" | ||
| rustflags = [ | ||
| "-Cllvm-args=-enable-emscripten-cxx-exceptions=0", | ||
| "-Cpanic=abort", | ||
| "-Crelocation-model=static", | ||
| ] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| [package] | ||
| name = "bindgen_integration" | ||
| edition = "2021" | ||
|
|
||
| [lib] | ||
| crate-type = ["staticlib"] | ||
|
|
||
| [dependencies] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| use wasm_bindgen::prelude::*; | ||
|
|
||
| #[wasm_bindgen] | ||
| pub fn rs_add(a: i32, b: i32) -> i32 { | ||
| return a + b; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14845,7 +14845,7 @@ def test_embool(self): | |
| @requires_rust | ||
| def test_rust_integration_basics(self): | ||
| copytree(test_file('rust/basics'), '.') | ||
| self.run_process(['cargo', 'build', '--target=wasm32-unknown-emscripten']) | ||
| self.run_process(['cargo', 'build']) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why isn't Maybe just revert these changes to |
||
| lib = 'target/wasm32-unknown-emscripten/debug/libbasics.a' | ||
| self.assertExists(lib) | ||
|
|
||
|
|
@@ -14857,6 +14857,31 @@ def test_rust_integration_basics(self): | |
| }''') | ||
| self.do_runf('main.cpp', 'Hello from rust!', cflags=[lib]) | ||
|
|
||
| @requires_rust | ||
| def test_wasm_bindgen_integration(self): | ||
| copytree(test_file('rust/bindgen_integration'), '.') | ||
| self.run_process(['cargo', 'add', 'wasm-bindgen']) | ||
| self.run_process(['cargo', 'build']) | ||
| lib = 'target/wasm32-unknown-emscripten/debug/libbindgen_integration.a' | ||
| self.assertExists(lib) | ||
| self.assertExists(os.path.abspath(config.WASM_BINDGEN[0])) | ||
|
|
||
| create_file('main.cpp', '') | ||
| create_file('post.js', ''' | ||
| Module.onRuntimeInitialized = () => { | ||
| out(Module.rs_add(17, 25)); | ||
| }; | ||
| ''') | ||
| emcc_args = [ | ||
| lib, | ||
| '-sWASM_BINDGEN', | ||
| '--post-js', | ||
| 'post.js', | ||
| '-lexports.js', | ||
| ] | ||
|
|
||
| self.do_runf('main.cpp', '42', cflags=emcc_args) | ||
|
|
||
| def test_relative_em_cache(self): | ||
| with env_modify({'EM_CACHE': 'foo'}): | ||
| self.assert_fail([EMCC, '-c', test_file('hello_world.c')], 'emcc: error: environment variable EM_CACHE must be an absolute path: foo') | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -273,12 +273,43 @@ def lld_flags_for_executable(external_symbols): | |
| return cmd | ||
|
|
||
|
|
||
| def lld_flags(args): | ||
| def get_wasm_bindgen_exported_symbols(input_files): | ||
| if not os.path.exists(LLVM_NM): | ||
| exit_with_error('llvm-nm not found in LLVM directory: %s', LLVM_NM) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would just remove these two lines. We don't have these checks elsewhere. See |
||
|
|
||
| nm_args = [ | ||
| LLVM_NM, | ||
| '--defined-only', | ||
| '--extern-only', | ||
| '--format=just-symbols', | ||
| '--print-file-name', | ||
| '--quiet', | ||
| ] | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We allow lines up to 100 chars in our python code.. maybe just put this on a singe line? |
||
| if input_files is not None: | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can |
||
| nm_args += input_files | ||
|
|
||
| result = run_process(nm_args, stdout=subprocess.PIPE) | ||
| symbols = [] | ||
| for line in result.stdout.splitlines(): | ||
| (path, symbol) = line.split() | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No braces on the LHS here.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this work if there are spaces in the filenames? |
||
| # Skip mangled (non-C) symbols | ||
| if symbol.startswith(('_Z', '_R', 'anon.')): | ||
| continue | ||
| symbols.append(symbol) | ||
|
|
||
| return symbols | ||
|
|
||
|
|
||
| def lld_flags(args, linker_inputs=None): | ||
| # lld doesn't currently support --start-group/--end-group since the | ||
| # semantics are more like the windows linker where there is no need for | ||
| # grouping. | ||
| args = [a for a in args if a not in {'--start-group', '--end-group'}] | ||
|
|
||
| if settings.WASM_BINDGEN: | ||
| exported_symbols = get_wasm_bindgen_exported_symbols(linker_inputs) | ||
| args.extend(f'--export-if-defined={e}' for e in exported_symbols) | ||
|
|
||
| # Emscripten currently expects linkable output (SIDE_MODULE/MAIN_MODULE) to | ||
| # include all archive contents. | ||
| if settings.LINKABLE: | ||
|
|
@@ -312,7 +343,7 @@ def lld_flags(args): | |
| return args | ||
|
|
||
|
|
||
| def link_lld(args, target, external_symbols=None): | ||
| def link_lld(args, target, external_symbols=None, linker_inputs=None): | ||
| # runs lld to link things. | ||
| if not os.path.exists(WASM_LD): | ||
| exit_with_error('linker binary not found in LLVM directory: %s', WASM_LD) | ||
|
|
@@ -321,7 +352,7 @@ def link_lld(args, target, external_symbols=None): | |
| # normal linker flags that are used when building and executable | ||
| if '--relocatable' not in args and '-r' not in args: | ||
| cmd += lld_flags_for_executable(external_symbols) | ||
| cmd += lld_flags(args) | ||
| cmd += lld_flags(args, linker_inputs) | ||
| cmd = get_command_with_possible_response_file(cmd) | ||
| check_call(cmd) | ||
|
|
||
|
|
@@ -1254,6 +1285,28 @@ def run_wasm_opt(infile, outfile=None, args=[], **kwargs): # noqa | |
| return run_binaryen_command('wasm-opt', infile, outfile, args=args, **kwargs) | ||
|
|
||
|
|
||
| def run_wasm_bindgen(infile, outfile=None, args=[], **kwargs): # noqa | ||
| bindgen_out_dir = get_emscripten_temp_dir() + '/bindgen_out/' | ||
|
|
||
| cmd = config.WASM_BINDGEN + [ | ||
| infile, | ||
| '--keep-lld-exports', | ||
| '--keep-debug', | ||
| '--out-dir', | ||
| bindgen_out_dir, | ||
| ] | ||
| check_call(cmd) | ||
|
|
||
| # Don't try to predict the .wasm filename that wasm-bindgen outputs. Instead | ||
| # just grab the .wasm file itself. | ||
| all_output_files = os.listdir(bindgen_out_dir) | ||
| new_wasm_file = list(filter(lambda x: x.endswith('.wasm'), all_output_files))[0] | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use list comprehension here. e.g. |
||
| if outfile is None: | ||
| outfile = infile | ||
|
|
||
| shutil.copyfile(bindgen_out_dir + new_wasm_file, outfile) | ||
|
|
||
|
|
||
| intermediate_counter = 0 | ||
|
|
||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,6 +27,7 @@ | |
| CACHE = None | ||
| PORTS = None | ||
| COMPILER_WRAPPER = None | ||
| WASM_BINDGEN = None | ||
|
|
||
| # Set by init() | ||
| EM_CONFIG = None | ||
|
|
@@ -60,6 +61,7 @@ def fix_js_engine(old, new): | |
| def normalize_config_settings(): | ||
| global CACHE, PORTS, LLVM_ADD_VERSION, CLANG_ADD_VERSION, CLOSURE_COMPILER | ||
| global NODE_JS, NODE_JS_TEST, V8_ENGINE, JS_ENGINES, SPIDERMONKEY_ENGINE, WASM_ENGINES | ||
| global WASM_BINDGEN | ||
|
|
||
| SPIDERMONKEY_ENGINE = fix_js_engine(SPIDERMONKEY_ENGINE, listify(SPIDERMONKEY_ENGINE)) | ||
| NODE_JS = fix_js_engine(NODE_JS, listify(NODE_JS)) | ||
|
|
@@ -68,6 +70,7 @@ def normalize_config_settings(): | |
| JS_ENGINES = [listify(engine) for engine in JS_ENGINES] | ||
| WASM_ENGINES = [listify(engine) for engine in WASM_ENGINES] | ||
| CLOSURE_COMPILER = listify(CLOSURE_COMPILER) | ||
| WASM_BINDGEN = listify(WASM_BINDGEN) | ||
| if not CACHE: | ||
| CACHE = path_from_root('cache') | ||
| if not PORTS: | ||
|
|
@@ -131,6 +134,7 @@ def parse_config_file(): | |
| 'CACHE', | ||
| 'PORTS', | ||
| 'COMPILER_WRAPPER', | ||
| 'WASM_BINDGEN', | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we avoid the new config setting completely and just rely on wasm-bindgen being in the PATH when a user added This is what we do for |
||
| ) | ||
|
|
||
| # Only propagate certain settings from the config file. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -680,7 +680,7 @@ def create_tsd_exported_runtime_methods(metadata): | |
| return utils.read_file(in_temp(f'{file}.d.ts')) | ||
|
|
||
|
|
||
| def create_tsd(metadata, embind_tsd): | ||
| def create_tsd(metadata, embind_tsd, bindgen_tsd=None): | ||
| out = '// TypeScript bindings for emscripten-generated code. Automatically generated at compile time.\n' | ||
| if settings.EXPORTED_RUNTIME_METHODS: | ||
| out += create_tsd_exported_runtime_methods(metadata) | ||
|
|
@@ -712,6 +712,12 @@ def create_tsd(metadata, embind_tsd): | |
| # Add in embind definitions. | ||
| if embind_tsd: | ||
| export_interfaces += ' & EmbindModule' | ||
| if settings.WASM_BINDGEN and bindgen_tsd: | ||
| for file_path in bindgen_tsd: | ||
| with open(file_path, encoding='utf-8') as file: | ||
| for line in file: | ||
| out += f'{line}' | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can these 3 lines be replaced with just |
||
| export_interfaces += ' & BindgenModule' | ||
| out += f'export type MainModule = {export_interfaces};\n' | ||
| if settings.MODULARIZE: | ||
| return_type = 'MainModule' | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we just add
~/.cargo/binto the PATH instead? (see commend in config.py)