Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 42 additions & 28 deletions cpython-windows/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -674,17 +674,17 @@ def hack_project_files(
pass

# Our custom OpenSSL build has applink.c in a different location from the
# binary OpenSSL distribution. This is no longer relevant for 3.12+ per
# https://github.com/python/cpython/pull/131839, so we allow it to fail.swe
try:
# binary OpenSSL distribution.
# Starting with 3.12 CPython on Windows is built without uplink support
# so this modification is not needed.
# https://github.com/python/cpython/pull/131839
if meets_python_maximum_version(python_version, "3.11"):
ssl_proj = pcbuild_path / "_ssl.vcxproj"
static_replace_in_file(
ssl_proj,
rb'<ClCompile Include="$(opensslIncludeDir)\applink.c">',
rb'<ClCompile Include="$(opensslIncludeDir)\openssl\applink.c">',
)
except NoSearchStringError:
pass

# Python 3.12+ uses the the pre-built tk-windows-bin 8.6.12 which doesn't
# have a standalone zlib DLL, so we remove references to it. For Python
Expand Down Expand Up @@ -791,6 +791,7 @@ def build_openssl_for_arch(
build_root: pathlib.Path,
*,
jom_archive,
with_uplink: bool = False,
):
nasm_version = DOWNLOADS["nasm-windows-bin"]["version"]

Expand All @@ -810,14 +811,15 @@ def build_openssl_for_arch(

source_root = build_root / ("openssl-%s" % openssl_version)

# uplink.c tries to find the OPENSSL_Applink function exported from the current
# executable. However, it is exported from _ssl[_d].pyd in shared builds. So
# update its sounce to look for it from there.
static_replace_in_file(
source_root / "ms" / "uplink.c",
b"((h = GetModuleHandle(NULL)) == NULL)",
b'((h = GetModuleHandleA("_ssl.pyd")) == NULL) if ((h = GetModuleHandleA("_ssl_d.pyd")) == NULL) if ((h = GetModuleHandle(NULL)) == NULL)',
)
if with_uplink:
# uplink.c tries to find the OPENSSL_Applink function exported from the
# current executable. However, it is exported from _ssl[_d].pyd in shared
# builds. So update its source to look for it from there.
static_replace_in_file(
source_root / "ms" / "uplink.c",
b"((h = GetModuleHandle(NULL)) == NULL)",
b'((h = GetModuleHandleA("_ssl.pyd")) == NULL) if ((h = GetModuleHandleA("_ssl_d.pyd")) == NULL) if ((h = GetModuleHandle(NULL)) == NULL)',
)

if arch == "x86":
configure = "VC-WIN32"
Expand All @@ -831,26 +833,27 @@ def build_openssl_for_arch(
else:
raise Exception("unhandled architecture: %s" % arch)

# The official CPython OpenSSL builds hack ms/uplink.c to change the
# ``GetModuleHandle(NULL)`` invocation to load things from _ssl.pyd
# instead. But since we statically link the _ssl extension, this hackery
# is not required.

# Set DESTDIR to affect install location.
dest_dir = build_root / "install"
env["DESTDIR"] = str(dest_dir)
install_root = dest_dir / prefix

configure_args = [
str(perl_path),
"Configure",
configure,
"no-idea",
"no-mdc2",
"no-tests",
"--prefix=/%s" % prefix,
]
if with_uplink:
log("building OpenSSL with uplink support for Python <3.12")
else:
configure_args.append("no-uplink")

exec_and_log(
[
str(perl_path),
"Configure",
configure,
"no-idea",
"no-mdc2",
"no-tests",
"--prefix=/%s" % prefix,
],
configure_args,
source_root,
{
**env,
Expand Down Expand Up @@ -889,6 +892,7 @@ def build_openssl(
perl_path: pathlib.Path,
arch: str,
dest_archive: pathlib.Path,
with_uplink: bool = False,
):
"""Build OpenSSL from sources using the Perl executable specified."""

Expand Down Expand Up @@ -916,6 +920,7 @@ def build_openssl(
nasm_archive,
root_32,
jom_archive=jom_archive,
with_uplink=with_uplink,
)
elif arch == "amd64":
root_64.mkdir()
Expand All @@ -927,6 +932,7 @@ def build_openssl(
nasm_archive,
root_64,
jom_archive=jom_archive,
with_uplink=with_uplink,
)
elif arch == "arm64":
root_arm64.mkdir()
Expand All @@ -938,6 +944,7 @@ def build_openssl(
nasm_archive,
root_arm64,
jom_archive=jom_archive,
with_uplink=with_uplink,
)
else:
raise Exception("unhandled architecture: %s" % arch)
Expand Down Expand Up @@ -1987,8 +1994,14 @@ def main() -> None:
else:
openssl_entry = "openssl-3.5"

openssl_with_uplink = args.python in ["cpython-3.10", "cpython-3.11"]
if openssl_with_uplink:
openssl_build_options = f"{build_options}-uplink"
else:
openssl_build_options = f"{build_options}-no-uplink"

openssl_archive = BUILD / (
"%s-%s-%s.tar" % (openssl_entry, target_triple, build_options)
"%s-%s-%s.tar" % (openssl_entry, target_triple, openssl_build_options)
)
if not openssl_archive.exists():
perl_path = fetch_strawberry_perl() / "perl" / "bin" / "perl.exe"
Expand All @@ -1998,6 +2011,7 @@ def main() -> None:
perl_path,
arch,
dest_archive=openssl_archive,
with_uplink=openssl_with_uplink,
)

libffi_archive = BUILD / ("libffi-%s-%s.tar" % (target_triple, build_options))
Expand Down
20 changes: 20 additions & 0 deletions pythonbuild/disttests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,26 @@ def test_ssl(self):

ssl.create_default_context()

@unittest.skipIf(os.name != "nt", "Windows-specific OpenSSL uplink regression")
def test_ssl_with_keylogfile(self):
# Validate that a SSLContext can be created when SSLKEYLOGFILE is set
# https://github.com/astral-sh/python-build-standalone/issues/640
import ssl

with tempfile.TemporaryDirectory(ignore_cleanup_errors=True) as td:
keylog_path = str(Path(td) / "sslkeylog.log")
original_keylog_path = os.environ.get("SSLKEYLOGFILE")
os.environ["SSLKEYLOGFILE"] = keylog_path
try:
context = ssl.create_default_context()
self.assertEqual(context.keylog_filename, keylog_path)
del context
finally:
if original_keylog_path is None:
os.environ.pop("SSLKEYLOGFILE", None)
else:
os.environ["SSLKEYLOGFILE"] = original_keylog_path

@unittest.skipIf(
sys.version_info[:2] < (3, 13),
"Free-threaded builds are only available in 3.13+",
Expand Down
Loading