Skip to content

Commit 848260e

Browse files
walkingeyerobotMitch Foley
andauthored
Bazel add html support (#765)
* allow the bazel toolchain to output html files * allow for cc_binary rule names to end in .js * fix python name * continue to call emcc instead of em++ for now * small cleanup Co-authored-by: Mitch Foley <[email protected]>
1 parent ac98ca3 commit 848260e

File tree

4 files changed

+61
-25
lines changed

4 files changed

+61
-25
lines changed

bazel/emscripten_toolchain/crosstool.bzl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,11 @@ def _impl(ctx):
436436
# https://emscripten.org/docs/debugging/Sanitizers.html
437437
feature(name = "wasm_asan"),
438438
feature(name = "wasm_ubsan"),
439+
440+
feature(
441+
name = "output_format_js",
442+
enabled = True,
443+
),
439444
]
440445

441446
crosstool_default_flag_sets = [
@@ -547,6 +552,11 @@ def _impl(ctx):
547552
flags = ["-s", "PRINTF_LONG_DOUBLE=1"],
548553
features = ["precise_long_double_printf"],
549554
),
555+
flag_set(
556+
actions = all_link_actions,
557+
flags = ["--oformat=js"],
558+
features = ["output_format_js"],
559+
),
550560

551561
# Opt
552562
flag_set(

bazel/emscripten_toolchain/link_wrapper.py

Lines changed: 48 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,20 @@
33
44
This wrapper currently serves the following purposes.
55
6-
1. Ensures we always link to file with .js extension. The upstream default
7-
it to link to an llvm bitcode file which is never (AFAICT) want to do that.
8-
9-
2. When building with --config=wasm the final output is multiple files, usually
6+
1. When building with --config=wasm the final output is multiple files, usually
107
at least one .js and one .wasm file. Since the cc_binary link step only
118
allows a single output, we must tar up the outputs into a single file.
129
13-
3. Add quotes around arguments that need them in the response file to work
10+
2. Add quotes around arguments that need them in the response file to work
1411
around a bazel quirk.
12+
13+
3. Ensure the external_debug_info section of the wasm points at the correct
14+
bazel path.
1515
"""
1616

1717
from __future__ import print_function
1818

19+
import argparse
1920
import os
2021
import subprocess
2122
import sys
@@ -25,20 +26,8 @@
2526
param_filename = sys.argv[1][1:]
2627
param_file_args = [l.strip() for l in open(param_filename, 'r').readlines()]
2728

28-
output_index = param_file_args.index('-o') + 1
29-
orig_output = js_output = param_file_args[output_index]
30-
outdir = os.path.dirname(orig_output)
31-
32-
# google3-only(TODO(b/139440956): Default to False once the bug is fixed)
33-
replace_response_file = any(' ' in a for a in param_file_args)
34-
35-
if not os.path.splitext(orig_output)[1]:
36-
js_output = orig_output + '.js'
37-
param_file_args[output_index] = js_output
38-
replace_response_file = True
39-
4029
# Re-write response file if needed.
41-
if replace_response_file:
30+
if any(' ' in a for a in param_file_args):
4231
new_param_filename = param_filename + '.modified'
4332
with open(new_param_filename, 'w') as f:
4433
for param in param_file_args:
@@ -54,8 +43,41 @@
5443
if rtn != 0:
5544
sys.exit(1)
5645

57-
js_name = os.path.basename(js_output)
58-
base_name = os.path.splitext(js_name)[0]
46+
# Parse the arguments that we gave to the linker to determine what the output
47+
# file is named and what the output format is.
48+
parser = argparse.ArgumentParser(add_help=False)
49+
parser.add_argument('-o')
50+
parser.add_argument('--oformat')
51+
options = parser.parse_known_args(param_file_args)[0]
52+
output_file = options.o
53+
oformat = options.oformat
54+
outdir = os.path.dirname(output_file)
55+
base_name = os.path.basename(output_file)
56+
57+
# The output file name is the name of the build rule that was built.
58+
# Add an appropriate file extension based on --oformat.
59+
if oformat is not None:
60+
base_name_split = os.path.splitext(base_name)
61+
62+
# If the output name has no extension, give it the appropriate extension.
63+
if not base_name_split[1]:
64+
os.rename(output_file, output_file + '.' + oformat)
65+
66+
# If the output name does have an extension and it matches the output format,
67+
# change the base_name so it doesn't have an extension.
68+
elif base_name_split[1] == '.' + oformat:
69+
base_name = base_name_split[0]
70+
71+
# If the output name does have an extension and it does not match the output
72+
# format, change the base_name so it doesn't have an extension and rename
73+
# the output_file so it has the proper extension.
74+
# Note that if you do something like name your build rule "foo.js" and pass
75+
# "--oformat=html", emscripten will write to the same file for both the js and
76+
# html output, overwriting the js output entirely with the html.
77+
# Please don't do that.
78+
else:
79+
base_name = base_name_split[0]
80+
os.rename(output_file, os.path.join(outdir, base_name + '.' + oformat))
5981

6082
files = []
6183
extensions = [
@@ -67,7 +89,8 @@
6789
'.worker.js',
6890
'.data',
6991
'.js.symbols',
70-
'.wasm.debug.wasm'
92+
'.wasm.debug.wasm',
93+
'.html'
7194
]
7295

7396
for ext in extensions:
@@ -112,7 +135,7 @@
112135
binary_part = '1' + binary_part
113136
final_bytes.append(int(binary_part, 2))
114137
# Finally, add the actual filename.
115-
final_bytes.extend(base_name + '.wasm.debug.wasm')
138+
final_bytes.extend((base_name + '.wasm.debug.wasm').encode())
116139

117140
# Write our length + filename bytes to a temp file.
118141
with open('debugsection.tmp', 'wb+') as f:
@@ -134,11 +157,11 @@
134157
if len(files) > 1:
135158
cmd = ['tar', 'cf', 'tmp.tar'] + files
136159
subprocess.check_call(cmd, cwd=outdir)
137-
os.rename(os.path.join(outdir, 'tmp.tar'), orig_output)
160+
os.rename(os.path.join(outdir, 'tmp.tar'), output_file)
138161
elif len(files) == 1:
139162
# Otherwise, if only have a single output than move it to the expected name
140-
if files[0] != os.path.basename(orig_output):
141-
os.rename(os.path.join(outdir, files[0]), orig_output)
163+
if files[0] != os.path.basename(output_file):
164+
os.rename(os.path.join(outdir, files[0]), output_file)
142165
else:
143166
print('emcc.py did not appear to output any known files!')
144167
sys.exit(1)

bazel/emscripten_toolchain/wasm_binary.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ def main():
7777
ensure(os.path.join(args.output_path, stem + '.fetch.js'))
7878
ensure(os.path.join(args.output_path, stem + '.js.symbols'))
7979
ensure(os.path.join(args.output_path, stem + '.wasm.debug.wasm'))
80+
ensure(os.path.join(args.output_path, stem + '.html'))
8081

8182

8283
if __name__ == '__main__':

bazel/emscripten_toolchain/wasm_cc_binary.bzl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ def _wasm_binary_impl(ctx):
7474
ctx.outputs.data,
7575
ctx.outputs.symbols,
7676
ctx.outputs.dwarf,
77+
ctx.outputs.html,
7778
]
7879

7980
ctx.actions.run(
@@ -103,6 +104,7 @@ def _wasm_binary_outputs(name, cc_target):
103104
"data": "{}/{}.data".format(name, basename),
104105
"symbols": "{}/{}.js.symbols".format(name, basename),
105106
"dwarf": "{}/{}.wasm.debug.wasm".format(name, basename),
107+
"html": "{}/{}.html".format(name, basename),
106108
}
107109

108110
return outputs

0 commit comments

Comments
 (0)