Compare commits

..

10 Commits

Author SHA1 Message Date
emscripten-bot
c0bb220cb6 Release 4.0.23 (#1648)
With emscripten-releases revisions:

https://chromium.googlesource.com/emscripten-releases/+/aaa43392544d695232b70eda706d751f18980c2a
(LTO)

https://chromium.googlesource.com/emscripten-releases/+/7b0b10e1a743dabe4b66759c90ff2bcbed0d3b8d
(asserts)
2026-01-09 16:21:07 -08:00
Sam Clegg
fc80c2d544 Convert test code to use subprocess.run. NFC (#1647) 2025-12-18 15:29:15 -08:00
emscripten-bot
15915cad55 Release 4.0.22 (#1644)
With emscripten-releases revisions:

https://chromium.googlesource.com/emscripten-releases/+/bebaf7e50e31865b0724f17eaa52e161e2dfef5a
(LTO)
https://chromium.googlesource.com/emscripten-releases/+/64750c136
(asserts)
2025-12-17 20:59:04 -08:00
Sam Clegg
334f6902fe [CI] Update macos resource class. NFC (#1643)
See
https://circleci.com/changelog/deprecation-of-mac-m1-and-m2-resource-classes/

Sadly this also means we have to bump the version of macOS that we test
on from 12.6.1 to 13.2.1
2025-12-15 13:25:49 -08:00
Sam Clegg
11ea2ee53d Python fixes from ruff check (#1641) 2025-12-15 09:57:48 -08:00
emscripten-bot
b2436aafa7 Release 4.0.21 (#1638)
With emscripten-releases revisions:

https://chromium.googlesource.com/emscripten-releases/+/d70a5da89b3e673bf6a482724478fc17e81e575e
(LTO)

https://chromium.googlesource.com/emscripten-releases/+/50deeb529cc8a08f952af8a3087d2e27e0f77c3e
(asserts)
2025-12-02 10:36:51 -08:00
Sam Clegg
6e4471361a More usage of python3 features (#1636)
Mostly f-strings, but a few others too.
2025-12-01 17:11:00 -08:00
Sam Clegg
a040059ae2 Bump min macOS version to 11.0 (#1637)
We already specify 11.0 for `CMAKE_OSX_DEPLOYMENT_TARGET` in
`cmake_configure`

See #1634
2025-12-01 12:32:48 -08:00
Joshua T.
5a3430bbe3 Correctly tag latest built docker image (#1635)
Additionally removes the `alias` and `only_alias` arguments and adds a
single `tag` argument - this makes it clear about the tag being pushed
to.

Fixes: #1631
2025-11-27 19:32:52 +00:00
Sam Clegg
753652fd30 Update information about linux version used to build prebuilt binaries. NFC (#1633) 2025-11-26 15:10:11 -08:00
13 changed files with 282 additions and 201 deletions

View File

@@ -14,10 +14,10 @@ executors:
# brew itself which takes more than 4 minutes.
HOMEBREW_NO_AUTO_UPDATE: "1"
macos:
# Corresponds to macOS 12.6.1
# Corresponds to macOS 13.2.1
# See https://circleci.com/docs/guides/execution-managed/using-macos/#supported-xcode-versions
xcode: "14.0.1"
resource_class: macos.m1.medium.gen1
xcode: "14.3.1"
resource_class: m4pro.medium
linux_arm64:
machine:
image: ubuntu-2004:2023.07.1
@@ -75,7 +75,7 @@ commands:
.\test\test_bazel.ps1
jobs:
flake8:
lint:
executor: ubuntu
steps:
- checkout
@@ -85,12 +85,12 @@ jobs:
apt-get update -q
apt-get install -q -y python3-pip
- run:
name: python3 flake8
name: python lint
command: |
python3 -m pip install --upgrade pip
python3 -m pip install flake8==7.1.1
python3 -m pip install flake8==7.1.1 ruff==0.14.1
python3 -m flake8 --show-source --statistics --extend-exclude=./scripts
python3 -m ruff check
test-linux:
executor: ubuntu
environment:
@@ -238,7 +238,7 @@ jobs:
name: push image
command: |
docker login -u "$DOCKER_USER" -p "$DOCKER_PASS"
make -C ./docker version=${CIRCLE_TAG} alias=${CIRCLE_TAG}-x64 only_alias=true push
make -C ./docker version=${CIRCLE_TAG} tag=${CIRCLE_TAG}-x64 push
publish-docker-image-arm64:
executor: linux_arm64
@@ -254,7 +254,7 @@ jobs:
name: push image
command: |
docker login -u "$DOCKER_USER" -p "$DOCKER_PASS"
make -C ./docker version=${CIRCLE_TAG} alias=${CIRCLE_TAG}-arm64 only_alias=true push
make -C ./docker version=${CIRCLE_TAG} tag=${CIRCLE_TAG}-arm64 push
publish-docker-image-multiplatform:
executor: linux_arm64
@@ -264,7 +264,8 @@ jobs:
name: push image
command: |
docker login -u "$DOCKER_USER" -p "$DOCKER_PASS"
make -C ./docker version=${CIRCLE_TAG} alias="latest" push-multiplatform
make -C ./docker version=${CIRCLE_TAG} tag=${CIRCLE_TAG} push-multiplatform
make -C ./docker version=${CIRCLE_TAG} tag="latest" push-multiplatform
test-bazel7-linux:
executor: ubuntu
@@ -314,9 +315,9 @@ jobs:
- test-bazel-windows
workflows:
flake8:
lint:
jobs:
- flake8
- lint
test-linux:
jobs:
- test-linux

View File

@@ -55,8 +55,7 @@ https://emscripten.org/docs/building_from_source/toolchain_what_is_needed.html.
### Mac OS X
- For Intel-based Macs, macOS 10.13 or newer. For ARM64 M1 based Macs, macOS
11.0 or newer.
- macOS 11.0 or newer.
- `java`: For running closure compiler (optional). After installing emscripten
via emsdk, typing 'emcc --help' should pop up a OS X dialog "Java is not
installed. To open java, you need a Java SE 6 runtime. Would you like to
@@ -68,10 +67,12 @@ https://emscripten.org/docs/building_from_source/toolchain_what_is_needed.html.
- `python`: Version 3.8 or above.
- `java`: For running closure compiler (optional)
The emsdk pre-compiled binaries are built against Ubuntu/Focal 20.04 LTS and
therefore depend on system libraries compatible with versions of `glibc` and
`libstdc++` present in that release. If your linux distribution is very old
you may not be able to use the pre-compiled binaries packages.
The emsdk pre-compiled binaries are built against debian/stretch (for x86_64)
and debian/bullseye (for arm64) sysroots and therefore depend on system
libraries compatible with the version of `glibc` (and other libraries) present
in those releases. If your linux distribution is very old you may not be able to
use the pre-compiled binaries packages. Note that `libc++` is statically linked
so there should be no issues with older versions of `libstdc++` or `libc++`.
### Windows

View File

@@ -1,6 +1,6 @@
module(
name = "emsdk",
version = "4.0.20",
version = "4.0.23",
)
bazel_dep(name = "platforms", version = "0.0.11")

View File

@@ -14,7 +14,6 @@ This wrapper currently serves the following purposes.
bazel path.
"""
from __future__ import print_function
import argparse
import os
@@ -24,7 +23,7 @@ import sys
# Only argument should be @path/to/parameter/file
assert sys.argv[1][0] == '@', sys.argv
param_filename = sys.argv[1][1:]
param_file_args = [line.strip() for line in open(param_filename, 'r').readlines()]
param_file_args = [line.strip() for line in open(param_filename).readlines()]
# Re-write response file if needed.
if any(' ' in a for a in param_file_args):
@@ -151,7 +150,7 @@ if os.path.exists(wasm_base + '.debug.wasm') and os.path.exists(wasm_base):
'--add-section=external_debug_info=' + base_name + '_debugsection.tmp'])
# Make sure we have at least one output file.
if not len(files):
if not files:
print('emcc.py did not appear to output any known files!')
sys.exit(1)

View File

@@ -2,6 +2,30 @@
# DO NOT MODIFY
EMSCRIPTEN_TAGS = {
"4.0.23": struct(
hash = "aaa43392544d695232b70eda706d751f18980c2a",
sha_linux = "5f1565fe45a1223cedf3b0300f5089c2c64954d2895b2aaedc85043c719be965",
sha_linux_arm64 = "d8ed075930b397d3aed8a1c7558db1918ade786349b07d5c042dfa7f65f78563",
sha_mac = "5823ef26fd45b5a960960fb53026513d43922eac7a1e3ba12d1344d7a94699f9",
sha_mac_arm64 = "54234e108d6612eca5dc9280d1779ccec3d49ad4d8c8562a6b2046b0a5a8d5d4",
sha_win = "c2e1b9a2eed20f9d5903780b559ce2b384132713f07936512e6d962dd5a5dae6",
),
"4.0.22": struct(
hash = "bebaf7e50e31865b0724f17eaa52e161e2dfef5a",
sha_linux = "81219e78defb2f46d12a67a2fa6d128344d850f1e4375b173a324036236b40ef",
sha_linux_arm64 = "5bff19114edb410863634b14e3f459e7d90dc5339f7e5c7bdd5a946356b1dd54",
sha_mac = "a40cfb7c4c4f8cac9c6521475abca80f20986c8c438d46ac745bb4316d838a30",
sha_mac_arm64 = "1a3fb183385682e790cf617824681dc72c2b30e8db2afc9189d0dadea9ccd466",
sha_win = "4a29b0bdc3d477ce39a9e6cd508f16dc19ea1bf861a66c1d715e0264ab550e45",
),
"4.0.21": struct(
hash = "d70a5da89b3e673bf6a482724478fc17e81e575e",
sha_linux = "e8516b903cd4dc16bf5aa2aacd826adcff5ff1d97d3d88e5e3871decd94cd8b2",
sha_linux_arm64 = "415024a22f84424c713b117c3a24cefb98ec03737e02063074010fdc8eaeb334",
sha_mac = "f17989b3528cd14971fe75ae9b2aa7d8cc4cc5bbb6d408660e059f822e108a46",
sha_mac_arm64 = "7960d8d33243f2f7acdac157136c6e93680550361ed71bb225fcfa25b6bbb2fb",
sha_win = "79b3a6b77cf8015cc07ad25353f17f1828e731b0d8757a046071fffbdd5aaf70",
),
"4.0.20": struct(
hash = "c387d7a7e9537d0041d2c3ae71b7538cc978104e",
sha_linux = "a06e7ddda0c168f7ad52e6e0509c98db3545dcb254d3b9052e9e6b8423eaee7d",

View File

@@ -3,8 +3,7 @@
# Emscripten version to build: Should match the version that has been already released.
# i.e.: 1.39.18
version =
alias =
only_alias =
tag =
image_name ?= emscripten/emsdk
@@ -22,14 +21,13 @@ test: test_dockerimage.sh .TEST
docker run --rm -u `id -u`:`id -g` -w /emsdk/docker --net=host --entrypoint /bin/bash ${image_name}:${version} $<
push: .TEST
ifndef only_alias
docker push ${image_name}:${version}
endif
ifdef alias
docker tag ${image_name}:${version} ${image_name}:${alias}
docker push ${image_name}:${alias}
ifdef tag
docker tag ${image_name}:${version} ${image_name}:${tag}
docker push ${image_name}:${tag}
endif
push-multiplatform: .TEST
docker manifest create ${image_name}:${version} $(foreach platform,x64 arm64,--amend ${image_name}:${version}-$(platform))
docker manifest push ${image_name}:${version}
ifdef tag
docker manifest create ${image_name}:${tag} $(foreach platform,x64 arm64,--amend ${image_name}:${version}-$(platform))
docker manifest push ${image_name}:${tag}
endif

View File

@@ -1,6 +1,6 @@
{
"aliases": {
"latest": "4.0.20",
"latest": "4.0.23",
"latest-sdk": "latest",
"latest-arm64-linux": "latest",
"latest-64bit": "latest",
@@ -10,6 +10,12 @@
"latest-releases-upstream": "latest"
},
"releases": {
"4.0.23": "aaa43392544d695232b70eda706d751f18980c2a",
"4.0.23-asserts": "7b0b10e1a743dabe4b66759c90ff2bcbed0d3b8d",
"4.0.22": "bebaf7e50e31865b0724f17eaa52e161e2dfef5a",
"4.0.22-asserts": "64750c136",
"4.0.21": "d70a5da89b3e673bf6a482724478fc17e81e575e",
"4.0.21-asserts": "50deeb529cc8a08f952af8a3087d2e27e0f77c3e",
"4.0.20": "c387d7a7e9537d0041d2c3ae71b7538cc978104e",
"4.0.20-asserts": "d4fdf09ba6e7a2d75bc1f643370caf4519021e89",
"4.0.19": "8b01e2ec3f33e6b94842096d7312ce4ef5f33f6c",

280
emsdk.py
View File

@@ -5,7 +5,6 @@
# found in the LICENSE file.
import copy
from collections import OrderedDict
import errno
import json
import multiprocessing
@@ -20,16 +19,17 @@ import sys
import sysconfig
import tarfile
import zipfile
from collections import OrderedDict
if os.name == 'nt':
import winreg
import ctypes.wintypes
import winreg
from urllib.parse import urljoin
from urllib.request import urlopen
import functools
if sys.version_info < (3, 0):
print(f'error: emsdk requires python 3.0 or above ({sys.executable} {sys.version})', file=sys.stderr)
if sys.version_info < (3, 2): # noqa: UP036
print(f'error: emsdk requires python 3.2 or above ({sys.executable} {sys.version})', file=sys.stderr)
sys.exit(1)
emsdk_packages_url = 'https://storage.googleapis.com/webassembly/emscripten-releases-builds/deps/'
@@ -51,7 +51,10 @@ extra_release_tag = None
# being run. Useful for debugging.
VERBOSE = int(os.getenv('EMSDK_VERBOSE', '0'))
QUIET = int(os.getenv('EMSDK_QUIET', '0'))
TTY_OUTPUT = not os.getenv('EMSDK_NOTTY', not sys.stdout.isatty())
if os.getenv('EMSDK_NOTTY'):
TTY_OUTPUT = False
else:
TTY_OUTPUT = sys.stdout.isatty()
def info(msg):
@@ -132,7 +135,7 @@ else:
# platform.machine() may return AMD64 on windows, so standardize the case.
machine = os.getenv('EMSDK_ARCH', platform.machine().lower())
if machine.startswith('x64') or machine.startswith('amd64') or machine.startswith('x86_64'):
if machine.startswith(('x64', 'amd64', 'x86_64')):
ARCH = 'x86_64'
elif machine.endswith('86'):
ARCH = 'x86'
@@ -203,17 +206,17 @@ def parse_github_url_and_refspec(url):
return ('', '', None)
if url.endswith(('/tree/', '/tree', '/commit/', '/commit')):
raise Exception('Malformed git URL and refspec ' + url + '!')
raise Exception(f'Malformed git URL and refspec {url}!')
if '/tree/' in url:
if url.endswith('/'):
raise Exception('Malformed git URL and refspec ' + url + '!')
raise Exception(f'Malformed git URL and refspec {url}!')
url, refspec = url.split('/tree/')
remote_name = url.split('/')[-2]
return (url, refspec, remote_name)
elif '/commit/' in url:
if url.endswith('/'):
raise Exception('Malformed git URL and refspec ' + url + '!')
raise Exception(f'Malformed git URL and refspec {url}!')
url, refspec = url.split('/commit/')
remote_name = url.split('/')[-2]
return (url, refspec, remote_name)
@@ -324,7 +327,7 @@ def remove_tree(d):
os.chmod(path, stat.S_IWRITE)
func(path)
else:
raise
raise exc_info[1]
shutil.rmtree(d, onerror=remove_readonly_and_try_again)
except Exception as e:
debug_print('remove_tree threw an exception, ignoring: ' + str(e))
@@ -340,13 +343,13 @@ def win_set_environment_variable_direct(key, value, system=True):
# Register locally from CURRENT USER section.
folder = winreg.OpenKeyEx(winreg.HKEY_CURRENT_USER, 'Environment', 0, winreg.KEY_ALL_ACCESS)
winreg.SetValueEx(folder, key, 0, winreg.REG_EXPAND_SZ, value)
debug_print('Set key=' + key + ' with value ' + value + ' in registry.')
debug_print(f'Set key={key} with value {value} in registry.')
return True
except Exception as e:
# 'Access is denied.'
if e.args[3] == 5:
exit_with_error('failed to set the environment variable \'' + key + '\'! Setting environment variables permanently requires administrator access. Please rerun this command with administrative privileges. This can be done for example by holding down the Ctrl and Shift keys while opening a command prompt in start menu.')
errlog('Failed to write environment variable ' + key + ':')
exit_with_error(f'failed to set the environment variable \'{key}\'! Setting environment variables permanently requires administrator access. Please rerun this command with administrative privileges. This can be done for example by holding down the Ctrl and Shift keys while opening a command prompt in start menu.')
errlog(f'Failed to write environment variable {key}:')
errlog(str(e))
return False
finally:
@@ -384,7 +387,7 @@ def win_get_environment_variable(key, system=True, user=True, fallback=True):
# this catch is if both the registry key threw an exception and the key is not in os.environ
if e.args[0] != 2:
# 'The system cannot find the file specified.'
errlog('Failed to read environment variable ' + key + ':')
errlog(f'Failed to read environment variable {key}:')
errlog(str(e))
return None
return value
@@ -416,16 +419,16 @@ def win_set_environment_variable(key, value, system, user):
# Escape % signs so that we don't expand references to environment variables.
value = value.replace('%', '^%')
if len(value) >= 1024:
exit_with_error('the new environment variable ' + key + ' is more than 1024 characters long! A value this long cannot be set via command line: please add the environment variable specified above to system environment manually via Control Panel.')
exit_with_error(f'the new environment variable {key} is more than 1024 characters long! A value this long cannot be set via command line: please add the environment variable specified above to system environment manually via Control Panel.')
cmd = ['SETX', key, value]
debug_print(str(cmd))
retcode = subprocess.call(cmd, stdout=subprocess.PIPE)
if retcode != 0:
errlog('ERROR! Failed to set environment variable ' + key + '=' + value + '. You may need to set it manually.')
errlog(f'ERROR! Failed to set environment variable {key}={value}. You may need to set it manually.')
else:
return True
except Exception as e:
errlog('ERROR! Failed to set environment variable ' + key + '=' + value + ':')
errlog(f'ERROR! Failed to set environment variable {key}={value}:')
errlog(str(e))
errlog('You may need to set it manually.')
@@ -467,7 +470,7 @@ def win_set_environment_variables(env_vars_to_add, system, user):
def win_delete_environment_variable(key, system=True, user=True):
debug_print('win_delete_environment_variable(key=' + key + ', system=' + str(system) + ')')
debug_print(f'win_delete_environment_variable(key={key}, system={system})')
return win_set_environment_variable(key, None, system, user)
@@ -481,19 +484,14 @@ def sdk_path(path):
# Removes a single file, suppressing exceptions on failure.
def rmfile(filename):
debug_print('rmfile(' + filename + ')')
debug_print(f'rmfile({filename})')
if os.path.lexists(filename):
os.remove(filename)
# http://stackoverflow.com/questions/600268/mkdir-p-functionality-in-python
def mkdir_p(path):
debug_print('mkdir_p(' + path + ')')
try:
os.makedirs(path)
except OSError as exc: # Python >2.5
if exc.errno != errno.EEXIST or not os.path.isdir(path):
raise
debug_print(f'mkdir_p({path})')
os.makedirs(path, exist_ok=True)
def is_nonempty_directory(path):
@@ -513,7 +511,7 @@ def run(cmd, cwd=None, quiet=False):
# http://pythonicprose.blogspot.fi/2009/10/python-extract-targz-archive.html
def untargz(source_filename, dest_dir):
print("Unpacking '" + source_filename + "' to '" + dest_dir + "'")
print(f"Unpacking '{source_filename}' to '{dest_dir}'")
mkdir_p(dest_dir)
returncode = run(['tar', '-xvf' if VERBOSE else '-xf', sdk_path(source_filename), '--strip', '1'], cwd=dest_dir)
# tfile = tarfile.open(source_filename, 'r:gz')
@@ -531,7 +529,7 @@ def fix_potentially_long_windows_pathname(pathname):
# Test if emsdk calls fix_potentially_long_windows_pathname() with long
# relative paths (which is problematic)
if not os.path.isabs(pathname) and len(pathname) > 200:
errlog('Warning: Seeing a relative path "' + pathname + '" which is dangerously long for being referenced as a short Windows path name. Refactor emsdk to be able to handle this!')
errlog(f'Warning: Seeing a relative path "{pathname}" which is dangerously long for being referenced as a short Windows path name. Refactor emsdk to be able to handle this!')
if pathname.startswith('\\\\?\\'):
return pathname
pathname = os.path.normpath(pathname.replace('/', '\\'))
@@ -554,7 +552,7 @@ def move_with_overwrite(src, dest):
# http://stackoverflow.com/questions/12886768/simple-way-to-unzip-file-in-python-on-all-oses
def unzip(source_filename, dest_dir):
print("Unpacking '" + source_filename + "' to '" + dest_dir + "'")
print(f"Unpacking '{source_filename}' to '{dest_dir}'")
mkdir_p(dest_dir)
common_subdir = None
try:
@@ -609,11 +607,11 @@ def unzip(source_filename, dest_dir):
if common_subdir:
remove_tree(unzip_to_dir)
except zipfile.BadZipfile as e:
errlog("Unzipping file '" + source_filename + "' failed due to reason: " + str(e) + "! Removing the corrupted zip file.")
errlog(f"Unzipping file '{source_filename}' failed due to reason: {e}! Removing the corrupted zip file.")
rmfile(source_filename)
return False
except Exception as e:
errlog("Unzipping file '" + source_filename + "' failed due to reason: " + str(e))
errlog(f"Unzipping file '{source_filename}' failed due to reason: {e}")
return False
return True
@@ -724,12 +722,12 @@ def download_with_urllib(url, file_name):
# On success, returns the filename on the disk pointing to the destination file that was produced
# On failure, returns None.
def download_file(url, dstpath, download_even_if_exists=False,
filename_prefix='', silent=False):
debug_print('download_file(url=' + url + ', dstpath=' + dstpath + ')')
filename_prefix=''):
debug_print(f'download_file(url={url}, dstpath={dstpath})')
file_name = get_download_target(url, dstpath, filename_prefix)
if os.path.exists(file_name) and not download_even_if_exists:
print("File '" + file_name + "' already downloaded, skipping.")
print(f"File '{file_name}' already downloaded, skipping.")
return file_name
mkdir_p(os.path.dirname(file_name))
@@ -744,7 +742,7 @@ def download_file(url, dstpath, download_even_if_exists=False,
else:
download_with_urllib(url, file_name)
except Exception as e:
errlog("Error: Downloading URL '" + url + "': " + str(e))
errlog(f"Error: Downloading URL '{url}': {e}")
return None
except KeyboardInterrupt:
rmfile(file_name)
@@ -754,7 +752,7 @@ def download_file(url, dstpath, download_even_if_exists=False,
def run_get_output(cmd, cwd=None):
debug_print('run_get_output(cmd=' + str(cmd) + ', cwd=' + str(cwd) + ')')
debug_print(f'run_get_output(cmd={cmd}, cwd={cwd})')
process = subprocess.Popen(cmd, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, env=os.environ.copy(), universal_newlines=True)
stdout, stderr = process.communicate()
return (process.returncode, stdout, stderr)
@@ -822,31 +820,31 @@ def get_git_remotes(repo_path):
def git_clone(url, dstpath, branch, remote_name='origin'):
debug_print('git_clone(url=' + url + ', dstpath=' + dstpath + ')')
debug_print(f'git_clone(url={url}, dstpath={dstpath})')
if os.path.isdir(os.path.join(dstpath, '.git')):
remotes = get_git_remotes(dstpath)
if remote_name in remotes:
debug_print('Repository ' + url + ' with remote "' + remote_name + '" already cloned to directory ' + dstpath + ', skipping.')
debug_print(f'Repository {url} with remote "{remote_name}" already cloned to directory {dstpath}, skipping.')
return True
else:
debug_print('Repository ' + url + ' with remote "' + remote_name + '" already cloned to directory ' + dstpath + ', but remote has not yet been added. Creating.')
debug_print(f'Repository {url} with remote "{remote_name}" already cloned to directory {dstpath}, but remote has not yet been added. Creating.')
return run([GIT(), 'remote', 'add', remote_name, url], cwd=dstpath) == 0
mkdir_p(dstpath)
git_clone_args = ['--recurse-submodules', '--branch', branch] # Do not check out a branch (installer will issue a checkout command right after)
if GIT_CLONE_SHALLOW:
git_clone_args += ['--depth', '1']
print('Cloning from ' + url + '...')
print(f'Cloning from {url}...')
return run([GIT(), 'clone', '-o', remote_name] + git_clone_args + [url, dstpath]) == 0
def git_pull(repo_path, branch_or_tag, remote_name='origin'):
debug_print('git_pull(repo_path=' + repo_path + ', branch/tag=' + branch_or_tag + ', remote_name=' + remote_name + ')')
debug_print(f'git_pull(repo_path={repo_path}, branch/tag={branch_or_tag}, remote_name={remote_name})')
ret = run([GIT(), 'fetch', '--quiet', remote_name], repo_path)
if ret != 0:
return False
try:
print("Fetching latest changes to the branch/tag '" + branch_or_tag + "' for '" + repo_path + "'...")
print(f"Fetching latest changes to the branch/tag '{branch_or_tag}' for '{repo_path}'...")
ret = run([GIT(), 'fetch', '--quiet', remote_name], repo_path)
if ret != 0:
return False
@@ -871,13 +869,13 @@ def git_pull(repo_path, branch_or_tag, remote_name='origin'):
except Exception:
errlog('git operation failed!')
return False
print("Successfully updated and checked out branch/tag '" + branch_or_tag + "' on repository '" + repo_path + "'")
print(f"Successfully updated and checked out branch/tag '{branch_or_tag}' on repository '{repo_path}'")
print("Current repository version: " + git_repo_version(repo_path))
return True
def git_clone_checkout_and_pull(url, dstpath, branch, override_remote_name='origin'):
debug_print('git_clone_checkout_and_pull(url=' + url + ', dstpath=' + dstpath + ', branch=' + branch + ', override_remote_name=' + override_remote_name + ')')
debug_print(f'git_clone_checkout_and_pull(url={url}, dstpath={dstpath}, branch={branch}, override_remote_name={override_remote_name})')
# Make sure the repository is cloned first
success = git_clone(url, dstpath, branch, override_remote_name)
@@ -940,19 +938,19 @@ def llvm_build_bin_dir(tool):
return os.path.join(build_dir, 'bin')
def build_env(generator):
build_env = os.environ.copy()
def build_env():
env = os.environ.copy()
# To work around a build issue with older Mac OS X builds, add -stdlib=libc++ to all builds.
# See https://groups.google.com/forum/#!topic/emscripten-discuss/5Or6QIzkqf0
if MACOS:
build_env['CXXFLAGS'] = ((build_env['CXXFLAGS'] + ' ') if hasattr(build_env, 'CXXFLAGS') else '') + '-stdlib=libc++'
env['CXXFLAGS'] = ((env['CXXFLAGS'] + ' ') if hasattr(env, 'CXXFLAGS') else '') + '-stdlib=libc++'
if WINDOWS:
# MSBuild.exe has an internal mechanism to avoid N^2 oversubscription of threads in its two-tier build model, see
# https://devblogs.microsoft.com/cppblog/improved-parallelism-in-msbuild/
build_env['UseMultiToolTask'] = 'true'
build_env['EnforceProcessCountAcrossBuilds'] = 'true'
return build_env
env['UseMultiToolTask'] = 'true'
env['EnforceProcessCountAcrossBuilds'] = 'true'
return env
# Find path to cmake executable, as one of the activated tools, in PATH, or from installed tools.
@@ -970,13 +968,13 @@ def find_cmake():
if tool.id == 'cmake' and tool.is_active():
cmake_exe = locate_cmake_from_tool(tool)
if cmake_exe:
info('Found installed+activated CMake tool at "' + cmake_exe + '"')
info(f'Found installed+activated CMake tool at "{cmake_exe}"')
return cmake_exe
# 2. If cmake already exists in PATH, then use that cmake to configure the build.
cmake_exe = which('cmake')
if cmake_exe:
info('Found CMake from PATH at "' + cmake_exe + '"')
info(f'Found CMake from PATH at "{cmake_exe}"')
return cmake_exe
# 3. Finally, if user has installed a cmake tool, but has not activated that, then use
@@ -987,14 +985,14 @@ def find_cmake():
if tool.id == 'cmake' and tool.is_installed():
cmake_exe = locate_cmake_from_tool(tool)
if cmake_exe:
info('Found installed CMake tool at "' + cmake_exe + '"')
info(f'Found installed CMake tool at "{cmake_exe}"')
return cmake_exe
exit_with_error('Unable to find "cmake" in PATH, or as installed/activated tool! Please install CMake first')
def make_build(build_root, build_type):
debug_print('make_build(build_root=' + build_root + ', build_type=' + build_type + ')')
debug_print(f'make_build(build_root={build_root}, build_type={build_type})')
if CPU_CORES > 1:
print('Performing a parallel build with ' + str(CPU_CORES) + ' cores.')
else:
@@ -1016,9 +1014,9 @@ def make_build(build_root, build_type):
# Build
try:
print('Running build: ' + str(make))
ret = subprocess.check_call(make, cwd=build_root, env=build_env(CMAKE_GENERATOR))
ret = subprocess.check_call(make, cwd=build_root, env=build_env())
if ret != 0:
errlog('Build failed with exit code ' + ret + '!')
errlog('Build failed with exit code {ret}!')
errlog('Working directory: ' + build_root)
return False
except Exception as e:
@@ -1030,7 +1028,7 @@ def make_build(build_root, build_type):
return True
def cmake_configure(generator, build_root, src_root, build_type, extra_cmake_args=[]):
def cmake_configure(generator, build_root, src_root, build_type, extra_cmake_args):
debug_print('cmake_configure(generator=' + str(generator) + ', build_root=' + str(build_root) + ', src_root=' + str(src_root) + ', build_type=' + str(build_type) + ', extra_cmake_args=' + str(extra_cmake_args) + ')')
# Configure
if not os.path.isdir(build_root):
@@ -1063,9 +1061,9 @@ def cmake_configure(generator, build_root, src_root, build_type, extra_cmake_arg
# Create a file 'recmake.bat/sh' in the build root that user can call to
# manually recmake the build tree with the previous build params
open(os.path.join(build_root, 'recmake.' + ('bat' if WINDOWS else 'sh')), 'w').write(' '.join(map(quote_parens, cmdline)))
ret = subprocess.check_call(cmdline, cwd=build_root, env=build_env(CMAKE_GENERATOR))
ret = subprocess.check_call(cmdline, cwd=build_root, env=build_env())
if ret != 0:
errlog('CMake invocation failed with exit code ' + ret + '!')
errlog('CMake invocation failed with exit code {ret}!')
errlog('Working directory: ' + build_root)
return False
except OSError as e:
@@ -1091,9 +1089,7 @@ def cmake_configure(generator, build_root, src_root, build_type, extra_cmake_arg
def xcode_sdk_version():
try:
output = subprocess.check_output(['xcrun', '--show-sdk-version'])
if sys.version_info >= (3,):
output = output.decode('utf8')
output = subprocess.check_output(['xcrun', '--show-sdk-version'], universal_newlines=True)
return output.strip().split('.')
except Exception:
return subprocess.checkplatform.mac_ver()[0].split('.')
@@ -1120,7 +1116,7 @@ def cmake_host_platform():
'arm64': 'ARM64',
'arm': 'ARM',
'x86_64': 'x64',
'x86': 'x86'
'x86': 'x86',
}
return arch_to_cmake_host_platform[ARCH]
@@ -1158,7 +1154,7 @@ def build_llvm(tool):
enable_assertions = ENABLE_LLVM_ASSERTIONS.lower() == 'on' or (ENABLE_LLVM_ASSERTIONS == 'auto' and build_type.lower() != 'release' and build_type.lower() != 'minsizerel')
if ARCH == 'x86' or ARCH == 'x86_64':
if ARCH in ('x86', 'x86_64'):
targets_to_build = 'WebAssembly;X86'
elif ARCH == 'arm':
targets_to_build = 'WebAssembly;ARM'
@@ -1387,13 +1383,13 @@ def download_firefox(tool):
# Abort if detaching was not successful
if os.path.exists(mount_point):
raise Exception('Previous mount of Firefox already exists at "' + mount_point + '", unable to proceed.')
raise Exception(f'Previous mount of Firefox already exists at "{mount_point}", unable to proceed.')
# Mount the archive
run(['hdiutil', 'attach', filename])
firefox_dir = os.path.join(mount_point, app_name)
if not os.path.isdir(firefox_dir):
raise Exception('Unable to find Firefox directory "' + firefox_dir + '" inside app image.')
raise Exception(f'Unable to find Firefox directory "{firefox_dir}" inside app image.')
# And install by copying the files from the archive
shutil.copytree(firefox_dir, root)
@@ -1512,7 +1508,7 @@ def emscripten_npm_install(tool, directory):
# Binaryen build scripts:
def binaryen_build_root(tool):
build_root = tool.installation_path().strip()
if build_root.endswith('/') or build_root.endswith('\\'):
if build_root.endswith(('/', '\\')):
build_root = build_root[:-1]
generator_prefix = cmake_generator_prefix()
build_root = build_root + generator_prefix + '_' + str(tool.bitness) + 'bit_binaryen'
@@ -1522,7 +1518,7 @@ def binaryen_build_root(tool):
def uninstall_binaryen(tool):
debug_print('uninstall_binaryen(' + str(tool) + ')')
build_root = binaryen_build_root(tool)
print("Deleting path '" + build_root + "'")
print(f"Deleting path '{build_root}'")
remove_tree(build_root)
@@ -1563,23 +1559,22 @@ def build_binaryen_tool(tool):
def download_and_extract(archive, dest_dir, filename_prefix='', clobber=True):
debug_print('download_and_extract(archive=' + archive + ', dest_dir=' + dest_dir + ')')
debug_print('download_and_extract(archive={archive}, dest_dir={dest_dir})')
url = urljoin(emsdk_packages_url, archive)
def try_download(url, silent=False):
return download_file(url, download_dir, not KEEP_DOWNLOADS,
filename_prefix, silent=silent)
def try_download(url):
return download_file(url, download_dir, not KEEP_DOWNLOADS, filename_prefix)
# Special hack for the wasm-binaries we transitioned from `.bzip2` to
# `.xz`, but we can't tell from the version/url which one to use, so
# try one and then fall back to the other.
success = False
if 'wasm-binaries' in archive and os.path.splitext(archive)[1] == '.xz':
success = try_download(url, silent=True)
success = try_download(url)
if not success:
alt_url = url.replace('.tar.xz', '.tbz2')
success = try_download(alt_url, silent=True)
success = try_download(alt_url)
if success:
url = alt_url
@@ -1653,7 +1648,7 @@ def load_em_config():
EM_CONFIG_DICT.clear()
lines = []
try:
lines = open(EM_CONFIG_PATH, "r").read().split('\n')
lines = open(EM_CONFIG_PATH).read().split('\n')
except Exception:
pass
for line in lines:
@@ -1723,7 +1718,7 @@ def extract_newest_node_nightly_version(versions):
def download_node_nightly(tool):
nightly_versions = fetch_nightly_node_versions()
latest_nightly = extract_newest_node_nightly_version(nightly_versions)
print('Latest Node.js Nightly download available is "' + latest_nightly + '"')
print('Latest Node.js Nightly download available is "{latest_nightly}"')
output_dir = os.path.abspath('node/nightly-' + latest_nightly)
# Node.js zip structure quirk: Linux and macOS archives have a /bin,
@@ -1794,9 +1789,9 @@ def generate_em_config(active_tools, permanently_activate, system):
for name, value in activated_config.items():
if value.startswith('['):
cfg += name + " = " + value + "\n"
cfg += f'{name} = {value}\n'
else:
cfg += name + " = '" + value + "'\n"
cfg += f"{name} = '{value}'\n"
emroot = find_emscripten_root(active_tools)
if emroot:
@@ -1856,14 +1851,11 @@ def find_msbuild_dir():
return ''
class Tool(object):
class Tool:
def __init__(self, data):
# Convert the dictionary representation of the tool in 'data' to members of
# this class for convenience.
for key, value in data.items():
# Python2 compat, convert unicode to str
if sys.version_info < (3,) and isinstance(value, unicode): # noqa
value = value.encode('Latin-1')
setattr(self, key, value)
# Cache the name ID of this Tool (these are read very often)
@@ -1916,7 +1908,7 @@ class Tool(object):
for tool_name in self.uses:
tool = find_tool(tool_name)
if not tool:
debug_print('Tool ' + str(self) + ' depends on ' + tool_name + ' which does not exist!')
debug_print(f'Tool {self} depends on {tool_name} which does not exist!')
continue
if tool.needs_compilation():
return True
@@ -2008,14 +2000,13 @@ class Tool(object):
def is_installed_version(self):
version_file_path = self.get_version_file_path()
if os.path.isfile(version_file_path):
with open(version_file_path, 'r') as version_file:
with open(version_file_path) as version_file:
return version_file.read().strip() == self.name
return False
def update_installed_version(self):
with open(self.get_version_file_path(), 'w') as version_file:
version_file.write(self.name + '\n')
return None
def is_installed(self, skip_version_check=False):
# If this tool/sdk depends on other tools, require that all dependencies are
@@ -2024,7 +2015,7 @@ class Tool(object):
for tool_name in self.uses:
tool = find_tool(tool_name)
if tool is None:
errlog("Manifest error: No tool by name '" + tool_name + "' found! This may indicate an internal SDK error!")
errlog(f"Manifest error: No tool by name '{tool_name}' found! This may indicate an internal SDK error!")
return False
if not tool.is_installed():
return False
@@ -2072,7 +2063,7 @@ class Tool(object):
for key, value in activated_cfg.items():
if key not in EM_CONFIG_DICT:
debug_print(str(self) + ' is not active, because key="' + key + '" does not exist in .emscripten')
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
@@ -2080,7 +2071,7 @@ class Tool(object):
config_value = EM_CONFIG_DICT[key].replace("emsdk_path + '", "'" + EMSDK_PATH)
config_value = config_value.strip("'")
if config_value != value:
debug_print(str(self) + ' is not active, because key="' + key + '" has value "' + config_value + '" but should have 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
@@ -2090,7 +2081,7 @@ class Tool(object):
for env in envs:
key, value = parse_key_value(env)
if key not in os.environ or to_unix_path(os.environ[key]) != to_unix_path(value):
debug_print(str(self) + ' is not active, because environment variable key="' + key + '" has value "' + str(os.getenv(key)) + '" but should have value "' + value + '"')
debug_print(f'{self} is not active, because environment variable key="{key}" has value "{os.getenv(key)}" but should have value "{value}"')
return False
if hasattr(self, 'activated_path'):
@@ -2098,7 +2089,7 @@ class Tool(object):
for p in path:
path_items = os.environ['PATH'].replace('\\', '/').split(ENVPATH_SEPARATOR)
if not normalized_contains(path_items, p):
debug_print(str(self) + ' is not active, because environment variable PATH item "' + p + '" is not present (PATH=' + os.environ['PATH'] + ')')
debug_print(f'{self} is not active, because environment variable PATH item "{p}" is not present (PATH={os.environ["PATH"]})')
return False
return True
@@ -2130,7 +2121,7 @@ class Tool(object):
already being installed.
"""
if self.can_be_installed() is not True:
exit_with_error("The tool '" + str(self) + "' is not available due to the reason: " + self.can_be_installed())
exit_with_error(f"The tool '{self}' is not available due to the reason: {self.can_be_installed()}")
if self.id == 'sdk':
return self.install_sdk()
@@ -2141,17 +2132,17 @@ class Tool(object):
"""Returns True if any SDK component was installed of False all componented
were already installed.
"""
print("Installing SDK '" + str(self) + "'..")
print(f"Installing SDK '{self}'..")
installed = False
for tool_name in self.uses:
tool = find_tool(tool_name)
if tool is None:
exit_with_error("manifest error: No tool by name '" + tool_name + "' found! This may indicate an internal SDK error!")
exit_with_error(f"manifest error: No tool by name '{tool_name}' found! This may indicate an internal SDK error!")
installed |= tool.install()
if not installed:
print("All SDK components already installed: '" + str(self) + "'.")
print(f"All SDK components already installed: '{self}'.")
return False
if getattr(self, 'custom_install_script', None) == 'emscripten_npm_install':
@@ -2164,7 +2155,7 @@ class Tool(object):
if not emscripten_npm_install(self, emscripten_dir):
exit_with_error('post-install step failed: emscripten_npm_install')
print("Done installing SDK '" + str(self) + "'.")
print(f"Done installing SDK '{self}'.")
return True
def install_tool(self):
@@ -2176,10 +2167,10 @@ class Tool(object):
# installed every time when requested, since the install step is then used to git
# pull the tool to a newer version.
if self.is_installed() and not hasattr(self, 'git_branch'):
print("Skipped installing " + self.name + ", already installed.")
print(f"Skipped installing {self.name}, already installed.")
return False
print("Installing tool '" + str(self) + "'..")
print(f"Installing tool '{self}'..")
url = self.download_url()
custom_install_scripts = {
@@ -2187,7 +2178,7 @@ class Tool(object):
'build_ninja': build_ninja,
'build_ccache': build_ccache,
'download_node_nightly': download_node_nightly,
'download_firefox': download_firefox
'download_firefox': download_firefox,
}
if hasattr(self, 'custom_install_script') and self.custom_install_script in custom_install_scripts:
success = custom_install_scripts[self.custom_install_script](self)
@@ -2227,12 +2218,12 @@ class Tool(object):
with open(emscripten_version_file_path, 'w') as f:
f.write('"%s"\n' % version)
print("Done installing tool '" + str(self) + "'.")
print(f"Done installing tool '{self}'.")
# Sanity check that the installation succeeded, and if so, remove unneeded
# leftover installation files.
if not self.is_installed(skip_version_check=True):
exit_with_error("installation of '" + str(self) + "' failed, but no error was detected. Either something went wrong with the installation, or this may indicate an internal emsdk error.")
exit_with_error(f"installation of '{self}' failed, but no error was detected. Either something went wrong with the installation, or this may indicate an internal emsdk error.")
self.cleanup_temp_install_files()
self.update_installed_version()
@@ -2244,22 +2235,22 @@ class Tool(object):
url = self.download_url()
if url.endswith(ARCHIVE_SUFFIXES):
download_target = get_download_target(url, download_dir, getattr(self, 'download_prefix', ''))
debug_print("Deleting temporary download: " + download_target)
debug_print(f"Deleting temporary download: {download_target}")
rmfile(download_target)
def uninstall(self):
if not self.is_installed():
print("Tool '" + str(self) + "' was not installed. No need to uninstall.")
print(f"Tool '{self}' was not installed. No need to uninstall.")
return
print("Uninstalling tool '" + str(self) + "'..")
print(f"Uninstalling tool '{self}'..")
if hasattr(self, 'custom_uninstall_script'):
if self.custom_uninstall_script == 'uninstall_binaryen':
uninstall_binaryen(self)
else:
raise Exception('Unknown custom_uninstall_script directive "' + self.custom_uninstall_script + '"!')
print("Deleting path '" + self.installation_path() + "'")
raise Exception(f'Unknown custom_uninstall_script directive "{self.custom_uninstall_script}"!')
print(f"Deleting path '{self.installation_path()}'")
remove_tree(self.installation_path())
print("Done uninstalling '" + str(self) + "'.")
print(f"Done uninstalling '{self}'.")
def dependencies(self):
if not hasattr(self, 'uses'):
@@ -2412,22 +2403,13 @@ def get_emscripten_releases_tot():
def get_release_hash(arg, releases_info):
return releases_info.get(arg, None) or releases_info.get('sdk-' + arg + '-64bit')
return releases_info.get(arg, None) or releases_info.get(f'sdk-{arg}-64bit')
def version_key(ver):
return tuple(map(int, re.split('[._-]', ver)[:3]))
# A sort function that is compatible with both Python 2 and Python 3 using a
# custom comparison function.
def python_2_3_sorted(arr, cmp):
if sys.version_info >= (3,):
return sorted(arr, key=functools.cmp_to_key(cmp))
else:
return sorted(arr, cmp=cmp)
def is_emsdk_sourced_from_github():
return os.path.exists(os.path.join(EMSDK_PATH, '.git'))
@@ -2443,11 +2425,11 @@ def update_emsdk():
# Lists all legacy (pre-emscripten-releases) tagged versions directly in the Git
# repositories. These we can pull and compile from source.
def load_legacy_emscripten_tags():
return open(sdk_path('legacy-emscripten-tags.txt'), 'r').read().split('\n')
return open(sdk_path('legacy-emscripten-tags.txt')).read().split('\n')
def load_legacy_binaryen_tags():
return open(sdk_path('legacy-binaryen-tags.txt'), 'r').read().split('\n')
return open(sdk_path('legacy-binaryen-tags.txt')).read().split('\n')
def remove_prefix(s, prefix):
@@ -2479,7 +2461,7 @@ def load_file_index_list(filename):
def load_releases_info():
if not hasattr(load_releases_info, 'cached_info'):
try:
text = open(sdk_path('emscripten-releases-tags.json'), 'r').read()
text = open(sdk_path('emscripten-releases-tags.json')).read()
load_releases_info.cached_info = json.loads(text)
except Exception as e:
print('Error parsing emscripten-releases-tags.json!')
@@ -2502,7 +2484,7 @@ def load_releases_tags():
tags = []
info = load_releases_info()
for version, sha in sorted(info['releases'].items(), key=lambda x: version_key(x[0])):
for _version, sha in sorted(info['releases'].items(), key=lambda x: version_key(x[0])):
tags.append(sha)
if extra_release_tag:
@@ -2524,15 +2506,9 @@ def load_releases_versions():
return versions
def is_string(s):
if sys.version_info[0] >= 3:
return isinstance(s, str)
return isinstance(s, basestring) # noqa
def load_sdk_manifest():
try:
manifest = json.loads(open(sdk_path("emsdk_manifest.json"), "r").read())
manifest = json.loads(open(sdk_path("emsdk_manifest.json")).read())
except Exception as e:
print('Error parsing emsdk_manifest.json!')
print(str(e))
@@ -2566,7 +2542,7 @@ def load_sdk_manifest():
return version_key(ver) == version_key(reference)
if cmp_operand == '!=':
return version_key(ver) != version_key(reference)
raise Exception('Invalid cmp_operand "' + cmp_operand + '"!')
raise Exception(f'Invalid cmp_operand "{cmp_operand}"!')
def passes_filters(param, ver, filters):
for v in filters:
@@ -2583,7 +2559,7 @@ def load_sdk_manifest():
t2 = copy.copy(t)
found_param = False
for p, v in vars(t2).items():
if is_string(v) and param in v:
if isinstance(v, str) and param in v:
t2.__dict__[p] = v.replace(param, ver)
found_param = True
if not found_param:
@@ -2703,7 +2679,7 @@ def set_active_tools(tools_to_activate, permanently_activate, system):
if tools_to_activate:
tools = [x for x in tools_to_activate if not x.is_sdk]
print('Setting the following tools as active:\n ' + '\n '.join(map(lambda x: str(x), tools)))
print('Setting the following tools as active:\n ' + '\n '.join([str(t) for t in tools]))
print('')
generate_em_config(tools_to_activate, permanently_activate, system)
@@ -2889,15 +2865,15 @@ def construct_env_with_vars(env_vars_to_add):
continue
info(key + ' = ' + value)
if POWERSHELL:
env_string += '$env:' + key + '="' + value + '"\n'
env_string += f'$env:{key}="{value}"\n'
elif CMD:
env_string += 'SET ' + key + '=' + value + '\n'
env_string += f'SET {key}={value}\n'
elif CSH:
env_string += 'setenv ' + key + ' "' + value + '";\n'
env_string += f'setenv {key} "{value}";\n'
elif FISH:
env_string += 'set -gx ' + key + ' "' + value + '";\n'
env_string += f'set -gx {key} "{value}";\n'
elif BASH:
env_string += 'export ' + key + '="' + value + '";\n'
env_string += f'export {key}="{value}";\n'
else:
assert False
@@ -2913,9 +2889,9 @@ def construct_env_with_vars(env_vars_to_add):
# of the SDK but we want to remove that from the current environment
# if no such tool is active.
# Ignore certain keys that are inputs to emsdk itself.
ignore_keys = set(['EMSDK_POWERSHELL', 'EMSDK_CSH', 'EMSDK_CMD', 'EMSDK_BASH', 'EMSDK_FISH',
'EMSDK_NUM_CORES', 'EMSDK_NOTTY', 'EMSDK_KEEP_DOWNLOADS'])
env_keys_to_add = set(pair[0] for pair in env_vars_to_add)
ignore_keys = {'EMSDK_POWERSHELL', 'EMSDK_CSH', 'EMSDK_CMD', 'EMSDK_BASH', 'EMSDK_FISH',
'EMSDK_NUM_CORES', 'EMSDK_NOTTY', 'EMSDK_KEEP_DOWNLOADS'}
env_keys_to_add = {pair[0] for pair in env_vars_to_add}
for key in os.environ:
if key.startswith('EMSDK_') or key in ('EM_CACHE', 'EM_CONFIG'):
if key not in env_keys_to_add and key not in ignore_keys:
@@ -2979,7 +2955,7 @@ def expand_sdk_name(name, activating):
return name
def main(args):
def main(args): # noqa: C901, PLR0911, PLR0912, PLR0915
if not args:
errlog("Missing command; Type 'emsdk help' to get a list of commands.")
return 1
@@ -3162,11 +3138,11 @@ def main(args):
tool_name, url_and_refspec = forked_url.split('@')
t = find_tool(tool_name)
if not t:
errlog('Failed to find tool ' + tool_name + '!')
errlog('Failed to find tool {tool_name}!')
return False
else:
t.url, t.git_branch, t.remote_name = parse_github_url_and_refspec(url_and_refspec)
debug_print('Reading git repository URL "' + t.url + '" and git branch "' + t.git_branch + '" for Tool "' + tool_name + '".')
debug_print(f'Reading git repository URL "{t.url}" and git branch "{t.git_branch}" for Tool "{tool_name}".')
forked_url = extract_string_arg('--override-repository')
@@ -3179,7 +3155,7 @@ def main(args):
CMAKE_GENERATOR = build_generator.group(1)
args[i] = ''
else:
errlog("Cannot parse CMake generator string: " + args[i] + ". Try wrapping generator string with quotes")
errlog(f"Cannot parse CMake generator string: {args[i]}. Try wrapping generator string with quotes")
return 1
elif args[i].startswith('--build='):
build_type = re.match(r'^--build=(.+)$', args[i])
@@ -3192,10 +3168,10 @@ def main(args):
CMAKE_BUILD_TYPE_OVERRIDE = build_types[build_type_index]
args[i] = ''
except Exception:
errlog('Unknown CMake build type "' + build_type + '" specified! Please specify one of ' + str(build_types))
errlog(f'Unknown CMake build type "{build_type}" specified! Please specify one of {build_types}')
return 1
else:
errlog("Invalid command line parameter " + args[i] + ' specified!')
errlog(f'Invalid command line parameter {args[i]} specified!')
return 1
args = [x for x in args if x]
@@ -3206,7 +3182,7 @@ def main(args):
sdk = find_sdk(name)
return 'INSTALLED' if sdk and sdk.is_installed() else ''
if (LINUX or MACOS or WINDOWS) and (ARCH == 'x86' or ARCH == 'x86_64'):
if (LINUX or MACOS or WINDOWS) and ARCH in ('x86', 'x86_64'):
print('The *recommended* precompiled SDK download is %s (%s).' % (find_latest_version(), find_latest_hash()))
print()
print('To install/activate it use:')
@@ -3301,7 +3277,7 @@ def main(args):
print('Items marked with * are activated for the current user.')
if has_partially_active_tools[0]:
env_cmd = 'emsdk_env.bat' if WINDOWS else 'source ./emsdk_env.sh'
print('Items marked with (*) are selected for use, but your current shell environment is not configured to use them. Type "' + env_cmd + '" to set up your current shell to use them' + (', or call "emsdk activate --permanent <name_of_sdk>" to permanently activate them.' if WINDOWS else '.'))
print(f'Items marked with (*) are selected for use, but your current shell environment is not configured to use them. Type "{env_cmd}" to set up your current shell to use them' + (', or call "emsdk activate --permanent <name_of_sdk>" to permanently activate them.' if WINDOWS else '.'))
if not arg_old:
print('')
print("To access the historical archived versions, type 'emsdk list --old'")
@@ -3334,7 +3310,7 @@ def main(args):
elif cmd == 'update-tags':
errlog('`update-tags` is not longer needed. To install the latest tot release just run `install tot`')
return 0
elif cmd == 'activate' or cmd == 'deactivate':
elif cmd in ('activate', 'deactivate'):
if arg_permanent:
print('Registering active Emscripten environment permanently')
print('')
@@ -3353,7 +3329,7 @@ def main(args):
print('Deactivating tool ' + str(tool) + '.')
tools_to_activate.remove(tool)
else:
print('Tool "' + arg + '" was not active, no need to deactivate.')
print(f'Tool "{arg}" was not active, no need to deactivate.')
if not tools_to_activate:
errlog('No tools/SDKs specified to activate! Usage:\n emsdk activate tool/sdk1 [tool/sdk2] [...]')
return 1
@@ -3375,7 +3351,7 @@ def main(args):
CPU_CORES = int(multicore.group(1))
args[i] = ''
else:
errlog("Invalid command line parameter " + args[i] + ' specified!')
errlog(f'Invalid command line parameter {args[i]} specified!')
return 1
elif args[i] == '--shallow':
GIT_CLONE_SHALLOW = True
@@ -3412,12 +3388,12 @@ def main(args):
return 1
tool = find_tool(args[0])
if tool is None:
errlog("Error: Tool by name '" + args[0] + "' was not found.")
errlog(f"Error: Tool by name '{args[0]}' was not found.")
return 1
tool.uninstall()
return 0
errlog("Unknown command '" + cmd + "' given! Type 'emsdk help' to get a list of commands.")
errlog(f"Unknown command '{cmd}' given! Type 'emsdk help' to get a list of commands.")
return 1

74
pyproject.toml Normal file
View File

@@ -0,0 +1,74 @@
[project]
requires-python = ">=3.2"
[tool.ruff]
line-length = 100
exclude = [
"./cache/",
"./node_modules/",
"./site/source/_themes/",
"./site/source/conf.py",
"./system/lib/",
"./test/third_party/",
"./third_party/",
"./tools/filelock.py",
"./tools/scons/",
".git",
]
lint.select = [
"ARG",
"ASYNC",
"B",
"C4",
"C90",
"COM",
"E",
"F",
"I",
"PERF",
"PIE",
"PL",
"UP",
"W",
"YTT",
]
lint.external = [ "D" ]
lint.ignore = [
"B011", # See https://github.com/PyCQA/flake8-bugbear/issues/66
"B023",
"B026",
"E402",
"E501",
"E721",
"E741",
"PERF203",
"PERF401",
"PLC0415",
"PLR0915",
"PLR1704",
"PLR5501",
"PLW0602",
"PLW0603",
"PLW1510",
"PLW2901",
"UP030", # TODO
"UP031", # TODO
"UP032", # TODO
]
lint.per-file-ignores."emrun.py" = [ "PLE0704" ]
lint.per-file-ignores."tools/ports/*.py" = [ "ARG001", "ARG005" ]
lint.per-file-ignores."test/other/ports/*.py" = [ "ARG001" ]
lint.per-file-ignores."test/parallel_testsuite.py" = [ "ARG002" ]
lint.per-file-ignores."test/test_benchmark.py" = [ "ARG002" ]
lint.mccabe.max-complexity = 51 # Recommended: 10
lint.pylint.allow-magic-value-types = [
"bytes",
"float",
"int",
"str",
]
lint.pylint.max-args = 15 # Recommended: 5
lint.pylint.max-branches = 50 # Recommended: 12
lint.pylint.max-returns = 16 # Recommended: 6
lint.pylint.max-statements = 142 # Recommended: 50

View File

@@ -9,9 +9,10 @@ import hashlib
import json
import os
import re
import requests
import sys
import requests
STORAGE_URL = 'https://storage.googleapis.com/webassembly/emscripten-releases-builds'
EMSDK_ROOT = os.path.dirname(os.path.dirname(__file__))
@@ -51,7 +52,7 @@ def revisions_item(version, latest_hash):
def insert_revision(item):
with open(BAZEL_REVISIONS_FILE, 'r') as f:
with open(BAZEL_REVISIONS_FILE) as f:
lines = f.readlines()
lines.insert(lines.index('EMSCRIPTEN_TAGS = {\n') + 1, item)
@@ -61,7 +62,7 @@ def insert_revision(item):
def update_module_version(version):
with open(BAZEL_MODULE_FILE, 'r') as f:
with open(BAZEL_MODULE_FILE) as f:
content = f.read()
pattern = r'(module\(\s*name = "emsdk",\s*version = )"\d+.\d+.\d+",\n\)'
@@ -74,7 +75,7 @@ def update_module_version(version):
f.write(content)
def main(argv):
def main():
version, latest_hash = get_latest_info()
update_module_version(version)
item = revisions_item(version, latest_hash)
@@ -84,4 +85,4 @@ def main(argv):
if __name__ == '__main__':
sys.exit(main(sys.argv))
sys.exit(main())

View File

@@ -11,11 +11,12 @@ For the windows version we also alter the directory layout to add the 'bin'
directory.
"""
import urllib.request
import subprocess
import sys
import os
import shutil
import subprocess
import sys
import urllib.request
from zip import unzip_cmd, zip_cmd
# When adjusting this version, visit

View File

@@ -26,11 +26,11 @@ import glob
import multiprocessing
import os
import platform
import urllib.request
import shutil
import subprocess
import sys
import urllib.request
from subprocess import check_call
from zip import unzip_cmd, zip_cmd
version = '3.13.3'

View File

@@ -32,19 +32,18 @@ def listify(x):
return [x]
def check_call(cmd, **args):
def check_call(cmd, **kwargs):
if type(cmd) is not list:
cmd = cmd.split()
print('running: %s' % cmd)
args['universal_newlines'] = True
subprocess.check_call(cmd, **args)
subprocess.run(cmd, check=True, text=True, **kwargs)
def checked_call_with_output(cmd, expected=None, unexpected=None, stderr=None, env=None):
cmd = cmd.split(' ')
print('running: %s' % cmd)
try:
stdout = subprocess.check_output(cmd, stderr=stderr, universal_newlines=True, env=env)
stdout = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=stderr, check=True, text=True, env=env).stdout
except subprocess.CalledProcessError as e:
print(e.stderr)
print(e.stdout)
@@ -59,8 +58,9 @@ def checked_call_with_output(cmd, expected=None, unexpected=None, stderr=None, e
def failing_call_with_output(cmd, expected, env=None):
proc = subprocess.Popen(cmd.split(' '), stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, env=env)
stdout, stderr = proc.communicate()
proc = subprocess.run(cmd.split(' '), capture_output=True, text=True, env=env)
stdout = proc.stdout
stderr = proc.stderr
if WINDOWS:
print('warning: skipping part of failing_call_with_output() due to error codes not being propagated (see #592)')
else: