vendor: EMSDK 6.0.6 scripts at e26beb67ef18a37d71d522d8b7096b20a4d3d0f9

This commit is contained in:
Gitea Mirror Bot
2026-08-22 00:06:30 +08:00
commit c47b3c2696
138 changed files with 14232 additions and 0 deletions
+69
View File
@@ -0,0 +1,69 @@
load("@rules_cc//cc:defs.bzl", "cc_library")
load("@rules_python//python:py_binary.bzl", "py_binary")
load("//:emscripten_deps.bzl", "emscripten_repo_name")
load("//:remote_emscripten_repository.bzl", "create_toolchains", "emscripten_toolchain_name")
load(":platform_info.bzl", "platform_info")
package(default_visibility = ["//visibility:public"])
# dlmalloc.bc is implicitly added by the emscripten toolchain
cc_library(name = "malloc")
platform_info(
name = "platform_info",
script_extension = select({
"@platforms//os:windows": ".bat",
"//conditions:default": ".sh",
}),
visibility = [":__subpackages__"],
)
create_toolchains(
name = emscripten_toolchain_name("linux"),
exec_compatible_with = [
"@platforms//os:linux",
"@platforms//cpu:x86_64",
],
repo_name = emscripten_repo_name("linux"),
)
create_toolchains(
name = emscripten_toolchain_name("linux_arm64"),
exec_compatible_with = [
"@platforms//os:linux",
"@platforms//cpu:arm64",
],
repo_name = emscripten_repo_name("linux_arm64"),
)
create_toolchains(
name = emscripten_toolchain_name("mac"),
exec_compatible_with = [
"@platforms//os:macos",
"@platforms//cpu:x86_64",
],
repo_name = emscripten_repo_name("mac"),
)
create_toolchains(
name = emscripten_toolchain_name("mac_arm64"),
exec_compatible_with = [
"@platforms//os:macos",
"@platforms//cpu:arm64",
],
repo_name = emscripten_repo_name("mac_arm64"),
)
create_toolchains(
name = emscripten_toolchain_name("win"),
exec_compatible_with = [
"@platforms//os:windows",
"@platforms//cpu:x86_64",
],
repo_name = emscripten_repo_name("win"),
)
py_binary(
name = "wasm_binary",
srcs = ["wasm_binary.py"],
)
+16
View File
@@ -0,0 +1,16 @@
import os
import platform
ROOT_DIR = os.environ["ROOT_DIR"]
EMSCRIPTEN_ROOT = os.environ["EMSCRIPTEN"]
BINARYEN_ROOT = os.path.join(ROOT_DIR, os.environ["EM_BIN_PATH"])
LLVM_ROOT = os.path.join(BINARYEN_ROOT, "bin")
NODE_JS = os.path.join(ROOT_DIR, os.environ["NODE_JS_PATH"])
FROZEN_CACHE = True
# This works around an issue with Bazel RBE where the symlinks in node_modules/.bin
# are uploaded as the linked files, which means the cli.js cannot load its
# dependencies from the expected locations.
# See https://github.com/emscripten-core/emscripten/pull/16640 for more
CLOSURE_COMPILER = [NODE_JS, os.path.join(EMSCRIPTEN_ROOT, "node_modules",
"google-closure-compiler", "cli.js")]
+5
View File
@@ -0,0 +1,5 @@
@ECHO OFF
call %~dp0\env.bat
"%EMSDK_PYTHON%" %EMSCRIPTEN%\emar.py %*
+5
View File
@@ -0,0 +1,5 @@
#!/bin/bash
source $(dirname $0)/env.sh
exec $EMSDK_PYTHON $EMSCRIPTEN/emar.py "$@"
+5
View File
@@ -0,0 +1,5 @@
@ECHO OFF
call %~dp0\env.bat
"%EMSDK_PYTHON%" %EMSCRIPTEN%\emcc.py %*
+5
View File
@@ -0,0 +1,5 @@
#!/bin/bash
source $(dirname $0)/env.sh
exec $EMSDK_PYTHON $EMSCRIPTEN/emcc.py "$@"
+5
View File
@@ -0,0 +1,5 @@
@ECHO OFF
call %~dp0\env.bat
"%EMSDK_PYTHON%" %~dp0\link_wrapper.py %*
+5
View File
@@ -0,0 +1,5 @@
#!/bin/bash
source $(dirname $0)/env.sh
exec $EMSDK_PYTHON $(dirname $0)/link_wrapper.py "$@"
+7
View File
@@ -0,0 +1,7 @@
@ECHO OFF
set ROOT_DIR=%EXT_BUILD_ROOT%
if "%ROOT_DIR%"=="" set ROOT_DIR=%CD%
set EMSCRIPTEN=%ROOT_DIR%\%EM_BIN_PATH%\emscripten
set EM_CONFIG=%ROOT_DIR%\%EM_CONFIG_PATH%
set EMSDK_PYTHON=%ROOT_DIR%\%BAZEL_PYTHON_RELPATH%
+6
View File
@@ -0,0 +1,6 @@
#!/bin/bash
export ROOT_DIR=${EXT_BUILD_ROOT:-$(pwd -P)}
export EMSCRIPTEN=$ROOT_DIR/$EM_BIN_PATH/emscripten
export EM_CONFIG=$ROOT_DIR/$EM_CONFIG_PATH
export EMSDK_PYTHON=$ROOT_DIR/$BAZEL_PYTHON_RELPATH
+164
View File
@@ -0,0 +1,164 @@
#!/usr/bin/env python
"""wrapper around emcc link step.
This wrapper currently serves the following purposes.
1. When building with --config=wasm the final output is multiple files, usually
at least one .js and one .wasm file. Since the cc_binary link step only
allows a single output, we must tar up the outputs into a single file.
2. Add quotes around arguments that need them in the response file to work
around a bazel quirk.
3. Ensure the external_debug_info section of the wasm points at the correct
bazel path.
"""
import argparse
import os
import subprocess
import sys
# Only argument should be @path/to/parameter/file
assert sys.argv[1][0] == '@', sys.argv
param_filename = sys.argv[1][1:]
param_file_args = [line.strip() for line in open(param_filename).readlines()]
# Re-write response file if needed.
if any(' ' in a for a in param_file_args):
new_param_filename = param_filename + '.modified'
with open(new_param_filename, 'w') as f:
for param in param_file_args:
if ' ' in param:
f.write('"%s"' % param)
else:
f.write(param)
f.write('\n')
sys.argv[1] = '@' + new_param_filename
emxx_py = os.path.join(os.environ['EMSCRIPTEN'], 'em++.py')
rtn = subprocess.call([sys.executable, emxx_py, *sys.argv[1:]])
if rtn != 0:
sys.exit(1)
# Parse the arguments that we gave to the linker to determine what the output
# file is named and what the output format is.
parser = argparse.ArgumentParser(add_help=False)
parser.add_argument('-o')
parser.add_argument('--oformat')
options = parser.parse_known_args(param_file_args)[0]
output_file = options.o
oformat = options.oformat
outdir = os.path.normpath(os.path.dirname(output_file))
base_name = os.path.basename(output_file)
# The output file name is the name of the build rule that was built.
# Add an appropriate file extension based on --oformat.
if oformat is not None:
base_name_split = os.path.splitext(base_name)
# If the output name has no extension, give it the appropriate extension.
if not base_name_split[1]:
os.replace(output_file, output_file + '.' + oformat)
# If the output name does have an extension and it matches the output format,
# change the base_name so it doesn't have an extension.
elif base_name_split[1] == '.' + oformat:
base_name = base_name_split[0]
# If the output name does have an extension and it does not match the output
# format, change the base_name so it doesn't have an extension and rename
# the output_file so it has the proper extension.
# Note that if you do something like name your build rule "foo.js" and pass
# "--oformat=html", emscripten will write to the same file for both the js and
# html output, overwriting the js output entirely with the html.
# Please don't do that.
else:
base_name = base_name_split[0]
os.replace(output_file, os.path.join(outdir, base_name + '.' + oformat))
files = []
extensions = [
'.js',
'.wasm',
'.wasm.map',
'.data',
'.js.symbols',
'.wasm.debug.wasm',
'.html',
'.ts',
'.d.ts',
]
for ext in extensions:
filename = base_name + ext
if os.path.exists(os.path.join(outdir, filename)):
files.append(filename)
wasm_base = os.path.join(outdir, base_name + '.wasm')
if os.path.exists(wasm_base + '.debug.wasm') and os.path.exists(wasm_base):
# If we have a .wasm.debug.wasm file and a .wasm file, we need to rewrite the
# section in the .wasm file that refers to it. The path that's in there
# is the blaze output path; we want it to be just the filename.
llvm_objcopy = os.path.join(
os.environ['EM_BIN_PATH'], 'bin/llvm-objcopy')
# First, check to make sure the .wasm file has the header that needs to be
# rewritten.
rtn = subprocess.call([
llvm_objcopy,
'--dump-section=external_debug_info=/dev/null',
wasm_base], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if rtn == 0:
# If llvm-objcopy did not return an error, the external_debug_info section
# must exist, so we're good to continue.
# Next we need to convert length of the filename to LEB128.
# Start by converting the length of the filename to a bit string.
bit_string = '{0:b}'.format(len(base_name + '.wasm.debug.wasm'))
# Pad the bit string with 0s so that its length is a multiple of 7.
while len(bit_string) % 7 != 0:
bit_string = '0' + bit_string
# Break up our bit string into chunks of 7.
# We do this backwards because the final format is little-endian.
final_bytes = bytearray()
for i in reversed(range(0, len(bit_string), 7)):
binary_part = bit_string[i:i + 7]
if i != 0:
# Every chunk except the last one needs to be prepended with '1'.
# The length of each chunk is 7, so that one has an implicit '0'.
binary_part = '1' + binary_part
final_bytes.append(int(binary_part, 2))
# Finally, add the actual filename.
final_bytes.extend((base_name + '.wasm.debug.wasm').encode())
# Write our length + filename bytes to a temp file.
with open(base_name + '_debugsection.tmp', 'wb+') as f:
f.write(final_bytes)
f.close()
# First delete the old section.
subprocess.check_call([
llvm_objcopy,
wasm_base,
'--remove-section=external_debug_info'])
# Rewrite section with the new size and filename from the temp file.
subprocess.check_call([
llvm_objcopy,
wasm_base,
'--add-section=external_debug_info=' + base_name + '_debugsection.tmp'])
# Make sure we have at least one output file.
if not files:
print('em++.py did not appear to output any known files!')
sys.exit(1)
# cc_binary must output exactly one file; put all the output files in a tarball.
cmd = ['tar', 'cf', base_name + '.tar', *files]
subprocess.check_call(cmd, cwd=outdir)
os.replace(os.path.join(outdir, base_name + '.tar'), output_file)
sys.exit(0)
@@ -0,0 +1,22 @@
"""Defines a provider and rule for platform information.
"""
PlatformInfo = provider(
doc = "Provides some info about a platform",
fields = {
"script_extension": "The script extension for the platform, e.g. '.sh' or '.bat'",
},
)
platform_info = rule(
implementation = lambda ctx: PlatformInfo(
script_extension = ctx.attr.script_extension,
),
attrs = {
"script_extension": attr.string(
mandatory = True,
values = [".sh", ".bat"],
doc = "The script extension for the platform, e.g. '.sh' or '.bat'",
),
},
)
File diff suppressed because it is too large Load Diff
+56
View File
@@ -0,0 +1,56 @@
"""Unpackages a bazel emscripten archive for use in a bazel BUILD rule.
This script will take a tar archive containing the output of the emscripten
toolchain. This file contains any output files produced by a wasm_cc_binary or a
cc_binary built with --config=wasm. The files are extracted into the given
output paths.
The contents of the archive are expected to match the given outputs extnames.
This script and its accompanying Bazel rule should allow you to extract a
WebAssembly binary into a larger web application.
"""
import argparse
import os
import tarfile
def ensure(f):
if not os.path.exists(f):
with open(f, 'w'):
pass
def main():
parser = argparse.ArgumentParser()
parser.add_argument('--archive', help='The archive to extract from.')
parser.add_argument('--outputs', help='Comma separated list of files that should be extracted from the archive. Only the extname has to match a file in the archive.')
parser.add_argument('--allow_empty_outputs', help='If an output listed in --outputs does not exist, create it anyways.', action='store_true')
args = parser.parse_args()
args.archive = os.path.normpath(args.archive)
args.outputs = args.outputs.split(",")
tar = tarfile.open(args.archive)
for member in tar.getmembers():
extname = '.' + member.name.split('.', 1)[1]
for idx, output in enumerate(args.outputs):
if output.endswith(extname):
member_file = tar.extractfile(member)
with open(output, "wb") as output_file:
output_file.write(member_file.read())
args.outputs.pop(idx)
break
for output in args.outputs:
extname = '.' + output.split('.', 1)[1]
if args.allow_empty_outputs:
ensure(output)
else:
print("[ERROR] Archive does not contain file with extname: %s" % extname)
if __name__ == '__main__':
main()
@@ -0,0 +1,216 @@
"""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.simd != "off":
features.append("wasm_simd")
if attr.simd == "relaxed_simd":
features.append("wasm_relaxed_simd")
platform = "@emsdk//:platform_wasm"
if attr.standalone:
platform = "@emsdk//:platform_wasi"
features.append("wasm_standalone")
return {
"//command_line_option:compiler": "emscripten",
"//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": [platform],
# This is hardcoded to an empty cc_library because the malloc library
# is implicitly added by the emscripten toolchain
"//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: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",
".data",
".js.symbols",
".wasm.debug.wasm",
".html",
".ts",
".d.ts",
]
_WASM_BINARY_COMMON_ATTRS = {
"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.string(
default = "off",
values = ["off", "simd128", "relaxed_simd"],
doc = "Enable SIMD support via emscripten.",
),
"standalone": 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)),
),
OutputGroupInfo(_wasm_tar = cc_target.files),
]
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.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)),
),
OutputGroupInfo(_wasm_tar = cc_target.files),
]
_wasm_cc_binary = rule(
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),
"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(
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)
@@ -0,0 +1,6 @@
"""Rules related to C++ and WebAssembly.
"""
load(":wasm_cc_binary.bzl", _wasm_cc_binary = "wasm_cc_binary")
wasm_cc_binary = _wasm_cc_binary