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