xref: /linux/scripts/kconfig/merge_config.sh (revision ca220141fa8ebae09765a242076b2b77338106b0)
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