diff --git a/.gitignore b/.gitignore index 0477fa957b..02e94fdcd0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ *.pyc __pycache__ +# Track which tools are currently active +/activated.json + # Support for --embedded configs /.emscripten /.emscripten.old diff --git a/emsdk.py b/emsdk.py index 10fc194ba1..0e48b583b5 100644 --- a/emsdk.py +++ b/emsdk.py @@ -1597,7 +1597,94 @@ def get_required_path(active_tools): # Returns the absolute path to the file '.emscripten' for the current user on # this system. EM_CONFIG_PATH = os.path.join(EMSDK_PATH, ".emscripten") -EM_CONFIG_DICT = {} +ACTIVATED_JSON_PATH = os.path.join(EMSDK_PATH, "activated.json") +ACTIVATED_TOOLS = set() + + +def get_active_legacy(): + """Legacy support for users without existing `activated.json` files. + + Older versions of emsdk would imply active status purely based on the + emscripten config file. This function uses that old method to generate + an initial `activated.json` state. + """ + if not os.path.exists(EM_CONFIG_PATH): + return [] + + def load_em_config(): + em_config_dict = {} + try: + lines = read_file(EM_CONFIG_PATH).splitlines() + for line in lines: + try: + key, value = parse_key_value(line) + if value: + em_config_dict[key] = value + except Exception: + pass + except Exception: + pass + return em_config_dict + + em_config_dict = load_em_config() + + def is_active_legacy(tool): + if not tool.is_installed(): + return False + + deps = tool.dependencies() + for dep in deps: + if not is_active_legacy(dep): + return False + + activated_cfg = tool.activated_config() + if tool.legacy_cfg: + name, value = to_unix_path(tool.expand_vars(tool.legacy_cfg)).split('=') + activated_cfg[name] = value.strip("'") + + if not activated_cfg: + return len(deps) > 0 + + for key, value in activated_cfg.items(): + if key not in em_config_dict: + return False + config_value = em_config_dict[key].replace("emsdk_path + '", "'" + EMSDK_PATH).replace("$CFGDIR", EMSDK_PATH).strip("'") + if config_value != value: + return False + + return True + + active = [] + for sdk in sdks: + if is_active_legacy(sdk): + active.append(sdk) + for tool in tools: + if is_active_legacy(tool): + active.append(tool) + + return active + + +def load_activated_json(): + ACTIVATED_TOOLS.clear() + if not os.path.exists(ACTIVATED_JSON_PATH): + save_activated_json(get_active_legacy()) + return + + try: + data = json.loads(read_file(ACTIVATED_JSON_PATH)) + assert isinstance(data, dict), f'Expected dict in {ACTIVATED_JSON_PATH}' + ACTIVATED_TOOLS.update(data.get('active_tools', [])) + except Exception as e: + errlog(f'warning: failed to parse {ACTIVATED_JSON_PATH}: {e}') + + +def save_activated_json(active_tools): + names = sorted(unique_items([str(t) for t in active_tools])) + data = {'active_tools': names} + write_file(ACTIVATED_JSON_PATH, json.dumps(data, indent=2) + '\n') + # Now re-load the newly generated file to update the `ACTIVATED_TOOLS` global state. + load_activated_json() def parse_key_value(line): @@ -1612,22 +1699,6 @@ def parse_key_value(line): return (key, '') -def load_em_config(): - EM_CONFIG_DICT.clear() - lines = [] - try: - lines = read_file(EM_CONFIG_PATH).splitlines() - except Exception: - pass - for line in lines: - try: - key, value = parse_key_value(line) - if value: - EM_CONFIG_DICT[key] = value - except Exception: - pass - - def find_emscripten_root(active_tools): """Find the currently active emscripten root. @@ -1820,6 +1891,7 @@ class Tool: cmake_build_type = None install_path = None activated_path_skip = False + legacy_cfg = None activated_cfg = None activated_env = None arch = None @@ -2026,23 +2098,7 @@ def is_active(self): if not tool.is_active(): return False - activated_cfg = self.activated_config() - if not activated_cfg: - return len(deps) > 0 - - for key, value in activated_cfg.items(): - if key not in EM_CONFIG_DICT: - debug_print(f'{self} is not active, because key="{key}" does not exist in .emscripten') - return False - - # all paths are stored dynamically relative to the emsdk root, so - # normalize those first. - config_value = EM_CONFIG_DICT[key].replace("emsdk_path + '", "'" + EMSDK_PATH).replace("$CFGDIR", EMSDK_PATH) - config_value = config_value.strip("'") - if config_value != value: - debug_print(f'{self} is not active, because key="{key}" has value "{config_value}" but should have value "{value}"') - return False - return True + return str(self) in ACTIVATED_TOOLS def is_env_active(self): """Returns true if the system environment variables requires by this tool are currently active.""" @@ -2623,6 +2679,7 @@ def set_active_tools(tools_to_activate, permanently_activate, system): print('') generate_em_config(tools_to_activate, permanently_activate, system) + save_activated_json(tools_to_activate) # Construct a .bat or .ps1 script that will be invoked to set env. vars and PATH # We only do this on cmd or powershell since emsdk.bat/ps1 is able to modify the @@ -3072,8 +3129,8 @@ def extract_string_arg(name): activating = cmd == 'activate' args = [expand_sdk_name(a, activating=activating) for a in args] - load_em_config() load_sdk_manifest() + load_activated_json() # Apply any overrides to git branch names to clone from. forked_url = extract_string_arg('--override-repository') diff --git a/emsdk_manifest.json b/emsdk_manifest.json index 46255d3950..0b15c546b3 100644 --- a/emsdk_manifest.json +++ b/emsdk_manifest.json @@ -278,9 +278,9 @@ "version": "4.2.0-rc3", "bitness": 64, "arch": "x86_64", - "activated_cfg": "EMSDK_CMAKE='%installation_dir%/bin/cmake%.exe%'", + "legacy_cfg": "EMSDK_CMAKE='%installation_dir%/bin/cmake%.exe%'", "activated_path": "%installation_dir%/bin", - "activated_cfg_macos": "EMSDK_CMAKE='%installation_dir%/CMake.app/Contents/bin/cmake%.exe%'", + "legacy_cfg_macos": "EMSDK_CMAKE='%installation_dir%/CMake.app/Contents/bin/cmake%.exe%'", "activated_path_macos": "%installation_dir%/CMake.app/Contents/bin", "url_windows": "https://github.com/Kitware/CMake/releases/download/v4.2.0-rc3/cmake-4.2.0-rc3-windows-x86_64.zip", "url_linux": "https://github.com/Kitware/CMake/releases/download/v4.2.0-rc3/cmake-4.2.0-rc3-linux-x86_64.tar.gz", @@ -291,9 +291,9 @@ "version": "4.2.0-rc3", "bitness": 64, "arch": "arm64", - "activated_cfg": "EMSDK_CMAKE='%installation_dir%/bin/cmake%.exe%'", + "legacy_cfg": "EMSDK_CMAKE='%installation_dir%/bin/cmake%.exe%'", "activated_path": "%installation_dir%/bin", - "activated_cfg_macos": "EMSDK_CMAKE='%installation_dir%/CMake.app/Contents/bin/cmake%.exe%'", + "legacy_cfg_macos": "EMSDK_CMAKE='%installation_dir%/CMake.app/Contents/bin/cmake%.exe%'", "activated_path_macos": "%installation_dir%/CMake.app/Contents/bin", "url_windows": "https://github.com/Kitware/CMake/releases/download/v4.2.0-rc3/cmake-4.2.0-rc3-windows-arm64.zip", "url_linux": "https://github.com/Kitware/CMake/releases/download/v4.2.0-rc3/cmake-4.2.0-rc3-linux-aarch64.tar.gz", @@ -306,7 +306,7 @@ "bitness": 64, "arch": "x86_64", "url_windows": "python-3.9.2-4-amd64+pywin32.zip", - "activated_cfg": "PYTHON='%installation_dir%/python.exe'", + "legacy_cfg": "PYTHON='%installation_dir%/python.exe'", "activated_env": "EMSDK_PYTHON=%installation_dir%/python.exe" }, { @@ -316,9 +316,9 @@ "arch": "x86_64", "url_windows": "python-3.9.2-1-embed-amd64+pywin32.zip", "url_macos": "python-3.9.2-3-macos-x86_64.tar.gz", - "activated_cfg_windows": "PYTHON='%installation_dir%/python.exe'", + "legacy_cfg_windows": "PYTHON='%installation_dir%/python.exe'", "activated_env_windows": "EMSDK_PYTHON=%installation_dir%/python.exe", - "activated_cfg_macos": "PYTHON='%installation_dir%/bin/python3'", + "legacy_cfg_macos": "PYTHON='%installation_dir%/bin/python3'", "activated_env_macos": "EMSDK_PYTHON=%installation_dir%/bin/python3;SSL_CERT_FILE=%installation_dir%/lib/python3.9/site-packages/certifi/cacert.pem" }, { @@ -327,7 +327,7 @@ "bitness": 64, "arch": "arm64", "url_macos": "python-3.9.2-1-macos-arm64.tar.gz", - "activated_cfg": "PYTHON='%installation_dir%/bin/python3'", + "legacy_cfg": "PYTHON='%installation_dir%/bin/python3'", "activated_env": "EMSDK_PYTHON=%installation_dir%/bin/python3;SSL_CERT_FILE=%installation_dir%/lib/python3.9/site-packages/certifi/cacert.pem" }, @@ -338,9 +338,9 @@ "arch": "x86_64", "url_windows": "python-3.13.3-0-win-amd64.zip", "url_macos": "python-3.13.3-0-macos-x86_64.tar.gz", - "activated_cfg_windows": "PYTHON='%installation_dir%/python.exe'", + "legacy_cfg_windows": "PYTHON='%installation_dir%/python.exe'", "activated_env_windows": "EMSDK_PYTHON=%installation_dir%/python.exe", - "activated_cfg_macos": "PYTHON='%installation_dir%/bin/python3'", + "legacy_cfg_macos": "PYTHON='%installation_dir%/bin/python3'", "activated_env_macos": "EMSDK_PYTHON=%installation_dir%/bin/python3;SSL_CERT_FILE=%installation_dir%/lib/python3.13/site-packages/certifi/cacert.pem" }, { @@ -350,9 +350,9 @@ "arch": "arm64", "url_windows": "python-3.13.3-0-win-arm64.zip", "url_macos": "python-3.13.3-0-macos-arm64.tar.gz", - "activated_cfg_windows": "PYTHON='%installation_dir%/python.exe'", + "legacy_cfg_windows": "PYTHON='%installation_dir%/python.exe'", "activated_env_windows": "EMSDK_PYTHON=%installation_dir%/python.exe", - "activated_cfg_macos": "PYTHON='%installation_dir%/bin/python3'", + "legacy_cfg_macos": "PYTHON='%installation_dir%/bin/python3'", "activated_env_macos": "EMSDK_PYTHON=%installation_dir%/bin/python3;SSL_CERT_FILE=%installation_dir%/lib/python3.13/site-packages/certifi/cacert.pem" }, @@ -361,7 +361,7 @@ "version": "68.12.0esr", "bitness": 64, "url": "downloaded via mozdownload script, but a dummy directive is placed here so emsdk understands this Tool to be downloaded from the web", - "activated_cfg": "EMSDK_ACTIVATED_TEST_BROWSER=%installation_dir%/%macos_app_bundle_prefix%firefox%.exe%", + "legacy_cfg": "EMSDK_ACTIVATED_TEST_BROWSER=%installation_dir%/%macos_app_bundle_prefix%firefox%.exe%", "activated_env": "EMTEST_BROWSER=%installation_dir%/%macos_app_bundle_prefix%firefox%.exe%", "custom_install_script": "download_firefox", "is_old": true @@ -371,7 +371,7 @@ "version": "78.15.0esr", "bitness": 64, "url": "downloaded via mozdownload script, but a dummy directive is placed here so emsdk understands this Tool to be downloaded from the web", - "activated_cfg": "EMSDK_ACTIVATED_TEST_BROWSER=%installation_dir%/%macos_app_bundle_prefix%firefox%.exe%", + "legacy_cfg": "EMSDK_ACTIVATED_TEST_BROWSER=%installation_dir%/%macos_app_bundle_prefix%firefox%.exe%", "activated_env": "EMTEST_BROWSER=%installation_dir%/%macos_app_bundle_prefix%firefox%.exe%", "custom_install_script": "download_firefox", "is_old": true @@ -381,7 +381,7 @@ "version": "91.13.0esr", "bitness": 64, "url": "downloaded via mozdownload script, but a dummy directive is placed here so emsdk understands this Tool to be downloaded from the web", - "activated_cfg": "EMSDK_ACTIVATED_TEST_BROWSER=%installation_dir%/%macos_app_bundle_prefix%firefox%.exe%", + "legacy_cfg": "EMSDK_ACTIVATED_TEST_BROWSER=%installation_dir%/%macos_app_bundle_prefix%firefox%.exe%", "activated_env": "EMTEST_BROWSER=%installation_dir%/%macos_app_bundle_prefix%firefox%.exe%", "custom_install_script": "download_firefox", "is_old": true @@ -391,7 +391,7 @@ "version": "102.15.1esr", "bitness": 64, "url": "downloaded via mozdownload script, but a dummy directive is placed here so emsdk understands this Tool to be downloaded from the web", - "activated_cfg": "EMSDK_ACTIVATED_TEST_BROWSER=%installation_dir%/%macos_app_bundle_prefix%firefox%.exe%", + "legacy_cfg": "EMSDK_ACTIVATED_TEST_BROWSER=%installation_dir%/%macos_app_bundle_prefix%firefox%.exe%", "activated_env": "EMTEST_BROWSER=%installation_dir%/%macos_app_bundle_prefix%firefox%.exe%", "custom_install_script": "download_firefox", "is_old": true @@ -401,7 +401,7 @@ "version": "115.28.0esr", "bitness": 64, "url": "downloaded via mozdownload script, but a dummy directive is placed here so emsdk understands this Tool to be downloaded from the web", - "activated_cfg": "EMSDK_ACTIVATED_TEST_BROWSER=%installation_dir%/%macos_app_bundle_prefix%firefox%.exe%", + "legacy_cfg": "EMSDK_ACTIVATED_TEST_BROWSER=%installation_dir%/%macos_app_bundle_prefix%firefox%.exe%", "activated_env": "EMTEST_BROWSER=%installation_dir%/%macos_app_bundle_prefix%firefox%.exe%", "custom_install_script": "download_firefox", "is_old": true @@ -411,7 +411,7 @@ "version": "128.14.0esr", "bitness": 64, "url": "downloaded via mozdownload script, but a dummy directive is placed here so emsdk understands this Tool to be downloaded from the web", - "activated_cfg": "EMSDK_ACTIVATED_TEST_BROWSER=%installation_dir%/%macos_app_bundle_prefix%firefox%.exe%", + "legacy_cfg": "EMSDK_ACTIVATED_TEST_BROWSER=%installation_dir%/%macos_app_bundle_prefix%firefox%.exe%", "activated_env": "EMTEST_BROWSER=%installation_dir%/%macos_app_bundle_prefix%firefox%.exe%", "custom_install_script": "download_firefox" }, @@ -420,7 +420,7 @@ "version": "140.3.1esr", "bitness": 64, "url": "downloaded via mozdownload script, but a dummy directive is placed here so emsdk understands this Tool to be downloaded from the web", - "activated_cfg": "EMSDK_ACTIVATED_TEST_BROWSER=%installation_dir%/%macos_app_bundle_prefix%firefox%.exe%", + "legacy_cfg": "EMSDK_ACTIVATED_TEST_BROWSER=%installation_dir%/%macos_app_bundle_prefix%firefox%.exe%", "activated_env": "EMTEST_BROWSER=%installation_dir%/%macos_app_bundle_prefix%firefox%.exe%", "custom_install_script": "download_firefox" }, @@ -430,7 +430,7 @@ "bitness": 64, "url": "downloaded via mozdownload script, but a dummy directive is placed here so emsdk understands this Tool to be downloaded from the web", "git_branch": "dummy field, to instruct emsdk to attempt to reinstall this tool even if it is installed, to check for new version", - "activated_cfg": "EMSDK_ACTIVATED_TEST_BROWSER=%actual_installation_dir%/%macos_app_bundle_prefix%firefox%.exe%", + "legacy_cfg": "EMSDK_ACTIVATED_TEST_BROWSER=%actual_installation_dir%/%macos_app_bundle_prefix%firefox%.exe%", "activated_env": "EMTEST_BROWSER=%actual_installation_dir%/%macos_app_bundle_prefix%firefox%.exe%", "custom_install_script": "download_firefox", "custom_is_installed_script": "is_firefox_installed" @@ -441,7 +441,7 @@ "bitness": 64, "url": "downloaded via mozdownload script, but a dummy directive is placed here so emsdk understands this Tool to be downloaded from the web", "git_branch": "dummy field, to instruct emsdk to attempt to reinstall this tool even if it is installed, to check for new version", - "activated_cfg": "EMSDK_ACTIVATED_TEST_BROWSER=%actual_installation_dir%/%macos_app_bundle_prefix%firefox%.exe%", + "legacy_cfg": "EMSDK_ACTIVATED_TEST_BROWSER=%actual_installation_dir%/%macos_app_bundle_prefix%firefox%.exe%", "activated_env": "EMTEST_BROWSER=%actual_installation_dir%/%macos_app_bundle_prefix%firefox%.exe%", "custom_install_script": "download_firefox", "custom_is_installed_script": "is_firefox_installed" @@ -452,7 +452,7 @@ "bitness": 64, "url": "downloaded via mozdownload script, but a dummy directive is placed here so emsdk understands this Tool to be downloaded from the web", "git_branch": "dummy field, to instruct emsdk to attempt to reinstall this tool even if it is installed, to check for new version", - "activated_cfg": "EMSDK_ACTIVATED_TEST_BROWSER=%actual_installation_dir%/%macos_app_bundle_prefix%firefox%.exe%", + "legacy_cfg": "EMSDK_ACTIVATED_TEST_BROWSER=%actual_installation_dir%/%macos_app_bundle_prefix%firefox%.exe%", "activated_env": "EMTEST_BROWSER=%actual_installation_dir%/%macos_app_bundle_prefix%firefox%.exe%", "custom_install_script": "download_firefox", "custom_is_installed_script": "is_firefox_installed" @@ -463,7 +463,7 @@ "bitness": 64, "url": "downloaded via mozdownload script, but a dummy directive is placed here so emsdk understands this Tool to be downloaded from the web", "git_branch": "dummy field, to instruct emsdk to attempt to reinstall this tool even if it is installed, to check for new version", - "activated_cfg": "EMSDK_ACTIVATED_TEST_BROWSER=%actual_installation_dir%/%macos_app_bundle_prefix%firefox%.exe%", + "legacy_cfg": "EMSDK_ACTIVATED_TEST_BROWSER=%actual_installation_dir%/%macos_app_bundle_prefix%firefox%.exe%", "activated_env": "EMTEST_BROWSER=%actual_installation_dir%/%macos_app_bundle_prefix%firefox%.exe%", "custom_install_script": "download_firefox", "custom_is_installed_script": "is_firefox_installed" @@ -596,7 +596,7 @@ "version": "7.1.0", "bitness": 64, "url_windows": "mingw_7.1.0_64bit.zip", - "activated_cfg": "MINGW_ROOT='%installation_dir%'", + "legacy_cfg": "MINGW_ROOT='%installation_dir%'", "activated_path": "%installation_dir%/bin" }, { @@ -607,7 +607,7 @@ "url_linux": "ninja-1.13.2-linux-x64.zip", "url_macos": "ninja-1.13.2-mac.zip", "arch": "x86_64", - "activated_cfg": "NINJA_ROOT='%installation_dir%'", + "legacy_cfg": "NINJA_ROOT='%installation_dir%'", "activated_path": "%installation_dir%" }, { @@ -618,7 +618,7 @@ "url_linux": "ninja-1.13.2-linux-aarch64.zip", "url_macos": "ninja-1.13.2-mac.zip", "arch": "arm64", - "activated_cfg": "NINJA_ROOT='%installation_dir%'", + "legacy_cfg": "NINJA_ROOT='%installation_dir%'", "activated_path": "%installation_dir%" }, { @@ -627,7 +627,7 @@ "bitness": 64, "url": "https://github.com/ninja-build/ninja.git", "git_branch": "release", - "activated_cfg": "NINJA=%installation_dir%/bin", + "legacy_cfg": "NINJA=%installation_dir%/bin", "activated_path": "%installation_dir%/bin", "cmake_build_type": "Release", "custom_install_script": "build_ninja" diff --git a/test/test.py b/test/test.py index ef3f09b7f6..3b889353f4 100755 --- a/test/test.py +++ b/test/test.py @@ -275,6 +275,29 @@ def test_config_contents(self): f.write(old_ver) run_emsdk('activate 6.0.3') + def test_activated_migration(self): + print('test migration from .emscripten to activated.json') + activated_json = os.path.abspath('activated.json') + if os.path.exists(activated_json): + os.remove(activated_json) + + run_emsdk('install 6.0.3') + run_emsdk('activate 6.0.3') + + # Remove activated.json to force migration from .emscripten + self.assertTrue(os.path.exists(activated_json)) + os.remove(activated_json) + + # Re-running emsdk list should populate activated.json from .emscripten + checked_call_with_output(emsdk + ' list', expected='INSTALLED') + self.assertTrue(os.path.exists(activated_json)) + with open(activated_json) as f: + data = json.load(f) + self.assertIn('active_tools', data) + for t in data['active_tools']: + print('active:', t) + self.assertIn('node-24.19.0-64bit', data['active_tools']) + def test_lib_building(self): print('building proper system libraries') do_lib_building(upstream_emcc())