@@ -175,6 +175,12 @@ else
175175 patch -p1 -i ${ROOT} /patch-ctypes-callproc-legacy.patch
176176fi
177177
178+ if [ -n " ${PYTHON_MEETS_MINIMUM_VERSION_3_13} " ]; then
179+ patch -p1 -i ${ROOT} /patch-cpython-relocatable-sysconfig-3.13.patch
180+ elif [ -n " ${PYTHON_MEETS_MINIMUM_VERSION_3_10} " ]; then
181+ patch -p1 -i ${ROOT} /patch-cpython-relocatable-sysconfig-3.10.patch
182+ fi
183+
178184# On Windows, CPython looks for the Tcl/Tk libraries relative to the base prefix,
179185# which we want. But on Unix, it doesn't. This patch applies similar behavior on Unix,
180186# thereby ensuring that the Tcl/Tk libraries are found in the correct location.
883889# that a) it works on as many machines as possible b) doesn't leak details
884890# about the build environment, which is non-portable.
885891cat > ${ROOT} /hack_sysconfig.py << EOF
892+ import ast
886893import json
887894import os
888895import sys
@@ -894,7 +901,7 @@ FREETHREADED = sysconfig.get_config_var("Py_GIL_DISABLED")
894901MAJMIN = ".".join([str(sys.version_info[0]), str(sys.version_info[1])])
895902LIB_SUFFIX = "t" if FREETHREADED else ""
896903PYTHON_CONFIG = os.path.join(ROOT, "install", "bin", "python%s-config" % MAJMIN)
897- PLATFORM_CONFIG = os.path.join(ROOT, sysconfig.get_config_var("LIBPL").lstrip("/") )
904+ PLATFORM_CONFIG = sysconfig.get_config_var("LIBPL")
898905MAKEFILE = os.path.join(PLATFORM_CONFIG, "Makefile")
899906SYSCONFIGDATA = os.path.join(
900907 ROOT,
@@ -925,51 +932,56 @@ def replace_in_all(search, replace):
925932 replace_in_file(SYSCONFIGDATA, search, replace)
926933
927934
928- def replace_in_sysconfigdata(search, replace, keys):
929- """Replace a string in the sysconfigdata file for select keys."""
930- with open(SYSCONFIGDATA, "rb") as fh:
931- data = fh.read()
935+ def _find_build_time_vars_assign(module):
936+ """Return the Assign node for 'build_time_vars = {...}' or None."""
937+ for node in module.body:
938+ if isinstance(node, ast.Assign):
939+ for target in node.targets:
940+ if isinstance(target, ast.Name) and target.id == "build_time_vars":
941+ return node
942+ return None
932943
933- globals_dict = {}
934- locals_dict = {}
935- exec(data, globals_dict, locals_dict)
936- build_time_vars = locals_dict['build_time_vars']
937944
938- for key in keys:
939- if key in build_time_vars:
940- build_time_vars[key] = build_time_vars[key].replace(search, replace)
945+ def _patch_build_time_vars(assign_node, keys, search, replace):
946+ """Patch plain string values for the given keys inside the dict literal."""
947+ if not isinstance(assign_node.value, ast.Dict):
948+ return
941949
942- with open(SYSCONFIGDATA, "wb") as fh:
943- fh.write(b'# system configuration generated and used by the sysconfig module\n')
944- fh.write(('build_time_vars = %s' % json.dumps(build_time_vars, indent=4, sort_keys=True)).encode("utf-8"))
945- fh.close()
950+ d: ast.Dict = assign_node.value
951+ for key_node, val_node in zip(d.keys, d.values):
952+ k = key_node.value
953+ if k in keys and isinstance(val_node, ast.Constant) and isinstance(val_node.value, str):
954+ val_node.value = val_node.value.replace(search, replace)
946955
947956
948- def format_sysconfigdata():
949- """Reformat the sysconfigdata file to avoid implicit string concatenations.
957+ def replace_in_sysconfigdata(search, replace, keys):
958+ """Replace a string in the sysconfigdata file for select keys."""
959+ with open(SYSCONFIGDATA, "r", encoding="utf-8") as fh:
960+ source = fh.read()
950961
951- In some Python versions, the sysconfigdata file contains implicit string
952- concatenations that extend over multiple lines, which make string replacement
953- much harder. This function reformats the file to avoid this issue.
962+ module = ast.parse(source)
963+ assign = _find_build_time_vars_assign(module)
964+ if assign is None:
965+ # Nothing to do if build_time_vars isn't present.
966+ return
954967
955- See: https://github.com/python/cpython/blob/a03efb533a58fd13fb0cc7f4a5c02c8406a407bd/Mac/BuildScript/build-installer.py#L1360C1-L1385C15.
956- """
957- with open(SYSCONFIGDATA, "rb") as fh:
958- data = fh.read()
968+ # Patch the dict
969+ _patch_build_time_vars(assign, keys, search, replace)
959970
960- globals_dict = {}
961- locals_dict = {}
962- exec(data, globals_dict, locals_dict)
963- build_time_vars = locals_dict['build_time_vars']
971+ # Compute the textual prefix up to (but not including) the start of build_time_vars.
972+ lines = source.splitlines()
973+ start_line = assign.lineno # 1-based
974+ head_text = "\n".join(lines[: start_line - 1])
964975
965- with open(SYSCONFIGDATA, "wb") as fh:
966- fh.write(b'# system configuration generated and used by the sysconfig module\n')
967- fh.write(('build_time_vars = %s' % json.dumps(build_time_vars, indent=4, sort_keys=True)).encode("utf-8"))
968- fh.close()
976+ # Unparse the patched assignment
977+ assign_src = ast.unparse(assign)
969978
979+ # Rewrite: preserved head + patched assignment + trailing newline
980+ new_source = head_text + ("\n" if head_text and not head_text.endswith("\n") else "")
981+ new_source += assign_src + "\n"
970982
971- # Format sysconfig to ensure that string replacements take effect.
972- format_sysconfigdata( )
983+ with open(SYSCONFIGDATA, "w", encoding="utf-8") as fh:
984+ fh.write(new_source )
973985
974986# Remove ` -Werror=unguarded-availability-new` from ` CFLAGS` and ` CPPFLAGS` .
975987# These flags are passed along when building extension modules. In that context,
@@ -1063,7 +1075,7 @@ metadata = {
10631075 "python_paths_abstract": sysconfig.get_paths(expand=False),
10641076 "python_exe": "install/bin/python%s%s" % (sysconfig.get_python_version(), sys.abiflags),
10651077 "python_major_minor_version": sysconfig.get_python_version(),
1066- "python_stdlib_platform_config": sysconfig.get_config_var("LIBPL").lstrip("/") ,
1078+ "python_stdlib_platform_config": sysconfig.get_config_var("LIBPL"),
10671079 "python_config_vars": {k: str(v) for k, v in sysconfig.get_config_vars().items()},
10681080}
10691081
0 commit comments