Skip to content

Commit a3c1d77

Browse files
committed
Add Module['scriptDirectory'] to fix loading files that should be relative to main .js file on Node.js.
1 parent 6d2798e commit a3c1d77

File tree

9 files changed

+78
-0
lines changed

9 files changed

+78
-0
lines changed

emcc.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2553,6 +2553,8 @@ def generate_html(target, options, js_target, target_basename,
25532553
} else if (Module['memoryInitializerPrefixURL']) {
25542554
memoryInitializer = Module['memoryInitializerPrefixURL'] + memoryInitializer;
25552555
}
2556+
// If URL to memory initializer is relative, treat it relative with respect to Module['scriptDirectory'], if that is present.
2557+
if (Module['scriptDirectory'] && !/^(?:[a-z]+:)?[\/\\\\]\/?/i.test(memoryInitializer)) memoryInitializer = Module['scriptDirectory'] + memoryInitializer;
25562558
Module['memoryInitializerRequestURL'] = memoryInitializer;
25572559
var meminitXHR = Module['memoryInitializerRequest'] = new XMLHttpRequest();
25582560
meminitXHR.open('GET', memoryInitializer, true);

src/Fetch.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ var Fetch = {
115115
// of these are passed, then the default URL 'pthread-main.js' relative to the main html file is loaded.
116116
if (typeof Module['locateFile'] === 'function') fetchJs = Module['locateFile'](fetchJs);
117117
else if (Module['pthreadMainPrefixURL']) fetchJs = Module['pthreadMainPrefixURL'] + fetchJs;
118+
fetchJs = joinUrl(Module['scriptDirectory'], fetchJs);
118119
Fetch.worker = new Worker(fetchJs);
119120
Fetch.worker.onmessage = function(e) {
120121
Module['print']('fetch-worker sent a message: ' + e.filename + ':' + e.lineno + ': ' + e.message);

src/library_debugger_toolkit.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,7 @@ var CyberDWARFHeapPrinter = function(cdFileLocation) {
451451
} else if (Module['cdInitializerPrefixURL']) {
452452
cdFileLocation = Module['cdInitializerPrefixURL'] + cdFileLocation;
453453
}
454+
cdFileLocation = joinUrl(Module['scriptDirectory'], cdFileLocation);
454455
if (ENVIRONMENT_IS_NODE || ENVIRONMENT_IS_SHELL) {
455456
var data = Module['read'](cdFileLocation);
456457
install_cyberdwarf(data);

src/library_pthread.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ var LibraryPThread = {
259259
// of these are passed, then the default URL 'pthread-main.js' relative to the main html file is loaded.
260260
if (typeof Module['locateFile'] === 'function') pthreadMainJs = Module['locateFile'](pthreadMainJs);
261261
else if (Module['pthreadMainPrefixURL']) pthreadMainJs = Module['pthreadMainPrefixURL'] + pthreadMainJs;
262+
pthreadMainJs = joinUrl(Module['scriptDirectory'], pthreadMainJs);
262263
var worker = new Worker(pthreadMainJs);
263264

264265
worker.onmessage = function(e) {

src/postamble.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@ if (memoryInitializer) {
4545
} else if (Module['memoryInitializerPrefixURL']) {
4646
memoryInitializer = Module['memoryInitializerPrefixURL'] + memoryInitializer;
4747
}
48+
memoryInitializer = joinUrl(Module['scriptDirectory'], memoryInitializer);
4849
}
50+
4951
if (ENVIRONMENT_IS_NODE || ENVIRONMENT_IS_SHELL) {
5052
var data = Module['readBinary'](memoryInitializer);
5153
HEAPU8.set(data, GLOBAL_BASE);

src/preamble.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -833,6 +833,18 @@ function stackTrace() {
833833
return demangleAll(js);
834834
}
835835

836+
function isAbsolutePath(path) {
837+
return isDataURI(path) || /^(?:[a-z]+:)?[\/\\]\/?/i.test(path);
838+
}
839+
840+
// Joins relative URL 'b' to URL 'a'. 'a' may be empty, in which case 'b' is returned.
841+
// If 'b' is an absolute URL, then 'a' is ignored and 'b' is returned as-is as well.
842+
function joinUrl(a, b) {
843+
#if ASSERTIONS
844+
if (a && !a.endsWith('/')) throw 'First parameter to joinUrl() should end in /!';
845+
#endif
846+
return (a && !isAbsolutePath(b)) ? a + b : b;
847+
}
836848
// Memory management
837849

838850
var PAGE_SIZE = 16384;
@@ -1994,6 +2006,9 @@ function integrateWasmJS() {
19942006
asmjsCodeFile = Module['locateFile'](asmjsCodeFile);
19952007
}
19962008
}
2009+
wasmTextFile = joinUrl(Module['scriptDirectory'], wasmTextFile);
2010+
wasmBinaryFile = joinUrl(Module['scriptDirectory'], wasmBinaryFile);
2011+
asmjsCodeFile = joinUrl(Module['scriptDirectory'], asmjsCodeFile);
19972012

19982013
// utilities
19992014

src/shell.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@ var currentScriptUrl = (typeof document !== 'undefined' && document.currentScrip
8888
if (ENVIRONMENT_IS_NODE) {
8989
// Expose functionality in the same simple way that the shells work
9090
// Note that we pollute the global namespace here, otherwise we break in node
91+
if (!Module['scriptDirectory']) Module['scriptDirectory'] = __dirname + '/';
92+
9193
var nodeFS;
9294
var nodePath;
9395

tests/test_other.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8118,3 +8118,56 @@ def test_noderawfs_disables_embedding(self):
81188118
assert expected in err
81198119
err = run_process(base + ['--embed-files', 'somefile'], stderr=PIPE, check=False).stderr
81208120
assert expected in err
8121+
8122+
# Tests that Emscripten-compiled applications can be run from a relative path with node command line that is different than the current working directory.
8123+
def test_node_js_run_from_different_directory(self):
8124+
args = ['-O3', '-s', 'WASM=1', '--memory-init-file', '1', '-s', 'BINARYEN_METHOD="asmjs,native-wasm"']
8125+
# Test that .mem.js is loaded up properly even if running the build output from a separate directory.
8126+
os.mkdir('subdir')
8127+
Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world.c'), '-o', os.path.join('subdir', 'a.js')] + args).communicate()
8128+
ret = Popen(NODE_JS + [os.path.join('subdir', 'a.js')], stdout=PIPE).communicate()[0]
8129+
try_delete('subdir')
8130+
assert 'hello, world!' in ret
8131+
8132+
# Test that the build is loaded properly when Module.locateFile is being used, where Module.locateFile() specifies an absolute path already.
8133+
os.mkdir('subdir')
8134+
open(os.path.join('subdir', 'pre.js'), 'w').write('''
8135+
var Module = {};
8136+
Module.memoryInitializerPrefixURL = 'this_should_be_getting_ignored_since_locateFile_is_specified/';
8137+
Module.locateFile = function(f) { return __dirname + '/' + f; }
8138+
''')
8139+
8140+
Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world.c'), '-o', os.path.join('subdir', 'a.js'), '--pre-js', os.path.join('subdir', 'pre.js')] + args).communicate()
8141+
ret = Popen(NODE_JS + [os.path.join('subdir', 'a.js')], stdout=PIPE).communicate()[0]
8142+
try_delete('subdir')
8143+
assert 'hello, world!' in ret
8144+
8145+
# Test that the build is loaded properly when Module.locateFile is being used, and it returns a relative path.
8146+
os.mkdir('subdir')
8147+
open(os.path.join('subdir', 'pre.js'), 'w').write('''
8148+
var Module = {};
8149+
Module.memoryInitializerPrefixURL = 'this_should_be_getting_ignored_since_locateFile_is_specified/';
8150+
Module.locateFile = function(f) { return 'data/' + f; }
8151+
''')
8152+
8153+
Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world.c'), '-o', os.path.join('subdir', 'a.js'), '--pre-js', os.path.join('subdir', 'pre.js')] + args).communicate()
8154+
os.mkdir(os.path.join('subdir', 'data'))
8155+
os.rename(os.path.join('subdir', 'a.js.mem'), os.path.join('subdir', 'data', 'a.js.mem'))
8156+
os.rename(os.path.join('subdir', 'a.asm.js'), os.path.join('subdir', 'data', 'a.asm.js'))
8157+
ret = Popen(NODE_JS + [os.path.join('subdir', 'a.js')], stdout=PIPE).communicate()[0]
8158+
try_delete('subdir')
8159+
assert 'hello, world!' in ret
8160+
8161+
# Test that the build is loaded properly when memoryInitializerPrefixURL is being used, and it returns a relative path.
8162+
os.mkdir('subdir')
8163+
open(os.path.join('subdir', 'pre.js'), 'w').write('''
8164+
var Module = {};
8165+
Module.memoryInitializerPrefixURL = 'data/';
8166+
''')
8167+
8168+
Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world.c'), '-o', os.path.join('subdir', 'a.js'), '--pre-js', os.path.join('subdir', 'pre.js')] + args).communicate()
8169+
os.mkdir(os.path.join('subdir', 'data'))
8170+
os.rename(os.path.join('subdir', 'a.js.mem'), os.path.join('subdir', 'data', 'a.js.mem'))
8171+
ret = Popen(NODE_JS + [os.path.join('subdir', 'a.js')], stdout=PIPE).communicate()[0]
8172+
try_delete('subdir')
8173+
assert 'hello, world!' in ret

tools/file_packager.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -848,6 +848,7 @@ def was_seen(name):
848848
var REMOTE_METADATA_NAME = typeof Module['locateFile'] === 'function' ?
849849
Module['locateFile']('%(metadata_file)s') :
850850
((Module['filePackagePrefixURL'] || '') + '%(metadata_file)s');
851+
REMOTE_METADATA_NAME = joinUrl(Module['scriptDirectory'], REMOTE_METADATA_NAME);
851852
var xhr = new XMLHttpRequest();
852853
xhr.onreadystatechange = function() {
853854
if (xhr.readyState === 4 && xhr.status === 200) {

0 commit comments

Comments
 (0)