1"""Bazel rules for [wasm-bindgen](https://crates.io/crates/wasm-bindgen)""" 2 3load("//rust:defs.bzl", "rust_common") 4load("//wasm_bindgen:providers.bzl", "RustWasmBindgenInfo") 5load("//wasm_bindgen/private:transitions.bzl", "wasm_bindgen_transition") 6 7def rust_wasm_bindgen_action(ctx, toolchain, wasm_file, target_output, bindgen_flags = []): 8 """Spawn a `RustWasmBindgen` action. 9 10 Args: 11 ctx (ctx): _description_ 12 toolchain (ToolchainInfo): _description_ 13 wasm_file (Target): _description_ 14 target_output (str): _description_ 15 bindgen_flags (list, optional): _description_. Defaults to []. 16 17 Returns: 18 RustWasmBindgenInfo: _description_ 19 """ 20 bindgen_bin = toolchain.bindgen 21 22 # Since the `wasm_file` attribute is behind a transition, it will be converted 23 # to a list. 24 if len(wasm_file) == 1: 25 if rust_common.crate_info in wasm_file[0]: 26 target = wasm_file[0] 27 crate_info = target[rust_common.crate_info] 28 29 # Provide a helpful warning informing users how to use the rule 30 if rust_common.crate_info in target: 31 supported_types = ["cdylib", "bin"] 32 if crate_info.type not in supported_types: 33 fail("The target '{}' is not a supported type: {}".format( 34 ctx.attr.crate.label, 35 supported_types, 36 )) 37 38 progress_message_label = target.label 39 input_file = crate_info.output 40 else: 41 wasm_files = wasm_file[0][DefaultInfo].files.to_list() 42 if len(wasm_files) != 1: 43 fail("Unexpected number of wasm files: {}".format(wasm_files)) 44 45 progress_message_label = wasm_files[0].path 46 input_file = wasm_files[0] 47 else: 48 fail("wasm_file is expected to be a transitioned label attr on `{}`. Got `{}`".format( 49 ctx.label, 50 wasm_file, 51 )) 52 53 bindgen_wasm_module = ctx.actions.declare_file(ctx.label.name + "_bg.wasm") 54 55 js_out = [ctx.actions.declare_file(ctx.label.name + ".js")] 56 ts_out = [] 57 if not "--no-typescript" in bindgen_flags: 58 ts_out.append(ctx.actions.declare_file(ctx.label.name + ".d.ts")) 59 60 if target_output == "bundler": 61 js_out.append(ctx.actions.declare_file(ctx.label.name + "_bg.js")) 62 if not "--no-typescript" in bindgen_flags: 63 ts_out.append(ctx.actions.declare_file(ctx.label.name + "_bg.wasm.d.ts")) 64 65 outputs = [bindgen_wasm_module] + js_out + ts_out 66 67 args = ctx.actions.args() 68 args.add("--target", target_output) 69 args.add("--out-dir", bindgen_wasm_module.dirname) 70 args.add("--out-name", ctx.label.name) 71 args.add_all(bindgen_flags) 72 args.add(input_file) 73 74 ctx.actions.run( 75 executable = bindgen_bin, 76 inputs = [input_file], 77 outputs = outputs, 78 mnemonic = "RustWasmBindgen", 79 progress_message = "Generating WebAssembly bindings for {}...".format(progress_message_label), 80 arguments = [args], 81 ) 82 83 return RustWasmBindgenInfo( 84 wasm = bindgen_wasm_module, 85 js = depset(js_out), 86 ts = depset(ts_out), 87 ) 88 89def _rust_wasm_bindgen_impl(ctx): 90 toolchain = ctx.toolchains[Label("//wasm_bindgen:toolchain_type")] 91 92 info = rust_wasm_bindgen_action( 93 ctx = ctx, 94 toolchain = toolchain, 95 wasm_file = ctx.attr.wasm_file, 96 target_output = ctx.attr.target, 97 bindgen_flags = ctx.attr.bindgen_flags, 98 ) 99 100 return [ 101 DefaultInfo( 102 files = depset([info.wasm], transitive = [info.js, info.ts]), 103 ), 104 info, 105 ] 106 107WASM_BINDGEN_ATTR = { 108 "bindgen_flags": attr.string_list( 109 doc = "Flags to pass directly to the bindgen executable. See https://github.com/rustwasm/wasm-bindgen/ for details.", 110 ), 111 "target": attr.string( 112 doc = "The type of output to generate. See https://rustwasm.github.io/wasm-bindgen/reference/deployment.html for details.", 113 default = "bundler", 114 values = ["web", "bundler", "nodejs", "no-modules", "deno"], 115 ), 116 "wasm_file": attr.label( 117 doc = "The `.wasm` file or crate to generate bindings for.", 118 allow_single_file = True, 119 cfg = wasm_bindgen_transition, 120 mandatory = True, 121 ), 122 "_allowlist_function_transition": attr.label( 123 default = Label("//tools/allowlists/function_transition_allowlist"), 124 ), 125} 126 127rust_wasm_bindgen = rule( 128 implementation = _rust_wasm_bindgen_impl, 129 doc = """\ 130Generates javascript and typescript bindings for a webassembly module using [wasm-bindgen][ws]. 131 132[ws]: https://rustwasm.github.io/docs/wasm-bindgen/ 133 134An example of this rule in use can be seen at [@rules_rust//examples/wasm](../examples/wasm) 135""", 136 attrs = { 137 "bindgen_flags": attr.string_list( 138 doc = "Flags to pass directly to the bindgen executable. See https://github.com/rustwasm/wasm-bindgen/ for details.", 139 ), 140 "target": attr.string( 141 doc = "The type of output to generate. See https://rustwasm.github.io/wasm-bindgen/reference/deployment.html for details.", 142 default = "bundler", 143 values = ["web", "bundler", "nodejs", "no-modules", "deno"], 144 ), 145 "wasm_file": attr.label( 146 doc = "The `.wasm` file or crate to generate bindings for.", 147 allow_single_file = True, 148 cfg = wasm_bindgen_transition, 149 mandatory = True, 150 ), 151 "_allowlist_function_transition": attr.label( 152 default = Label("//tools/allowlists/function_transition_allowlist"), 153 ), 154 }, 155 toolchains = [ 156 str(Label("//wasm_bindgen:toolchain_type")), 157 ], 158) 159 160def _rust_wasm_bindgen_toolchain_impl(ctx): 161 return platform_common.ToolchainInfo( 162 bindgen = ctx.executable.bindgen, 163 ) 164 165rust_wasm_bindgen_toolchain = rule( 166 implementation = _rust_wasm_bindgen_toolchain_impl, 167 doc = """\ 168The tools required for the `rust_wasm_bindgen` rule. 169 170In cases where users want to control or change the version of `wasm-bindgen` used by [rust_wasm_bindgen](#rust_wasm_bindgen), 171a unique toolchain can be created as in the example below: 172 173```python 174load("@rules_rust//bindgen:bindgen.bzl", "rust_bindgen_toolchain") 175 176rust_bindgen_toolchain( 177 bindgen = "//3rdparty/crates:wasm_bindgen_cli__bin", 178) 179 180toolchain( 181 name = "wasm_bindgen_toolchain", 182 toolchain = "wasm_bindgen_toolchain_impl", 183 toolchain_type = "@rules_rust//wasm_bindgen:toolchain_type", 184) 185``` 186 187Now that you have your own toolchain, you need to register it by 188inserting the following statement in your `WORKSPACE` file: 189 190```python 191register_toolchains("//my/toolchains:wasm_bindgen_toolchain") 192``` 193 194For additional information, see the [Bazel toolchains documentation][toolchains]. 195 196[toolchains]: https://docs.bazel.build/versions/master/toolchains.html 197""", 198 attrs = { 199 "bindgen": attr.label( 200 doc = "The label of a `wasm-bindgen-cli` executable.", 201 executable = True, 202 cfg = "exec", 203 ), 204 }, 205) 206