1#!/bin/sh 2# SPDX-License-Identifier: GPL-2.0 3# 4# merge_config.sh - Takes a list of config fragment values, and merges 5# them one by one. Provides warnings on overridden values, and specified 6# values that did not make it to the resulting .config file (due to missed 7# dependencies or config symbol removal). 8# 9# Portions reused from kconf_check and generate_cfg: 10# http://git.yoctoproject.org/cgit/cgit.cgi/yocto-kernel-tools/tree/tools/kconf_check 11# http://git.yoctoproject.org/cgit/cgit.cgi/yocto-kernel-tools/tree/tools/generate_cfg 12# 13# Copyright (c) 2009-2010 Wind River Systems, Inc. 14# Copyright 2011 Linaro 15 16set -e 17 18clean_up() { 19 rm -f "$TMP_FILE" 20 rm -f "$TMP_FILE.new" 21} 22 23usage() { 24 echo "Usage: $0 [OPTIONS] [CONFIG [...]]" 25 echo " -h display this help text" 26 echo " -m only merge the fragments, do not execute the make command" 27 echo " -n use allnoconfig instead of alldefconfig" 28 echo " -r list redundant entries when merging fragments" 29 echo " -y make builtin have precedence over modules" 30 echo " -O dir to put generated output files. Consider setting \$KCONFIG_CONFIG instead." 31 echo " -s strict mode. Fail if the fragment redefines any value." 32 echo " -Q disable warning messages for overridden options." 33 echo 34 echo "Used prefix: '$CONFIG_PREFIX'. You can redefine it with \$CONFIG_ environment variable." 35} 36 37RUNMAKE=true 38ALLTARGET=alldefconfig 39WARNREDUN=false 40BUILTIN=false 41OUTPUT=. 42STRICT=false 43CONFIG_PREFIX=${CONFIG_-CONFIG_} 44WARNOVERRIDE=echo 45 46if [ -z "$AWK" ]; then 47 AWK=awk 48fi 49 50while true; do 51 case $1 in 52 "-n") 53 ALLTARGET=allnoconfig 54 shift 55 continue 56 ;; 57 "-m") 58 RUNMAKE=false 59 shift 60 continue 61 ;; 62 "-h") 63 usage 64 exit 65 ;; 66 "-r") 67 WARNREDUN=true 68 shift 69 continue 70 ;; 71 "-y") 72 BUILTIN=true 73 shift 74 continue 75 ;; 76 "-O") 77 if [ -d $2 ];then 78 OUTPUT=$(echo $2 | sed 's/\/*$//') 79 else 80 echo "output directory $2 does not exist" 1>&2 81 exit 1 82 fi 83 shift 2 84 continue 85 ;; 86 "-s") 87 STRICT=true 88 shift 89 continue 90 ;; 91 "-Q") 92 WARNOVERRIDE=true 93 shift 94 continue 95 ;; 96 *) 97 break 98 ;; 99 esac 100done 101 102if [ "$#" -lt 1 ] ; then 103 usage 104 exit 105fi 106 107if [ -z "$KCONFIG_CONFIG" ]; then 108 if [ "$OUTPUT" != . ]; then 109 KCONFIG_CONFIG=$(readlink -m -- "$OUTPUT/.config") 110 else 111 KCONFIG_CONFIG=.config 112 fi 113fi 114 115INITFILE=$1 116shift; 117 118if [ ! -r "$INITFILE" ]; then 119 echo "The base file '$INITFILE' does not exist. Creating one..." >&2 120 touch "$INITFILE" 121fi 122 123MERGE_LIST=$* 124 125TMP_FILE=$(mktemp ./.tmp.config.XXXXXXXXXX) 126 127echo "Using $INITFILE as base" 128 129trap clean_up EXIT 130 131cat $INITFILE > $TMP_FILE 132 133PROCESSED_FILES="" 134 135# Merge files, printing warnings on overridden values 136for ORIG_MERGE_FILE in $MERGE_LIST ; do 137 echo "Merging $ORIG_MERGE_FILE" 138 if [ ! -r "$ORIG_MERGE_FILE" ]; then 139 echo "The merge file '$ORIG_MERGE_FILE' does not exist. Exit." >&2 140 exit 1 141 fi 142 143 # Check for duplicate input files 144 case " $PROCESSED_FILES " in 145 *" $ORIG_MERGE_FILE "*) 146 ${WARNOVERRIDE} "WARNING: Input file provided multiple times: $ORIG_MERGE_FILE" 147 ;; 148 esac 149 150 # Use awk for single-pass processing instead of per-symbol grep/sed 151 if ! "$AWK" -v prefix="$CONFIG_PREFIX" \ 152 -v warnoverride="$WARNOVERRIDE" \ 153 -v strict="$STRICT" \ 154 -v builtin="$BUILTIN" \ 155 -v warnredun="$WARNREDUN" ' 156 BEGIN { 157 strict_violated = 0 158 cfg_regex = "^" prefix "[a-zA-Z0-9_]+" 159 notset_regex = "^# " prefix "[a-zA-Z0-9_]+ is not set$" 160 } 161 162 # Extract config name from a line, returns "" if not a config line 163 function get_cfg(line) { 164 if (match(line, cfg_regex)) { 165 return substr(line, RSTART, RLENGTH) 166 } else if (match(line, notset_regex)) { 167 # Extract CONFIG_FOO from "# CONFIG_FOO is not set" 168 sub(/^# /, "", line) 169 sub(/ is not set$/, "", line) 170 return line 171 } 172 return "" 173 } 174 175 function warn_builtin(cfg, prev, new) { 176 if (warnoverride == "true") return 177 print cfg ": -y passed, will not demote y to m" 178 print "Previous value: " prev 179 print "New value: " new 180 print "" 181 } 182 183 function warn_redefined(cfg, prev, new) { 184 if (warnoverride == "true") return 185 print "Value of " cfg " is redefined by fragment " mergefile ":" 186 print "Previous value: " prev 187 print "New value: " new 188 print "" 189 } 190 191 function warn_redundant(cfg) { 192 if (warnredun != "true" || warnoverride == "true") return 193 print "Value of " cfg " is redundant by fragment " mergefile ":" 194 } 195 196 # First pass: read merge file, store all lines and index 197 FILENAME == ARGV[1] { 198 mergefile = FILENAME 199 merge_lines[FNR] = $0 200 merge_total = FNR 201 cfg = get_cfg($0) 202 if (cfg != "") { 203 merge_cfg[cfg] = $0 204 merge_cfg_line[cfg] = FNR 205 } 206 next 207 } 208 209 # Second pass: process base file (TMP_FILE) 210 FILENAME == ARGV[2] { 211 cfg = get_cfg($0) 212 213 # Not a config or not in merge file - keep it 214 if (cfg == "" || !(cfg in merge_cfg)) { 215 print $0 >> ARGV[3] 216 next 217 } 218 219 prev_val = $0 220 new_val = merge_cfg[cfg] 221 222 # BUILTIN: do not demote y to m 223 if (builtin == "true" && new_val ~ /=m$/ && prev_val ~ /=y$/) { 224 warn_builtin(cfg, prev_val, new_val) 225 print $0 >> ARGV[3] 226 skip_merge[merge_cfg_line[cfg]] = 1 227 next 228 } 229 230 # Values equal - redundant 231 if (prev_val == new_val) { 232 warn_redundant(cfg) 233 next 234 } 235 236 # "=n" is the same as "is not set" 237 if (prev_val ~ /=n$/ && new_val ~ / is not set$/) { 238 print $0 >> ARGV[3] 239 next 240 } 241 242 # Values differ - redefined 243 warn_redefined(cfg, prev_val, new_val) 244 if (strict == "true") { 245 strict_violated = 1 246 } 247 } 248 249 # output file, skip all lines 250 FILENAME == ARGV[3] { 251 nextfile 252 } 253 254 END { 255 # Newline in case base file lacks trailing newline 256 print "" >> ARGV[3] 257 # Append merge file, skipping lines marked for builtin preservation 258 for (i = 1; i <= merge_total; i++) { 259 if (!(i in skip_merge)) { 260 print merge_lines[i] >> ARGV[3] 261 } 262 } 263 if (strict_violated) { 264 exit 1 265 } 266 }' \ 267 "$ORIG_MERGE_FILE" "$TMP_FILE" "$TMP_FILE.new"; then 268 # awk exited non-zero, strict mode was violated 269 STRICT_MODE_VIOLATED=true 270 fi 271 mv "$TMP_FILE.new" "$TMP_FILE" 272 PROCESSED_FILES="$PROCESSED_FILES $ORIG_MERGE_FILE" 273done 274if [ "$STRICT_MODE_VIOLATED" = "true" ]; then 275 echo "The fragment redefined a value and strict mode had been passed." 276 exit 1 277fi 278 279if [ "$RUNMAKE" = "false" ]; then 280 cp -T -- "$TMP_FILE" "$KCONFIG_CONFIG" 281 echo "#" 282 echo "# merged configuration written to $KCONFIG_CONFIG (needs make)" 283 echo "#" 284 exit 285fi 286 287# If we have an output dir, setup the O= argument, otherwise leave 288# it blank, since O=. will create an unnecessary ./source softlink 289OUTPUT_ARG="" 290if [ "$OUTPUT" != "." ] ; then 291 OUTPUT_ARG="O=$OUTPUT" 292fi 293 294 295# Use the merged file as the starting point for: 296# alldefconfig: Fills in any missing symbols with Kconfig default 297# allnoconfig: Fills in any missing symbols with # CONFIG_* is not set 298make KCONFIG_ALLCONFIG=$TMP_FILE $OUTPUT_ARG $ALLTARGET 299 300# Check all specified config values took effect (might have missed-dependency issues) 301if ! "$AWK" -v prefix="$CONFIG_PREFIX" \ 302 -v warnoverride="$WARNOVERRIDE" \ 303 -v strict="$STRICT" \ 304 -v warnredun="$WARNREDUN" ' 305BEGIN { 306 strict_violated = 0 307 cfg_regex = "^" prefix "[a-zA-Z0-9_]+" 308 notset_regex = "^# " prefix "[a-zA-Z0-9_]+ is not set$" 309} 310 311# Extract config name from a line, returns "" if not a config line 312function get_cfg(line) { 313 if (match(line, cfg_regex)) { 314 return substr(line, RSTART, RLENGTH) 315 } else if (match(line, notset_regex)) { 316 # Extract CONFIG_FOO from "# CONFIG_FOO is not set" 317 sub(/^# /, "", line) 318 sub(/ is not set$/, "", line) 319 return line 320 } 321 return "" 322} 323 324function warn_mismatch(cfg, merged, final) { 325 if (warnredun == "true") return 326 if (final == "" && !(merged ~ / is not set$/ || merged ~ /=n$/)) { 327 print "WARNING: Value requested for " cfg " not in final .config" 328 print "Requested value: " merged 329 print "Actual value: " final 330 } else if (final == "" && merged ~ / is not set$/) { 331 # not set, pass 332 } else if (merged == "" && final != "") { 333 print "WARNING: " cfg " not in merged config but added in final .config:" 334 print "Requested value: " merged 335 print "Actual value: " final 336 } else { 337 print "WARNING: " cfg " differs:" 338 print "Requested value: " merged 339 print "Actual value: " final 340 } 341} 342 343# First pass: read effective config file, store all lines 344FILENAME == ARGV[1] { 345 cfg = get_cfg($0) 346 if (cfg != "") { 347 config_cfg[cfg] = $0 348 } 349 next 350} 351 352# Second pass: process merged config and compare against effective config 353{ 354 cfg = get_cfg($0) 355 if (cfg == "") next 356 357 # strip trailing comment 358 sub(/[[:space:]]+#.*/, "", $0) 359 merged_val = $0 360 final_val = config_cfg[cfg] 361 362 if (merged_val == final_val) next 363 364 if (merged_val ~ /=n$/ && final_val ~ / is not set$/) next 365 if (merged_val ~ /=n$/ && final_val == "") next 366 367 warn_mismatch(cfg, merged_val, final_val) 368 369 if (strict == "true") { 370 strict_violated = 1 371 } 372} 373 374END { 375 if (strict_violated) { 376 exit 1 377 } 378}' \ 379"$KCONFIG_CONFIG" "$TMP_FILE"; then 380 # awk exited non-zero, strict mode was violated 381 STRICT_MODE_VIOLATED=true 382fi 383 384if [ "$STRICT" == "true" ] && [ "$STRICT_MODE_VIOLATED" == "true" ]; then 385 echo "Requested and effective config differ" 386 exit 1 387fi 388