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 outfile="$TMP_FILE.new" \ 155 -v builtin="$BUILTIN" \ 156 -v warnredun="$WARNREDUN" ' 157 BEGIN { 158 strict_violated = 0 159 cfg_regex = "^" prefix "[a-zA-Z0-9_]+" 160 notset_regex = "^# " prefix "[a-zA-Z0-9_]+ is not set$" 161 } 162 163 # Extract config name from a line, returns "" if not a config line 164 function get_cfg(line) { 165 if (match(line, cfg_regex)) { 166 return substr(line, RSTART, RLENGTH) 167 } else if (match(line, notset_regex)) { 168 # Extract CONFIG_FOO from "# CONFIG_FOO is not set" 169 sub(/^# /, "", line) 170 sub(/ is not set$/, "", line) 171 return line 172 } 173 return "" 174 } 175 176 function warn_builtin(cfg, prev, new) { 177 if (warnoverride == "true") return 178 print cfg ": -y passed, will not demote y to m" 179 print "Previous value: " prev 180 print "New value: " new 181 print "" 182 } 183 184 function warn_redefined(cfg, prev, new) { 185 if (warnoverride == "true") return 186 print "Value of " cfg " is redefined by fragment " mergefile ":" 187 print "Previous value: " prev 188 print "New value: " new 189 print "" 190 } 191 192 function warn_redundant(cfg) { 193 if (warnredun != "true" || warnoverride == "true") return 194 print "Value of " cfg " is redundant by fragment " mergefile ":" 195 } 196 197 # First pass: read merge file, store all lines and index 198 FILENAME == ARGV[1] { 199 mergefile = FILENAME 200 merge_lines[FNR] = $0 201 merge_total = FNR 202 cfg = get_cfg($0) 203 if (cfg != "") { 204 merge_cfg[cfg] = $0 205 merge_cfg_line[cfg] = FNR 206 } 207 next 208 } 209 210 # Second pass: process base file (TMP_FILE) 211 FILENAME == ARGV[2] { 212 cfg = get_cfg($0) 213 214 # Not a config or not in merge file - keep it 215 if (cfg == "" || !(cfg in merge_cfg)) { 216 print $0 >> outfile 217 next 218 } 219 220 prev_val = $0 221 new_val = merge_cfg[cfg] 222 223 # BUILTIN: do not demote y to m 224 if (builtin == "true" && new_val ~ /=m$/ && prev_val ~ /=y$/) { 225 warn_builtin(cfg, prev_val, new_val) 226 print $0 >> outfile 227 skip_merge[merge_cfg_line[cfg]] = 1 228 next 229 } 230 231 # Values equal - redundant 232 if (prev_val == new_val) { 233 warn_redundant(cfg) 234 next 235 } 236 237 # "=n" is the same as "is not set" 238 if (prev_val ~ /=n$/ && new_val ~ / is not set$/) { 239 print $0 >> outfile 240 next 241 } 242 243 # Values differ - redefined 244 warn_redefined(cfg, prev_val, new_val) 245 if (strict == "true") { 246 strict_violated = 1 247 } 248 } 249 250 END { 251 # Newline in case base file lacks trailing newline 252 print "" >> outfile 253 # Append merge file, skipping lines marked for builtin preservation 254 for (i = 1; i <= merge_total; i++) { 255 if (!(i in skip_merge)) { 256 print merge_lines[i] >> outfile 257 } 258 } 259 if (strict_violated) { 260 exit 1 261 } 262 }' \ 263 "$ORIG_MERGE_FILE" "$TMP_FILE"; then 264 # awk exited non-zero, strict mode was violated 265 STRICT_MODE_VIOLATED=true 266 fi 267 mv "$TMP_FILE.new" "$TMP_FILE" 268 PROCESSED_FILES="$PROCESSED_FILES $ORIG_MERGE_FILE" 269done 270if [ "$STRICT_MODE_VIOLATED" = "true" ]; then 271 echo "The fragment redefined a value and strict mode had been passed." 272 exit 1 273fi 274 275if [ "$RUNMAKE" = "false" ]; then 276 cp -T -- "$TMP_FILE" "$KCONFIG_CONFIG" 277 echo "#" 278 echo "# merged configuration written to $KCONFIG_CONFIG (needs make)" 279 echo "#" 280 exit 281fi 282 283# If we have an output dir, setup the O= argument, otherwise leave 284# it blank, since O=. will create an unnecessary ./source softlink 285OUTPUT_ARG="" 286if [ "$OUTPUT" != "." ] ; then 287 OUTPUT_ARG="O=$OUTPUT" 288fi 289 290 291# Use the merged file as the starting point for: 292# alldefconfig: Fills in any missing symbols with Kconfig default 293# allnoconfig: Fills in any missing symbols with # CONFIG_* is not set 294make KCONFIG_ALLCONFIG=$TMP_FILE $OUTPUT_ARG $ALLTARGET 295 296# Check all specified config values took effect (might have missed-dependency issues) 297if ! "$AWK" -v prefix="$CONFIG_PREFIX" \ 298 -v warnoverride="$WARNOVERRIDE" \ 299 -v strict="$STRICT" \ 300 -v warnredun="$WARNREDUN" ' 301BEGIN { 302 strict_violated = 0 303 cfg_regex = "^" prefix "[a-zA-Z0-9_]+" 304 notset_regex = "^# " prefix "[a-zA-Z0-9_]+ is not set$" 305} 306 307# Extract config name from a line, returns "" if not a config line 308function get_cfg(line) { 309 if (match(line, cfg_regex)) { 310 return substr(line, RSTART, RLENGTH) 311 } else if (match(line, notset_regex)) { 312 # Extract CONFIG_FOO from "# CONFIG_FOO is not set" 313 sub(/^# /, "", line) 314 sub(/ is not set$/, "", line) 315 return line 316 } 317 return "" 318} 319 320function warn_mismatch(cfg, merged, final) { 321 if (warnredun == "true") return 322 if (final == "" && !(merged ~ / is not set$/ || merged ~ /=n$/)) { 323 print "WARNING: Value requested for " cfg " not in final .config" 324 print "Requested value: " merged 325 print "Actual value: " final 326 } else if (final == "" && merged ~ / is not set$/) { 327 # not set, pass 328 } else if (merged == "" && final != "") { 329 print "WARNING: " cfg " not in merged config but added in final .config:" 330 print "Requested value: " merged 331 print "Actual value: " final 332 } else { 333 print "WARNING: " cfg " differs:" 334 print "Requested value: " merged 335 print "Actual value: " final 336 } 337} 338 339# First pass: read effective config file, store all lines 340FILENAME == ARGV[1] { 341 cfg = get_cfg($0) 342 if (cfg != "") { 343 config_cfg[cfg] = $0 344 } 345 next 346} 347 348# Second pass: process merged config and compare against effective config 349{ 350 cfg = get_cfg($0) 351 if (cfg == "") next 352 353 # strip trailing comment 354 sub(/[[:space:]]+#.*/, "", $0) 355 merged_val = $0 356 final_val = config_cfg[cfg] 357 358 if (merged_val == final_val) next 359 360 if (merged_val ~ /=n$/ && final_val ~ / is not set$/) next 361 if (merged_val ~ /=n$/ && final_val == "") next 362 363 warn_mismatch(cfg, merged_val, final_val) 364 365 if (strict == "true") { 366 strict_violated = 1 367 } 368} 369 370END { 371 if (strict_violated) { 372 exit 1 373 } 374}' \ 375"$KCONFIG_CONFIG" "$TMP_FILE"; then 376 # awk exited non-zero, strict mode was violated 377 STRICT_MODE_VIOLATED=true 378fi 379 380if [ "$STRICT" = "true" ] && [ "$STRICT_MODE_VIOLATED" = "true" ]; then 381 echo "Requested and effective config differ" 382 exit 1 383fi 384