From 49d3bbe7f304c869a1aed8762a4782140eb870bf Mon Sep 17 00:00:00 2001 From: Ryan Lester Date: Mon, 12 Jun 2017 22:00:02 -0400 Subject: [PATCH] Add SINGLE_FILE option to embed all subresources into emitted JS As discussed in #5279, subresource paths are converted into base64 data URIs. --- emcc.py | 38 +++++++++++++++++++++++++++----------- src/settings.js | 7 +++++++ 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/emcc.py b/emcc.py index 654d26cda16f1..3e75d663ec30b 100755 --- a/emcc.py +++ b/emcc.py @@ -27,7 +27,7 @@ if __name__ == '__main__': ToolchainProfiler.record_process_start() -import os, sys, shutil, tempfile, subprocess, shlex, time, re, logging, urllib +import os, sys, shutil, tempfile, subprocess, shlex, time, re, logging, urllib, base64 from subprocess import PIPE from tools import shared, jsrun, system_libs from tools.shared import execute, suffix, unsuffixed, unsuffixed_basename, WINDOWS, safe_move @@ -513,6 +513,16 @@ def filter_emscripten_options(argv): # ---------------- Utilities --------------- + # Returns the run-time subresource location for accessing by such means as XHR + def get_subresource_location(path, media_type): + if shared.Settings.SINGLE_FILE: + f = open(path, 'rb') + data = base64.b64encode(f.read()) + f.close() + return 'data:' + media_type + ';base64,' + data + else: + return os.path.basename(path) + seen_names = {} def uniquename(name): if name not in seen_names: @@ -799,7 +809,7 @@ def detect_fixed_language_mode(args): options.separate_asm = True logging.warning('forcing separate asm output (--separate-asm), because -s PRECISE_F32=2 or -s USE_PTHREADS=2 was passed.') if options.separate_asm: - shared.Settings.SEPARATE_ASM = os.path.basename(asm_target) + shared.Settings.SEPARATE_ASM = get_subresource_location(asm_target, 'application/javascript') if 'EMCC_STRICT' in os.environ: shared.Settings.STRICT = os.environ.get('EMCC_STRICT') != '0' @@ -1116,9 +1126,9 @@ def check(input_file): if shared.Settings.BINARYEN: # set file locations, so that JS glue can find what it needs - shared.Settings.WASM_TEXT_FILE = os.path.basename(wasm_text_target) - shared.Settings.WASM_BINARY_FILE = os.path.basename(wasm_binary_target) - shared.Settings.ASMJS_CODE_FILE = os.path.basename(asm_target) + shared.Settings.WASM_TEXT_FILE = get_subresource_location(wasm_text_target, 'text/plain') + shared.Settings.WASM_BINARY_FILE = get_subresource_location(wasm_binary_target, 'application/octet-stream') + shared.Settings.ASMJS_CODE_FILE = get_subresource_location(asm_target, 'application/javascript') shared.Settings.ASM_JS = 2 # when targeting wasm, we use a wasm Memory, but that is not compatible with asm.js opts shared.Settings.GLOBAL_BASE = 1024 # leave some room for mapping global vars @@ -1624,12 +1634,12 @@ def repl(m): # Copy into temp dir as well, so can be run there too shared.safe_copy(memfile, os.path.join(shared.get_emscripten_temp_dir(), os.path.basename(memfile))) if not shared.Settings.BINARYEN: - return 'memoryInitializer = "%s";' % os.path.basename(memfile) + return 'memoryInitializer = "%s";' % get_subresource_location(memfile, 'application/octet-stream') else: # with wasm, we may have the mem init file in the wasm binary already return ('memoryInitializer = Module["wasmJSMethod"].indexOf("asmjs") >= 0 || ' 'Module["wasmJSMethod"].indexOf("interpret-asm2wasm") >= 0 ? "%s" : null;' - % os.path.basename(memfile)) + % get_subresource_location(memfile, 'application/octet-stream')) src = re.sub(shared.JS.memory_initializer_pattern, repl, open(final).read(), count=1) open(final + '.mem.js', 'w').write(src) final += '.mem.js' @@ -2397,6 +2407,8 @@ def generate_html(target, options, js_target, target_basename, ''' % (shared.Settings.EMTERPRETIFY_FILE, script.inline) if options.memory_init_file: + memfile_location = get_subresource_location(memfile, 'application/octet-stream') + # start to load the memory init file in the HTML, in parallel with the JS script.un_src() script.inline = (''' @@ -2412,13 +2424,15 @@ def generate_html(target, options, js_target, target_basename, meminitXHR.responseType = 'arraybuffer'; meminitXHR.send(null); })(); -''' % os.path.basename(memfile)) + script.inline +''' % memfile_location) + script.inline # Download .asm.js if --separate-asm was passed in an asm.js build, or if 'asmjs' is one # of the wasm run methods. if not options.separate_asm or (shared.Settings.BINARYEN and 'asmjs' not in shared.Settings.BINARYEN_METHOD): assert len(asm_mods) == 0, 'no --separate-asm means no client code mods are possible' else: + asm_target_location = get_subresource_location(asm_target, 'application/javascript') + script.un_src() if len(asm_mods) == 0: # just load the asm, then load the rest @@ -2431,7 +2445,7 @@ def generate_html(target, options, js_target, target_basename, }, 1); // delaying even 1ms is enough to allow compilation memory to be reclaimed }; document.body.appendChild(script); -''' % (os.path.basename(asm_target), script.inline) +''' % (asm_target_location, script.inline) else: # may need to modify the asm code, load it as text, modify, and load asynchronously script.inline = ''' @@ -2454,9 +2468,11 @@ def generate_html(target, options, js_target, target_basename, document.body.appendChild(script); }; codeXHR.send(null); -''' % (os.path.basename(asm_target), '\n'.join(asm_mods), script.inline) +''' % (asm_target_location, '\n'.join(asm_mods), script.inline) if shared.Settings.BINARYEN and not shared.Settings.BINARYEN_ASYNC_COMPILATION: + wasm_binary_target_location = get_subresource_location(wasm_binary_target, 'application/octet-stream') + # We need to load the wasm file before anything else, it has to be synchronously ready TODO: optimize script.un_src() script.inline = ''' @@ -2468,7 +2484,7 @@ def generate_html(target, options, js_target, target_basename, %s }; wasmXHR.send(null); -''' % (os.path.basename(wasm_binary_target), script.inline) +''' % (wasm_binary_target_location, script.inline) html = open(target, 'wb') html_contents = shell.replace('{{{ SCRIPT }}}', script.replacement()) diff --git a/src/settings.js b/src/settings.js index c4c668e78c7e3..dcbe0c4529a1b 100644 --- a/src/settings.js +++ b/src/settings.js @@ -856,6 +856,13 @@ var FETCH = 0; // If nonzero, enables emscripten_fetch API. 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. +var SINGLE_FILE = 0; // If set to 1, embeds all subresources in the emitted JS file + // by converting their file names into base64 data URIs. + // + // Note that using this option may require a change to consuming + // pages' Content Security Policies, specifically adding data: + // to their connect-src directives. + var WASM_TEXT_FILE = ''; // name of the file containing wasm text, if relevant var WASM_BINARY_FILE = ''; // name of the file containing wasm binary, if relevant var ASMJS_CODE_FILE = ''; // name of the file containing asm.js, if relevant