From 51bf400cf535bf36fdc6796119901f91caa46acc Mon Sep 17 00:00:00 2001 From: Kohei Tokunaga Date: Mon, 6 Apr 2026 07:51:32 +0000 Subject: [PATCH 1/3] tools/response_file.py: remove dependency on the shared lib Signed-off-by: Kohei Tokunaga --- tools/building.py | 24 +++++++++++++++++++++++- tools/response_file.py | 24 +----------------------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/tools/building.py b/tools/building.py index bc09df6d26012..ea8933d351630 100644 --- a/tools/building.py +++ b/tools/building.py @@ -348,11 +348,33 @@ def get_command_with_possible_response_file(cmd): return cmd logger.debug('using response file for %s' % cmd[0]) - filename = response_file.create_response_file(cmd[1:], shared.TEMP_DIR) + filename = create_response_file(cmd[1:], shared.TEMP_DIR) new_cmd = [cmd[0], "@" + filename] return new_cmd +def create_response_file(args, directory): + """Routes the given cmdline param list in args into a new response file and + returns the filename to it. + """ + # Backslashes and other special chars need to be escaped in the response file. + contents = response_file.create_response_file_contents(args) + + response_fd, response_filename = tempfile.mkstemp(prefix='emscripten_', suffix='.rsp.utf-8', dir=directory, text=True) + + with os.fdopen(response_fd, 'w', encoding='utf-8') as f: + f.write(contents) + + if DEBUG: + logging.warning(f'Creating response file {response_filename} with following contents: {contents}') + + # Register the created .rsp file to be automatically cleaned up once this + # process finishes, so that caller does not have to remember to do it. + shared.get_temp_files().note(response_filename) + + return response_filename + + def emar(action, output_filename, filenames, stdout=None, stderr=None, env=None): utils.delete_file(output_filename) cmd = [EMAR, action, output_filename] + filenames diff --git a/tools/response_file.py b/tools/response_file.py index 12555c5094fe9..6f1d9bbaa60be 100644 --- a/tools/response_file.py +++ b/tools/response_file.py @@ -8,7 +8,7 @@ import shlex import tempfile -from . import shared +# Do not import shared.py so that file_packager.py can run without setting up LLVM_ROOT. from .utils import WINDOWS DEBUG = int(os.environ.get('EMCC_DEBUG', '0')) @@ -39,28 +39,6 @@ def escape(arg): return contents -def create_response_file(args, directory): - """Routes the given cmdline param list in args into a new response file and - returns the filename to it. - """ - # Backslashes and other special chars need to be escaped in the response file. - contents = create_response_file_contents(args) - - response_fd, response_filename = tempfile.mkstemp(prefix='emscripten_', suffix='.rsp.utf-8', dir=directory, text=True) - - with os.fdopen(response_fd, 'w', encoding='utf-8') as f: - f.write(contents) - - if DEBUG: - logging.warning(f'Creating response file {response_filename} with following contents: {contents}') - - # Register the created .rsp file to be automatically cleaned up once this - # process finishes, so that caller does not have to remember to do it. - shared.get_temp_files().note(response_filename) - - return response_filename - - def expand_response_file(arg): """Reads a response file, and returns the list of cmdline params found in the file. From b9cf8c6bff6f64ea0c4e182e985b2d8cb4f88894 Mon Sep 17 00:00:00 2001 From: Kohei Tokunaga Date: Mon, 6 Apr 2026 07:52:32 +0000 Subject: [PATCH 2/3] tools/js_manipulation.py: remove dependency on the shared lib Signed-off-by: Kohei Tokunaga --- tools/js_manipulation.py | 33 ++------------------------------- tools/link.py | 32 +++++++++++++++++++++++++++++++- 2 files changed, 33 insertions(+), 32 deletions(-) diff --git a/tools/js_manipulation.py b/tools/js_manipulation.py index 37e3c12f5b728..cdda49c1de0f7 100644 --- a/tools/js_manipulation.py +++ b/tools/js_manipulation.py @@ -5,7 +5,8 @@ import re -from . import shared, utils +# Do not import shared.py so that file_packager.py can run without setting up LLVM_ROOT. +from . import utils from .settings import settings emscripten_license = '''\ @@ -28,36 +29,6 @@ emscripten_license_regex = r'\/\*\*?(\s*\*?\s*@license)?(\s*\*?\s*Copyright \d+ The Emscripten Authors\s*\*?\s*SPDX-License-Identifier: MIT)+\s*\*\/\s*' -def add_files_pre_js(pre_js_list, files_pre_js): - # the normal thing is to just combine the pre-js content - filename = shared.get_temp_files().get('.js').name - utils.write_file(filename, files_pre_js) - pre_js_list.insert(0, filename) - if not settings.ASSERTIONS: - return - - # if a user pre-js tramples the file code's changes to Module.preRun - # that could be confusing. show a clear error at runtime if assertions are - # enabled - pre = shared.get_temp_files().get('.js').name - post = shared.get_temp_files().get('.js').name - utils.write_file(pre, ''' - // All the pre-js content up to here must remain later on, we need to run - // it. - if ((typeof ENVIRONMENT_IS_WASM_WORKER != 'undefined' && ENVIRONMENT_IS_WASM_WORKER) || (typeof ENVIRONMENT_IS_PTHREAD != 'undefined' && ENVIRONMENT_IS_PTHREAD) || (typeof ENVIRONMENT_IS_AUDIO_WORKLET != 'undefined' && ENVIRONMENT_IS_AUDIO_WORKLET)) Module['preRun'] = []; - var necessaryPreJSTasks = Module['preRun'].slice(); - ''') - utils.write_file(post, ''' - if (!Module['preRun']) throw 'Module.preRun should exist because file support used it; did a pre-js delete it?'; - necessaryPreJSTasks.forEach((task) => { - if (Module['preRun'].indexOf(task) < 0) throw 'All preRun tasks that exist before user pre-js code should remain after; did you replace Module or modify Module.preRun?'; - }); - ''') - - pre_js_list.insert(1, pre) - pre_js_list.append(post) - - def handle_license(js_target): # ensure we emit the license if and only if we need to, and exactly once js = utils.read_file(js_target) diff --git a/tools/link.py b/tools/link.py index eb850b597b87e..13593115a4656 100644 --- a/tools/link.py +++ b/tools/link.py @@ -3008,7 +3008,7 @@ def package_files(options, target): if options.preload_files: # Preloading files uses --pre-js code that runs before the module is loaded. file_code = shared.check_call(cmd, stdout=PIPE).stdout - js_manipulation.add_files_pre_js(settings.PRE_JS_FILES, file_code) + add_files_pre_js(settings.PRE_JS_FILES, file_code) else: # Otherwise, we are embedding files, which does not require --pre-js code, # and instead relies on a static constructor to populate the filesystem. @@ -3017,6 +3017,36 @@ def package_files(options, target): return rtn +def add_files_pre_js(pre_js_list, files_pre_js): + # the normal thing is to just combine the pre-js content + filename = shared.get_temp_files().get('.js').name + utils.write_file(filename, files_pre_js) + pre_js_list.insert(0, filename) + if not settings.ASSERTIONS: + return + + # if a user pre-js tramples the file code's changes to Module.preRun + # that could be confusing. show a clear error at runtime if assertions are + # enabled + pre = shared.get_temp_files().get('.js').name + post = shared.get_temp_files().get('.js').name + utils.write_file(pre, ''' + // All the pre-js content up to here must remain later on, we need to run + // it. + if ((typeof ENVIRONMENT_IS_WASM_WORKER != 'undefined' && ENVIRONMENT_IS_WASM_WORKER) || (typeof ENVIRONMENT_IS_PTHREAD != 'undefined' && ENVIRONMENT_IS_PTHREAD) || (typeof ENVIRONMENT_IS_AUDIO_WORKLET != 'undefined' && ENVIRONMENT_IS_AUDIO_WORKLET)) Module['preRun'] = []; + var necessaryPreJSTasks = Module['preRun'].slice(); + ''') + utils.write_file(post, ''' + if (!Module['preRun']) throw 'Module.preRun should exist because file support used it; did a pre-js delete it?'; + necessaryPreJSTasks.forEach((task) => { + if (Module['preRun'].indexOf(task) < 0) throw 'All preRun tasks that exist before user pre-js code should remain after; did you replace Module or modify Module.preRun?'; + }); + ''') + + pre_js_list.insert(1, pre) + pre_js_list.append(post) + + @ToolchainProfiler.profile_block('calculate linker inputs') def phase_calculate_linker_inputs(options, linker_args): using_lld = not (options.oformat == OFormat.OBJECT and settings.LTO) From 95aaa8f281aeba57b43e3eca697de4d77a0d09b8 Mon Sep 17 00:00:00 2001 From: Kohei Tokunaga Date: Mon, 6 Apr 2026 08:06:55 +0000 Subject: [PATCH 3/3] file_packager.py: remove global import of the shared lib Signed-off-by: Kohei Tokunaga --- tools/file_packager.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/file_packager.py b/tools/file_packager.py index 6591548a3c3a1..88075db6d1acd 100755 --- a/tools/file_packager.py +++ b/tools/file_packager.py @@ -84,7 +84,8 @@ __rootdir__ = os.path.dirname(__scriptdir__) sys.path.insert(0, __rootdir__) -from tools import diagnostics, js_manipulation, shared, utils +# Do not import shared.py globally so that this command can run without setting up LLVM_ROOT unless the user explicitly specifies flags that depend on it. +from tools import diagnostics, js_manipulation, utils from tools.response_file import substitute_response_files DEBUG = os.environ.get('EMCC_DEBUG') @@ -252,6 +253,7 @@ def escape(c): def generate_object_file(data_files): + from tools import shared embed_files = [f for f in data_files if f.mode == 'embed'] assert embed_files @@ -727,6 +729,7 @@ def generate_preload_js(data_target, data_files, metadata): js_manipulation.escape_for_js_string(data_target)) else: # LZ4FS usage + from tools import shared temp = data_target + '.orig' shutil.move(data_target, temp) meta = shared.run_js_tool(utils.path_from_root('tools/lz4-compress.mjs'),