add a wasm bazel toolchain (#603)
This commit is contained in:
146
bazel/emscripten_toolchain/link_wrapper.py
Normal file
146
bazel/emscripten_toolchain/link_wrapper.py
Normal file
@@ -0,0 +1,146 @@
|
||||
#!/usr/bin/env python
|
||||
"""wrapper around emcc link step.
|
||||
|
||||
This wrapper currently serves the following purposes.
|
||||
|
||||
1. Ensures we always link to file with .js extension. The upstream default
|
||||
it to link to an llvm bitcode file which is never (AFAICT) want to do that.
|
||||
|
||||
2. 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.
|
||||
|
||||
3. Add quotes around arguments that need them in the response file to work
|
||||
around a bazel quirk.
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
# Only argument should be @path/to/parameter/file
|
||||
assert sys.argv[1][0] == '@'
|
||||
param_filename = sys.argv[1][1:]
|
||||
param_file_args = [l.strip() for l in open(param_filename, 'r').readlines()]
|
||||
|
||||
output_index = param_file_args.index('-o') + 1
|
||||
orig_output = js_output = param_file_args[output_index]
|
||||
outdir = os.path.dirname(orig_output)
|
||||
|
||||
# google3-only(TODO(b/139440956): Default to False once the bug is fixed)
|
||||
replace_response_file = any(' ' in a for a in param_file_args)
|
||||
|
||||
if not os.path.splitext(orig_output)[1]:
|
||||
js_output = orig_output + '.js'
|
||||
param_file_args[output_index] = js_output
|
||||
replace_response_file = True
|
||||
|
||||
# Re-write response file if needed.
|
||||
if replace_response_file:
|
||||
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
|
||||
|
||||
emcc_py = os.path.join(os.environ['EMSCRIPTEN'], 'emcc.py')
|
||||
rtn = subprocess.call(['python3', emcc_py] + sys.argv[1:])
|
||||
if rtn != 0:
|
||||
sys.exit(1)
|
||||
|
||||
js_name = os.path.basename(js_output)
|
||||
base_name = os.path.splitext(js_name)[0]
|
||||
|
||||
files = []
|
||||
extensions = [
|
||||
'.js',
|
||||
'.wasm',
|
||||
'.wasm.map',
|
||||
'.js.mem',
|
||||
'.fetch.js',
|
||||
'.worker.js',
|
||||
'.data',
|
||||
'.js.symbols',
|
||||
'.wasm.debug.wasm'
|
||||
]
|
||||
|
||||
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['EMSCRIPTEN'], 'llvm-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')
|
||||
|
||||
# Write our length + filename bytes to a temp file.
|
||||
with open('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=debugsection.tmp'])
|
||||
|
||||
# If we have more than one output file then create tarball
|
||||
if len(files) > 1:
|
||||
cmd = ['tar', 'cf', 'tmp.tar'] + files
|
||||
subprocess.check_call(cmd, cwd=outdir)
|
||||
os.rename(os.path.join(outdir, 'tmp.tar'), orig_output)
|
||||
elif len(files) == 1:
|
||||
# Otherwise, if only have a single output than move it to the expected name
|
||||
if files[0] != os.path.basename(orig_output):
|
||||
os.rename(os.path.join(outdir, files[0]), orig_output)
|
||||
else:
|
||||
print('emcc.py did not appear to output any known files!')
|
||||
sys.exit(1)
|
||||
|
||||
sys.exit(0)
|
||||
Reference in New Issue
Block a user