Skip to content

Commit

Permalink
Fix freethreaded include on Windows (#368)
Browse files Browse the repository at this point in the history
  • Loading branch information
zanieb authored Oct 15, 2024
1 parent ace5f06 commit 78e57bd
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 11 deletions.
56 changes: 46 additions & 10 deletions cpython-windows/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -845,6 +845,7 @@ def run_msbuild(
platform: str,
python_version: str,
windows_sdk_version: str,
freethreaded: bool,
):
args = [
str(msbuild),
Expand All @@ -867,6 +868,9 @@ def run_msbuild(
f"/property:DefaultWindowsSDKVersion={windows_sdk_version}",
]

if freethreaded:
args.append("/property:DisableGil=true")

exec_and_log(args, str(pcbuild_path), os.environ)


Expand Down Expand Up @@ -1118,6 +1122,7 @@ def collect_python_build_artifacts(
arch: str,
config: str,
openssl_entry: str,
freethreaded: bool,
):
"""Collect build artifacts from Python.
Expand Down Expand Up @@ -1243,6 +1248,20 @@ def find_additional_dependencies(project: pathlib.Path):

return set()

if arch == "amd64":
abi_platform = "win_amd64"
elif arch == "win32":
abi_platform = "win32"
else:
raise ValueError("unhandled arch: %s" % arch)

if freethreaded:
abi_tag = ".cp%st-%s" % (python_majmin, abi_platform)
lib_suffix = "t"
else:
abi_tag = ""
lib_suffix = ""

# Copy object files for core sources into their own directory.
core_dir = out_dir / "build" / "core"
core_dir.mkdir(parents=True)
Expand All @@ -1263,12 +1282,12 @@ def find_additional_dependencies(project: pathlib.Path):
exts = ("lib", "exp")

for ext in exts:
source = outputs_path / ("python%s.%s" % (python_majmin, ext))
dest = core_dir / ("python%s.%s" % (python_majmin, ext))
source = outputs_path / ("python%s%s.%s" % (python_majmin, lib_suffix, ext))
dest = core_dir / ("python%s%s.%s" % (python_majmin, lib_suffix, ext))
log("copying %s" % source)
shutil.copyfile(source, dest)

res["core"]["shared_lib"] = "install/python%s.dll" % python_majmin
res["core"]["shared_lib"] = "install/python%s%s.dll" % (python_majmin, lib_suffix)

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

# Copy the extension static library.
ext_static = outputs_path / ("%s.lib" % ext)
dest = dest_dir / ("%s.lib" % ext)
ext_static = outputs_path / ("%s%s.lib" % (ext, abi_tag))
dest = dest_dir / ("%s%s.lib" % (ext, abi_tag))
log("copying static extension %s" % ext_static)
shutil.copyfile(ext_static, dest)

res["extensions"][ext][0]["shared_lib"] = "install/DLLs/%s.pyd" % ext
res["extensions"][ext][0]["shared_lib"] = "install/DLLs/%s%s.pyd" % (
ext,
abi_tag,
)

lib_dir = out_dir / "build" / "lib"
lib_dir.mkdir()
Expand Down Expand Up @@ -1394,6 +1416,7 @@ def build_cpython(
) -> pathlib.Path:
parsed_build_options = set(build_options.split("+"))
pgo = "pgo" in parsed_build_options
freethreaded = "freethreaded" in parsed_build_options

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

if freethreaded:
(major, minor, _) = python_version.split(".")
python_exe = f"python{major}.{minor}t.exe"
else:
python_exe = "python.exe"

if arch == "amd64":
build_platform = "x64"
build_directory = "amd64"
Expand Down Expand Up @@ -1507,6 +1536,7 @@ def build_cpython(
platform=build_platform,
python_version=python_version,
windows_sdk_version=windows_sdk_version,
freethreaded=freethreaded,
)

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

tests = subprocess.run(
Expand Down Expand Up @@ -1572,6 +1602,7 @@ def build_cpython(
platform=build_platform,
python_version=python_version,
windows_sdk_version=windows_sdk_version,
freethreaded=freethreaded,
)
artifact_config = "PGUpdate"

Expand All @@ -1583,6 +1614,7 @@ def build_cpython(
platform=build_platform,
python_version=python_version,
windows_sdk_version=windows_sdk_version,
freethreaded=freethreaded,
)
artifact_config = "Release"

Expand Down Expand Up @@ -1615,6 +1647,9 @@ def build_cpython(
"--include-venv",
]

if freethreaded:
args.append("--include-freethreaded")

# CPython 3.12 removed distutils.
if not meets_python_minimum_version(python_version, "3.12"):
args.append("--include-distutils")
Expand All @@ -1639,7 +1674,7 @@ def build_cpython(
# Install pip and setuptools.
exec_and_log(
[
str(install_dir / "python.exe"),
str(install_dir / python_exe),
"-m",
"pip",
"install",
Expand All @@ -1656,7 +1691,7 @@ def build_cpython(
if meets_python_maximum_version(python_version, "3.11"):
exec_and_log(
[
str(install_dir / "python.exe"),
str(install_dir / python_exe),
"-m",
"pip",
"install",
Expand Down Expand Up @@ -1691,6 +1726,7 @@ def build_cpython(
build_directory,
artifact_config,
openssl_entry=openssl_entry,
freethreaded=freethreaded,
)

for ext, init_fn in sorted(builtin_extensions.items()):
Expand Down Expand Up @@ -1775,7 +1811,7 @@ def build_cpython(
}

# Collect information from running Python script.
python_exe = out_dir / "python" / "install" / "python.exe"
python_exe = out_dir / "python" / "install" / python_exe
metadata_path = td / "metadata.json"
env = dict(os.environ)
env["ROOT"] = str(out_dir / "python")
Expand Down
2 changes: 1 addition & 1 deletion cpython-windows/generate_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
).decode("ascii"),
"python_paths": {},
"python_paths_abstract": sysconfig.get_paths(expand=False),
"python_exe": "install/python.exe",
"python_exe": f"install/{os.path.basename(sys.executable)}",
"python_major_minor_version": sysconfig.get_python_version(),
"python_config_vars": {k: str(v) for k, v in sysconfig.get_config_vars().items()},
}
Expand Down
2 changes: 2 additions & 0 deletions src/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ const PE_ALLOWED_LIBRARIES: &[&str] = &[
"python311.dll",
"python312.dll",
"python313.dll",
"python313t.dll",
"sqlite3.dll",
"tcl86t.dll",
"tk86t.dll",
Expand Down Expand Up @@ -2087,6 +2088,7 @@ fn verify_distribution_behavior(dist_path: &Path) -> Result<Vec<String>> {
.stdout_to_stderr()
.unchecked()
.env("TARGET_TRIPLE", &python_json.target_triple)
.env("BUILD_OPTIONS", &python_json.build_options)
.run()?;

if !output.status.success() {
Expand Down
14 changes: 14 additions & 0 deletions src/verify_distribution.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,20 @@ def test_ssl(self):

ssl.create_default_context()

@unittest.skipIf(
sys.version_info[:2] < (3, 13),
"Free-threaded builds are only available in 3.13+",
)
def test_gil_disabled(self):
import sysconfig

if "freethreaded" in os.environ.get("BUILD_OPTIONS", "").split("+"):
wanted = 1
else:
wanted = 0

self.assertEqual(sysconfig.get_config_var("Py_GIL_DISABLED"), wanted)

@unittest.skipIf("TCL_LIBRARY" not in os.environ, "TCL_LIBRARY not set")
@unittest.skipIf("DISPLAY" not in os.environ, "DISPLAY not set")
def test_tkinter(self):
Expand Down

0 comments on commit 78e57bd

Please sign in to comment.