#!/usr/bin/env node /** * @license * Copyright 2010 The Emscripten Authors * SPDX-License-Identifier: MIT */ // JavaScript compiler, main entry point import assert from 'node:assert'; import {parseArgs} from 'node:util'; import { applySettings, loadDefaultSettings, printErr, readFile, timer, } from '../src/utility.mjs'; timer.start('startup') loadDefaultSettings(); const options = { help: {type: 'boolean', short: 'h'}, 'symbols-only': {type: 'boolean'}, output: {type: 'string', short: 'o'}, }; const {values, positionals} = parseArgs({options, allowPositionals: true}); if (values.help) { console.log(`\ Main entry point for JS compiler If no -o file is specified then the generated code is written to stdout. Usage: compiler.mjs [-o out.js] [--symbols-only]`); process.exit(0); } timer.start('read settings') // Load settings from JSON passed on the command line let settingsFile = positionals[0]; assert(settingsFile, 'settings file not specified'); if (settingsFile == '-') { // Read settings json from stdin (FD 0) settingsFile = 0; } const userSettings = JSON.parse(readFile(settingsFile)); applySettings(userSettings); timer.stop('read settings') export const symbolsOnly = values['symbols-only']; // TODO(sbc): Remove EMCC_BUILD_DIR at some point. It used to be required // back when ran the JS compiler with overridden CWD. process.env['EMCC_BUILD_DIR'] = process.cwd(); // In case compiler.mjs is run directly (as in gen_sig_info) // ALL_INCOMING_MODULE_JS_API might not be populated yet. if (!ALL_INCOMING_MODULE_JS_API.size) { ALL_INCOMING_MODULE_JS_API = INCOMING_MODULE_JS_API; } if (symbolsOnly) { INCLUDE_FULL_LIBRARY = 1; } // Side modules are pure wasm and have no JS assert( !SIDE_MODULE || (ASYNCIFY && symbolsOnly), 'JS compiler should only run on side modules if asyncify is used.', ); // Load compiler code timer.start('dynamic imports') // We can't use static import statements here because several of these // file depend on having the settings defined in the global scope (which // we do dynamically above. await import('../src/modules.mjs'); await import('../src/parseTools.mjs'); if (!STRICT) { await import('../src/parseTools_legacy.mjs'); } const jsifier = await import('../src/jsifier.mjs'); timer.stop('dynamic imports') timer.stop('startup') // =============================== // Main // =============================== try { timer.start('runJSify') await jsifier.runJSify(values.output, symbolsOnly); timer.stop('runJSify') } catch (err) { if (err.toString().includes('Aborting compilation due to previous errors')) { // Compiler failed on user error, don't print the stacktrace in this case. printErr(err); } else { // Compiler failed on internal compiler error! printErr('Internal compiler error JS compiler'); printErr('Please create a bug report at https://github.com/emscripten-core/emscripten/issues/'); printErr( 'with a log of the build and the input files used to run. Exception message: "' + (err.stack || err), ); } // Work around a node.js bug where stdout buffer is not flushed at process exit: // Instead of process.exit() directly, wait for stdout flush event. // See https://github.com/joyent/node/issues/1669 and https://github.com/emscripten-core/emscripten/issues/2582 // Workaround is based on https://github.com/RReverser/acorn/commit/50ab143cecc9ed71a2d66f78b4aec3bb2e9844f6 process.stdout.once('drain', () => process.exit(1)); // Make sure to print something to force the drain event to occur, in case the // stdout buffer was empty. console.log(' '); // Work around another node bug where sometimes 'drain' is never fired - make // another effort to emit the exit status, after a significant delay (if node // hasn't fired drain by then, give up) setTimeout(() => process.exit(1), 500); }