1010from typing import IO , MutableMapping
1111
1212from ..common import DARWIN , MACOS_DEVELOPMENT_TARGET , arches
13- from .common import Dirs , build_openssl , build_sqlite , builds , finalize , runcmd
13+ from .common import (
14+ Dirs ,
15+ build_openssl ,
16+ build_sqlite ,
17+ builds ,
18+ finalize ,
19+ get_dependency_version ,
20+ runcmd ,
21+ )
1422
1523ARCHES = arches [DARWIN ]
1624
@@ -38,6 +46,54 @@ def populate_env(env: MutableMapping[str, str], dirs: Dirs) -> None:
3846 env ["CFLAGS" ] = " " .join (cflags ).format (prefix = dirs .prefix )
3947
4048
49+ def update_expat (dirs : Dirs , env : MutableMapping [str , str ]) -> None :
50+ """
51+ Update the bundled expat library to the latest version.
52+
53+ Python ships with an older bundled expat. This function updates it
54+ to the latest version for security and bug fixes.
55+ """
56+ import pathlib
57+ import shutil
58+ import glob
59+ import urllib .request
60+ import tarfile
61+
62+ # Get version from JSON
63+ expat_info = get_dependency_version ("expat" , "darwin" )
64+ if not expat_info :
65+ # No update needed, use bundled version
66+ return
67+
68+ version = expat_info ["version" ]
69+ version_tag = version .replace ("." , "_" )
70+ url = f"https://github.com/libexpat/libexpat/releases/download/R_{ version_tag } /expat-{ version } .tar.xz"
71+
72+ expat_dir = pathlib .Path (dirs .source ) / "Modules" / "expat"
73+ if not expat_dir .exists ():
74+ # No expat directory, skip
75+ return
76+
77+ # Download expat tarball
78+ tmpbuild = pathlib .Path (dirs .tmpbuild )
79+ tarball_path = tmpbuild / f"expat-{ version } .tar.xz"
80+ urllib .request .urlretrieve (url , str (tarball_path ))
81+
82+ # Extract tarball
83+ with tarfile .open (tarball_path ) as tar :
84+ tar .extractall (path = str (tmpbuild ))
85+
86+ # Copy source files to Modules/expat/
87+ expat_source_dir = tmpbuild / f"expat-{ version } " / "lib"
88+ for source_file in ["*.h" , "*.c" ]:
89+ for file_path in glob .glob (str (expat_source_dir / source_file )):
90+ target_file = expat_dir / pathlib .Path (file_path ).name
91+ # Remove old file if it exists
92+ if target_file .exists ():
93+ target_file .unlink ()
94+ shutil .copy2 (file_path , str (expat_dir ))
95+
96+
4197def build_python (env : MutableMapping [str , str ], dirs : Dirs , logfp : IO [str ]) -> None :
4298 """
4399 Run the commands to build Python.
@@ -49,6 +105,9 @@ def build_python(env: MutableMapping[str, str], dirs: Dirs, logfp: IO[str]) -> N
49105 :param logfp: A handle for the log file
50106 :type logfp: file
51107 """
108+ # Update bundled expat to latest version
109+ update_expat (dirs , env )
110+
52111 env ["LDFLAGS" ] = "-Wl,-rpath,{prefix}/lib {ldflags}" .format (
53112 prefix = dirs .prefix , ldflags = env ["LDFLAGS" ]
54113 )
@@ -77,33 +136,66 @@ def build_python(env: MutableMapping[str, str], dirs: Dirs, logfp: IO[str]) -> N
77136
78137build = builds .add ("darwin" , populate_env = populate_env )
79138
139+ # Get dependency versions from JSON (with fallback to hardcoded values)
140+ openssl_info = get_dependency_version ("openssl" , "darwin" )
141+ if openssl_info :
142+ openssl_version = openssl_info ["version" ]
143+ openssl_url = openssl_info ["url" ]
144+ openssl_checksum = openssl_info ["sha256" ]
145+ else :
146+ openssl_version = "3.5.4"
147+ openssl_url = "https://github.com/openssl/openssl/releases/download/openssl-{version}/openssl-{version}.tar.gz"
148+ openssl_checksum = "b75daac8e10f189abe28a076ba5905d363e4801f"
149+
80150build .add (
81151 "openssl" ,
82152 build_func = build_openssl ,
83153 download = {
84- "url" : "https://github.com/openssl/openssl/releases/download/openssl-{version}/openssl-{version}.tar.gz" ,
85- "version" : "3.5.4" ,
86- "checksum" : "b75daac8e10f189abe28a076ba5905d363e4801f" ,
154+ "url" : openssl_url ,
155+ "version" : openssl_version ,
156+ "checksum" : openssl_checksum ,
87157 },
88158)
89159
160+ # Get XZ version from JSON
161+ xz_info = get_dependency_version ("xz" , "darwin" )
162+ if xz_info :
163+ xz_version = xz_info ["version" ]
164+ xz_url = xz_info ["url" ]
165+ xz_checksum = xz_info ["sha256" ]
166+ else :
167+ xz_version = "5.8.1"
168+ xz_url = "http://tukaani.org/xz/xz-{version}.tar.gz"
169+ xz_checksum = "ed4d5589c4cfe84e1697bd02a9954b76af336931"
170+
90171build .add (
91172 "XZ" ,
92173 download = {
93- "url" : "http://tukaani.org/xz/xz-{version}.tar.gz" ,
94- "version" : "5.8.1" ,
95- "checksum" : "ed4d5589c4cfe84e1697bd02a9954b76af336931" ,
174+ "url" : xz_url ,
175+ "version" : xz_version ,
176+ "checksum" : xz_checksum ,
96177 },
97178)
98179
180+ # Get SQLite version from JSON
181+ sqlite_info = get_dependency_version ("sqlite" , "darwin" )
182+ if sqlite_info :
183+ sqlite_url = sqlite_info ["url" ]
184+ sqlite_checksum = sqlite_info ["sha256" ]
185+ sqlite_version_num = sqlite_info .get ("sqliteversion" , "3500400" )
186+ else :
187+ sqlite_version_num = "3500400"
188+ sqlite_url = "https://sqlite.org/2025/sqlite-autoconf-{version}.tar.gz"
189+ sqlite_checksum = "145048005c777796dd8494aa1cfed304e8c34283"
190+
99191build .add (
100192 name = "SQLite" ,
101193 build_func = build_sqlite ,
102194 download = {
103- "url" : "https://sqlite.org/2025/sqlite-autoconf-{version}.tar.gz" ,
195+ "url" : sqlite_url ,
104196 "fallback_url" : "https://woz.io/relenv/dependencies/sqlite-autoconf-{version}.tar.gz" ,
105- "version" : "3500400" ,
106- "checksum" : "145048005c777796dd8494aa1cfed304e8c34283" ,
197+ "version" : sqlite_version_num ,
198+ "checksum" : sqlite_checksum ,
107199 },
108200)
109201
0 commit comments