@@ -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(
24922572def 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
25852679def 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:
26052802def 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