Skip to content

Commit 78e57bd

Browse files
authored
Fix freethreaded include on Windows (#368)
1 parent ace5f06 commit 78e57bd

File tree

4 files changed

+63
-11
lines changed

4 files changed

+63
-11
lines changed

cpython-windows/build.py

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -845,6 +845,7 @@ def run_msbuild(
845845
platform: str,
846846
python_version: str,
847847
windows_sdk_version: str,
848+
freethreaded: bool,
848849
):
849850
args = [
850851
str(msbuild),
@@ -867,6 +868,9 @@ def run_msbuild(
867868
f"/property:DefaultWindowsSDKVersion={windows_sdk_version}",
868869
]
869870

871+
if freethreaded:
872+
args.append("/property:DisableGil=true")
873+
870874
exec_and_log(args, str(pcbuild_path), os.environ)
871875

872876

@@ -1118,6 +1122,7 @@ def collect_python_build_artifacts(
11181122
arch: str,
11191123
config: str,
11201124
openssl_entry: str,
1125+
freethreaded: bool,
11211126
):
11221127
"""Collect build artifacts from Python.
11231128
@@ -1243,6 +1248,20 @@ def find_additional_dependencies(project: pathlib.Path):
12431248

12441249
return set()
12451250

1251+
if arch == "amd64":
1252+
abi_platform = "win_amd64"
1253+
elif arch == "win32":
1254+
abi_platform = "win32"
1255+
else:
1256+
raise ValueError("unhandled arch: %s" % arch)
1257+
1258+
if freethreaded:
1259+
abi_tag = ".cp%st-%s" % (python_majmin, abi_platform)
1260+
lib_suffix = "t"
1261+
else:
1262+
abi_tag = ""
1263+
lib_suffix = ""
1264+
12461265
# Copy object files for core sources into their own directory.
12471266
core_dir = out_dir / "build" / "core"
12481267
core_dir.mkdir(parents=True)
@@ -1263,12 +1282,12 @@ def find_additional_dependencies(project: pathlib.Path):
12631282
exts = ("lib", "exp")
12641283

12651284
for ext in exts:
1266-
source = outputs_path / ("python%s.%s" % (python_majmin, ext))
1267-
dest = core_dir / ("python%s.%s" % (python_majmin, ext))
1285+
source = outputs_path / ("python%s%s.%s" % (python_majmin, lib_suffix, ext))
1286+
dest = core_dir / ("python%s%s.%s" % (python_majmin, lib_suffix, ext))
12681287
log("copying %s" % source)
12691288
shutil.copyfile(source, dest)
12701289

1271-
res["core"]["shared_lib"] = "install/python%s.dll" % python_majmin
1290+
res["core"]["shared_lib"] = "install/python%s%s.dll" % (python_majmin, lib_suffix)
12721291

12731292
# We hack up pythoncore.vcxproj and the list in it when this function
12741293
# runs isn't totally accurate. We hardcode the list from the CPython
@@ -1354,12 +1373,15 @@ def find_additional_dependencies(project: pathlib.Path):
13541373
res["extensions"][ext] = [entry]
13551374

13561375
# Copy the extension static library.
1357-
ext_static = outputs_path / ("%s.lib" % ext)
1358-
dest = dest_dir / ("%s.lib" % ext)
1376+
ext_static = outputs_path / ("%s%s.lib" % (ext, abi_tag))
1377+
dest = dest_dir / ("%s%s.lib" % (ext, abi_tag))
13591378
log("copying static extension %s" % ext_static)
13601379
shutil.copyfile(ext_static, dest)
13611380

1362-
res["extensions"][ext][0]["shared_lib"] = "install/DLLs/%s.pyd" % ext
1381+
res["extensions"][ext][0]["shared_lib"] = "install/DLLs/%s%s.pyd" % (
1382+
ext,
1383+
abi_tag,
1384+
)
13631385

13641386
lib_dir = out_dir / "build" / "lib"
13651387
lib_dir.mkdir()
@@ -1394,6 +1416,7 @@ def build_cpython(
13941416
) -> pathlib.Path:
13951417
parsed_build_options = set(build_options.split("+"))
13961418
pgo = "pgo" in parsed_build_options
1419+
freethreaded = "freethreaded" in parsed_build_options
13971420

13981421
msbuild = find_msbuild(msvc_version)
13991422
log("found MSBuild at %s" % msbuild)
@@ -1425,6 +1448,12 @@ def build_cpython(
14251448
# as we do for Unix builds.
14261449
mpdecimal_archive = None
14271450

1451+
if freethreaded:
1452+
(major, minor, _) = python_version.split(".")
1453+
python_exe = f"python{major}.{minor}t.exe"
1454+
else:
1455+
python_exe = "python.exe"
1456+
14281457
if arch == "amd64":
14291458
build_platform = "x64"
14301459
build_directory = "amd64"
@@ -1507,6 +1536,7 @@ def build_cpython(
15071536
platform=build_platform,
15081537
python_version=python_version,
15091538
windows_sdk_version=windows_sdk_version,
1539+
freethreaded=freethreaded,
15101540
)
15111541

15121542
# build-windows.py sets some environment variables which cause the
@@ -1526,7 +1556,7 @@ def build_cpython(
15261556
# test execution. We work around this by invoking the test harness
15271557
# separately for each test.
15281558
instrumented_python = (
1529-
pcbuild_path / build_directory / "instrumented" / "python.exe"
1559+
pcbuild_path / build_directory / "instrumented" / python_exe
15301560
)
15311561

15321562
tests = subprocess.run(
@@ -1572,6 +1602,7 @@ def build_cpython(
15721602
platform=build_platform,
15731603
python_version=python_version,
15741604
windows_sdk_version=windows_sdk_version,
1605+
freethreaded=freethreaded,
15751606
)
15761607
artifact_config = "PGUpdate"
15771608

@@ -1583,6 +1614,7 @@ def build_cpython(
15831614
platform=build_platform,
15841615
python_version=python_version,
15851616
windows_sdk_version=windows_sdk_version,
1617+
freethreaded=freethreaded,
15861618
)
15871619
artifact_config = "Release"
15881620

@@ -1615,6 +1647,9 @@ def build_cpython(
16151647
"--include-venv",
16161648
]
16171649

1650+
if freethreaded:
1651+
args.append("--include-freethreaded")
1652+
16181653
# CPython 3.12 removed distutils.
16191654
if not meets_python_minimum_version(python_version, "3.12"):
16201655
args.append("--include-distutils")
@@ -1639,7 +1674,7 @@ def build_cpython(
16391674
# Install pip and setuptools.
16401675
exec_and_log(
16411676
[
1642-
str(install_dir / "python.exe"),
1677+
str(install_dir / python_exe),
16431678
"-m",
16441679
"pip",
16451680
"install",
@@ -1656,7 +1691,7 @@ def build_cpython(
16561691
if meets_python_maximum_version(python_version, "3.11"):
16571692
exec_and_log(
16581693
[
1659-
str(install_dir / "python.exe"),
1694+
str(install_dir / python_exe),
16601695
"-m",
16611696
"pip",
16621697
"install",
@@ -1691,6 +1726,7 @@ def build_cpython(
16911726
build_directory,
16921727
artifact_config,
16931728
openssl_entry=openssl_entry,
1729+
freethreaded=freethreaded,
16941730
)
16951731

16961732
for ext, init_fn in sorted(builtin_extensions.items()):
@@ -1775,7 +1811,7 @@ def build_cpython(
17751811
}
17761812

17771813
# Collect information from running Python script.
1778-
python_exe = out_dir / "python" / "install" / "python.exe"
1814+
python_exe = out_dir / "python" / "install" / python_exe
17791815
metadata_path = td / "metadata.json"
17801816
env = dict(os.environ)
17811817
env["ROOT"] = str(out_dir / "python")

cpython-windows/generate_metadata.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
).decode("ascii"),
2727
"python_paths": {},
2828
"python_paths_abstract": sysconfig.get_paths(expand=False),
29-
"python_exe": "install/python.exe",
29+
"python_exe": f"install/{os.path.basename(sys.executable)}",
3030
"python_major_minor_version": sysconfig.get_python_version(),
3131
"python_config_vars": {k: str(v) for k, v in sysconfig.get_config_vars().items()},
3232
}

src/validation.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ const PE_ALLOWED_LIBRARIES: &[&str] = &[
126126
"python311.dll",
127127
"python312.dll",
128128
"python313.dll",
129+
"python313t.dll",
129130
"sqlite3.dll",
130131
"tcl86t.dll",
131132
"tk86t.dll",
@@ -2087,6 +2088,7 @@ fn verify_distribution_behavior(dist_path: &Path) -> Result<Vec<String>> {
20872088
.stdout_to_stderr()
20882089
.unchecked()
20892090
.env("TARGET_TRIPLE", &python_json.target_triple)
2091+
.env("BUILD_OPTIONS", &python_json.build_options)
20902092
.run()?;
20912093

20922094
if !output.status.success() {

src/verify_distribution.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,20 @@ def test_ssl(self):
141141

142142
ssl.create_default_context()
143143

144+
@unittest.skipIf(
145+
sys.version_info[:2] < (3, 13),
146+
"Free-threaded builds are only available in 3.13+",
147+
)
148+
def test_gil_disabled(self):
149+
import sysconfig
150+
151+
if "freethreaded" in os.environ.get("BUILD_OPTIONS", "").split("+"):
152+
wanted = 1
153+
else:
154+
wanted = 0
155+
156+
self.assertEqual(sysconfig.get_config_var("Py_GIL_DISABLED"), wanted)
157+
144158
@unittest.skipIf("TCL_LIBRARY" not in os.environ, "TCL_LIBRARY not set")
145159
@unittest.skipIf("DISPLAY" not in os.environ, "DISPLAY not set")
146160
def test_tkinter(self):

0 commit comments

Comments
 (0)