import os
import shutil
import sys
import logging
from typing import List, Optional
from . import utils, diagnostics
from .utils import path_from_root, exit_with_error, __rootpath__
logger = logging.getLogger('config')
EMSCRIPTEN_ROOT = __rootpath__
NODE_JS = None
BINARYEN_ROOT = None
LLVM_ADD_VERSION = None
CLANG_ADD_VERSION = None
CLOSURE_COMPILER = None
FROZEN_CACHE = None
CACHE = None
PORTS = None
COMPILER_WRAPPER = None
EM_CONFIG = None
NODE_JS_TEST = None
SPIDERMONKEY_ENGINE = None
V8_ENGINE: Optional[List[str]] = None
LLVM_ROOT = None
JS_ENGINES: List[List[str]] = []
WASMER = None
WASMTIME = None
WASM_ENGINES: List[List[str]] = []
def listify(x):
if x is None or type(x) is list:
return x
return [x]
def fix_js_engine(old, new):
if old is None:
return
global JS_ENGINES
JS_ENGINES = [new if x == old else x for x in JS_ENGINES]
return new
def normalize_config_settings():
global CACHE, PORTS, LLVM_ADD_VERSION, CLANG_ADD_VERSION, CLOSURE_COMPILER
global NODE_JS, NODE_JS_TEST, V8_ENGINE, JS_ENGINES, SPIDERMONKEY_ENGINE, WASM_ENGINES
SPIDERMONKEY_ENGINE = fix_js_engine(SPIDERMONKEY_ENGINE, listify(SPIDERMONKEY_ENGINE))
NODE_JS = fix_js_engine(NODE_JS, listify(NODE_JS))
NODE_JS_TEST = fix_js_engine(NODE_JS_TEST, listify(NODE_JS_TEST))
V8_ENGINE = fix_js_engine(V8_ENGINE, listify(V8_ENGINE))
JS_ENGINES = [listify(engine) for engine in JS_ENGINES]
WASM_ENGINES = [listify(engine) for engine in WASM_ENGINES]
CLOSURE_COMPILER = listify(CLOSURE_COMPILER)
if not CACHE:
CACHE = path_from_root('cache')
if not PORTS:
PORTS = os.path.join(CACHE, 'ports')
def set_config_from_tool_location(config_key, tool_binary, f):
val = globals()[config_key]
if val is None:
path = shutil.which(tool_binary)
if not path:
if not os.path.isfile(EM_CONFIG):
diagnostics.warn('config file not found: %s. You can create one by hand or run `emcc --generate-config`', EM_CONFIG)
exit_with_error('%s not set in config (%s), and `%s` not found in PATH', config_key, EM_CONFIG, tool_binary)
globals()[config_key] = f(path)
elif not val:
exit_with_error('%s is set to empty value in %s', config_key, EM_CONFIG)
def parse_config_file():
"""Parse the emscripten config file using python's exec.
Also check EM_<KEY> environment variables to override specific config keys.
"""
config = {'__file__': EM_CONFIG}
config_text = utils.read_file(EM_CONFIG)
try:
exec(config_text, config)
except Exception as e:
exit_with_error('error in evaluating config file (%s): %s, text: %s', EM_CONFIG, str(e), config_text)
CONFIG_KEYS = (
'NODE_JS',
'NODE_JS_TEST',
'BINARYEN_ROOT',
'SPIDERMONKEY_ENGINE',
'V8_ENGINE',
'LLVM_ROOT',
'LLVM_ADD_VERSION',
'CLANG_ADD_VERSION',
'CLOSURE_COMPILER',
'JS_ENGINES',
'WASMER',
'WASMTIME',
'WASM_ENGINES',
'FROZEN_CACHE',
'CACHE',
'PORTS',
'COMPILER_WRAPPER',
)
for key in CONFIG_KEYS:
env_var = 'EM_' + key
env_value = os.environ.get(env_var)
if env_value is not None:
if env_value in ('', '0'):
env_value = None
if env_var in ('EM_JS_ENGINES', 'EM_WASM_ENGINES'):
env_value = env_value.split(',')
if env_var in ('EM_CONFIG', 'EM_CACHE', 'EM_PORTS', 'EM_LLVM_ROOT', 'EM_BINARYEN_ROOT'):
if not os.path.isabs(env_value):
exit_with_error(f'environment variable {env_var} must be an absolute path: {env_value}')
globals()[key] = env_value
elif key in config:
globals()[key] = config[key]
def read_config():
if os.path.isfile(EM_CONFIG):
parse_config_file()
LEGACY_ENV_VARS = {
'LLVM': 'EM_LLVM_ROOT',
'BINARYEN': 'EM_BINARYEN_ROOT',
'NODE': 'EM_NODE_JS',
'LLVM_ADD_VERSION': 'EM_LLVM_ADD_VERSION',
'CLANG_ADD_VERSION': 'EM_CLANG_ADD_VERSION',
}
for key, new_key in LEGACY_ENV_VARS.items():
env_value = os.environ.get(key)
if env_value and new_key not in os.environ:
msg = f'legacy environment variable found: `{key}`. Please switch to using `{new_key}` instead`'
if key == 'NODE':
logger.debug(msg)
else:
logger.warning(msg)
set_config_from_tool_location('LLVM_ROOT', 'clang', os.path.dirname)
set_config_from_tool_location('NODE_JS', 'node', lambda x: x)
set_config_from_tool_location('BINARYEN_ROOT', 'wasm-opt', lambda x: os.path.dirname(os.path.dirname(x)))
normalize_config_settings()
def generate_config(path):
if os.path.exists(path):
exit_with_error(f'config file already exists: `{path}`')
config_data = utils.read_file(path_from_root('tools/config_template.py'))
config_data = config_data.splitlines()[3:]
config_data = '\n'.join(config_data) + '\n'
llvm_root = os.path.dirname(shutil.which('wasm-ld') or '/usr/bin/wasm-ld')
config_data = config_data.replace('\'{{{ LLVM_ROOT }}}\'', repr(llvm_root))
binaryen_root = os.path.dirname(os.path.dirname(shutil.which('wasm-opt') or '/usr/local/bin/wasm-opt'))
config_data = config_data.replace('\'{{{ BINARYEN_ROOT }}}\'', repr(binaryen_root))
node = shutil.which('node') or shutil.which('nodejs') or 'node'
config_data = config_data.replace('\'{{{ NODE }}}\'', repr(node))
utils.write_file(path, config_data)
print('''\
An Emscripten settings file has been generated at:
%s
It contains our best guesses for the important paths, which are:
LLVM_ROOT = %s
BINARYEN_ROOT = %s
NODE_JS = %s
Please edit the file if any of those are incorrect.\
''' % (path, llvm_root, binaryen_root, node), file=sys.stderr)
def find_config_file():
embedded_config = path_from_root('.emscripten')
emsdk_root = os.path.dirname(os.path.dirname(path_from_root()))
emsdk_embedded_config = os.path.join(emsdk_root, '.emscripten')
user_home_config = os.path.expanduser('~/.emscripten')
if '--em-config' in sys.argv:
i = sys.argv.index('--em-config')
if len(sys.argv) <= i + 1:
exit_with_error('--em-config must be followed by a filename')
del sys.argv[i]
return sys.argv.pop(i)
if 'EM_CONFIG' in os.environ:
return os.environ['EM_CONFIG']
if os.path.isfile(embedded_config):
return embedded_config
if os.path.isfile(emsdk_embedded_config):
return emsdk_embedded_config
if os.path.isfile(user_home_config):
return user_home_config
return embedded_config
def init():
global EM_CONFIG
EM_CONFIG = find_config_file()
if '\n' in EM_CONFIG:
exit_with_error('inline EM_CONFIG data no longer supported. Please use a config file.')
EM_CONFIG = os.path.expanduser(EM_CONFIG)
if '--generate-config' in sys.argv:
generate_config(EM_CONFIG)
sys.exit(0)
if os.path.isfile(EM_CONFIG):
logger.debug(f'using config file: {EM_CONFIG}')
else:
logger.debug('config file not found; using default config')
os.environ['EM_CONFIG'] = EM_CONFIG
read_config()
init()