diff --git a/README.md b/README.md index c98222e837..25216dea50 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ https://emscripten.org/docs/building_from_source/toolchain_what_is_needed.html. ### Linux -- `python`: Version 3.9.2 or above. +- `python`: Version 3.13.0 or above. - `java`: For running closure compiler (optional) The emsdk pre-compiled binaries are built against Ubuntu/Focal 20.04 LTS and diff --git a/emsdk b/emsdk index 78c0288ca0..4cb42a11ad 100755 --- a/emsdk +++ b/emsdk @@ -8,7 +8,10 @@ # First look for python bundled in Emsdk if [ -z "$EMSDK_PYTHON" ]; then - PYTHON3="$(dirname "$0")/python/3.9.2-1_64bit/bin/python3" + PYTHON3="$(dirname "$0")/python/3.13.0-0_64bit/bin/python3" + if [ ! -f "$PYTHON3" ]; then + PYTHON3="$(dirname "$0")/python/3.9.2-1_64bit/bin/python3" + fi if [ -f "$PYTHON3" ]; then EMSDK_PYTHON="$PYTHON3" diff --git a/emsdk.bat b/emsdk.bat index d0c599d3ff..3e9fdf04f7 100644 --- a/emsdk.bat +++ b/emsdk.bat @@ -7,6 +7,14 @@ setlocal :: When using our bundled python we never want the users :: PYTHONHOME or PYTHONPATH :: https://github.com/emscripten-core/emsdk/issues/598 + +if exist "%~dp0python\3.13.0-0_64bit\python.exe" ( + set EMSDK_PY="%~dp0python\3.13.0-0_64bit\python.exe" + set PYTHONHOME= + set PYTHONPATH= + goto end +) + if exist "%~dp0python\3.9.2-1_64bit\python.exe" ( set EMSDK_PY="%~dp0python\3.9.2-1_64bit\python.exe" set PYTHONHOME= diff --git a/emsdk.ps1 b/emsdk.ps1 index e9e6008c32..1562fd6a6a 100644 --- a/emsdk.ps1 +++ b/emsdk.ps1 @@ -1,6 +1,7 @@ $ScriptDirectory = Split-Path -parent $PSCommandPath $PythonLocations = $( + "python\3.13.0-0_64bit\python.exe", "python\3.9.2-1_64bit\python.exe", "python\3.9.2-nuget_64bit\python.exe" ) diff --git a/emsdk.py b/emsdk.py index 20185d278e..99f063fd94 100644 --- a/emsdk.py +++ b/emsdk.py @@ -143,11 +143,7 @@ def exit_with_error(msg): elif machine.endswith('86'): ARCH = 'x86' elif machine.startswith('aarch64') or machine.lower().startswith('arm64'): - if WINDOWS: - errlog('No support for Windows on Arm, fallback to x64') - ARCH = 'x86_64' - else: - ARCH = 'arm64' + ARCH = 'arm64' elif machine.startswith('arm'): ARCH = 'arm' else: diff --git a/emsdk_manifest.json b/emsdk_manifest.json index ec7c9f44ec..538a1f45ab 100644 --- a/emsdk_manifest.json +++ b/emsdk_manifest.json @@ -192,6 +192,44 @@ "activated_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" }, + + { + "id": "python", + "version": "3.13.0", + "bitness": 64, + "arch": "x86_64", + "windows_url": "python-3.13.0-0-win-amd64.zip", + "activated_cfg": "PYTHON='%installation_dir%/python.exe'", + "activated_env": "EMSDK_PYTHON=%installation_dir%/python.exe" + }, + { + "id": "python", + "version": "3.13.0", + "bitness": 64, + "arch": "arm64", + "windows_url": "python-3.13.0-0-win-arm64.zip", + "activated_cfg": "PYTHON='%installation_dir%/python.exe'", + "activated_env": "EMSDK_PYTHON=%installation_dir%/python.exe" + }, + { + "id": "python", + "version": "3.13.0", + "bitness": 64, + "arch": "x86_64", + "macos_url": "python-3.13.0-0-macos-x86_64.tar.gz", + "activated_cfg": "PYTHON='%installation_dir%/bin/python3'", + "activated_env": "EMSDK_PYTHON=%installation_dir%/bin/python3;SSL_CERT_FILE=%installation_dir%/lib/python3.13/site-packages/certifi/cacert.pem" + }, + { + "id": "python", + "version": "3.13.0", + "bitness": 64, + "arch": "arm64", + "macos_url": "python-3.13.0-0-macos-arm64.tar.gz", + "activated_cfg": "PYTHON='%installation_dir%/bin/python3'", + "activated_env": "EMSDK_PYTHON=%installation_dir%/bin/python3;SSL_CERT_FILE=%installation_dir%/lib/python3.13/site-packages/certifi/cacert.pem" + }, + { "id": "java", "version": "8.152", @@ -368,13 +406,13 @@ { "version": "main", "bitness": 64, - "uses": ["python-3.9.2-nuget-64bit", "llvm-git-main-64bit", "node-20.18.0-64bit", "emscripten-main-64bit", "binaryen-main-64bit"], + "uses": ["python-3.13.0-64bit", "llvm-git-main-64bit", "node-20.18.0-64bit", "emscripten-main-64bit", "binaryen-main-64bit"], "os": "win" }, { "version": "main", "bitness": 64, - "uses": ["python-3.9.2-64bit", "llvm-git-main-64bit", "node-20.18.0-64bit", "emscripten-main-64bit", "binaryen-main-64bit"], + "uses": ["python-3.13.0-64bit", "llvm-git-main-64bit", "node-20.18.0-64bit", "emscripten-main-64bit", "binaryen-main-64bit"], "os": "macos" }, { diff --git a/scripts/update_python.py b/scripts/update_python.py index ae889d620c..1c1b0cee65 100755 --- a/scripts/update_python.py +++ b/scripts/update_python.py @@ -10,15 +10,19 @@ We only supply binaries for windows and macOS, but we do it very different ways for those two OSes. Windows recipe: - 1. Download the "embeddable zip file" version of python from python.org - 2. Remove .pth file to work around https://bugs.python.org/issue34841 - 3. Download and install pywin32 in the `site-packages` directory - 4. Re-zip and upload to storage.google.com + 1. Download precompiled version of python from NuGet package manager, + either the package "python" for AMD64, or "pythonarm64" for ARM64. + 2. Set up pip and install pywin32 and psutil via pip for emrun to work. + 3. Re-zip and upload to storage.google.com macOS recipe: 1. Clone cpython 2. Use homebrew to install and configure openssl (for static linking!) 3. Build cpython from source and use `make install` to create archive. + +Raspberry Pi Debian 12 (Bookworm): + 1. Before calling this script, run "sudo apt install libssl-dev", or otherwise + Python won't be able to use SSL. """ import glob @@ -32,29 +36,35 @@ from subprocess import check_call from zip import unzip_cmd, zip_cmd -version = '3.9.2' +version = '3.13.0' major_minor_version = '.'.join(version.split('.')[:2]) # e.g. '3.9.2' -> '3.9' -download_url = 'https://www.nuget.org/api/v2/package/python/%s' % version # This is not part of official Python version, but a repackaging number appended by emsdk # when a version of Python needs to be redownloaded. -revision = '4' +revision = '0' -pywin32_version = '227' -pywin32_base = 'https://github.com/mhammond/pywin32/releases/download/b%s/' % pywin32_version +PSUTIL = 'psutil==6.0.0' upload_base = 'gs://webassembly/emscripten-releases-builds/deps/' +# Detects whether current python interpreter architecture is ARM64 or AMD64 +# If running AMD64 python on an ARM64 Windows, this still intentionally returns AMD64 +def find_python_arch(): + import sysconfig + arch = sysconfig.get_platform().lower() + if 'amd64' in arch: + return 'amd64' + if 'arm64' in arch: + return 'arm64' + raise f'Unknown Python sysconfig platform "{arch}" (neither AMD64 or ARM64)' def make_python_patch(): - pywin32_filename = 'pywin32-%s.win-amd64-py%s.exe' % (pywin32_version, major_minor_version) - filename = 'python-%s-amd64.zip' % (version) - out_filename = 'python-%s-%s-amd64+pywin32.zip' % (version, revision) - if not os.path.exists(pywin32_filename): - url = pywin32_base + pywin32_filename - print('Downloading pywin32: ' + url) - urllib.request.urlretrieve(url, pywin32_filename) + python_arch = find_python_arch() + package_name = 'pythonarm64' if python_arch == 'arm64' else 'python' + download_url = f'https://www.nuget.org/api/v2/package/{package_name}/{version}' + filename = f'python-{version}-win-{python_arch}.zip' + out_filename = f'python-{version}-{revision}-win-{python_arch}.zip' if not os.path.exists(filename): print(f'Downloading python: {download_url} to {filename}') @@ -64,19 +74,17 @@ def make_python_patch(): check_call(unzip_cmd() + [os.path.abspath(filename)], cwd='python-nuget') os.remove(filename) - os.mkdir('pywin32') - rtn = subprocess.call(unzip_cmd() + [os.path.abspath(pywin32_filename)], cwd='pywin32') - assert rtn in [0, 1] - - os.mkdir(os.path.join('python-nuget', 'lib')) - shutil.move(os.path.join('pywin32', 'PLATLIB'), os.path.join('python-nuget', 'toolss', 'Lib', 'site-packages')) + src_dir = os.path.join('python-nuget', 'tools') + python_exe = os.path.join(src_dir, 'python.exe') + check_call([python_exe, '-m', 'ensurepip', '--upgrade']) + check_call([python_exe, '-m', 'pip', 'install', 'pywin32==308']) + check_call([python_exe, '-m', 'pip', 'install', PSUTIL]) - check_call(zip_cmd() + [os.path.join('..', '..', out_filename), '.'], cwd='python-nuget/tools') + check_call(zip_cmd() + [os.path.join('..', '..', out_filename), '.'], cwd=src_dir) print('Created: %s' % out_filename) # cleanup if everything went fine shutil.rmtree('python-nuget') - shutil.rmtree('pywin32') if '--upload' in sys.argv: upload_url = upload_base + out_filename @@ -94,7 +102,7 @@ def build_python(): check_call(['brew', 'install', 'openssl', 'xz', 'pkg-config']) if platform.machine() == 'x86_64': prefix = '/usr/local' - min_macos_version = '10.11' + min_macos_version = '10.13' elif platform.machine() == 'arm64': prefix = '/opt/homebrew' min_macos_version = '11.0' @@ -149,7 +157,7 @@ def build_python(): # Install psutil module. This is needed by emrun to track when browser # process quits. - check_call([pybin, pip, 'install', 'psutil']) + check_call([pybin, pip, 'install', PSUTIL]) dirname = 'python-%s-%s' % (version, revision) if os.path.isdir(dirname):