diff --git a/ChangeLog.markdown b/ChangeLog.markdown index 0ccc38b22755e..99f1a798e2892 100644 --- a/ChangeLog.markdown +++ b/ChangeLog.markdown @@ -10,6 +10,10 @@ Not all changes are documented here. In particular, new features, user-oriented Current Trunk ------------- + - Fix `Module.locateFile` to resolve relative paths to *.wasm, *.mem and other files relatively to JavaScript file rather than current working directory + - Add second argument `prefix` to `Module.locateFile` function that contains path to JavaScript file where files are loaded from by default + - Remove `Module.*PrefixURL` APIs (use `Module.locateFile` instead) + v1.38.8: 07/06/2018 ------------------- - Fix a regression in 1.38.7 with binaryen no longer bundling binaryen.js (which emscripten doesn't need, that's just for handwritten JS users, but emscripten did check for its prescence). diff --git a/emcc.py b/emcc.py index 86e33eff9ddec..8f0f78d919d36 100755 --- a/emcc.py +++ b/emcc.py @@ -2598,7 +2598,10 @@ def modularize(): %(src)s return %(EXPORT_NAME)s; -}%(instantiate)s; +}; +%(EXPORT_NAME)s = %(EXPORT_NAME)s.bind({ + _currentScript: typeof document !== 'undefined' ? document.currentScript : undefined +})%(instantiate)s; ''' % { 'EXPORT_NAME': shared.Settings.EXPORT_NAME, 'src': src, @@ -2704,11 +2707,7 @@ def generate_html(target, options, js_target, target_basename, script.un_src() script.inline = (''' var memoryInitializer = '%s'; - if (typeof Module['locateFile'] === 'function') { - memoryInitializer = Module['locateFile'](memoryInitializer); - } else if (Module['memoryInitializerPrefixURL']) { - memoryInitializer = Module['memoryInitializerPrefixURL'] + memoryInitializer; - } + memoryInitializer = Module['locateFile'] ? Module['locateFile'](memoryInitializer, '') : memoryInitializer; Module['memoryInitializerRequestURL'] = memoryInitializer; var meminitXHR = Module['memoryInitializerRequest'] = new XMLHttpRequest(); meminitXHR.open('GET', memoryInitializer, true); diff --git a/site/source/docs/api_reference/module.rst b/site/source/docs/api_reference/module.rst index 981f61b2bd9ae..21994b601f111 100644 --- a/site/source/docs/api_reference/module.rst +++ b/site/source/docs/api_reference/module.rst @@ -4,7 +4,7 @@ Module object ============= -``Module`` is a global JavaScript object with attributes that Emscripten-generated code calls at various points in its execution. +``Module`` is a global JavaScript object with attributes that Emscripten-generated code calls at various points in its execution. Developers can provide an implementation of ``Module`` to control the execution of code. For example, to define how notification messages from Emscripten are displayed, developers implement the :js:attr:`Module.print` attribute. @@ -20,7 +20,7 @@ Developers can provide an implementation of ``Module`` to control the execution Creating the Module object ========================== -Use emcc's :ref:`pre-js option` to add JavaScript code that defines (or extends) the ``Module`` object with the behaviour you need. +Use emcc's :ref:`pre-js option` to add JavaScript code that defines (or extends) the ``Module`` object with the behaviour you need. When generating only JavaScript (as opposed to HTML), no ``Module`` object is created by default, and the behaviour is entirely defined by the developer. For example, creating a ``Module`` object with the following code will cause all notifications from the program to be calls to ``alert()``. @@ -51,15 +51,9 @@ The following ``Module`` attributes affect code execution. Set them to customize The commandline arguments. The value of ``arguments`` contains the values returned if compiled code checks ``argc`` and ``argv``. - -.. js:attribute:: Module.filePackagePrefixURL - - This is the "prefix" URL for a preloaded data file that is hosted separately from its JavaScript and HTML files (it includes the full path up to, but not including, the data file). See :ref:`packaging-files-data-file-location` for more information. - - .. js:attribute:: Module.locateFile - If set, this method will be called when the runtime needs to load a file, such as a ``.wasm`` WebAssembly file, ``.mem`` memory init file, or a file generated by the file packager. The function receives the URL, and should return the actual URL. This lets you host file packages or the ``.mem`` file etc. on a different location than the current directory (which is the default expectation), for example if you want to host them on a CDN. Note that ``locateFile`` is sort of a generalization of ``Module.filePackagePrefixURL``. + If set, this method will be called when the runtime needs to load a file, such as a ``.wasm`` WebAssembly file, ``.mem`` memory init file, or a file generated by the file packager. The function receives the relative path to the file as configured in build process and a ``prefix`` (path to JavaScript file), and should return the actual URL. This lets you host file packages or the ``.mem`` file etc. on a different location than the directory of JavaScript file (which is the default expectation), for example if you want to host them on a CDN. NOTE: ``prefix`` might be empty string for memory initializer file is loaded in parallel with JS or in packager, so be careful with those. .. js:attribute:: Module.logReadFiles @@ -87,7 +81,7 @@ The following ``Module`` attributes affect code execution. Set them to customize .. js:attribute:: Module.preInit A function (or array of functions) that must be called before global initializers run, but after basic initialization of the JavaScript runtime. This is typically used for :ref:`File System operations `. - + .. js:attribute:: Module.preinitializedWebGLContext If building with -s GL_PREINITIALIZED_CONTEXT=1 set, you can set ``Module.preinitializedWebGLContext`` to a precreated instance of a WebGL context, which will be used later when initializing WebGL in C/C++ side. Precreating the GL context is useful if doing GL side loading (shader compilation, texture loading etc.) parallel to other page startup actions, and/or for detecting WebGL feature support, such as GL version or compressed texture support up front on a page before or in parallel to loading up any compiled code. @@ -101,7 +95,7 @@ The following ``Module`` attributes affect code execution. Set them to customize .. js:attribute:: Module.print Called when something is printed to standard output (stdout) - + .. js:attribute:: Module.printErr Called when something is printed to standard error (stderr) @@ -140,7 +134,7 @@ The generated program is able to detect its execution environment by checking th However, sometimes it may be needed to override the detected environment: a typical use case would be module bundlers (like webpack): they are executed by nodejs but the final output is for browser. -In order to do that, you can dictate your preferred execution environment by setting the ``Module.ENVIRONMENT`` variable to one of those allowed values: +In order to do that, you can dictate your preferred execution environment by setting the ``Module.ENVIRONMENT`` variable to one of those allowed values: ``WEB`` diff --git a/site/source/docs/porting/files/packaging_files.rst b/site/source/docs/porting/files/packaging_files.rst index 62391aefb68c5..3b419e8398179 100644 --- a/site/source/docs/porting/files/packaging_files.rst +++ b/site/source/docs/porting/files/packaging_files.rst @@ -4,10 +4,10 @@ Packaging Files =============== -This topic shows how to package the files that will be used to populate :ref:`Emscripten's virtual file system ` when the page is loaded. +This topic shows how to package the files that will be used to populate :ref:`Emscripten's virtual file system ` when the page is loaded. There are two alternatives for how files are packaged: *preloading* and *embedding*. Embedding puts the specified files inside the generated JavaScript, while preloading packages the files separately. Embedding files is much less efficient than preloading and should only be used when packaging small numbers of small files. Preloading also enables the option to separately host the data. - + *Emcc* uses the *file packager* to package the files and generate the :ref:`File System API ` calls that create and load the file system at run time. While *Emcc* is the recommended tool for packaging, there are cases where it can make sense to run the *file packager* manually. With ``--use-preload-plugins``, files can be automatically decoded based on @@ -23,9 +23,9 @@ The command below shows how to package files for preloading: .. code-block:: bash ./emcc file.cpp -o file.html --preload-file asset_dir - + The command generates **file.html**, **file.js** and **file.data**. The **.data** file contains all the files in **asset_dir/**, and is loaded by **file.js**. - + .. note:: The :ref:`Tutorial ` demonstrates preloading using the **hello_world_file.cpp** test code. @@ -36,7 +36,7 @@ The command for embedding is shown below. In this case *emcc* generates **file.h ./emcc file.cpp -o file.html --embed-file asset_dir -By default, the files to be packaged should be nested in or below the compile-time command prompt directory. At runtime the same nested file structure is mapped to the virtual file system, with the root corresponding to the command prompt directory. +By default, the files to be packaged should be nested in or below the compile-time command prompt directory. At runtime the same nested file structure is mapped to the virtual file system, with the root corresponding to the command prompt directory. For example, consider a file structure **dir1/dir2/dir3/asset_dir/** where the project is compiled from **dir2**. When we package **asset_dir**, we specify its relative location **dir3/asset_dir/**: @@ -51,27 +51,27 @@ The ``@`` symbol can be used to map packaged files from any location in the loca .. _packaging-files-file-packager: -Packaging using the file packager tool +Packaging using the file packager tool ====================================== -You can also run the *file packager* manually using the instructions at the top of `file_packager.py `_. +You can also run the *file packager* manually using the instructions at the top of `file_packager.py `_. The file packager generates a **.data** file and **.js** file. The **.js** file contains the code to use the data file, and must be loaded *before* loading your main compiled code. .. note:: - - Using the *file packager* allows you to run file packaging separately from compiling the code. + - Using the *file packager* allows you to run file packaging separately from compiling the code. - You can load multiple datafiles by running the file packager on each and loading the **.js** outputs. See `BananaBread `_ for an example of this (`cube2/js/game-setup.js `_). - + .. _packaging-files-data-file-location: Changing the data file location =============================== -By default, the **.data** file containing all the preloaded files is loaded from the same URL as your **.js** file. In some cases it may be useful to have the data file in a different location from the other files — for example if your **.html** and **.js** change a lot you may want to keep the data file on a fast CDN somewhere else. +By default, the **.data** file containing all the preloaded files is loaded from the same URL as your **.js** file. In some cases it may be useful to have the data file in a different location from the other files — for example if your **.html** and **.js** change a lot you may want to keep the data file on a fast CDN somewhere else. -This model is supported by changing the :js:attr:`Module.filePackagePrefixURL` to be the URL where the data file is stored (this is a prefix, so should include the full path before the data's file name). The attribute must be specified in a `` + + ''') + + self.run_browser('test-subdir.html', None, '/report_result?0') diff --git a/tests/test_other.py b/tests/test_other.py index c9494dad83d0e..04ce2d7c81d5d 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -5102,7 +5102,7 @@ def do(name, moar_opts): do('normal', []) do('no_nuthin', ['-s', 'EXPORTED_RUNTIME_METHODS=[]']) print(' ', sizes) - assert abs(sizes['no_nuthin'] - sizes['normal']) < 10 + assert abs(sizes['no_nuthin'] - sizes['normal']) < 15 assert sizes['no_nuthin'] < absolute test(['-s', 'ASSERTIONS=0'], 95000) # we don't care about code size with assertions test(['-O1'], 80000) @@ -8526,3 +8526,11 @@ def test_html_preprocess(self): T4:NO_EXIT_RUNTIME > 1 T5:NO_EXIT_RUNTIME T6:(else) !NO_EXIT_RUNTIME""", output) + + # Tests that Emscripten-compiled applications can be run from a relative path with node command line that is different than the current working directory. + def test_node_js_run_from_different_directory(self): + if not os.path.exists('subdir'): + os.mkdir('subdir') + run_process([PYTHON, EMCC, path_from_root('tests', 'hello_world.c'), '-o', os.path.join('subdir', 'a.js'), '-O3']) + ret = run_process(NODE_JS + [os.path.join('subdir', 'a.js')], stdout=PIPE).stdout + assert 'hello, world!' in ret diff --git a/tools/file_packager.py b/tools/file_packager.py index f702432fdcb64..752e824f3b49e 100644 --- a/tools/file_packager.py +++ b/tools/file_packager.py @@ -457,7 +457,7 @@ def was_seen(name): Module['removeRunDependency']('datafile_%s'); ''' % (meta, shared.JS.escape_for_js_string(data_target)) - package_uuid = uuid.uuid4(); + package_uuid = uuid.uuid4() package_name = data_target statinfo = os.stat(package_name) remote_package_size = statinfo.st_size @@ -478,9 +478,7 @@ def was_seen(name): Module['locateFile'] = Module['locateFilePackage']; err('warning: you defined Module.locateFilePackage, that has been renamed to Module.locateFile (using your locateFilePackage for now)'); } - var REMOTE_PACKAGE_NAME = typeof Module['locateFile'] === 'function' ? - Module['locateFile'](REMOTE_PACKAGE_BASE) : - ((Module['filePackagePrefixURL'] || '') + REMOTE_PACKAGE_BASE); + var REMOTE_PACKAGE_NAME = Module['locateFile'] ? Module['locateFile'](REMOTE_PACKAGE_BASE, '') : REMOTE_PACKAGE_BASE; ''' % (shared.JS.escape_for_js_string(data_target), shared.JS.escape_for_js_string(remote_package_name)) metadata['remote_package_size'] = remote_package_size metadata['package_uuid'] = str(package_uuid) @@ -735,9 +733,7 @@ def was_seen(name): function runMetaWithFS() { Module['addRunDependency']('%(metadata_file)s'); - var REMOTE_METADATA_NAME = typeof Module['locateFile'] === 'function' ? - Module['locateFile']('%(metadata_file)s') : - ((Module['filePackagePrefixURL'] || '') + '%(metadata_file)s'); + var REMOTE_METADATA_NAME = Module['locateFile'] ? Module['locateFile']('%(metadata_file)s', '') : '%(metadata_file)s'; var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function() { if (xhr.readyState === 4 && xhr.status === 200) {