Skip to content

Commit 6166193

Browse files
authored
Don't clobber existing .wasm file in -sSINGLE_FILE mode (#21739)
Use a temp file for when the wasm is not going to be part of the output. As part of this change I had to update --emit-tsd to output relative to the JS rather than the wasm file, since the wasm file might be in the tmp directory after this change.
1 parent 51ff47f commit 6166193

File tree

3 files changed

+81
-60
lines changed

3 files changed

+81
-60
lines changed

test/test_other.py

Lines changed: 61 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -4912,7 +4912,12 @@ def test_warn_dylibs(self):
49124912
'O2': [['-O2']],
49134913
'O3': [['-O3']],
49144914
})
4915-
def test_symbol_map(self, opts):
4915+
@parameterized({
4916+
'': [1],
4917+
'wasm2js': [0],
4918+
'wasm2js_2': [2],
4919+
})
4920+
def test_symbol_map(self, wasm, opts):
49164921
def get_symbols_lines(symbols_file):
49174922
self.assertTrue(os.path.isfile(symbols_file), "Symbols file %s isn't created" % symbols_file)
49184923
# check that the map is correct
@@ -4939,10 +4944,8 @@ def guess_symbols_file_type(symbols_file):
49394944
UNMINIFIED_HEAP8 = 'var HEAP8 = new '
49404945
UNMINIFIED_MIDDLE = 'function middle'
49414946

4942-
for wasm in [0, 1, 2]:
4943-
print(opts, wasm)
4944-
self.clear()
4945-
create_file('src.c', r'''
4947+
self.clear()
4948+
create_file('src.c', r'''
49464949
#include <emscripten.h>
49474950

49484951
EM_JS(int, run_js, (), {
@@ -4962,47 +4965,48 @@ def guess_symbols_file_type(symbols_file):
49624965
EM_ASM({ _middle() });
49634966
}
49644967
''')
4965-
cmd = [EMCC, 'src.c', '--emit-symbol-map'] + opts
4966-
if wasm != 1:
4967-
cmd.append(f'-sWASM={wasm}')
4968-
self.run_process(cmd)
4968+
cmd = [EMCC, 'src.c', '--emit-symbol-map'] + opts
4969+
if wasm != 1:
4970+
cmd.append(f'-sWASM={wasm}')
4971+
self.run_process(cmd)
49694972

4970-
minified_middle = get_minified_middle('a.out.js.symbols')
4971-
self.assertNotEqual(minified_middle, None, "Missing minified 'middle' function")
4972-
if wasm:
4973-
# stack traces are standardized enough that we can easily check that the
4974-
# minified name is actually in the output
4975-
stack_trace_reference = 'wasm-function[%s]' % minified_middle
4976-
out = self.run_js('a.out.js')
4977-
self.assertContained(stack_trace_reference, out)
4978-
# make sure there are no symbols in the wasm itself
4979-
wat = self.get_wasm_text('a.out.wasm')
4980-
for func_start in ('(func $middle', '(func $_middle'):
4981-
self.assertNotContained(func_start, wat)
4982-
4983-
# Ensure symbols file type according to `-sWASM=` mode
4984-
if wasm == 0:
4985-
self.assertEqual(guess_symbols_file_type('a.out.js.symbols'), 'js', 'Primary symbols file should store JS mappings')
4986-
elif wasm == 1:
4987-
self.assertEqual(guess_symbols_file_type('a.out.js.symbols'), 'wasm', 'Primary symbols file should store Wasm mappings')
4988-
elif wasm == 2:
4989-
# special case when both JS and Wasm targets are created
4990-
minified_middle_2 = get_minified_middle('a.out.wasm.js.symbols')
4991-
self.assertNotEqual(minified_middle_2, None, "Missing minified 'middle' function")
4992-
self.assertEqual(guess_symbols_file_type('a.out.js.symbols'), 'wasm', 'Primary symbols file should store Wasm mappings')
4993-
self.assertEqual(guess_symbols_file_type('a.out.wasm.js.symbols'), 'js', 'Secondary symbols file should store JS mappings')
4994-
4995-
# check we don't keep unnecessary debug info with wasm2js when emitting
4996-
# a symbol map
4997-
if wasm == 0 and '-O' in str(opts):
4998-
js = read_file('a.out.js')
4999-
self.assertNotContained(UNMINIFIED_HEAP8, js)
5000-
self.assertNotContained(UNMINIFIED_MIDDLE, js)
5001-
# verify those patterns would exist with more debug info
5002-
self.run_process(cmd + ['--profiling-funcs'])
5003-
js = read_file('a.out.js')
5004-
self.assertContained(UNMINIFIED_HEAP8, js)
5005-
self.assertContained(UNMINIFIED_MIDDLE, js)
4973+
minified_middle = get_minified_middle('a.out.js.symbols')
4974+
self.assertNotEqual(minified_middle, None, "Missing minified 'middle' function")
4975+
if wasm:
4976+
# stack traces are standardized enough that we can easily check that the
4977+
# minified name is actually in the output
4978+
stack_trace_reference = 'wasm-function[%s]' % minified_middle
4979+
out = self.run_js('a.out.js')
4980+
self.assertContained(stack_trace_reference, out)
4981+
# make sure there are no symbols in the wasm itself
4982+
wat = self.get_wasm_text('a.out.wasm')
4983+
for func_start in ('(func $middle', '(func $_middle'):
4984+
self.assertNotContained(func_start, wat)
4985+
4986+
# Ensure symbols file type according to `-sWASM=` mode
4987+
if wasm == 0:
4988+
self.assertEqual(guess_symbols_file_type('a.out.js.symbols'), 'js', 'Primary symbols file should store JS mappings')
4989+
elif wasm == 1:
4990+
self.assertEqual(guess_symbols_file_type('a.out.js.symbols'), 'wasm', 'Primary symbols file should store Wasm mappings')
4991+
elif wasm == 2:
4992+
# special case when both JS and Wasm targets are created
4993+
minified_middle_2 = get_minified_middle('a.out.wasm.js.symbols')
4994+
self.assertNotEqual(minified_middle_2, None, "Missing minified 'middle' function")
4995+
self.assertEqual(guess_symbols_file_type('a.out.js.symbols'), 'wasm', 'Primary symbols file should store Wasm mappings')
4996+
self.assertEqual(guess_symbols_file_type('a.out.wasm.js.symbols'), 'js', 'Secondary symbols file should store JS mappings')
4997+
return
4998+
4999+
# check we don't keep unnecessary debug info with wasm2js when emitting
5000+
# a symbol map
5001+
if wasm == 0 and '-O' in str(opts):
5002+
js = read_file('a.out.js')
5003+
self.assertNotContained(UNMINIFIED_HEAP8, js)
5004+
self.assertNotContained(UNMINIFIED_MIDDLE, js)
5005+
# verify those patterns would exist with more debug info
5006+
self.run_process(cmd + ['--profiling-funcs'])
5007+
js = read_file('a.out.js')
5008+
self.assertContained(UNMINIFIED_HEAP8, js)
5009+
self.assertContained(UNMINIFIED_MIDDLE, js)
50065010

50075011
@parameterized({
50085012
'': [[]],
@@ -9148,6 +9152,18 @@ def test_single_file_shell(self):
91489152
def test_single_file_shell_sync_compile(self):
91499153
self.do_runf('hello_world.c', emcc_args=['-sSINGLE_FILE', '-sWASM_ASYNC_COMPILATION=0'])
91509154

9155+
def test_single_file_no_clobber_wasm(self):
9156+
create_file('hello_world.wasm', 'not wasm')
9157+
self.do_runf('hello_world.c', emcc_args=['-sSINGLE_FILE'])
9158+
self.assertExists('hello_world.js')
9159+
self.assertFileContents('hello_world.wasm', 'not wasm')
9160+
9161+
def test_wasm2js_no_clobber_wasm(self):
9162+
create_file('hello_world.wasm', 'not wasm')
9163+
self.do_runf('hello_world.c', emcc_args=['-sWASM=0'])
9164+
self.assertExists('hello_world.js')
9165+
self.assertFileContents('hello_world.wasm', 'not wasm')
9166+
91519167
def test_emar_M(self):
91529168
create_file('file1', ' ')
91539169
create_file('file2', ' ')

tools/building.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -983,11 +983,7 @@ def strip(infile, outfile, debug=False, sections=None):
983983

984984
# extract the DWARF info from the main file, and leave the wasm with
985985
# debug into as a file on the side
986-
def emit_debug_on_side(wasm_file):
987-
# if the dwarf filename wasn't provided, use the default target + a suffix
988-
wasm_file_with_dwarf = settings.SEPARATE_DWARF
989-
if wasm_file_with_dwarf is True:
990-
wasm_file_with_dwarf = wasm_file + '.debug.wasm'
986+
def emit_debug_on_side(wasm_file, wasm_file_with_dwarf):
991987
embedded_path = settings.SEPARATE_DWARF_URL
992988
if not embedded_path:
993989
# a path was provided - make it relative to the wasm.

tools/link.py

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -789,6 +789,10 @@ def phase_linker_setup(options, state, newargs):
789789
if options.oformat in (OFormat.WASM, OFormat.BARE):
790790
# If the user asks directly for a wasm file then this *is* the target
791791
wasm_target = target
792+
elif settings.SINGLE_FILE or settings.WASM == 0:
793+
# In SINGLE_FILE or WASM2JS mode the wasm file is not part of the output at
794+
# all so we generate it the temp directory.
795+
wasm_target = in_temp(shared.replace_suffix(target, '.wasm'))
792796
else:
793797
# Otherwise the wasm file is produced alongside the final target.
794798
wasm_target = get_secondary_target(target, '.wasm')
@@ -1901,7 +1905,7 @@ def phase_post_link(options, state, in_wasm, wasm_target, target, js_syms):
19011905
phase_embind_aot(wasm_target, js_syms)
19021906

19031907
if options.emit_tsd:
1904-
phase_emit_tsd(options, wasm_target, js_syms, metadata)
1908+
phase_emit_tsd(options, wasm_target, state.js_target, js_syms, metadata)
19051909

19061910
if options.js_transform:
19071911
phase_source_transforms(options)
@@ -1984,14 +1988,14 @@ def run_embind_gen(wasm_target, js_syms, extra_settings):
19841988

19851989

19861990
@ToolchainProfiler.profile_block('emit tsd')
1987-
def phase_emit_tsd(options, wasm_target, js_syms, metadata):
1991+
def phase_emit_tsd(options, wasm_target, js_target, js_syms, metadata):
19881992
logger.debug('emit tsd')
19891993
filename = options.emit_tsd
19901994
embind_tsd = ''
19911995
if settings.EMBIND:
19921996
embind_tsd = run_embind_gen(wasm_target, js_syms, {'EMBIND_JS': False})
19931997
all_tsd = emscripten.create_tsd(metadata, embind_tsd)
1994-
out_file = os.path.join(os.path.dirname(wasm_target), filename)
1998+
out_file = os.path.join(os.path.dirname(js_target), filename)
19951999
write_file(out_file, all_tsd)
19962000

19972001

@@ -2261,32 +2265,37 @@ def phase_binaryen(target, options, wasm_target):
22612265

22622266
if settings.WASM != 2:
22632267
final_js = wasm2js
2264-
# if we only target JS, we don't need the wasm any more
2265-
delete_file(wasm_target)
22662268

22672269
save_intermediate('wasm2js')
22682270

2271+
generating_wasm = settings.WASM == 2 or not settings.WASM2JS
2272+
22692273
# emit the final symbols, either in the binary or in a symbol map.
22702274
# this will also remove debug info if we only kept it around in the intermediate invocations.
22712275
# note that if we aren't emitting a binary (like in wasm2js) then we don't
22722276
# have anything to do here.
22732277
if options.emit_symbol_map:
22742278
intermediate_debug_info -= 1
2275-
if os.path.exists(wasm_target):
2279+
if generating_wasm:
22762280
building.handle_final_wasm_symbols(wasm_file=wasm_target, symbols_file=symbols_file, debug_info=intermediate_debug_info)
22772281
save_intermediate_with_wasm('symbolmap', wasm_target)
22782282

2279-
if settings.DEBUG_LEVEL >= 3 and settings.SEPARATE_DWARF and os.path.exists(wasm_target):
2280-
building.emit_debug_on_side(wasm_target)
2283+
if settings.DEBUG_LEVEL >= 3 and settings.SEPARATE_DWARF and generating_wasm:
2284+
# if the dwarf filename wasn't provided, use the default target + a suffix
2285+
wasm_file_with_dwarf = settings.SEPARATE_DWARF
2286+
if wasm_file_with_dwarf is True:
2287+
# Historically this file has been called `.wasm.debug.wasm`
2288+
# TODO(sbc): Should this just be `.debug.wasm`
2289+
wasm_file_with_dwarf = get_secondary_target(target, '.wasm.debug.wasm')
2290+
building.emit_debug_on_side(wasm_target, wasm_file_with_dwarf)
22812291

22822292
# we have finished emitting the wasm, and so intermediate debug info will
22832293
# definitely no longer be used tracking it.
22842294
if debug_function_names:
22852295
intermediate_debug_info -= 1
22862296
assert intermediate_debug_info == 0
22872297
# strip debug info if it was not already stripped by the last command
2288-
if not debug_function_names and building.binaryen_kept_debug_info and \
2289-
building.os.path.exists(wasm_target):
2298+
if not debug_function_names and building.binaryen_kept_debug_info and generating_wasm:
22902299
with ToolchainProfiler.profile_block('strip_name_section'):
22912300
building.strip(wasm_target, wasm_target, debug=False, sections=["name"])
22922301

0 commit comments

Comments
 (0)