Skip to content

Commit fb0d9ab

Browse files
authored
file_packager.py: Add option to embed file data in wasm binary (#16050)
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 possibility of zero copy, memory-backed files - Less compatible with standalone wasm / WASI
1 parent e46b713 commit fb0d9ab

File tree

7 files changed

+223
-48
lines changed

7 files changed

+223
-48
lines changed

ChangeLog.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ See docs/process.md for more on how version tagging works.
2020

2121
3.1.3
2222
-----
23+
- The file packager now supports embedding files directly into wasm memory and
24+
`emcc` now uses this mode when the `--embed-file` option is used. If you
25+
use `file_packager` directly it is recommended that you switch to the new mode
26+
by adding `--obj-output` to the command line. (#16050)
2327

2428
3.1.2 - 20/01/2022
2529
------------------

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)
@@ -2700,7 +2732,7 @@ def phase_post_link(options, state, in_wasm, wasm_target, target):
27002732

27012733
phase_emscript(options, in_wasm, wasm_target, memfile)
27022734

2703-
phase_source_transforms(options, target)
2735+
phase_source_transforms(options)
27042736

27052737
if memfile and not settings.MINIMAL_RUNTIME:
27062738
# MINIMAL_RUNTIME doesn't use `var memoryInitializer` but instead expects Module['mem'] to
@@ -2731,33 +2763,9 @@ def phase_emscript(options, in_wasm, wasm_target, memfile):
27312763

27322764

27332765
@ToolchainProfiler.profile_block('source transforms')
2734-
def phase_source_transforms(options, target):
2766+
def phase_source_transforms(options):
27352767
global final_js
27362768

2737-
# Embed and preload files
2738-
if len(options.preload_files) or len(options.embed_files):
2739-
logger.debug('setting up files')
2740-
file_args = ['--from-emcc', '--export-name=' + settings.EXPORT_NAME]
2741-
if len(options.preload_files):
2742-
file_args.append('--preload')
2743-
file_args += options.preload_files
2744-
if len(options.embed_files):
2745-
file_args.append('--embed')
2746-
file_args += options.embed_files
2747-
if len(options.exclude_files):
2748-
file_args.append('--exclude')
2749-
file_args += options.exclude_files
2750-
if options.use_preload_cache:
2751-
file_args.append('--use-preload-cache')
2752-
if settings.LZ4:
2753-
file_args.append('--lz4')
2754-
if options.use_preload_plugins:
2755-
file_args.append('--use-preload-plugins')
2756-
if not settings.ENVIRONMENT_MAY_BE_NODE:
2757-
file_args.append('--no-node')
2758-
file_code = shared.check_call([shared.FILE_PACKAGER, shared.replace_suffix(target, '.data')] + file_args, stdout=PIPE).stdout
2759-
options.pre_js = js_manipulation.add_files_pre_js(options.pre_js, file_code)
2760-
27612769
# Apply pre and postjs files
27622770
if final_js and (options.pre_js or options.post_js):
27632771
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: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2590,7 +2590,7 @@ def test_file_packager_directory_with_single_quote(self):
25902590
assert json.dumps("direc'tory") in proc.stdout
25912591

25922592
def test_file_packager_mention_FORCE_FILESYSTEM(self):
2593-
MESSAGE = 'Remember to build the main file with -s FORCE_FILESYSTEM=1 so that it includes support for loading this file package'
2593+
MESSAGE = 'Remember to build the main file with `-sFORCE_FILESYSTEM` so that it includes support for loading this file package'
25942594
create_file('data.txt', 'data1')
25952595
# mention when running standalone
25962596
err = self.run_process([FILE_PACKAGER, 'test.data', '--preload', 'data.txt'], stdout=PIPE, stderr=PIPE).stderr
@@ -2608,7 +2608,11 @@ def test_file_packager_returns_error_if_target_equal_to_jsoutput(self):
26082608
def test_file_packager_embed(self):
26092609
create_file('data.txt', 'hello data')
26102610

2611-
self.run_process([FILE_PACKAGER, 'test.data', '--embed', 'data.txt', '--js-output=data.js'])
2611+
# Without --obj-output we issue a warning
2612+
err = self.run_process([FILE_PACKAGER, 'test.data', '--embed', 'data.txt', '--js-output=data.js'], stderr=PIPE).stderr
2613+
self.assertContained('--obj-output is recommended when using --embed', err)
2614+
2615+
self.run_process([FILE_PACKAGER, 'test.data', '--embed', 'data.txt', '--obj-output=data.o', '--js-output=data.js'])
26122616

26132617
create_file('test.c', '''
26142618
#include <stdio.h>
@@ -2623,7 +2627,7 @@ def test_file_packager_embed(self):
26232627
return 0;
26242628
}
26252629
''')
2626-
self.run_process([EMCC, '--pre-js=data.js', 'test.c', '-sFORCE_FILESYSTEM'])
2630+
self.run_process([EMCC, '--pre-js=data.js', 'test.c', 'data.o', '-sFORCE_FILESYSTEM'])
26272631
output = self.run_js('a.out.js')
26282632
self.assertContained('hello data', output)
26292633

0 commit comments

Comments
 (0)