Skip to content

Commit d9c260d

Browse files
authored
Enable SINGLE_FILE + PTHREADS html output (emscripten-core#22547)
In SINGLE_FILE mode, the JS is inlined directly into the generated HTML. However for pthreads builds we need to be able to create workers that use the same inlined JS. To enable this we encode the JS file into a data URL that can be used for the script `src` attribute. The pthread worker can then use this `src`. Prior to this change using SINGLE_FILE + PTHREADS + html output would simply fail.
1 parent c59f752 commit d9c260d

File tree

2 files changed

+26
-10
lines changed

2 files changed

+26
-10
lines changed

test/test_browser.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3693,11 +3693,17 @@ def test_pthread_gcc_spinlock(self):
36933693
'': ([],),
36943694
'O3': (['-O3'],),
36953695
'minimal_runtime': (['-sMINIMAL_RUNTIME'],),
3696+
'single_file': (['-sSINGLE_FILE'],),
36963697
})
36973698
def test_pthread_create(self, args):
36983699
self.btest_exit('pthread/test_pthread_create.c',
36993700
args=['-pthread', '-sPTHREAD_POOL_SIZE=8'] + args,
37003701
extra_tries=0) # this should be 100% deterministic
3702+
files = os.listdir('.')
3703+
if '-sSINGLE_FILE' in args:
3704+
self.assertEqual(len(files), 1, files)
3705+
else:
3706+
self.assertEqual(len(files), 4, files)
37013707

37023708
# Test that preallocating worker threads work.
37033709
def test_pthread_preallocates_workers(self):

tools/link.py

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2079,7 +2079,7 @@ def phase_final_emitting(options, state, target, wasm_target):
20792079
return
20802080

20812081
target_dir = os.path.dirname(os.path.abspath(target))
2082-
if settings.PTHREADS and not settings.STRICT:
2082+
if settings.PTHREADS and not settings.STRICT and not settings.SINGLE_FILE:
20832083
worker_file = shared.replace_suffix(target, get_worker_js_suffix())
20842084
write_file(worker_file, '''\
20852085
// This file is no longer used by emscripten and has been created as a placeholder
@@ -2535,14 +2535,19 @@ def generate_traditional_runtime_html(target, options, js_target, target_basenam
25352535
# Normal code generation path
25362536
script.src = base_js_target
25372537

2538-
# inline script for SINGLE_FILE output
25392538
if settings.SINGLE_FILE:
2540-
js_contents = script.inline or ''
2541-
if script.src:
2542-
js_contents += read_file(js_target)
2539+
# In SINGLE_FILE mode we either inline the script, or in the case
2540+
# of SHARED_MEMORY convert the entire thing into a data URL.
2541+
if settings.SHARED_MEMORY:
2542+
assert not script.inline
2543+
script.src = get_subresource_location(js_target)
2544+
else:
2545+
js_contents = script.inline or ''
2546+
if script.src:
2547+
js_contents += read_file(js_target)
2548+
script.src = None
2549+
script.inline = read_file(js_target)
25432550
delete_file(js_target)
2544-
script.src = None
2545-
script.inline = js_contents
25462551
else:
25472552
if not settings.WASM_ASYNC_COMPILATION:
25482553
# We need to load the wasm file before anything else, since it
@@ -2819,16 +2824,21 @@ def replacement(self):
28192824
"""Returns the script tag to replace the {{{ SCRIPT }}} tag in the target"""
28202825
assert (self.src or self.inline) and not (self.src and self.inline)
28212826
if self.src:
2822-
quoted_src = quote(self.src)
2827+
src = self.src
2828+
if src.startswith('data:'):
2829+
filename = src
2830+
else:
2831+
src = quote(self.src)
2832+
filename = f'./{src}'
28232833
if settings.EXPORT_ES6:
28242834
return f'''
28252835
<script type="module">
2826-
import initModule from "./{quoted_src}";
2836+
import initModule from "{filename}";
28272837
initModule(Module);
28282838
</script>
28292839
'''
28302840
else:
2831-
return f'<script async type="text/javascript" src="{quoted_src}"></script>'
2841+
return f'<script async type="text/javascript" src="{src}"></script>'
28322842
else:
28332843
return '<script>\n%s\n</script>' % self.inline
28342844

0 commit comments

Comments
 (0)