| """wasm_cc_binary rule for compiling C++ targets to WebAssembly. |
| """ |
| |
| def _wasm_transition_impl(settings, attr): |
| _ignore = (settings, attr) |
| |
| features = list(settings["//command_line_option:features"]) |
| linkopts = list(settings["//command_line_option:linkopt"]) |
| |
| if attr.threads == "emscripten": |
| # threads enabled |
| features.append("use_pthreads") |
| elif attr.threads == "off": |
| # threads disabled |
| features.append("-use_pthreads") |
| |
| if attr.exit_runtime == True: |
| features.append("exit_runtime") |
| |
| if attr.backend == "llvm": |
| features.append("llvm_backend") |
| elif attr.backend == "emscripten": |
| features.append("-llvm_backend") |
| |
| if attr.simd: |
| features.append("wasm_simd") |
| |
| return { |
| "//command_line_option:compiler": "emscripten", |
| "//command_line_option:crosstool_top": "@emsdk//emscripten_toolchain:everything", |
| "//command_line_option:cpu": "wasm", |
| "//command_line_option:features": features, |
| "//command_line_option:dynamic_mode": "off", |
| "//command_line_option:linkopt": linkopts, |
| "//command_line_option:platforms": [], |
| "//command_line_option:custom_malloc": "@emsdk//emscripten_toolchain:malloc", |
| } |
| |
| _wasm_transition = transition( |
| implementation = _wasm_transition_impl, |
| inputs = [ |
| "//command_line_option:features", |
| "//command_line_option:linkopt", |
| ], |
| outputs = [ |
| "//command_line_option:compiler", |
| "//command_line_option:cpu", |
| "//command_line_option:crosstool_top", |
| "//command_line_option:features", |
| "//command_line_option:dynamic_mode", |
| "//command_line_option:linkopt", |
| "//command_line_option:platforms", |
| "//command_line_option:custom_malloc", |
| ], |
| ) |
| |
| _ALLOW_OUTPUT_EXTNAMES = [ |
| ".js", |
| ".wasm", |
| ".wasm.map", |
| ".worker.js", |
| ".js.mem", |
| ".data", |
| ".fetch.js", |
| ".js.symbols", |
| ".wasm.debug.wasm", |
| ".html", |
| ] |
| |
| _WASM_BINARY_COMMON_ATTRS = { |
| "backend": attr.string( |
| default = "_default", |
| values = ["_default", "emscripten", "llvm"], |
| ), |
| "cc_target": attr.label( |
| cfg = _wasm_transition, |
| mandatory = True, |
| ), |
| "exit_runtime": attr.bool( |
| default = False, |
| ), |
| "threads": attr.string( |
| default = "_default", |
| values = ["_default", "emscripten", "off"], |
| ), |
| "simd": attr.bool( |
| default = False, |
| ), |
| "_allowlist_function_transition": attr.label( |
| default = "@bazel_tools//tools/allowlists/function_transition_allowlist", |
| ), |
| "_wasm_binary_extractor": attr.label( |
| executable = True, |
| allow_files = True, |
| cfg = "exec", |
| default = Label("@emsdk//emscripten_toolchain:wasm_binary"), |
| ), |
| } |
| |
| def _wasm_cc_binary_impl(ctx): |
| args = ctx.actions.args() |
| cc_target = ctx.attr.cc_target[0] |
| |
| for output in ctx.outputs.outputs: |
| valid_extname = False |
| for allowed_extname in _ALLOW_OUTPUT_EXTNAMES: |
| if output.path.endswith(allowed_extname): |
| valid_extname = True |
| break |
| if not valid_extname: |
| fail("Invalid output '{}'. Allowed extnames: {}".format(output.basename, ", ".join(_ALLOW_OUTPUT_EXTNAMES))) |
| |
| args.add_all("--archive", ctx.files.cc_target) |
| args.add_joined("--outputs", ctx.outputs.outputs, join_with = ",") |
| |
| ctx.actions.run( |
| inputs = ctx.files.cc_target, |
| outputs = ctx.outputs.outputs, |
| arguments = [args], |
| executable = ctx.executable._wasm_binary_extractor, |
| ) |
| |
| return DefaultInfo( |
| files = depset(ctx.outputs.outputs), |
| # This is needed since rules like web_test usually have a data |
| # dependency on this target. |
| data_runfiles = ctx.runfiles(transitive_files = depset(ctx.outputs.outputs)), |
| ) |
| |
| def _wasm_cc_binary_legacy_impl(ctx): |
| cc_target = ctx.attr.cc_target[0] |
| outputs = [ |
| ctx.outputs.loader, |
| ctx.outputs.wasm, |
| ctx.outputs.map, |
| ctx.outputs.mem, |
| ctx.outputs.fetch, |
| ctx.outputs.worker, |
| ctx.outputs.data, |
| ctx.outputs.symbols, |
| ctx.outputs.dwarf, |
| ctx.outputs.html, |
| ] |
| |
| args = ctx.actions.args() |
| args.add("--allow_empty_outputs") |
| args.add_all("--archive", ctx.files.cc_target) |
| args.add_joined("--outputs", outputs, join_with = ",") |
| |
| ctx.actions.run( |
| inputs = ctx.files.cc_target, |
| outputs = outputs, |
| arguments = [args], |
| executable = ctx.executable._wasm_binary_extractor, |
| ) |
| |
| return DefaultInfo( |
| executable = ctx.outputs.wasm, |
| files = depset(outputs), |
| # This is needed since rules like web_test usually have a data |
| # dependency on this target. |
| data_runfiles = ctx.runfiles(transitive_files = depset(outputs)), |
| ) |
| |
| _wasm_cc_binary = rule( |
| name = "wasm_cc_binary", |
| implementation = _wasm_cc_binary_impl, |
| attrs = dict( |
| _WASM_BINARY_COMMON_ATTRS, |
| outputs = attr.output_list( |
| allow_empty = False, |
| mandatory = True, |
| ), |
| ), |
| ) |
| |
| def _wasm_binary_legacy_outputs(name, cc_target): |
| basename = cc_target.name |
| basename = basename.split(".")[0] |
| outputs = { |
| "loader": "{}/{}.js".format(name, basename), |
| "wasm": "{}/{}.wasm".format(name, basename), |
| "map": "{}/{}.wasm.map".format(name, basename), |
| "mem": "{}/{}.js.mem".format(name, basename), |
| "fetch": "{}/{}.fetch.js".format(name, basename), |
| "worker": "{}/{}.worker.js".format(name, basename), |
| "data": "{}/{}.data".format(name, basename), |
| "symbols": "{}/{}.js.symbols".format(name, basename), |
| "dwarf": "{}/{}.wasm.debug.wasm".format(name, basename), |
| "html": "{}/{}.html".format(name, basename), |
| } |
| |
| return outputs |
| |
| _wasm_cc_binary_legacy = rule( |
| name = "wasm_cc_binary", |
| implementation = _wasm_cc_binary_legacy_impl, |
| attrs = _WASM_BINARY_COMMON_ATTRS, |
| outputs = _wasm_binary_legacy_outputs, |
| ) |
| |
| # Wraps a C++ Blaze target, extracting the appropriate files. |
| # |
| # This rule will transition to the emscripten toolchain in order |
| # to build the the cc_target as a WebAssembly binary. |
| # |
| # Args: |
| # name: The name of the rule. |
| # cc_target: The cc_binary or cc_library to extract files from. |
| def wasm_cc_binary(outputs = None, **kwargs): |
| # for backwards compatibility if no outputs are set the deprecated |
| # implementation is used. |
| if not outputs: |
| _wasm_cc_binary_legacy(**kwargs) |
| else: |
| _wasm_cc_binary(outputs = outputs, **kwargs) |