diff --git a/ci-targets.yaml b/ci-targets.yaml index 0662bbee..7a12bddf 100644 --- a/ci-targets.yaml +++ b/ci-targets.yaml @@ -10,6 +10,7 @@ darwin: - "3.12" - "3.13" - "3.14" + - "3.15" build_options: - debug - pgo+lto @@ -28,6 +29,7 @@ darwin: - "3.12" - "3.13" - "3.14" + - "3.15" build_options: - debug - pgo+lto @@ -48,6 +50,7 @@ linux: - "3.12" - "3.13" - "3.14" + - "3.15" build_options: - debug - pgo+lto @@ -67,6 +70,7 @@ linux: - "3.12" - "3.13" - "3.14" + - "3.15" build_options: - debug - noopt @@ -88,6 +92,7 @@ linux: - "3.12" - "3.13" - "3.14" + - "3.15" build_options: - debug - noopt @@ -109,6 +114,7 @@ linux: - "3.12" - "3.13" - "3.14" + - "3.15" build_options: - debug - noopt @@ -130,6 +136,7 @@ linux: - "3.12" - "3.13" - "3.14" + - "3.15" build_options: - debug - noopt @@ -151,6 +158,7 @@ linux: - "3.12" - "3.13" - "3.14" + - "3.15" build_options: - debug - noopt @@ -172,6 +180,7 @@ linux: - "3.12" - "3.13" - "3.14" + - "3.15" build_options: - debug - pgo+lto @@ -193,6 +202,7 @@ linux: - "3.12" - "3.13" - "3.14" + - "3.15" build_options: - debug - pgo+lto @@ -214,6 +224,7 @@ linux: - "3.12" - "3.13" - "3.14" + - "3.15" build_options: - debug - pgo+lto @@ -235,6 +246,7 @@ linux: - "3.12" - "3.13" - "3.14" + - "3.15" build_options: - debug - pgo+lto @@ -255,6 +267,7 @@ linux: - "3.12" - "3.13" - "3.14" + - "3.15" build_options: - debug+static - noopt+static @@ -281,6 +294,7 @@ linux: - "3.12" - "3.13" - "3.14" + - "3.15" build_options: - debug+static - noopt+static @@ -307,6 +321,7 @@ linux: - "3.12" - "3.13" - "3.14" + - "3.15" build_options: - debug+static - noopt+static @@ -333,6 +348,7 @@ linux: - "3.12" - "3.13" - "3.14" + - "3.15" build_options: - debug+static - noopt+static @@ -385,6 +401,7 @@ windows: - "3.12" - "3.13" - "3.14" + - "3.15" build_options: - pgo build_options_conditional: @@ -402,6 +419,7 @@ windows: - "3.12" - "3.13" - "3.14" + - "3.15" build_options: - pgo build_options_conditional: diff --git a/cpython-unix/Makefile b/cpython-unix/Makefile index 577f44fd..b4618d22 100644 --- a/cpython-unix/Makefile +++ b/cpython-unix/Makefile @@ -6,7 +6,7 @@ BUILD := $(HERE)/build.py NULL := SPACE := $(subst ,, ) -ALL_PYTHON_VERSIONS := 3.9 3.10 3.11 3.12 3.13 3.14 +ALL_PYTHON_VERSIONS := 3.9 3.10 3.11 3.12 3.13 3.14 3.15 ifndef PYBUILD_TARGET_TRIPLE $(error PYBUILD_TARGET_TRIPLE not defined) diff --git a/cpython-unix/build-cpython.sh b/cpython-unix/build-cpython.sh index 7f7c4c38..32992ee5 100755 --- a/cpython-unix/build-cpython.sh +++ b/cpython-unix/build-cpython.sh @@ -512,9 +512,13 @@ if [ -n "${CPYTHON_OPTIMIZED}" ]; then patch -p1 -i "${ROOT}/patch-jit-llvm-version-3.13.patch" fi - if [[ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_14}" ]]; then + if [[ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_14}" && -n "${PYTHON_MEETS_MAXIMUM_VERSION_3_14}" ]]; then patch -p1 -i "${ROOT}/patch-jit-llvm-version-3.14.patch" fi + + if [[ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_15}" ]]; then + patch -p1 -i "${ROOT}/patch-jit-llvm-version-3.15.patch" + fi fi fi @@ -609,10 +613,19 @@ if [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_14}" ]; then CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_func_explicit_bzero=no" fi +# Define the base PGO profiling task, which we'll extend below with ignores +export PROFILE_TASK='-m test --pgo' + # On 3.14+ `test_strftime_y2k` fails when cross-compiling for `x86_64_v2` and `x86_64_v3` targets on # Linux, so we ignore it. See https://github.com/python/cpython/issues/128104 if [[ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_14}" && -n "${CROSS_COMPILING}" && "${PYBUILD_PLATFORM}" != macos* ]]; then - export PROFILE_TASK='-m test --pgo --ignore test_strftime_y2k' + PROFILE_TASK="${PROFILE_TASK} --ignore test_strftime_y2k" +fi + +# On 3.15+ `test_json.test_recursion.TestCRecursion.test_highly_nested_objects_decoding` fails during +# PGO due to RecursionError not being raised as expected. See https://github.com/python/cpython/issues/140125 +if [[ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_15}" ]]; then + PROFILE_TASK="${PROFILE_TASK} --ignore test_json" fi # ./configure tries to auto-detect whether it can build 128-bit and 256-bit SIMD helpers for HACL, diff --git a/cpython-unix/build-main.py b/cpython-unix/build-main.py index e4276be2..366ec8cf 100755 --- a/cpython-unix/build-main.py +++ b/cpython-unix/build-main.py @@ -61,6 +61,7 @@ def main(): "cpython-3.12", "cpython-3.13", "cpython-3.14", + "cpython-3.15", }, default="cpython-3.11", help="Python distribution to build", diff --git a/cpython-unix/build.py b/cpython-unix/build.py index 662d7021..23d94602 100755 --- a/cpython-unix/build.py +++ b/cpython-unix/build.py @@ -393,13 +393,23 @@ def build_cpython_host( target_triple: str, build_options: list[str], dest_archive, + python_source=None, + entry_name=None, ): """Build binutils in the Docker image.""" - archive = download_entry(entry, DOWNLOADS_PATH) + if not python_source: + python_version = entry["version"] + archive = download_entry(entry_name, DOWNLOADS_PATH) + else: + python_version = os.environ["PYBUILD_PYTHON_VERSION"] + archive = DOWNLOADS_PATH / ("Python-%s.tar.xz" % python_version) + print("Compressing %s to %s" % (python_source, archive)) + with archive.open("wb") as fh: + create_tar_from_directory( + fh, python_source, path_prefix="Python-%s" % python_version + ) with build_environment(client, image) as build_env: - python_version = DOWNLOADS[entry]["version"] - build_env.install_toolchain( BUILD, host_platform, @@ -434,7 +444,7 @@ def build_cpython_host( # Set environment variables allowing convenient testing for Python # version ranges. - for v in ("3.9", "3.10", "3.11", "3.12", "3.13", "3.14"): + for v in ("3.9", "3.10", "3.11", "3.12", "3.13", "3.14", "3.15"): normal_version = v.replace(".", "_") if meets_python_minimum_version(python_version, v): @@ -706,12 +716,15 @@ def build_cpython( """Build CPython in a Docker image'""" parsed_build_options = set(build_options.split("+")) entry_name = "cpython-%s" % version - entry = DOWNLOADS[entry_name] if not python_source: + entry = DOWNLOADS[entry_name] python_version = entry["version"] python_archive = download_entry(entry_name, DOWNLOADS_PATH) else: + entry = DOWNLOADS.get(entry_name, {}) python_version = os.environ["PYBUILD_PYTHON_VERSION"] + entry.setdefault("licenses", ["Python-2.0", "CNRI-Python"]) + entry.setdefault("python_tag", "cp" + "".join(version.split("."))) python_archive = DOWNLOADS_PATH / ("Python-%s.tar.xz" % python_version) print("Compressing %s to %s" % (python_source, python_archive)) with python_archive.open("wb") as fh: @@ -804,7 +817,7 @@ def build_cpython( # Set environment variables allowing convenient testing for Python # version ranges. - for v in ("3.9", "3.10", "3.11", "3.12", "3.13", "3.14"): + for v in ("3.9", "3.10", "3.11", "3.12", "3.13", "3.14", "3.15"): normal_version = v.replace(".", "_") if meets_python_minimum_version(python_version, v): @@ -1024,6 +1037,18 @@ def main(): log_name = "%s-%s" % (action, host_platform) elif args.action.startswith("cpython-") and args.action.endswith("-host"): log_name = args.action + elif action.startswith("cpython-"): + version = ( + os.environ["PYBUILD_PYTHON_VERSION"] + if python_source + else DOWNLOADS[action]["version"] + ) + log_name = "%s-%s-%s-%s" % ( + action, + version, + target_triple, + build_options, + ) else: entry = DOWNLOADS[action] log_name = "%s-%s-%s-%s" % ( @@ -1229,14 +1254,21 @@ def main(): ) elif action.startswith("cpython-") and action.endswith("-host"): + entry_name = action[:-5] + if not python_source: + entry = DOWNLOADS[entry_name] + else: + entry = DOWNLOADS.get(entry_name, {}) build_cpython_host( client, get_image(client, ROOT, BUILD, docker_image, host_platform), - action[:-5], + entry, host_platform=host_platform, target_triple=target_triple, build_options=build_options, dest_archive=dest_archive, + python_source=python_source, + entry_name=entry_name, ) elif action in ( @@ -1246,6 +1278,7 @@ def main(): "cpython-3.12", "cpython-3.13", "cpython-3.14", + "cpython-3.15", ): build_cpython( settings, diff --git a/cpython-unix/patch-jit-llvm-version-3.15.patch b/cpython-unix/patch-jit-llvm-version-3.15.patch new file mode 100644 index 00000000..d87ac840 --- /dev/null +++ b/cpython-unix/patch-jit-llvm-version-3.15.patch @@ -0,0 +1,12 @@ +diff --git a/Tools/jit/_llvm.py b/Tools/jit/_llvm.py +--- a/Tools/jit/_llvm.py ++++ b/Tools/jit/_llvm.py +@@ -11,7 +11,7 @@ + import _targets + + +-_LLVM_VERSION = "19" ++_LLVM_VERSION = "20" + _EXTERNALS_LLVM_TAG = "llvm-19.1.7.0" + + diff --git a/cpython-unix/targets.yml b/cpython-unix/targets.yml index 73fe43cc..e2b018ef 100644 --- a/cpython-unix/targets.yml +++ b/cpython-unix/targets.yml @@ -70,6 +70,7 @@ aarch64-apple-darwin: - '3.12' - '3.13' - '3.14' + - '3.15' needs_toolchain: true host_cc: clang host_cxx: clang++ @@ -166,6 +167,7 @@ aarch64-unknown-linux-gnu: - '3.12' - '3.13' - '3.14' + - '3.15' docker_image_suffix: .debian9 needs_toolchain: true host_cc: clang @@ -260,6 +262,7 @@ armv7-unknown-linux-gnueabi: - '3.12' - '3.13' - '3.14' + - '3.15' docker_image_suffix: .cross host_cc: /usr/bin/x86_64-linux-gnu-gcc host_cxx: /usr/bin/x86_64-linux-gnu-g++ @@ -301,6 +304,7 @@ armv7-unknown-linux-gnueabihf: - '3.12' - '3.13' - '3.14' + - '3.15' docker_image_suffix: .cross host_cc: /usr/bin/x86_64-linux-gnu-gcc host_cxx: /usr/bin/x86_64-linux-gnu-g++ @@ -383,6 +387,7 @@ mips-unknown-linux-gnu: - '3.12' - '3.13' - '3.14' + - '3.15' docker_image_suffix: .cross host_cc: /usr/bin/x86_64-linux-gnu-gcc host_cxx: /usr/bin/x86_64-linux-gnu-g++ @@ -424,6 +429,7 @@ mipsel-unknown-linux-gnu: - '3.12' - '3.13' - '3.14' + - '3.15' docker_image_suffix: .cross host_cc: /usr/bin/x86_64-linux-gnu-gcc host_cxx: /usr/bin/x86_64-linux-gnu-g++ @@ -465,6 +471,7 @@ ppc64le-unknown-linux-gnu: - '3.12' - '3.13' - '3.14' + - '3.15' docker_image_suffix: .cross host_cc: /usr/bin/x86_64-linux-gnu-gcc host_cxx: /usr/bin/x86_64-linux-gnu-g++ @@ -506,6 +513,7 @@ riscv64-unknown-linux-gnu: - '3.12' - '3.13' - '3.14' + - '3.15' docker_image_suffix: .cross-riscv64 host_cc: /usr/bin/x86_64-linux-gnu-gcc host_cxx: /usr/bin/x86_64-linux-gnu-g++ @@ -547,6 +555,7 @@ s390x-unknown-linux-gnu: - '3.12' - '3.13' - '3.14' + - '3.15' docker_image_suffix: .cross host_cc: /usr/bin/x86_64-linux-gnu-gcc host_cxx: /usr/bin/x86_64-linux-gnu-g++ @@ -639,6 +648,7 @@ x86_64-apple-darwin: - '3.12' - '3.13' - '3.14' + - '3.15' needs_toolchain: true apple_sdk_platform: macosx host_cc: clang @@ -820,6 +830,7 @@ x86_64-unknown-linux-gnu: - '3.12' - '3.13' - '3.14' + - '3.15' needs_toolchain: true host_cc: clang host_cxx: clang++ @@ -866,6 +877,7 @@ x86_64_v2-unknown-linux-gnu: - '3.12' - '3.13' - '3.14' + - '3.15' needs_toolchain: true host_cc: clang host_cxx: clang++ @@ -913,6 +925,7 @@ x86_64_v3-unknown-linux-gnu: - '3.12' - '3.13' - '3.14' + - '3.15' needs_toolchain: true host_cc: clang host_cxx: clang++ @@ -960,6 +973,7 @@ x86_64_v4-unknown-linux-gnu: - '3.12' - '3.13' - '3.14' + - '3.15' needs_toolchain: true host_cc: clang host_cxx: clang++ @@ -1007,6 +1021,7 @@ x86_64-unknown-linux-musl: - '3.12' - '3.13' - '3.14' + - '3.15' needs_toolchain: true host_cc: clang host_cxx: clang++ @@ -1051,6 +1066,7 @@ x86_64_v2-unknown-linux-musl: - '3.12' - '3.13' - '3.14' + - '3.15' needs_toolchain: true host_cc: clang host_cxx: clang++ @@ -1096,6 +1112,7 @@ x86_64_v3-unknown-linux-musl: - '3.12' - '3.13' - '3.14' + - '3.15' needs_toolchain: true host_cc: clang host_cxx: clang++ @@ -1141,6 +1158,7 @@ x86_64_v4-unknown-linux-musl: - '3.12' - '3.13' - '3.14' + - '3.15' needs_toolchain: true host_cc: clang host_cxx: clang++ diff --git a/cpython-windows/build.py b/cpython-windows/build.py index c94aa4ab..d02559e0 100644 --- a/cpython-windows/build.py +++ b/cpython-windows/build.py @@ -1856,6 +1856,7 @@ def main() -> None: "cpython-3.12", "cpython-3.13", "cpython-3.14", + "cpython-3.15", }, default="cpython-3.11", help="Python distribution to build", diff --git a/pythonbuild/buildenv.py b/pythonbuild/buildenv.py index 4b2a0e6e..9266ad37 100644 --- a/pythonbuild/buildenv.py +++ b/pythonbuild/buildenv.py @@ -164,10 +164,9 @@ def copy_file(self, source: pathlib.Path, dest_path=None, dest_name=None): def install_toolchain_archive( self, build_dir, package_name, host_platform, version=None ): - entry = DOWNLOADS[package_name] basename = "%s-%s-%s.tar" % ( package_name, - version or entry["version"], + version or DOWNLOADS[package_name]["version"], host_platform, ) diff --git a/pythonbuild/downloads.py b/pythonbuild/downloads.py index 13023500..aa0bc99b 100644 --- a/pythonbuild/downloads.py +++ b/pythonbuild/downloads.py @@ -101,6 +101,15 @@ "license_file": "LICENSE.cpython.txt", "python_tag": "cp314", }, + "cpython-3.15": { + "url": "https://www.python.org/ftp/python/3.15.0/Python-3.15.0a1.tar.xz", + "size": 23646768, + "sha256": "3194939d488eeaeefdcf990d35542d9ad1ce788789c4e2305a2060eb7058e5a4", + "version": "3.15.0a1", + "licenses": ["Python-2.0", "CNRI-Python"], + "license_file": "LICENSE.cpython.txt", + "python_tag": "cp315", + }, "expat": { "url": "https://github.com/libexpat/libexpat/releases/download/R_2_6_3/expat-2.6.3.tar.xz", "size": 485600, @@ -284,10 +293,10 @@ "version": "0.13.1", }, "pip": { - "url": "https://files.pythonhosted.org/packages/ef/7d/500c9ad20238fcfcb4cb9243eede163594d7020ce87bd9610c9e02771876/pip-24.3.1-py3-none-any.whl", - "size": 1822182, - "sha256": "3790624780082365f47549d032f3770eeb2b1e8bd1f7b2e02dace1afa361b4ed", - "version": "24.3.1", + "url": "https://files.pythonhosted.org/packages/b7/3f/945ef7ab14dc4f9d7f40288d2df998d1837ee0888ec3659c813487572faa/pip-25.2-py3-none-any.whl", + "size": 1752557, + "sha256": "6d67a2b4e7f14d8b31b8b52648866fa717f45a1eb70e83002f4331d07e953717", + "version": "25.2", }, "readline": { # Mirrored from https://ftp.gnu.org/gnu/readline/readline-8.2.tar.gz diff --git a/src/validation.rs b/src/validation.rs index dbe89764..41992734 100644 --- a/src/validation.rs +++ b/src/validation.rs @@ -136,6 +136,8 @@ const PE_ALLOWED_LIBRARIES: &[&str] = &[ "python313t.dll", "python314.dll", "python314t.dll", + "python315.dll", + "python315t.dll", "sqlite3.dll", "tcl86t.dll", "tk86t.dll", @@ -149,6 +151,15 @@ const PE_ALLOWED_LIBRARIES_314: &[&str] = &[ "msvcrt.dll", // zlib loads this library ]; const PE_ALLOWED_LIBRARIES_ARM64: &[&str] = &["msvcrt.dll", "zlib1.dll"]; +const PE_ALLOWED_LIBRARIES_315: &[&str] = &[ + // See `PE_ALLOWED_LIBRARIES_314` for zlib-related libraries + "zlib1.dll", + "api-ms-win-crt-private-l1-1-0.dll", + "msvcrt.dll", + // `_remote_debugging` loads `ntdll` + // See https://github.com/python/cpython/pull/138710 + "ntdll.dll", +]; static GLIBC_MAX_VERSION_BY_TRIPLE: Lazy>> = Lazy::new(|| { @@ -371,6 +382,26 @@ static DARWIN_ALLOWED_DYLIBS: Lazy> = Lazy::new(|| { max_compatibility_version: "3.14.0".try_into().unwrap(), required: false, }, + MachOAllowedDylib { + name: "@executable_path/../lib/libpython3.15.dylib".to_string(), + max_compatibility_version: "3.15.0".try_into().unwrap(), + required: false, + }, + MachOAllowedDylib { + name: "@executable_path/../lib/libpython3.15d.dylib".to_string(), + max_compatibility_version: "3.15.0".try_into().unwrap(), + required: false, + }, + MachOAllowedDylib { + name: "@executable_path/../lib/libpython3.15t.dylib".to_string(), + max_compatibility_version: "3.15.0".try_into().unwrap(), + required: false, + }, + MachOAllowedDylib { + name: "@executable_path/../lib/libpython3.15td.dylib".to_string(), + max_compatibility_version: "3.15.0".try_into().unwrap(), + required: false, + }, MachOAllowedDylib { name: "/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit".to_string(), max_compatibility_version: "45.0.0".try_into().unwrap(), @@ -1495,6 +1526,11 @@ fn validate_pe<'data, Pe: ImageNtHeaders>( continue; } } + "3.15" => { + if PE_ALLOWED_LIBRARIES_315.contains(&lib.as_str()) { + continue; + } + } _ => {} } @@ -1653,7 +1689,7 @@ fn validate_extension_modules( "3.13" => { wanted.extend(GLOBAL_EXTENSIONS_PYTHON_3_13); } - "3.14" => { + "3.14" | "3.15" => { wanted.extend(GLOBAL_EXTENSIONS_PYTHON_3_14); } _ => { @@ -1678,7 +1714,7 @@ fn validate_extension_modules( wanted.extend(GLOBAL_EXTENSIONS_WINDOWS_PRE_3_13); } - if matches!(python_major_minor, "3.14") { + if matches!(python_major_minor, "3.14" | "3.15") { wanted.extend(GLOBAL_EXTENSIONS_WINDOWS_3_14); } @@ -1718,7 +1754,7 @@ fn validate_extension_modules( wanted.insert("_testexternalinspection"); } - if (is_linux || is_macos) && matches!(python_major_minor, "3.12" | "3.13" | "3.14") { + if (is_linux || is_macos) && matches!(python_major_minor, "3.12" | "3.13" | "3.14" | "3.15") { wanted.insert("_testsinglephase"); } @@ -1844,6 +1880,8 @@ fn validate_distribution( "3.13" } else if dist_filename.starts_with("cpython-3.14.") { "3.14" + } else if dist_filename.starts_with("cpython-3.15.") { + "3.15" } else { return Err(anyhow!("could not parse Python version from filename")); };