Skip to content

Commit 8a348c5

Browse files
committed
More cross-compile fixes, float word order.
1 parent 7af20c1 commit 8a348c5

File tree

3 files changed

+588
-89
lines changed

3 files changed

+588
-89
lines changed

Tools/configure/pyconf.py

Lines changed: 274 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -2214,6 +2214,76 @@ def run_program_output(
22142214
return ""
22152215

22162216

2217+
# ---------------------------------------------------------------------------
2218+
# Compile-time integer computation (for cross-compilation)
2219+
# ---------------------------------------------------------------------------
2220+
2221+
2222+
def _compute_int(expr: str, includes: str = "") -> int | None:
2223+
"""Determine the compile-time value of a C integer expression.
2224+
2225+
Uses the same technique as autoconf's AC_COMPUTE_INT / _AC_COMPUTE_INT:
2226+
a binary search using compile-time assertions of the form
2227+
``static int test_array [1 - 2 * !(EXPR)];`` which fail to compile
2228+
when the expression is false (negative-size array).
2229+
2230+
Works even when cross-compiling because it only needs to *compile*,
2231+
never to *run* the test program.
2232+
2233+
Returns the integer value, or None if it could not be determined.
2234+
"""
2235+
def _try(boolean_expr: str) -> bool:
2236+
src = (
2237+
f"{includes}\n"
2238+
"int main(void) {\n"
2239+
f" static int test_array [1 - 2 * !({boolean_expr})];\n"
2240+
" test_array[0] = 0;\n"
2241+
" return test_array[0];\n"
2242+
"}\n"
2243+
)
2244+
return _compile_test(src)
2245+
2246+
# Step 1: determine sign
2247+
if _try(f"({expr}) >= 0"):
2248+
# Non-negative: search upward from 0
2249+
ac_lo = 0
2250+
ac_mid = 0
2251+
while True:
2252+
if _try(f"({expr}) <= {ac_mid}"):
2253+
ac_hi = ac_mid
2254+
break
2255+
ac_lo = ac_mid + 1
2256+
if ac_lo <= ac_mid:
2257+
# Overflow
2258+
return None
2259+
ac_mid = 2 * ac_mid + 1
2260+
elif _try(f"({expr}) < 0"):
2261+
# Negative: search downward from -1
2262+
ac_hi = -1
2263+
ac_mid = -1
2264+
while True:
2265+
if _try(f"({expr}) >= {ac_mid}"):
2266+
ac_lo = ac_mid
2267+
break
2268+
ac_hi = ac_mid - 1
2269+
if ac_mid <= ac_hi:
2270+
# Overflow
2271+
return None
2272+
ac_mid = 2 * ac_mid
2273+
else:
2274+
return None
2275+
2276+
# Step 2: binary search between lo and hi
2277+
while ac_lo != ac_hi:
2278+
ac_mid = (ac_hi - ac_lo) // 2 + ac_lo
2279+
if _try(f"({expr}) <= {ac_mid}"):
2280+
ac_hi = ac_mid
2281+
else:
2282+
ac_lo = ac_mid + 1
2283+
2284+
return ac_lo
2285+
2286+
22172287
# ---------------------------------------------------------------------------
22182288
# Header / struct / member probes
22192289
# ---------------------------------------------------------------------------
@@ -2446,10 +2516,11 @@ def check_sizeof(
24462516
) -> int:
24472517
"""AC_CHECK_SIZEOF — determine sizeof(*type_*).
24482518
2449-
Compiles and runs a tiny program that prints the size. On failure or when
2450-
cross-compiling, returns *default* (or 0).
2519+
When not cross-compiling, compiles and runs a program that prints the
2520+
size. When cross-compiling (or when the run fails), uses a compile-time
2521+
binary search matching autoconf's AC_COMPUTE_INT technique.
2522+
Falls back to *default* (or 0) only if both methods fail.
24512523
Defines SIZEOF_<TYPE> as a side-effect.
2452-
*headers* lists extra headers to include in the test program.
24532524
"""
24542525
cache_key = "ac_cv_sizeof_" + _type_to_define(type_).lower()
24552526
own_checking = not _result_pending
@@ -2468,18 +2539,27 @@ def check_sizeof(
24682539
extra_includes = "".join(
24692540
f"#include <{h}>\n" for h in (headers or includes or [])
24702541
)
2471-
src = (
2472-
"#include <stdio.h>\n"
2473-
"#include <stddef.h>\n"
2474-
"#include <stdint.h>\n" + extra_includes + "int main(void) {\n"
2475-
f' printf("%zu\\n", sizeof({type_}));\n'
2476-
" return 0;\n"
2477-
"}\n"
2478-
)
2479-
output_str = run_program_output(src)
2480-
if output_str.strip().isdigit():
2481-
size = int(output_str.strip())
2482-
else:
2542+
size = None
2543+
if not cross_compiling:
2544+
src = (
2545+
"#include <stdio.h>\n"
2546+
"#include <stddef.h>\n"
2547+
"#include <stdint.h>\n" + extra_includes + "int main(void) {\n"
2548+
f' printf("%zu\\n", sizeof({type_}));\n'
2549+
" return 0;\n"
2550+
"}\n"
2551+
)
2552+
output_str = run_program_output(src)
2553+
if output_str.strip().isdigit():
2554+
size = int(output_str.strip())
2555+
if size is None:
2556+
# Cross-compiling or run failed: compile-time binary search
2557+
inc = (
2558+
"#include <stddef.h>\n"
2559+
"#include <stdint.h>\n" + extra_includes
2560+
)
2561+
size = _compute_int(f"(long int)(sizeof({type_}))", inc)
2562+
if size is None:
24832563
size = default if default is not None else 0
24842564
cache[cache_key] = str(size)
24852565
define_name = "SIZEOF_" + _type_to_define(type_)
@@ -2492,6 +2572,9 @@ def check_sizeof(
24922572
def check_alignof(type_: str) -> int:
24932573
"""AC_CHECK_ALIGNOF — determine alignment of *type_*.
24942574
2575+
When not cross-compiling, runs a program. When cross-compiling, uses
2576+
the compile-time binary search (offsetof on a struct with a leading
2577+
char member, matching autoconf's AC_CHECK_ALIGNOF).
24952578
Returns alignment as int; defines ALIGNOF_<TYPE> as a side-effect.
24962579
"""
24972580
cache_key = "ac_cv_alignof_" + _type_to_define(type_).lower()
@@ -2510,18 +2593,29 @@ def check_alignof(type_: str) -> int:
25102593
if own_checking:
25112594
result(str(alignment))
25122595
return alignment
2513-
src = (
2514-
"#include <stdio.h>\n"
2515-
"#include <stddef.h>\n"
2516-
"int main(void) {\n"
2517-
f' printf("%zu\\n", _Alignof({type_}));\n'
2518-
" return 0;\n"
2519-
"}\n"
2520-
)
2521-
output_str = run_program_output(src)
2522-
if output_str.strip().isdigit():
2523-
alignment = int(output_str.strip())
2524-
else:
2596+
alignment = None
2597+
if not cross_compiling:
2598+
src = (
2599+
"#include <stdio.h>\n"
2600+
"#include <stddef.h>\n"
2601+
"int main(void) {\n"
2602+
f' printf("%zu\\n", _Alignof({type_}));\n'
2603+
" return 0;\n"
2604+
"}\n"
2605+
)
2606+
output_str = run_program_output(src)
2607+
if output_str.strip().isdigit():
2608+
alignment = int(output_str.strip())
2609+
if alignment is None:
2610+
# Cross-compiling or run failed: compile-time binary search using
2611+
# offsetof(struct{char c; TYPE x;}, x), matching autoconf.
2612+
inc = "#include <stddef.h>\n"
2613+
expr = (
2614+
f"(long int)(offsetof("
2615+
f"struct {{ char c; {type_} x; }}, x))"
2616+
)
2617+
alignment = _compute_int(expr, inc)
2618+
if alignment is None:
25252619
alignment = 0
25262620
cache[cache_key] = str(alignment)
25272621
define_name = "ALIGNOF_" + _type_to_define(type_)
@@ -2583,18 +2677,121 @@ def check_c_const() -> None:
25832677

25842678

25852679
def check_c_bigendian() -> None:
2586-
"""AC_C_BIGENDIAN — detect byte order; defines WORDS_BIGENDIAN if big-endian."""
2587-
src = (
2588-
"#include <stdio.h>\n"
2589-
"int main(void) {\n"
2590-
" unsigned int x = 1;\n"
2591-
" unsigned char *p = (unsigned char *)&x;\n"
2592-
' printf("%d\\n", p[0] == 0 ? 1 : 0);\n'
2593-
" return 0;\n"
2594-
"}\n"
2595-
)
2596-
output_str = run_program_output(src).strip()
2597-
if output_str == "1":
2680+
"""AC_C_BIGENDIAN — detect byte order.
2681+
2682+
Matches autoconf's multi-stage detection:
2683+
1. Check sys/param.h BYTE_ORDER macros (compile-only, cross-safe).
2684+
2. Check limits.h _BIG_ENDIAN/_LITTLE_ENDIAN (compile-only, cross-safe).
2685+
3. When cross-compiling: link a program containing marker strings and
2686+
grep the binary for 'BIGenDianSyS' / 'LiTTleEnDian'.
2687+
4. When not cross-compiling: run a test program.
2688+
2689+
Defines WORDS_BIGENDIAN if big-endian, AC_APPLE_UNIVERSAL_BUILD if
2690+
universal.
2691+
"""
2692+
result_val = "unknown"
2693+
2694+
# Stage 1: sys/param.h BYTE_ORDER macros
2695+
if result_val == "unknown":
2696+
src_has_macros = (
2697+
"#include <sys/types.h>\n"
2698+
"#include <sys/param.h>\n"
2699+
"int main(void) {\n"
2700+
"#if ! (defined BYTE_ORDER && defined BIG_ENDIAN \\\n"
2701+
" && defined LITTLE_ENDIAN && BYTE_ORDER \\\n"
2702+
" && BIG_ENDIAN && LITTLE_ENDIAN)\n"
2703+
" bogus endian macros\n"
2704+
"#endif\n"
2705+
" return 0;\n"
2706+
"}\n"
2707+
)
2708+
if _compile_test(src_has_macros):
2709+
src_is_big = (
2710+
"#include <sys/types.h>\n"
2711+
"#include <sys/param.h>\n"
2712+
"int main(void) {\n"
2713+
"#if BYTE_ORDER != BIG_ENDIAN\n"
2714+
" not big endian\n"
2715+
"#endif\n"
2716+
" return 0;\n"
2717+
"}\n"
2718+
)
2719+
result_val = "yes" if _compile_test(src_is_big) else "no"
2720+
2721+
# Stage 2: limits.h _LITTLE_ENDIAN / _BIG_ENDIAN (e.g. Solaris)
2722+
if result_val == "unknown":
2723+
src_has_macros = (
2724+
"#include <limits.h>\n"
2725+
"int main(void) {\n"
2726+
"#if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN)\n"
2727+
" bogus endian macros\n"
2728+
"#endif\n"
2729+
" return 0;\n"
2730+
"}\n"
2731+
)
2732+
if _compile_test(src_has_macros):
2733+
src_is_big = (
2734+
"#include <limits.h>\n"
2735+
"int main(void) {\n"
2736+
"#ifndef _BIG_ENDIAN\n"
2737+
" not big endian\n"
2738+
"#endif\n"
2739+
" return 0;\n"
2740+
"}\n"
2741+
)
2742+
result_val = "yes" if _compile_test(src_is_big) else "no"
2743+
2744+
# Stage 3/4: runtime test or object-file grep
2745+
if result_val == "unknown":
2746+
if cross_compiling:
2747+
# Link a program with marker strings, grep the binary.
2748+
marker_src = (
2749+
"unsigned short int ascii_mm[] =\n"
2750+
" { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 };\n"
2751+
"unsigned short int ascii_ii[] =\n"
2752+
" { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 };\n"
2753+
"int use_ascii(int i) {\n"
2754+
" return ascii_mm[i] + ascii_ii[i];\n"
2755+
"}\n"
2756+
"int main(int argc, char **argv) {\n"
2757+
" char *p = argv[0];\n"
2758+
" ascii_mm[1] = *p++; ascii_ii[1] = *p++;\n"
2759+
" return use_ascii(argc);\n"
2760+
"}\n"
2761+
)
2762+
with tempfile.TemporaryDirectory() as tmp:
2763+
src_path = os.path.join(tmp, "conftest.c")
2764+
exe_path = os.path.join(tmp, "conftest")
2765+
with open(src_path, "w") as f:
2766+
f.write(_confdefs_preamble() + marker_src)
2767+
if _run_cc(src_path, exe_path):
2768+
try:
2769+
data = open(exe_path, "rb").read()
2770+
except OSError:
2771+
data = b""
2772+
has_big = b"BIGenDianSyS" in data
2773+
has_little = b"LiTTleEnDian" in data
2774+
if has_big and not has_little:
2775+
result_val = "yes"
2776+
elif has_little and not has_big:
2777+
result_val = "no"
2778+
else:
2779+
src = (
2780+
"#include <stdio.h>\n"
2781+
"int main(void) {\n"
2782+
" unsigned int x = 1;\n"
2783+
" unsigned char *p = (unsigned char *)&x;\n"
2784+
' printf("%d\\n", p[0] == 0 ? 1 : 0);\n'
2785+
" return 0;\n"
2786+
"}\n"
2787+
)
2788+
output_str = run_program_output(src).strip()
2789+
if output_str == "1":
2790+
result_val = "yes"
2791+
elif output_str == "0":
2792+
result_val = "no"
2793+
2794+
if result_val == "yes":
25982795
define(
25992796
"WORDS_BIGENDIAN",
26002797
1,
@@ -2605,29 +2802,47 @@ def check_c_bigendian() -> None:
26052802
def ax_c_float_words_bigendian(
26062803
on_big: Any = None, on_little: Any = None, on_unknown: Any = None
26072804
) -> None:
2608-
"""Check float word ordering; calls on_big/on_little/on_unknown callbacks."""
2805+
"""Check float word ordering; calls on_big/on_little/on_unknown callbacks.
2806+
2807+
Uses the same compile-and-grep technique as the autoconf
2808+
AX_C_FLOAT_WORDS_BIGENDIAN macro: the magic constant
2809+
9.090423496703681e+223 encodes as bytes containing 'noonsees' when
2810+
float words are big-endian and 'seesnoon' when little-endian.
2811+
We compile (link) the test program and search the resulting object
2812+
file for these marker strings, which works even when cross-compiling.
2813+
"""
26092814
src = (
2610-
"#include <stdio.h>\n"
2611-
"int main(void) {\n"
2612-
" double d = 1.0;\n"
2613-
" unsigned char *p = (unsigned char *)&d;\n"
2614-
" /* IEEE 754 double 1.0 = 3FF0000000000000 hex.\n"
2615-
" * big-endian: p[0]==0x3f (MSB first)\n"
2616-
" * little-endian: p[7]==0x3f (LSB first, MSB last)\n"
2617-
" */\n"
2618-
' if (p[0] == 0x3f) { printf("big\\n"); }\n'
2619-
' else if (p[7] == 0x3f) { printf("little\\n"); }\n'
2620-
' else { printf("unknown\\n"); }\n'
2621-
" return 0;\n"
2815+
"#include <stdlib.h>\n"
2816+
"static double m[] = {9.090423496703681e+223, 0.0};\n"
2817+
"int main(int argc, char *argv[]) {\n"
2818+
" m[atoi(argv[1])] += atof(argv[2]);\n"
2819+
" return m[atoi(argv[3])] > 0.0;\n"
26222820
"}\n"
26232821
)
2624-
output_str = run_program_output(src).strip()
2625-
if output_str == "big" and callable(on_big):
2626-
on_big()
2627-
elif output_str == "little" and callable(on_little):
2628-
on_little()
2629-
elif callable(on_unknown):
2630-
on_unknown()
2822+
with tempfile.TemporaryDirectory() as tmp:
2823+
src_path = os.path.join(tmp, "conftest.c")
2824+
exe_path = os.path.join(tmp, "conftest")
2825+
with open(src_path, "w") as f:
2826+
f.write(src)
2827+
if not _run_cc(src_path, exe_path):
2828+
if callable(on_unknown):
2829+
on_unknown()
2830+
return
2831+
# Read the linked binary and look for the marker strings.
2832+
try:
2833+
data = open(exe_path, "rb").read()
2834+
except OSError:
2835+
if callable(on_unknown):
2836+
on_unknown()
2837+
return
2838+
has_big = b"noonsees" in data
2839+
has_little = b"seesnoon" in data
2840+
if has_big and not has_little and callable(on_big):
2841+
on_big()
2842+
elif has_little and not has_big and callable(on_little):
2843+
on_little()
2844+
elif callable(on_unknown):
2845+
on_unknown()
26312846

26322847

26332848
# ---------------------------------------------------------------------------

0 commit comments

Comments
 (0)