Skip to content

Commit c45253d

Browse files
committed
Fixes for the WASI cross-compile build.
1 parent 3ae2716 commit c45253d

File tree

10 files changed

+127
-72
lines changed

10 files changed

+127
-72
lines changed

Tools/configure/conf_init.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,11 @@ def setup_source_dirs(v):
5959
srcdir_abs = pyconf.abspath(srcdir_raw)
6060
builddir = pyconf.abspath(".")
6161
out_of_tree = srcdir_abs != builddir
62-
v.export("srcdir", "." if not out_of_tree else srcdir_abs)
62+
# Use a relative path for out-of-tree builds. An absolute srcdir would
63+
# break WASI/Emscripten where VPATH is baked into the binary but the
64+
# host path doesn't exist in the guest sandbox.
65+
srcdir_rel = pyconf.relpath(srcdir_abs, builddir) if out_of_tree else "."
66+
v.export("srcdir", srcdir_rel)
6367
v.export("abs_srcdir", srcdir_abs)
6468
v.export("abs_builddir", builddir)
6569
pyconf.srcdir = srcdir_abs

Tools/configure/conf_optimization.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ def setup_pgo_flags(v):
186186
v.export("LLVM_PROFDATA")
187187

188188
v.LLVM_PROFDATA = pyconf.check_prog(
189-
"llvm-profdata", path=v.llvm_path, default=""
189+
"llvm-profdata", path=v.llvm_path, default="''"
190190
)
191191
v.export("LLVM_PROFDATA")
192192
LLVM_PROF_FOUND = (

Tools/configure/conf_output.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,9 @@ def generate_output(v):
4242
"# Edit this file for local setup changes\n",
4343
)
4444

45-
# Use relative paths when building in-place (srcdir == builddir)
46-
# so that generated files like Modules/config.c get a relative path
47-
# comment, matching the autoconf configure behaviour.
48-
srcdir_rel = pyconf.relpath(pyconf.srcdir) if pyconf.srcdir else "."
45+
# Use the srcdir substitution value (e.g. "." for in-place,
46+
# absolute path for out-of-tree) to match autoconf behaviour.
47+
srcdir_rel = v.srcdir or "."
4948
if not pyconf.cmd(
5049
[
5150
"/bin/sh",

Tools/configure/conf_platform.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,13 +91,15 @@ def setup_host_prefix(v):
9191
if not v.host_prefix:
9292
v.export(
9393
"host_prefix",
94-
"/" if v.ac_sys_system == "Emscripten" else "${prefix}",
94+
"/"
95+
if v.ac_sys_system in ("Emscripten", "WASI")
96+
else "${prefix}",
9597
)
9698

9799
if not v.host_exec_prefix:
98100
v.host_exec_prefix = (
99101
v.host_prefix
100-
if v.ac_sys_system == "Emscripten"
102+
if v.ac_sys_system in ("Emscripten", "WASI")
101103
else "${exec_prefix}"
102104
)
103105
v.export("host_exec_prefix")
@@ -431,8 +433,10 @@ def check_declarations(v):
431433
extra_includes=["sys/param.h"],
432434
)
433435

436+
# AC_CHECK_DECLS always defines HAVE_DECL_<NAME> to 0 or 1.
434437
pyconf.check_decl(
435438
"UT_NAMESIZE",
439+
define_name="HAVE_DECL_UT_NAMESIZE",
436440
on_found=_define_ut_namesize,
437441
extra_includes=["utmp.h"],
438442
)
@@ -441,6 +445,7 @@ def check_declarations(v):
441445
if v.ac_cv_libc != "musl":
442446
pyconf.check_decl(
443447
"PR_SET_VMA_ANON_NAME",
448+
define_name="HAVE_DECL_PR_SET_VMA_ANON_NAME",
444449
on_found=_define_pr_set_vma_anon_name,
445450
extra_includes=["linux/prctl.h", "sys/prctl.h"],
446451
)

Tools/configure/conf_syslibs.py

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,14 @@ def detect_dbm(v):
5050
v.have_gdbm_compat = False
5151

5252
# Only search for dbm_open if ndbm.h is present (mirrors AC_CHECK_HEADERS guard)
53+
# Wrap in save_env() to avoid leaking -lgdbm_compat into LIBS
54+
# (matches autoconf's WITH_SAVE_ENV).
5355
ac_cv_search_dbm_open = False
5456
if pyconf.check_header("ndbm.h"):
55-
ac_cv_search_dbm_open = pyconf.search_libs(
56-
"dbm_open", ["ndbm", "gdbm_compat"], required=False
57-
)
57+
with pyconf.save_env():
58+
ac_cv_search_dbm_open = pyconf.search_libs(
59+
"dbm_open", ["ndbm", "gdbm_compat"], required=False
60+
)
5861
if ac_cv_search_dbm_open and ac_cv_search_dbm_open is not False:
5962
if (
6063
"ndbm" in ac_cv_search_dbm_open
@@ -78,15 +81,10 @@ def detect_dbm(v):
7881
)
7982

8083
ac_cv_header_gdbm_dash_ndbm_h = pyconf.check_header("gdbm-ndbm.h")
81-
if ac_cv_header_gdbm_dash_ndbm_h:
82-
pyconf.define(
83-
"HAVE_GDBM_DASH_NDBM_H",
84-
1,
85-
"Define to 1 if you have the <gdbm-ndbm.h> header file.",
86-
)
8784

8885
if ac_cv_header_gdbm_slash_ndbm_h or ac_cv_header_gdbm_dash_ndbm_h:
89-
r = pyconf.search_libs("dbm_open", ["gdbm_compat"], required=False)
86+
with pyconf.save_env():
87+
r = pyconf.search_libs("dbm_open", ["gdbm_compat"], required=False)
9088
if r and r is not False:
9189
v.have_gdbm_compat = True
9290
else:

Tools/configure/conf_wasm.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,10 +113,10 @@ def _setup_emscripten_flags(v):
113113
v.LINKFORSHARED += " -sSTACK_SIZE=5MB"
114114
v.LINKFORSHARED += " -sTEXTDECODER=2"
115115

116-
if v.enable_wasm_dynamic_linking:
116+
if v.enable_wasm_dynamic_linking is True:
117117
v.LINKFORSHARED += " -sMAIN_MODULE"
118118

119-
if v.enable_wasm_pthreads:
119+
if v.enable_wasm_pthreads is True:
120120
v.CFLAGS_NODIST += " -pthread"
121121
v.LDFLAGS_NODIST += " -sUSE_PTHREADS"
122122
v.LINKFORSHARED += " -sPROXY_TO_PTHREAD"
@@ -150,8 +150,7 @@ def _setup_wasi_flags(v):
150150
)
151151
v.LIBS += " -lwasi-emulated-signal -lwasi-emulated-getpid -lwasi-emulated-process-clocks"
152152

153-
if v.enable_wasm_pthreads:
154-
v.CFLAGS += " -target wasm32-wasi-threads -pthread"
153+
if v.enable_wasm_pthreads is True:
155154
v.CFLAGS_NODIST += " -target wasm32-wasi-threads -pthread"
156155
v.LDFLAGS_NODIST += (
157156
" -target wasm32-wasi-threads -pthread"

Tools/configure/pyconf.py

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -75,13 +75,19 @@ def export(
7575
"""Mark a variable for Makefile substitution (AC_SUBST).
7676
7777
If *value* is given, assigns it to this Vars instance first.
78-
Otherwise looks up the value from this Vars instance.
78+
Otherwise looks up the value from this Vars instance (including
79+
environment variables via ``__getattr__``).
7980
The name is recorded in ``self._exports``.
8081
"""
8182
self._exports.add(name)
8283
if value is not self._SENTINEL:
8384
setattr(self, name, value)
8485
val = self.__dict__.get(name)
86+
if val is None:
87+
# Fall back to environment variable (mirrors __getattr__),
88+
# matching autoconf where AC_SUBST reads shell variables
89+
# which include inherited environment.
90+
val = os.environ.get(name, None)
8591
if val is not None:
8692
substs[name] = (
8793
format_yn(val) if isinstance(val, bool) else str(val)
@@ -1000,12 +1006,10 @@ def _populate_module_substs() -> None:
10001006
na = info.get("na", False)
10011007
if na:
10021008
state = "n/a"
1003-
elif not supported and enabled:
1004-
state = "missing"
1005-
elif not supported:
1006-
state = "n/a"
10071009
elif not enabled:
10081010
state = "disabled"
1011+
elif not supported:
1012+
state = "missing"
10091013
else:
10101014
state = "yes"
10111015
# _TRUE="" means "build this module"; only "yes" state builds
@@ -2343,10 +2347,15 @@ def _try(boolean_expr: str) -> bool:
23432347

23442348

23452349
def _header_name_to_define(header: str) -> str:
2346-
"""Convert 'sys/types.h' → 'HAVE_SYS_TYPES_H'."""
2347-
return "HAVE_" + header.upper().replace("/", "_").replace(
2348-
".", "_"
2349-
).replace("-", "_")
2350+
"""Convert 'sys/types.h' → 'HAVE_SYS_TYPES_H'.
2351+
2352+
Dashes are mapped to '_DASH_' to match autoconf's convention where
2353+
'gdbm-ndbm.h' produces HAVE_GDBM_DASH_NDBM_H (distinct from
2354+
'gdbm/ndbm.h' → HAVE_GDBM_NDBM_H).
2355+
"""
2356+
return "HAVE_" + header.upper().replace("-", "_DASH_").replace(
2357+
"/", "_"
2358+
).replace(".", "_")
23502359

23512360

23522361
def _ac_includes_default() -> str:
@@ -2399,9 +2408,9 @@ def check_header(
23992408
f"check_header: invalid header name {header!r} "
24002409
f"(must match [a-z0-9][a-z0-9_./+-]*)"
24012410
)
2402-
cache_key = "ac_cv_header_" + header.replace("/", "_").replace(
2403-
".", "_"
2404-
).replace("-", "_")
2411+
cache_key = "ac_cv_header_" + header.replace("-", "_dash_").replace(
2412+
"/", "_"
2413+
).replace(".", "_")
24052414
define_name = _header_name_to_define(header)
24062415
# Print "checking for <header>..." unless the caller already started one.
24072416
own_checking = not _result_pending
@@ -2606,8 +2615,10 @@ def check_sizeof(
26062615
if output_str.strip().isdigit():
26072616
size = int(output_str.strip())
26082617
if size is None:
2609-
# Cross-compiling or run failed: compile-time binary search
2610-
inc = "#include <stddef.h>\n#include <stdint.h>\n" + extra_includes
2618+
# Cross-compiling or run failed: compile-time binary search.
2619+
# Use AC_INCLUDES_DEFAULT (which includes stdio.h etc.) so that
2620+
# types like fpos_t are visible, matching autoconf behaviour.
2621+
inc = _ac_includes_default() + extra_includes
26112622
size = _compute_int(f"(long int)(sizeof({type_}))", inc)
26122623
if size is None:
26132624
size = default if default is not None else 0
@@ -3173,7 +3184,10 @@ def replace_funcs(funcs: list[str]) -> None:
31733184
missing = substs.get("LIBOBJS", "").split()
31743185
for func in funcs:
31753186
if not check_func(func):
3176-
missing.append(f"{func}.o")
3187+
# Match autoconf's final LIBOBJS fixup: prepend ${LIBOBJDIR}
3188+
# and insert $U before the extension so Makefile.pre.in can
3189+
# locate the replacement source under Python/.
3190+
missing.append(f"${{LIBOBJDIR}}{func}$U.o")
31773191
substs["LIBOBJS"] = " ".join(missing)
31783192

31793193

Tools/configure/test_pyconf.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1485,6 +1485,31 @@ def test_loading_cache_message(self, capsys, monkeypatch, tmp_path):
14851485
assert f"loading cache {cache_path}" in captured.out
14861486

14871487

1488+
# ---------------------------------------------------------------------------
1489+
# replace_funcs / LIBOBJS
1490+
# ---------------------------------------------------------------------------
1491+
1492+
1493+
@pytest.mark.skipif(not HAS_CC, reason="no C compiler available")
1494+
class TestReplaceFuncs:
1495+
def test_missing_func_added_to_libobjs(self, with_cc):
1496+
pyconf.replace_funcs(["__no_such_func_xyz_123__"])
1497+
libobjs = pyconf.substs.get("LIBOBJS", "")
1498+
assert "${LIBOBJDIR}__no_such_func_xyz_123__$U.o" in libobjs
1499+
1500+
def test_existing_func_not_in_libobjs(self, with_cc):
1501+
pyconf.replace_funcs(["printf"])
1502+
assert pyconf.substs.get("LIBOBJS", "") == ""
1503+
1504+
def test_libobjs_format_matches_autoconf(self, with_cc):
1505+
"""LIBOBJS entries must use ${LIBOBJDIR}name$U.o for Makefile.pre.in."""
1506+
pyconf.replace_funcs(["__no_such_a__", "__no_such_b__"])
1507+
entries = pyconf.substs["LIBOBJS"].split()
1508+
for entry in entries:
1509+
assert entry.startswith("${LIBOBJDIR}"), entry
1510+
assert "$U.o" in entry, entry
1511+
1512+
14881513
# ---------------------------------------------------------------------------
14891514

14901515
if __name__ == "__main__":

Tools/configure/transpiler/pyconf.awk

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,10 @@ function pyconf_export(name, value) {
211211
}
212212

213213
function v_export(name) {
214+
# Fall back to ENVIRON if V[name] is not set, matching
215+
# autoconf where AC_SUBST reads inherited environment variables.
216+
if (!(name in V) && name in ENVIRON)
217+
V[name] = ENVIRON[name]
214218
ENV[name] = V[name]
215219
MODIFIED_ENV[name] = 1
216220
}
@@ -562,7 +566,11 @@ function _pyconf_includes_from_list(hdr_list, n, arr, i, result) {
562566

563567
function _pyconf_header_to_define(header, d) {
564568
d = "HAVE_" header
565-
gsub(/[\/\.\+\-]/, "_", d)
569+
# Map "-" to "_DASH_" before other replacements so that
570+
# gdbm-ndbm.h -> HAVE_GDBM_DASH_NDBM_H (distinct from
571+
# gdbm/ndbm.h -> HAVE_GDBM_NDBM_H), matching autoconf.
572+
gsub(/-/, "_DASH_", d)
573+
gsub(/[\/\.\+]/, "_", d)
566574
d = toupper(d)
567575
return d
568576
}
@@ -586,7 +594,8 @@ function _pyconf_ac_includes_default( result) {
586594
function pyconf_check_header(header, prologue, default_inc, define, cache_key, source, cv, rc) {
587595
if (define == "") define = _pyconf_header_to_define(header)
588596
if (cache_key == "") cache_key = "ac_cv_header_" header
589-
gsub(/[\/\.\+\-]/, "_", cache_key)
597+
gsub(/-/, "_dash_", cache_key)
598+
gsub(/[\/\.\+]/, "_", cache_key)
590599

591600
if (cache_key in CACHE) {
592601
rc = (CACHE[cache_key] == "yes")
@@ -688,9 +697,9 @@ function pyconf_replace_funcs(funcs, n, i) {
688697
if (funcs[i] != "") {
689698
if (!pyconf_check_func(funcs[i])) {
690699
if (SUBST["LIBOBJS"] != "")
691-
SUBST["LIBOBJS"] = SUBST["LIBOBJS"] " " funcs[i] ".o"
700+
SUBST["LIBOBJS"] = SUBST["LIBOBJS"] " ${LIBOBJDIR}" funcs[i] "$U.o"
692701
else
693-
SUBST["LIBOBJS"] = funcs[i] ".o"
702+
SUBST["LIBOBJS"] = "${LIBOBJDIR}" funcs[i] "$U.o"
694703
}
695704
}
696705
}
@@ -1488,16 +1497,12 @@ function pyconf_stdlib_module(name, supported, enabled, cflags, ldflags, has_cfl
14881497
}
14891498
if (prev_na)
14901499
state = "n/a"
1491-
else if (supported == "yes" && enabled == "yes")
1492-
state = "yes"
1493-
else if (supported != "yes" && enabled == "yes")
1494-
state = "missing"
1495-
else if (supported != "yes")
1496-
state = "n/a"
14971500
else if (enabled != "yes")
14981501
state = "disabled"
1502+
else if (supported != "yes")
1503+
state = "missing"
14991504
else
1500-
state = "n/a"
1505+
state = "yes"
15011506
SUBST[key "_STATE"] = state
15021507
SUBST[key "_TRUE"] = (state == "yes") ? "" : "#"
15031508
if (has_cflags == "yes")

0 commit comments

Comments
 (0)