Skip to content

Commit be712ca

Browse files
committed
Add modularize to file_packager.py
fix python format apply more ruff fixes fix pipeline revert file to the most actual version and apply changes add modularize to options class add utilization of EXPORT_NAME with modularize option simplify declaration of the modularize function arg documentation address @sbc100 review Fix codegen new line formatting Add file_packager tests and changelog mention fix some stylistic issues gichange file_packager modularize test simplyfi tests fix test rename to export_es6 async function promise behaviour cleanup test address @sbc100 review simplify the test rename promise handlers introduce createRequire in es6 mode alternative way to import require
1 parent a105a6a commit be712ca

File tree

3 files changed

+93
-8
lines changed

3 files changed

+93
-8
lines changed

ChangeLog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ See docs/process.md for more on how version tagging works.
4141
- emcc will now error if `MINIMAL_RUNTIME_STREAMING_WASM_COMPILATION` or
4242
`MINIMAL_RUNTIME_STREAMING_WASM_INSTANTIATION` are used with `SINGLE_FILE`.
4343
These are fundamentally incompatible but were previously ignored. (#24849)
44+
- `--export-es6` flag was added to `file_packager.py` available when run
45+
standalone, to enable ES6 imports of generated JavaScript code (#24737)
4446

4547
4.0.12 - 08/01/25
4648
-----------------

test/test_other.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3919,6 +3919,11 @@ def test_file_packager_returns_error_if_target_equal_to_jsoutput(self):
39193919
err = self.expect_fail([FILE_PACKAGER, 'test.data', '--js-output=test.data'])
39203920
self.assertContained(MESSAGE, err)
39213921

3922+
def test_file_packager_returns_error_if_emcc_and_export_es6(self):
3923+
MESSAGE = 'error: Can\'t use --export-es6 option together with --from-emcc since the code should be embedded within emcc\'s code'
3924+
err = self.expect_fail([FILE_PACKAGER, 'test.data', '--export-es6', '--from-emcc'])
3925+
self.assertContained(MESSAGE, err)
3926+
39223927
def test_file_packager_embed(self):
39233928
create_file('data.txt', 'hello data')
39243929

@@ -3945,6 +3950,43 @@ def test_file_packager_embed(self):
39453950
output = self.run_js('a.out.js')
39463951
self.assertContained('hello data', output)
39473952

3953+
def test_file_packager_export_es6(self):
3954+
create_file('smth.txt', 'hello data')
3955+
self.run_process([FILE_PACKAGER, 'test.data', '--export-es6', '--preload', 'smth.txt', '--js-output=dataFileLoader.js'])
3956+
3957+
create_file('test.c', '''
3958+
#include <stdio.h>
3959+
#include <emscripten.h>
3960+
3961+
EMSCRIPTEN_KEEPALIVE int test_fun() {
3962+
FILE* f = fopen("smth.txt", "r");
3963+
char buf[64] = {0};
3964+
int rtn = fread(buf, 1, 64, f);
3965+
buf[rtn] = '\\0';
3966+
fclose(f);
3967+
printf("%s\\n", buf);
3968+
return 0;
3969+
}
3970+
''')
3971+
self.run_process([EMCC, 'test.c', '-sFORCE_FILESYSTEM', '-sMODULARIZE', '-sEXPORT_ES6', '-o', 'moduleFile.js'])
3972+
3973+
create_file('run.js', '''
3974+
import loadDataFile from './dataFileLoader.js'
3975+
import {default as loadModule} from './moduleFile.js'
3976+
3977+
loadModule().then((module) => {
3978+
loadDataFile(module)
3979+
.catch((cause) => Promise.reject(cause))
3980+
.then(() => {
3981+
module._test_fun();
3982+
}
3983+
);
3984+
});
3985+
''')
3986+
3987+
output = self.run_js('run.js')
3988+
self.assertContained('hello data', output)
3989+
39483990
@crossplatform
39493991
def test_file_packager_depfile(self):
39503992
create_file('data1.txt', 'data1')

tools/file_packager.py

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
2222
Usage:
2323
24-
file_packager TARGET [--preload A [B..]] [--embed C [D..]] [--exclude E [F..]] [--js-output=OUTPUT.js] [--no-force] [--use-preload-cache] [--indexedDB-name=EM_PRELOAD_CACHE] [--separate-metadata] [--lz4] [--use-preload-plugins] [--no-node] [--help]
24+
file_packager TARGET [--preload A [B..]] [--embed C [D..]] [--exclude E [F..]] [--js-output=OUTPUT.js] [--no-force] [--use-preload-cache] [--indexedDB-name=EM_PRELOAD_CACHE] [--separate-metadata] [--lz4] [--use-preload-plugins] [--no-node] [--export-es6] [--help]
2525
2626
--preload ,
2727
--embed See emcc --help for more details on those options.
@@ -41,6 +41,8 @@
4141
4242
--export-name=EXPORT_NAME Use custom export name (default is `Module`)
4343
44+
--export-es6 Wrap generated code inside ES6 exported function
45+
4446
--no-force Don't create output if no valid input file is specified.
4547
4648
--use-preload-cache Stores package in IndexedDB so that subsequent loads don't need to do XHR. Checks package version.
@@ -129,6 +131,7 @@ def __init__(self):
129131
self.use_preload_plugins = False
130132
self.support_node = True
131133
self.wasm64 = False
134+
self.export_es6 = False
132135

133136

134137
class DataFile:
@@ -362,7 +365,7 @@ def main(): # noqa: C901, PLR0912, PLR0915
362365
To revalidate these numbers, run `ruff check --select=C901,PLR091`.
363366
"""
364367
if len(sys.argv) == 1:
365-
err('''Usage: file_packager TARGET [--preload A [B..]] [--embed C [D..]] [--exclude E [F..]] [--js-output=OUTPUT.js] [--no-force] [--use-preload-cache] [--indexedDB-name=EM_PRELOAD_CACHE] [--separate-metadata] [--lz4] [--use-preload-plugins] [--no-node] [--help]
368+
err('''Usage: file_packager TARGET [--preload A [B..]] [--embed C [D..]] [--exclude E [F..]] [--js-output=OUTPUT.js] [--no-force] [--use-preload-cache] [--indexedDB-name=EM_PRELOAD_CACHE] [--separate-metadata] [--lz4] [--use-preload-plugins] [--no-node] [--export-es6] [--help]
366369
Try 'file_packager --help' for more details.''')
367370
return 1
368371

@@ -391,6 +394,9 @@ def main(): # noqa: C901, PLR0912, PLR0915
391394
elif arg == '--no-force':
392395
options.force = False
393396
leading = ''
397+
elif arg == '--export-es6':
398+
options.export_es6 = True
399+
leading = ''
394400
elif arg == '--use-preload-cache':
395401
options.use_preload_cache = True
396402
leading = ''
@@ -485,6 +491,11 @@ def main(): # noqa: C901, PLR0912, PLR0915
485491
diagnostics.error('TARGET should not be the same value of --js-output')
486492
return 1
487493

494+
if options.from_emcc and options.export_es6:
495+
diagnostics.error('Can\'t use --export-es6 option together with --from-emcc since the code should be embedded '
496+
'within emcc\'s code')
497+
return 1
498+
488499
walked.append(__file__)
489500
for file_ in data_files:
490501
if not should_ignore(file_.srcpath):
@@ -621,20 +632,38 @@ def generate_js(data_target, data_files, metadata):
621632
if options.from_emcc:
622633
ret = ''
623634
else:
624-
ret = '''
635+
if options.export_es6:
636+
ret = 'export default async function loadDataFile(Module) {\n'
637+
else:
638+
ret = '''
625639
var Module = typeof %(EXPORT_NAME)s != 'undefined' ? %(EXPORT_NAME)s : {};\n''' % {"EXPORT_NAME": options.export_name}
626640

627641
ret += '''
628642
Module['expectedDataFileDownloads'] ??= 0;
629-
Module['expectedDataFileDownloads']++;
630-
(() => {
643+
Module['expectedDataFileDownloads']++;'''
644+
645+
if not options.export_es6:
646+
ret += '''
647+
(() => {'''
648+
649+
ret += '''
631650
// Do not attempt to redownload the virtual filesystem data when in a pthread or a Wasm Worker context.
632651
var isPthread = typeof ENVIRONMENT_IS_PTHREAD != 'undefined' && ENVIRONMENT_IS_PTHREAD;
633652
var isWasmWorker = typeof ENVIRONMENT_IS_WASM_WORKER != 'undefined' && ENVIRONMENT_IS_WASM_WORKER;
634653
if (isPthread || isWasmWorker) return;\n'''
635654

636655
if options.support_node:
637656
ret += " var isNode = typeof process === 'object' && typeof process.versions === 'object' && typeof process.versions.node === 'string';\n"
657+
658+
if options.support_node and options.export_es6:
659+
ret += '''if (isNode) {
660+
const { createRequire } = await import('module');
661+
/** @suppress{duplicate} */
662+
var require = createRequire(import.meta.url);
663+
}\n'''
664+
665+
if options.export_es6:
666+
ret += 'return new Promise((loadDataResolve, loadDataReject) => {\n'
638667
ret += ' async function loadPackage(metadata) {\n'
639668

640669
code = '''
@@ -684,6 +713,8 @@ def generate_js(data_target, data_files, metadata):
684713
create_data = '''// canOwn this data in the filesystem, it is a slide into the heap that will never change
685714
Module['FS_createDataFile'](this.name, null, byteArray, true, true, true);
686715
Module['removeRunDependency'](`fp ${that.name}`);'''
716+
ready_promise = '''
717+
loadDataResolve();'''
687718

688719
if not options.lz4:
689720
# Data requests - for getting a block of data out of the big archive - have
@@ -710,14 +741,14 @@ def generate_js(data_target, data_files, metadata):
710741
finish: function(byteArray) {
711742
var that = this;
712743
%s
713-
this.requests[this.name] = null;
744+
this.requests[this.name] = null;%s
714745
}
715746
};
716747
717748
var files = metadata['files'];
718749
for (var i = 0; i < files.length; ++i) {
719750
new DataRequest(files[i]['start'], files[i]['end'], files[i]['audio'] || 0).open('GET', files[i]['filename']);
720-
}\n''' % (create_preloaded if options.use_preload_plugins else create_data)
751+
}\n''' % (create_preloaded if options.use_preload_plugins else create_data, ready_promise if options.export_es6 else '')
721752

722753
if options.has_embedded and not options.obj_output:
723754
diagnostics.warn('--obj-output is recommended when using --embed. This outputs an object file for linking directly into your application is more efficient than JS encoding')
@@ -958,6 +989,9 @@ def generate_js(data_target, data_files, metadata):
958989
return contents.buffer;
959990
}'''.strip()
960991

992+
reject_promise = '''
993+
loadDataReject();'''
994+
961995
ret += '''
962996
async function fetchRemotePackage(packageName, packageSize) {
963997
%(node_support_code)s
@@ -1128,7 +1162,14 @@ def generate_js(data_target, data_files, metadata):
11281162
}
11291163
loadPackage(%s);\n''' % json.dumps(metadata)
11301164

1131-
ret += '''
1165+
if options.export_es6:
1166+
ret += '''
1167+
});
1168+
}
1169+
// END the loadDataFile function
1170+
'''
1171+
else:
1172+
ret += '''
11321173
})();\n'''
11331174

11341175
return ret

0 commit comments

Comments
 (0)