@@ -1209,33 +1209,17 @@ def _process_config_files() -> None:
12091209
12101210
12111211def _write_config_status () -> None :
1212- """Write config.status script and its saved state.
1212+ """Write config.status with saved state embedded after ``exit 0`` .
12131213
1214- Generates:
1215- - config.status.json: serialized substs and defines so config.status
1216- can regenerate output files without re-running the full configure.
1217- - config.status: a shell script that handles --recheck (re-runs configure)
1218- and file regeneration (re-does @VAR@ substitution).
1214+ The shell preamble handles ``--recheck`` and delegates file
1215+ regeneration to ``pyconf.py --config-status``. All SUBST/DEFINES
1216+ data is appended after the ``@CONFIG_STATUS_DATA@`` marker so the
1217+ shell never sees it but Python can read it back.
12191218 """
1220- import json as _json
1221-
1222- # Serialize defines: convert tuples to lists for JSON
1223- serialized_defines = {}
1224- for name , entry in defines .items ():
1225- value , description , quoted = entry
1226- serialized_defines [name ] = [value , description , quoted ]
1227-
1228- state = {
1229- "substs" : dict (substs ),
1230- "defines" : serialized_defines ,
1231- "srcdir" : srcdir ,
1232- }
1233- with open ("config.status.json" , "w" ) as fh :
1234- _json .dump (state , fh , indent = 1 )
1235-
1236- # Write config.status as a shell script
12371219 config_args = substs .get ("CONFIG_ARGS" , "" )
1238- # The script delegates to pyconf._config_status_main() for file generation
1220+ sd = srcdir or "."
1221+ pyconf_path = os .path .join (sd , "Tools/configure/pyconf.py" )
1222+
12391223 script = f"""\
12401224 #!/bin/sh
12411225# Generated by configure. Do not edit.
@@ -1255,14 +1239,31 @@ def _write_config_status() -> None:
12551239 esac
12561240done
12571241
1258- # Regenerate files. If CONFIG_FILES is set in the environment, only
1259- # those files are processed. If positional arguments are given, they
1260- # are treated as the list of files. Otherwise all config files are
1261- # regenerated.
1262- exec python3 { os .path .join (srcdir or "." , "Tools/configure/pyconf.py" )} --config-status "$@"
1242+ # Regenerate files. pyconf.py reads saved data from the end of $0.
1243+ exec python3 { pyconf_path } --config-status "$0" "$@"
1244+
1245+ exit 0
12631246"""
1247+
12641248 with open ("config.status" , "w" ) as fh :
12651249 fh .write (script )
1250+
1251+ # Append data section after exit 0.
1252+ fh .write ("@CONFIG_STATUS_DATA@\n " )
1253+ fh .write ("@SUBST@\n " )
1254+ for key , val in substs .items ():
1255+ lines = val .split ("\n " ) if val else ["" ]
1256+ fh .write (f"{ key } \n { len (lines )} \n " )
1257+ for line in lines :
1258+ fh .write (f"{ line } \n " )
1259+ fh .write ("@DEFINES@\n " )
1260+ for name , entry in defines .items ():
1261+ value , description , quoted = entry
1262+ # Use repr() so ast.literal_eval() restores the exact type
1263+ fh .write (f"{ name } \n { repr (value )} \n { description } \n " )
1264+ fh .write (f"{ repr (quoted )} \n " )
1265+ fh .write ("@END@\n " )
1266+
12661267 import stat
12671268
12681269 current = os .stat ("config.status" ).st_mode
@@ -1271,38 +1272,75 @@ def _write_config_status() -> None:
12711272 )
12721273
12731274
1275+ def _load_config_status_data (path : str ) -> None :
1276+ """Read saved SUBST/DEFINES data from the end of *path*.
1277+
1278+ The file is expected to contain a ``@CONFIG_STATUS_DATA@`` marker
1279+ followed by ``@SUBST@``, key/nlines/value blocks, ``@DEFINES@``,
1280+ key/value/desc/quoted blocks, and ``@END@``.
1281+ """
1282+ import ast
1283+
1284+ global substs , defines , srcdir
1285+
1286+ with open (path ) as fh :
1287+ # Skip to the data marker
1288+ for line in fh :
1289+ if line .rstrip ("\n " ) == "@CONFIG_STATUS_DATA@" :
1290+ break
1291+ else :
1292+ raise RuntimeError (f"no @CONFIG_STATUS_DATA@ marker in { path } " )
1293+
1294+ # Expect @SUBST@
1295+ tag = fh .readline ().rstrip ("\n " )
1296+ assert tag == "@SUBST@" , f"expected @SUBST@, got { tag !r} "
1297+
1298+ substs = {}
1299+ while True :
1300+ key = fh .readline ().rstrip ("\n " )
1301+ if key == "@DEFINES@" :
1302+ break
1303+ nlines = int (fh .readline ().rstrip ("\n " ))
1304+ val_lines = [fh .readline ().rstrip ("\n " ) for _ in range (nlines )]
1305+ substs [key ] = "\n " .join (val_lines )
1306+
1307+ defines = {}
1308+ while True :
1309+ name = fh .readline ().rstrip ("\n " )
1310+ if name == "@END@" :
1311+ break
1312+ value = ast .literal_eval (fh .readline ().rstrip ("\n " ))
1313+ description = fh .readline ().rstrip ("\n " )
1314+ quoted = ast .literal_eval (fh .readline ().rstrip ("\n " ))
1315+ defines [name ] = (value , description , quoted )
1316+
1317+ srcdir = substs .get ("srcdir" , "." )
1318+
1319+
12741320def _config_status_main (argv : list [str ]) -> None :
12751321 """Entry point when config.status invokes pyconf for file regeneration.
12761322
1277- Loads saved state from config.status.json and regenerates the requested
1278- files (or all config files if none are specified) .
1323+ Loads saved state from config.status (embedded after the
1324+ ``@CONFIG_STATUS_DATA@`` marker) and regenerates the requested files .
12791325
12801326 Supports:
12811327 - ``./config.status`` — regenerate all config files and pyconfig.h
12821328 - ``./config.status Makefile.pre`` — regenerate only Makefile.pre
12831329 - ``CONFIG_FILES=Makefile.pre CONFIG_HEADERS= ./config.status``
12841330 """
1285- import json as _json
1286-
12871331 global substs , defines , srcdir
12881332
1289- with open ("config.status.json" ) as fh :
1290- state = _json .load (fh )
1333+ # First positional arg is the path to config.status itself
1334+ cs_path = argv [0 ] if argv else "config.status"
1335+ rest = argv [1 :]
12911336
1292- substs = state ["substs" ]
1293- # Restore defines: convert lists back to tuples
1294- defines = {}
1295- for name , entry in state ["defines" ].items ():
1296- defines [name ] = tuple (entry )
1297- srcdir = state ["srcdir" ]
1337+ _load_config_status_data (cs_path )
12981338
12991339 # Determine which files to process
1340+ file_args = [a for a in rest if not a .startswith ("-" )]
13001341 env_files = os .environ .get ("CONFIG_FILES" )
13011342 env_headers = os .environ .get ("CONFIG_HEADERS" )
13021343
1303- # Positional arguments (non-flag) override the file list
1304- file_args = [a for a in argv if not a .startswith ("-" )]
1305-
13061344 if file_args :
13071345 # Regenerate only the specified files
13081346 requested = set (file_args )
0 commit comments