Skip to content

Commit 49d3bbe

Browse files
committed
Add SINGLE_FILE option to embed all subresources into emitted JS
As discussed in #5279, subresource paths are converted into base64 data URIs.
1 parent 12c40a5 commit 49d3bbe

File tree

2 files changed

+34
-11
lines changed

2 files changed

+34
-11
lines changed

emcc.py

+27-11
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
if __name__ == '__main__':
2828
ToolchainProfiler.record_process_start()
2929

30-
import os, sys, shutil, tempfile, subprocess, shlex, time, re, logging, urllib
30+
import os, sys, shutil, tempfile, subprocess, shlex, time, re, logging, urllib, base64
3131
from subprocess import PIPE
3232
from tools import shared, jsrun, system_libs
3333
from tools.shared import execute, suffix, unsuffixed, unsuffixed_basename, WINDOWS, safe_move
@@ -513,6 +513,16 @@ def filter_emscripten_options(argv):
513513

514514
# ---------------- Utilities ---------------
515515

516+
# Returns the run-time subresource location for accessing by such means as XHR
517+
def get_subresource_location(path, media_type):
518+
if shared.Settings.SINGLE_FILE:
519+
f = open(path, 'rb')
520+
data = base64.b64encode(f.read())
521+
f.close()
522+
return 'data:' + media_type + ';base64,' + data
523+
else:
524+
return os.path.basename(path)
525+
516526
seen_names = {}
517527
def uniquename(name):
518528
if name not in seen_names:
@@ -799,7 +809,7 @@ def detect_fixed_language_mode(args):
799809
options.separate_asm = True
800810
logging.warning('forcing separate asm output (--separate-asm), because -s PRECISE_F32=2 or -s USE_PTHREADS=2 was passed.')
801811
if options.separate_asm:
802-
shared.Settings.SEPARATE_ASM = os.path.basename(asm_target)
812+
shared.Settings.SEPARATE_ASM = get_subresource_location(asm_target, 'application/javascript')
803813

804814
if 'EMCC_STRICT' in os.environ:
805815
shared.Settings.STRICT = os.environ.get('EMCC_STRICT') != '0'
@@ -1116,9 +1126,9 @@ def check(input_file):
11161126

11171127
if shared.Settings.BINARYEN:
11181128
# set file locations, so that JS glue can find what it needs
1119-
shared.Settings.WASM_TEXT_FILE = os.path.basename(wasm_text_target)
1120-
shared.Settings.WASM_BINARY_FILE = os.path.basename(wasm_binary_target)
1121-
shared.Settings.ASMJS_CODE_FILE = os.path.basename(asm_target)
1129+
shared.Settings.WASM_TEXT_FILE = get_subresource_location(wasm_text_target, 'text/plain')
1130+
shared.Settings.WASM_BINARY_FILE = get_subresource_location(wasm_binary_target, 'application/octet-stream')
1131+
shared.Settings.ASMJS_CODE_FILE = get_subresource_location(asm_target, 'application/javascript')
11221132

11231133
shared.Settings.ASM_JS = 2 # when targeting wasm, we use a wasm Memory, but that is not compatible with asm.js opts
11241134
shared.Settings.GLOBAL_BASE = 1024 # leave some room for mapping global vars
@@ -1624,12 +1634,12 @@ def repl(m):
16241634
# Copy into temp dir as well, so can be run there too
16251635
shared.safe_copy(memfile, os.path.join(shared.get_emscripten_temp_dir(), os.path.basename(memfile)))
16261636
if not shared.Settings.BINARYEN:
1627-
return 'memoryInitializer = "%s";' % os.path.basename(memfile)
1637+
return 'memoryInitializer = "%s";' % get_subresource_location(memfile, 'application/octet-stream')
16281638
else:
16291639
# with wasm, we may have the mem init file in the wasm binary already
16301640
return ('memoryInitializer = Module["wasmJSMethod"].indexOf("asmjs") >= 0 || '
16311641
'Module["wasmJSMethod"].indexOf("interpret-asm2wasm") >= 0 ? "%s" : null;'
1632-
% os.path.basename(memfile))
1642+
% get_subresource_location(memfile, 'application/octet-stream'))
16331643
src = re.sub(shared.JS.memory_initializer_pattern, repl, open(final).read(), count=1)
16341644
open(final + '.mem.js', 'w').write(src)
16351645
final += '.mem.js'
@@ -2397,6 +2407,8 @@ def generate_html(target, options, js_target, target_basename,
23972407
''' % (shared.Settings.EMTERPRETIFY_FILE, script.inline)
23982408

23992409
if options.memory_init_file:
2410+
memfile_location = get_subresource_location(memfile, 'application/octet-stream')
2411+
24002412
# start to load the memory init file in the HTML, in parallel with the JS
24012413
script.un_src()
24022414
script.inline = ('''
@@ -2412,13 +2424,15 @@ def generate_html(target, options, js_target, target_basename,
24122424
meminitXHR.responseType = 'arraybuffer';
24132425
meminitXHR.send(null);
24142426
})();
2415-
''' % os.path.basename(memfile)) + script.inline
2427+
''' % memfile_location) + script.inline
24162428

24172429
# Download .asm.js if --separate-asm was passed in an asm.js build, or if 'asmjs' is one
24182430
# of the wasm run methods.
24192431
if not options.separate_asm or (shared.Settings.BINARYEN and 'asmjs' not in shared.Settings.BINARYEN_METHOD):
24202432
assert len(asm_mods) == 0, 'no --separate-asm means no client code mods are possible'
24212433
else:
2434+
asm_target_location = get_subresource_location(asm_target, 'application/javascript')
2435+
24222436
script.un_src()
24232437
if len(asm_mods) == 0:
24242438
# just load the asm, then load the rest
@@ -2431,7 +2445,7 @@ def generate_html(target, options, js_target, target_basename,
24312445
}, 1); // delaying even 1ms is enough to allow compilation memory to be reclaimed
24322446
};
24332447
document.body.appendChild(script);
2434-
''' % (os.path.basename(asm_target), script.inline)
2448+
''' % (asm_target_location, script.inline)
24352449
else:
24362450
# may need to modify the asm code, load it as text, modify, and load asynchronously
24372451
script.inline = '''
@@ -2454,9 +2468,11 @@ def generate_html(target, options, js_target, target_basename,
24542468
document.body.appendChild(script);
24552469
};
24562470
codeXHR.send(null);
2457-
''' % (os.path.basename(asm_target), '\n'.join(asm_mods), script.inline)
2471+
''' % (asm_target_location, '\n'.join(asm_mods), script.inline)
24582472

24592473
if shared.Settings.BINARYEN and not shared.Settings.BINARYEN_ASYNC_COMPILATION:
2474+
wasm_binary_target_location = get_subresource_location(wasm_binary_target, 'application/octet-stream')
2475+
24602476
# We need to load the wasm file before anything else, it has to be synchronously ready TODO: optimize
24612477
script.un_src()
24622478
script.inline = '''
@@ -2468,7 +2484,7 @@ def generate_html(target, options, js_target, target_basename,
24682484
%s
24692485
};
24702486
wasmXHR.send(null);
2471-
''' % (os.path.basename(wasm_binary_target), script.inline)
2487+
''' % (wasm_binary_target_location, script.inline)
24722488

24732489
html = open(target, 'wb')
24742490
html_contents = shell.replace('{{{ SCRIPT }}}', script.replacement())

src/settings.js

+7
Original file line numberDiff line numberDiff line change
@@ -856,6 +856,13 @@ var FETCH = 0; // If nonzero, enables emscripten_fetch API.
856856

857857
var ASMFS = 0; // If set to 1, uses the multithreaded filesystem that is implemented within the asm.js module, using emscripten_fetch. Implies -s FETCH=1.
858858

859+
var SINGLE_FILE = 0; // If set to 1, embeds all subresources in the emitted JS file
860+
// by converting their file names into base64 data URIs.
861+
//
862+
// Note that using this option may require a change to consuming
863+
// pages' Content Security Policies, specifically adding data:
864+
// to their connect-src directives.
865+
859866
var WASM_TEXT_FILE = ''; // name of the file containing wasm text, if relevant
860867
var WASM_BINARY_FILE = ''; // name of the file containing wasm binary, if relevant
861868
var ASMJS_CODE_FILE = ''; // name of the file containing asm.js, if relevant

0 commit comments

Comments
 (0)