scripts: kconfig: merge_config.sh: refactor from shell/sed/grep to awk

merge_config.sh shell/sed/grep loop scales poorly and is slow.
With Yocto genericarm64 kernel and around 190 config fragments
the script takes more than 20 minutes to run on a fast build machine.
Re-implementation with awk does the same job in 10 seconds.
Using awk since it is likely available in the build environments
and using perl, python etc would introduce more complex runtime
dependencies. awk is good enough and lot better than shell/sed/grep.

Output stays the same but changed execution time means that
parallel job output may be ordered differently.

Signed-off-by: Anders Roxell <anders.roxell@linaro.org>
Signed-off-by: Mikko Rapeli <mikko.rapeli@linaro.org>
Link: https://patch.msgid.link/20260122105751.2186609-1-mikko.rapeli@linaro.org
Signed-off-by: Nathan Chancellor <nathan@kernel.org>
This commit is contained in:
Anders Roxell 2026-01-22 12:57:49 +02:00 committed by Nathan Chancellor
parent a081b57892
commit 5fa9b82cbc
No known key found for this signature in database
GPG Key ID: 1D6B269171C01A96

View File

@ -16,8 +16,8 @@
set -e set -e
clean_up() { clean_up() {
rm -f $TMP_FILE rm -f "$TMP_FILE"
rm -f $MERGE_FILE rm -f "$TMP_FILE.new"
} }
usage() { usage() {
@ -43,6 +43,10 @@ STRICT=false
CONFIG_PREFIX=${CONFIG_-CONFIG_} CONFIG_PREFIX=${CONFIG_-CONFIG_}
WARNOVERRIDE=echo WARNOVERRIDE=echo
if [ -z "$AWK" ]; then
AWK=awk
fi
while true; do while true; do
case $1 in case $1 in
"-n") "-n")
@ -117,11 +121,8 @@ if [ ! -r "$INITFILE" ]; then
fi fi
MERGE_LIST=$* MERGE_LIST=$*
SED_CONFIG_EXP1="s/^\(${CONFIG_PREFIX}[a-zA-Z0-9_]*\)=.*/\1/p"
SED_CONFIG_EXP2="s/^# \(${CONFIG_PREFIX}[a-zA-Z0-9_]*\) is not set$/\1/p"
TMP_FILE=$(mktemp ./.tmp.config.XXXXXXXXXX) TMP_FILE=$(mktemp ./.tmp.config.XXXXXXXXXX)
MERGE_FILE=$(mktemp ./.merge_tmp.config.XXXXXXXXXX)
echo "Using $INITFILE as base" echo "Using $INITFILE as base"
@ -136,42 +137,129 @@ for ORIG_MERGE_FILE in $MERGE_LIST ; do
echo "The merge file '$ORIG_MERGE_FILE' does not exist. Exit." >&2 echo "The merge file '$ORIG_MERGE_FILE' does not exist. Exit." >&2
exit 1 exit 1
fi fi
cat $ORIG_MERGE_FILE > $MERGE_FILE # Use awk for single-pass processing instead of per-symbol grep/sed
CFG_LIST=$(sed -n -e "$SED_CONFIG_EXP1" -e "$SED_CONFIG_EXP2" $MERGE_FILE) if ! "$AWK" -v prefix="$CONFIG_PREFIX" \
-v warnoverride="$WARNOVERRIDE" \
-v strict="$STRICT" \
-v builtin="$BUILTIN" \
-v warnredun="$WARNREDUN" '
BEGIN {
strict_violated = 0
cfg_regex = "^" prefix "[a-zA-Z0-9_]+"
notset_regex = "^# " prefix "[a-zA-Z0-9_]+ is not set$"
}
for CFG in $CFG_LIST ; do # Extract config name from a line, returns "" if not a config line
grep -q -w $CFG $TMP_FILE || continue function get_cfg(line) {
PREV_VAL=$(grep -w $CFG $TMP_FILE) if (match(line, cfg_regex)) {
NEW_VAL=$(grep -w $CFG $MERGE_FILE) return substr(line, RSTART, RLENGTH)
BUILTIN_FLAG=false } else if (match(line, notset_regex)) {
if [ "$BUILTIN" = "true" ] && [ "${NEW_VAL#CONFIG_*=}" = "m" ] && [ "${PREV_VAL#CONFIG_*=}" = "y" ]; then # Extract CONFIG_FOO from "# CONFIG_FOO is not set"
${WARNOVERRIDE} Previous value: $PREV_VAL sub(/^# /, "", line)
${WARNOVERRIDE} New value: $NEW_VAL sub(/ is not set$/, "", line)
${WARNOVERRIDE} -y passed, will not demote y to m return line
${WARNOVERRIDE} }
BUILTIN_FLAG=true return ""
elif [ "x$PREV_VAL" != "x$NEW_VAL" ] ; then }
${WARNOVERRIDE} Value of $CFG is redefined by fragment $ORIG_MERGE_FILE:
${WARNOVERRIDE} Previous value: $PREV_VAL function warn_builtin(cfg, prev, new) {
${WARNOVERRIDE} New value: $NEW_VAL if (warnoverride == "true") return
${WARNOVERRIDE} print cfg ": -y passed, will not demote y to m"
if [ "$STRICT" = "true" ]; then print "Previous value: " prev
STRICT_MODE_VIOLATED=true print "New value: " new
fi print ""
elif [ "$WARNREDUN" = "true" ]; then }
${WARNOVERRIDE} Value of $CFG is redundant by fragment $ORIG_MERGE_FILE:
fi function warn_redefined(cfg, prev, new) {
if [ "$BUILTIN_FLAG" = "false" ]; then if (warnoverride == "true") return
sed -i "/$CFG[ =]/d" $TMP_FILE print "Value of " cfg " is redefined by fragment " mergefile ":"
else print "Previous value: " prev
sed -i "/$CFG[ =]/d" $MERGE_FILE print "New value: " new
fi print ""
done }
# In case the previous file lacks a new line at the end
echo >> $TMP_FILE function warn_redundant(cfg) {
cat $MERGE_FILE >> $TMP_FILE if (warnredun != "true" || warnoverride == "true") return
print "Value of " cfg " is redundant by fragment " mergefile ":"
}
# First pass: read merge file, store all lines and index
FILENAME == ARGV[1] {
mergefile = FILENAME
merge_lines[FNR] = $0
merge_total = FNR
cfg = get_cfg($0)
if (cfg != "") {
merge_cfg[cfg] = $0
merge_cfg_line[cfg] = FNR
}
next
}
# Second pass: process base file (TMP_FILE)
FILENAME == ARGV[2] {
cfg = get_cfg($0)
# Not a config or not in merge file - keep it
if (cfg == "" || !(cfg in merge_cfg)) {
print $0 >> ARGV[3]
next
}
prev_val = $0
new_val = merge_cfg[cfg]
# BUILTIN: do not demote y to m
if (builtin == "true" && new_val ~ /=m$/ && prev_val ~ /=y$/) {
warn_builtin(cfg, prev_val, new_val)
print $0 >> ARGV[3]
skip_merge[merge_cfg_line[cfg]] = 1
next
}
# Values equal - redundant
if (prev_val == new_val) {
warn_redundant(cfg)
next
}
# "=n" is the same as "is not set"
if (prev_val ~ /=n$/ && new_val ~ / is not set$/) {
print $0 >> ARGV[3]
next
}
# Values differ - redefined
warn_redefined(cfg, prev_val, new_val)
if (strict == "true") {
strict_violated = 1
}
}
# output file, skip all lines
FILENAME == ARGV[3] {
nextfile
}
END {
# Newline in case base file lacks trailing newline
print "" >> ARGV[3]
# Append merge file, skipping lines marked for builtin preservation
for (i = 1; i <= merge_total; i++) {
if (!(i in skip_merge)) {
print merge_lines[i] >> ARGV[3]
}
}
if (strict_violated) {
exit 1
}
}' \
"$ORIG_MERGE_FILE" "$TMP_FILE" "$TMP_FILE.new"; then
# awk exited non-zero, strict mode was violated
STRICT_MODE_VIOLATED=true
fi
mv "$TMP_FILE.new" "$TMP_FILE"
done done
if [ "$STRICT_MODE_VIOLATED" = "true" ]; then if [ "$STRICT_MODE_VIOLATED" = "true" ]; then
echo "The fragment redefined a value and strict mode had been passed." echo "The fragment redefined a value and strict mode had been passed."
exit 1 exit 1