Skip to content

Commit 8e86c98

Browse files
committed
Fix config.status for awk version.
Rather than re-execing configure, make config.status regen the outputs. Change the Python version to store the data inside the config.status file, rather than using a separate .json file.
1 parent 7337f34 commit 8e86c98

File tree

4 files changed

+496
-56
lines changed

4 files changed

+496
-56
lines changed

Tools/configure/pyconf.py

Lines changed: 82 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1209,33 +1209,17 @@ def _process_config_files() -> None:
12091209

12101210

12111211
def _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
12561240
done
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+
12741320
def _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)

Tools/configure/transpiler/pyconf.awk

Lines changed: 88 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1857,36 +1857,118 @@ function _pyconf_process_config_files( i, inf, outf, outdir, saved_abs_srcdir
18571857
}
18581858
}
18591859

1860-
function _pyconf_write_config_status( outf, config_args, sd) {
1860+
function _pyconf_write_config_status( outf, config_args, sd, k, val, i, cf, cfx, sep) {
18611861
outf = "config.status"
18621862
config_args = SUBST["CONFIG_ARGS"]
18631863
if (config_args == "")
18641864
config_args = _pyconf_config_args
18651865
sd = SUBST["srcdir"]
18661866
if (sd == "")
18671867
sd = "."
1868+
1869+
# Save config file lists into SUBST so they get serialized.
1870+
cf = ""
1871+
sep = ""
1872+
for (i = 1; i <= _pyconf_config_file_count; i++) {
1873+
cf = cf sep CONFIG_FILES[i]
1874+
sep = " "
1875+
}
1876+
SUBST["_config_files"] = cf
1877+
cfx = ""
1878+
sep = ""
1879+
for (i = 1; i <= _pyconf_config_file_x_count; i++) {
1880+
cfx = cfx sep CONFIG_FILES_X[i]
1881+
sep = " "
1882+
}
1883+
SUBST["_config_files_x"] = cfx
1884+
1885+
# Write config.status as a thin shell wrapper.
1886+
# --recheck re-runs configure; everything else delegates to
1887+
# regen_status.awk which reads saved data from after the
1888+
# @CONFIG_STATUS_DATA@ marker at the end of this file.
18681889
printf "#!/bin/sh\n" > outf
18691890
printf "# Generated by configure. Do not edit.\n" >> outf
18701891
printf "#\n" >> outf
1871-
printf "# This script re-runs configure to regenerate output files.\n" >> outf
1892+
printf "# This script can regenerate output files from templates\n" >> outf
1893+
printf "# without re-running the full configure.\n" >> outf
18721894
printf "\n" >> outf
1873-
printf "CONFIG_ARGS=\"%s\"\n", config_args >> outf
1895+
printf "CONFIG_ARGS='%s'\n", config_args >> outf
1896+
printf "SRCDIR='%s'\n", sd >> outf
18741897
printf "\n" >> outf
18751898
printf "for arg in \"$@\"; do\n" >> outf
18761899
printf " case \"$arg\" in\n" >> outf
18771900
printf " --recheck)\n" >> outf
18781901
printf " echo \"running configure with args: $CONFIG_ARGS\"\n" >> outf
1879-
printf " exec %s/configure $CONFIG_ARGS\n", sd >> outf
1902+
printf " exec \"$SRCDIR\"/configure $CONFIG_ARGS\n" >> outf
18801903
printf " ;;\n" >> outf
18811904
printf " esac\n" >> outf
18821905
printf "done\n" >> outf
18831906
printf "\n" >> outf
1884-
printf "# Regenerate all output files by re-running configure.\n" >> outf
1885-
printf "exec %s/configure $CONFIG_ARGS\n", sd >> outf
1907+
# Emit _find_awk: prefer mawk/nawk, avoid gawk crash bug.
1908+
printf "_find_awk() {\n" >> outf
1909+
printf " for _c in mawk nawk awk; do\n" >> outf
1910+
printf " IFS=:\n" >> outf
1911+
printf " for _d in $PATH; do\n" >> outf
1912+
printf " if [ -x \"${_d}/${_c}\" ]; then\n" >> outf
1913+
printf " case $(\"${_d}/${_c}\" --version < /dev/null 2>&1) in\n" >> outf
1914+
printf " *\"GNU Awk\"*) ;;\n" >> outf
1915+
printf " *) echo \"${_d}/${_c}\"; return 0 ;;\n" >> outf
1916+
printf " esac\n" >> outf
1917+
printf " fi\n" >> outf
1918+
printf " done\n" >> outf
1919+
printf " unset IFS\n" >> outf
1920+
printf " done\n" >> outf
1921+
printf " echo awk\n" >> outf
1922+
printf "}\n" >> outf
1923+
printf "_awk=$(_find_awk)\n" >> outf
1924+
printf "\n" >> outf
1925+
printf "exec \"$_awk\" -f \"$SRCDIR\"/Tools/configure/transpiler/regen_status.awk \\\n" >> outf
1926+
printf " -v cs_args=\"$*\" \\\n" >> outf
1927+
printf " -v cs_config_files=\"${CONFIG_FILES-@UNSET@}\" \\\n" >> outf
1928+
printf " -v cs_config_headers=\"${CONFIG_HEADERS-@UNSET@}\" \\\n" >> outf
1929+
printf " -v srcdir=\"$SRCDIR\" \\\n" >> outf
1930+
printf " \"$0\"\n" >> outf
1931+
printf "\n" >> outf
1932+
printf "exit 0\n" >> outf
1933+
1934+
# Data section: the shell never reaches here (exit above).
1935+
# regen_status.awk scans for @CONFIG_STATUS_DATA@ to find this data.
1936+
printf "@CONFIG_STATUS_DATA@\n" >> outf
1937+
printf "@SUBST@\n" >> outf
1938+
for (k in SUBST) {
1939+
val = SUBST[k]
1940+
_cs_write_data_entry(outf, k, val)
1941+
}
1942+
printf "@DEFINES@\n" >> outf
1943+
for (i = 1; i <= _pyconf_defines_order_n; i++) {
1944+
k = _pyconf_defines_order[i]
1945+
if (k in DEFINES) {
1946+
printf "%s\n", k >> outf
1947+
printf "%s\n", DEFINES[k] >> outf
1948+
printf "%s\n", (k in DEFINE_DESC ? DEFINE_DESC[k] : "") >> outf
1949+
printf "%s\n", (DEFINE_QUOTED[k] ? "1" : "0") >> outf
1950+
}
1951+
}
1952+
printf "@END@\n" >> outf
1953+
18861954
close(outf)
18871955
system("chmod +x " _shell_quote(outf))
18881956
}
18891957

1958+
# Write a SUBST entry: key, line_count, value lines
1959+
function _cs_write_data_entry(outf, key, val, n, lines, i) {
1960+
n = split(val, lines, "\n")
1961+
if (n == 0) n = 1
1962+
printf "%s\n%d\n", key, n >> outf
1963+
if (val == "") {
1964+
printf "\n" >> outf
1965+
} else {
1966+
for (i = 1; i <= n; i++)
1967+
printf "%s\n", lines[i] >> outf
1968+
}
1969+
}
1970+
1971+
18901972
function _last_index(s, ch, i, last) {
18911973
last = 0
18921974
for (i = 1; i <= length(s); i++)

0 commit comments

Comments
 (0)