Skip to content

Commit 2bc217b

Browse files
committed
file_packager.py: Add option to embed file data in wasm binary
This change not only adds the new option but uses this option whenever file_packager is used from within emcc. Hopefully we can find a way deprecate and remove the old JS embedded since that seems strictly worse in almost ever way. - Larger code size (JS base64 encoding is larger than binary) - No possiblity of zero copy, memory-backed files - Less compatible with standalone wasm / WASI
1 parent 3dbf141 commit 2bc217b

File tree

6 files changed

+237
-44
lines changed

6 files changed

+237
-44
lines changed

emcc.py

Lines changed: 43 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -814,14 +814,7 @@ def array_contains_any_of(hay, needles):
814814

815815

816816
def get_clang_flags():
817-
return ['-target', get_llvm_target()]
818-
819-
820-
def get_llvm_target():
821-
if settings.MEMORY64:
822-
return 'wasm64-unknown-emscripten'
823-
else:
824-
return 'wasm32-unknown-emscripten'
817+
return ['-target', shared.get_llvm_target()]
825818

826819

827820
cflags = None
@@ -979,6 +972,41 @@ def get_subresource_location(path, data_uri=None):
979972
return os.path.basename(path)
980973

981974

975+
@ToolchainProfiler.profile_block('package_files')
976+
def package_files(options, target):
977+
rtn = []
978+
logger.debug('setting up files')
979+
file_args = ['--from-emcc', '--export-name=' + settings.EXPORT_NAME]
980+
if options.preload_files:
981+
file_args.append('--preload')
982+
file_args += options.preload_files
983+
if options.embed_files:
984+
file_args.append('--embed')
985+
file_args += options.embed_files
986+
if options.exclude_files:
987+
file_args.append('--exclude')
988+
file_args += options.exclude_files
989+
if options.use_preload_cache:
990+
file_args.append('--use-preload-cache')
991+
if settings.LZ4:
992+
file_args.append('--lz4')
993+
if options.use_preload_plugins:
994+
file_args.append('--use-preload-plugins')
995+
if not settings.ENVIRONMENT_MAY_BE_NODE:
996+
file_args.append('--no-node')
997+
if options.embed_files:
998+
object_file = in_temp('embedded_files.o')
999+
file_args += ['--obj-output=' + object_file]
1000+
rtn.append(object_file)
1001+
1002+
cmd = [shared.FILE_PACKAGER, shared.replace_suffix(target, '.data')] + file_args
1003+
file_code = shared.check_call(cmd, stdout=PIPE).stdout
1004+
1005+
options.pre_js = js_manipulation.add_files_pre_js(options.pre_js, file_code)
1006+
1007+
return rtn
1008+
1009+
9821010
run_via_emxx = False
9831011

9841012

@@ -1053,7 +1081,7 @@ def run(args):
10531081
return 0
10541082

10551083
if '-dumpmachine' in args:
1056-
print(get_llvm_target())
1084+
print(shared.get_llvm_target())
10571085
return 0
10581086

10591087
if '-dumpversion' in args: # gcc's doc states "Print the compiler version [...] and don't do anything else."
@@ -1129,6 +1157,10 @@ def run(args):
11291157
# Link object files using wasm-ld or llvm-link (for bitcode linking)
11301158
linker_arguments = phase_calculate_linker_inputs(options, state, linker_inputs)
11311159

1160+
# Embed and preload files
1161+
if len(options.preload_files) or len(options.embed_files):
1162+
linker_arguments += package_files(options, target)
1163+
11321164
if options.oformat == OFormat.OBJECT:
11331165
logger.debug(f'link_to_object: {linker_arguments} -> {target}')
11341166
building.link_to_object(linker_arguments, target)
@@ -2688,7 +2720,7 @@ def phase_post_link(options, state, in_wasm, wasm_target, target):
26882720

26892721
phase_emscript(options, in_wasm, wasm_target, memfile)
26902722

2691-
phase_source_transforms(options, target)
2723+
phase_source_transforms(options)
26922724

26932725
if memfile and not settings.MINIMAL_RUNTIME:
26942726
# MINIMAL_RUNTIME doesn't use `var memoryInitializer` but instead expects Module['mem'] to
@@ -2719,33 +2751,9 @@ def phase_emscript(options, in_wasm, wasm_target, memfile):
27192751

27202752

27212753
@ToolchainProfiler.profile_block('source transforms')
2722-
def phase_source_transforms(options, target):
2754+
def phase_source_transforms(options):
27232755
global final_js
27242756

2725-
# Embed and preload files
2726-
if len(options.preload_files) or len(options.embed_files):
2727-
logger.debug('setting up files')
2728-
file_args = ['--from-emcc', '--export-name=' + settings.EXPORT_NAME]
2729-
if len(options.preload_files):
2730-
file_args.append('--preload')
2731-
file_args += options.preload_files
2732-
if len(options.embed_files):
2733-
file_args.append('--embed')
2734-
file_args += options.embed_files
2735-
if len(options.exclude_files):
2736-
file_args.append('--exclude')
2737-
file_args += options.exclude_files
2738-
if options.use_preload_cache:
2739-
file_args.append('--use-preload-cache')
2740-
if settings.LZ4:
2741-
file_args.append('--lz4')
2742-
if options.use_preload_plugins:
2743-
file_args.append('--use-preload-plugins')
2744-
if not settings.ENVIRONMENT_MAY_BE_NODE:
2745-
file_args.append('--no-node')
2746-
file_code = shared.check_call([shared.FILE_PACKAGER, shared.replace_suffix(target, '.data')] + file_args, stdout=PIPE).stdout
2747-
options.pre_js = js_manipulation.add_files_pre_js(options.pre_js, file_code)
2748-
27492757
# Apply pre and postjs files
27502758
if final_js and (options.pre_js or options.post_js):
27512759
logger.debug('applying pre/postjses')

src/library_fs.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1590,7 +1590,11 @@ FS.staticInit();` +
15901590
return FS.create(path, mode);
15911591
},
15921592
createDataFile: function(parent, name, data, canRead, canWrite, canOwn) {
1593-
var path = name ? PATH.join2(typeof parent === 'string' ? parent : FS.getPath(parent), name) : parent;
1593+
var path = name;
1594+
if (parent) {
1595+
parent = typeof parent === 'string' ? parent : FS.getPath(parent);
1596+
path = name ? PATH.join2(parent, name) : parent;
1597+
}
15941598
var mode = FS.getMode(canRead, canWrite);
15951599
var node = FS.create(path, mode);
15961600
if (data) {

tests/other/metadce/hello_world_O3_MAIN_MODULE_2.funcs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ $__emscripten_stdout_seek
33
$__fwritex
44
$__stdio_write
55
$__towrite
6-
$__wasm_apply_global_relocs
6+
$__wasm_apply_data_relocs
77
$__wasm_call_ctors
8+
$__wasm_start
89
$dlmalloc
910
$legalstub$dynCall_jiji
1011
$main

tests/test_other.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2602,6 +2602,32 @@ def test_file_packager_returns_error_if_target_equal_to_jsoutput(self):
26022602
self.assertEqual(result.returncode, 1)
26032603
self.assertContained(MESSAGE, result.stderr)
26042604

2605+
def test_file_packager_embed(self):
2606+
create_file('data.txt', 'hello data')
2607+
2608+
# Without --obj-output we issue a warning
2609+
err = self.run_process([FILE_PACKAGER, 'test.data', '--embed', 'data.txt', '--js-output=data.js'], stderr=PIPE).stderr
2610+
self.assertContained('--obj-output is recommended when using --embed', err)
2611+
2612+
self.run_process([FILE_PACKAGER, 'test.data', '--embed', 'data.txt', '--obj-output=data.o', '--js-output=data.js'])
2613+
2614+
create_file('test.c', '''
2615+
#include <stdio.h>
2616+
2617+
int main() {
2618+
FILE* f = fopen("data.txt", "r");
2619+
char buf[64];
2620+
int rtn = fread(buf, 1, 64, f);
2621+
buf[rtn] = '\\0';
2622+
fclose(f);
2623+
printf("%s\\n", buf);
2624+
return 0;
2625+
}
2626+
''')
2627+
self.run_process([EMCC, '--pre-js=data.js', 'test.c', 'data.o', '-sFORCE_FILESYSTEM'])
2628+
output = self.run_js('a.out.js')
2629+
self.assertContained('hello data', output)
2630+
26052631
def test_headless(self):
26062632
shutil.copyfile(test_file('screenshot.png'), 'example.png')
26072633
self.run_process([EMCC, test_file('sdl_headless.c'), '-sHEADLESS'])

0 commit comments

Comments
 (0)