1# SPDX-License-Identifier: GPL-2.0-only
2# bash completion for GNU make with kbuild extension       -*- shell-script -*-
3
4# Load the default completion script for make. It is typically located at
5# /usr/share/bash-completion/completions/make, but we do not rely on it.
6__kbuild_load_default_make_completion()
7{
8	local -a dirs=("${BASH_COMPLETION_USER_DIR:-${XDG_DATA_HOME:-$HOME/.local/share}/bash-completion}/completions")
9	local ifs=$IFS IFS=: dir compfile this_dir
10
11	for dir in ${XDG_DATA_DIRS:-/usr/local/share:/usr/share}; do
12	        dirs+=("$dir"/bash-completion/completions)
13	done
14	IFS=$ifs
15
16	this_dir="$(realpath "$(dirname "${BASH_SOURCE[0]}")")"
17
18	for dir in "${dirs[@]}"; do
19		if [[ ! -d ${dir} || ${dir} = "${this_dir}" ]]; then
20			continue
21		fi
22
23		for compfile in make make.bash _make; do
24			compfile=$dir/$compfile
25			# Avoid trying to source dirs; https://bugzilla.redhat.com/903540
26			if [[ -f ${compfile} ]] && . "${compfile}" &>/dev/null; then
27
28				__kbuild_default_make_completion=$(
29					# shellcheck disable=SC2046 # word splitting is the point here
30					set -- $(complete -p make)
31
32					while [[ $# -gt 1 && "$1" != -F ]]; do
33						shift
34					done
35
36					if [[ "$1" = -F ]]; then
37						echo "$2"
38					fi
39				)
40
41				return
42			fi
43		done
44	done
45}
46
47__kbuild_load_default_make_completion
48
49__kbuild_handle_variable()
50{
51	local var=${1%%=*}
52	local cur=${cur#"${var}"=}
53	local srctree=$2
54	local keywords=()
55
56	case $var in
57	ARCH)
58		# sub-directories under arch/
59		keywords+=($(find "${srctree}/arch" -mindepth 1 -maxdepth 1 -type d -printf '%P\n'))
60		# architectures hard-coded in the top Makefile
61		keywords+=(i386 x86_64 sparc32 sparc64 parisc64)
62		;;
63	CROSS_COMPILE)
64		# toolchains with a full path
65		local cross_compile=()
66		local c c2
67		_filedir
68
69		for c in "${COMPREPLY[@]}"; do
70			# eval for tilde expansion
71			# suppress error, as this fails when it contains a space
72			eval "c2=${c}" 2>/dev/null || continue
73			if [[ ${c} == *-elfedit && ! -d ${c2} && -x ${c2} ]]; then
74				cross_compile+=("${c%elfedit}")
75			fi
76		done
77
78		# toolchains in the PATH environment
79		while read -r c; do
80			if [[ ${c} == *-elfedit ]]; then
81				keywords+=("${c%elfedit}")
82			fi
83		done < <(compgen -c)
84
85		COMPREPLY=()
86		_filedir -d
87
88		# Add cross_compile directly without passing it to compgen.
89		# Otherwise, toolchain paths with a tilde do not work.
90		# e.g.)
91		#   CROSS_COMPILE=~/0day/gcc-14.2.0-nolibc/aarch64-linux/bin/aarch64-linux-
92		COMPREPLY+=("${cross_compile[@]}")
93		;;
94	LLVM)
95		# LLVM=1 uses the default 'clang' etc.
96		keywords+=(1)
97
98		# suffix for a particular version. LLVM=-18 uses 'clang-18' etc.
99		while read -r c; do
100			if [[ ${c} == clang-[0-9]* ]]; then
101				keywords+=("${c#clang}")
102			fi
103		done < <(compgen -c)
104
105		# directory path to LLVM toolchains
106		_filedir -d
107		;;
108	KCONFIG_ALLCONFIG)
109		# KCONFIG_ALLCONFIG=1 selects the default fragment
110		keywords+=(1)
111		# or the path to a fragment file
112		_filedir
113		;;
114	C | KBUILD_CHECKSRC)
115		keywords+=(1 2)
116		;;
117	V | KBUILD_VERBOSE)
118		keywords+=({,1}{,2})
119		;;
120	W | KBUILD_EXTRA_WARN)
121		keywords+=({,1}{,2}{,3}{,c}{,e})
122		;;
123	KBUILD_ABS_SRCTREE | KBUILD_MODPOST_NOFINAL | KBUILD_MODPOST_WARN | \
124		CLIPPY | KBUILD_CLIPPY | KCONFIG_NOSILENTUPDATE | \
125		KCONFIG_OVERWRITECONFIG | KCONFIG_WARN_UNKNOWN_SYMBOL | \
126		KCONFIG_WERROR )
127		keywords+=(1)
128		;;
129	INSTALL_MOD_STRIP)
130		keywords+=(1 --strip-debug --strip-unneeded)
131		;;
132	O | KBUILD_OUTPUT | M | KBUILD_EXTMOD | MO | KBUILD_EXTMOD_OUTPUT | *_PATH)
133		# variables that take a directory.
134		_filedir -d
135		return
136		;;
137	KBUILD_EXTRA_SYMBOL | KBUILD_KCONFIG | KCONFIG_CONFIG)
138		# variables that take a file.
139		_filedir
140		return
141	esac
142
143	COMPREPLY+=($(compgen -W "${keywords[*]}" -- "${cur}"))
144}
145
146# Check the -C, -f options and 'source' symlink. Return the source tree we are
147# working in.
148__kbuild_get_srctree()
149{
150	local words=("$@")
151	local cwd makef_dir
152
153	# see if a path was specified with -C/--directory
154	for ((i = 1; i < ${#words[@]}; i++)); do
155		if [[ ${words[i]} == -@(C|-directory) ]]; then
156			# eval for tilde expansion.
157			# suppress error, as this fails when it contains a space
158			eval "cwd=${words[i + 1]}" 2>/dev/null
159			break
160		fi
161	done
162
163	if [[ -z ${cwd} ]]; then
164		cwd=.
165	fi
166
167	# see if a Makefile was specified with -f/--file/--makefile
168	for ((i = 1; i < ${#words[@]}; i++)); do
169		if [[ ${words[i]} == -@(f|-?(make)file) ]]; then
170			# eval for tilde expansion
171			# suppress error, as this fails when it contains a space
172			eval "makef_dir=${words[i + 1]%/*}" 2>/dev/null
173			break
174		fi
175	done
176
177	if [ -z "${makef_dir}" ]; then
178		makef_dir=${cwd}
179	elif [[ ${makef_dir} != /* ]]; then
180		makef_dir=${cwd}/${makef_dir}
181	fi
182
183	# If ${makef_dir} is a build directory created by the O= option, there
184	# is a symbolic link 'source', which points to the kernel source tree.
185	if [[ -L ${makef_dir}/source ]]; then
186		makef_dir=$(readlink "${makef_dir}/source")
187	fi
188
189	echo "${makef_dir}"
190}
191
192# Get SRCARCH to do a little more clever things
193__kbuild_get_srcarch()
194{
195	local words=("$@")
196	local arch srcarch uname_m
197
198	# see if ARCH= is explicitly specified
199	for ((i = 1; i < ${#words[@]}; i++)); do
200		if [[ ${words[i]} == ARCH=* ]]; then
201			arch=${words[i]#ARCH=}
202			break
203		fi
204	done
205
206	# If ARCH= is not specified, check the build marchine's architecture
207	if [[ -z ${arch} ]]; then
208		uname_m=$(uname -m)
209
210		# shellcheck disable=SC2209 # 'sh' is SuperH, not a shell command
211		case ${uname_m} in
212		arm64 | aarch64*) arch=arm64 ;;
213		arm* | sa110)     arch=arm ;;
214		i?86 | x86_64)    arch=x86 ;;
215		loongarch*)       arch=loongarch ;;
216		mips*)            arch=mips ;;
217		ppc*)             arch=powerpc ;;
218		riscv*)           arch=riscv ;;
219		s390x)            arch=s390 ;;
220		sh[234]*)         arch=sh ;;
221		sun4u)            arch=sparc64 ;;
222		*)                arch=${uname_m} ;;
223		esac
224	fi
225
226	case ${arch} in
227		parisc64)          srcarch=parisc ;;
228		sparc32 | sparc64) srcarch=sparc ;;
229		i386 | x86_64)     srcarch=x86 ;;
230		*)                 srcarch=${arch} ;;
231	esac
232
233	echo "$srcarch"
234}
235
236# small Makefile to parse obj-* syntax
237__kbuild_tmp_makefile()
238{
239cat <<'EOF'
240.PHONY: __default
241__default:
242	$(foreach m,$(obj-y) $(obj-m) $(obj-),$(foreach s, -objs -y -m -,$($(m:%.o=%$s))) $(m))
243EOF
244echo "include ${1}"
245}
246
247_make_for_kbuild ()
248{
249	# shellcheck disable=SC2034 # these are set by _init_completion
250	local cur prev words cword split
251	_init_completion -s || return
252
253	local srctree
254	srctree=$(__kbuild_get_srctree "${words[@]}")
255
256	# If 'kernel' and 'Documentation' directories are found, we assume this
257	# is a kernel tree. Otherwise, we fall back to the generic rule provided
258	# by the bash-completion project.
259	if [[ ! -d ${srctree}/kernel || ! -d ${srctree}/Documentation ]]; then
260		if [ -n "${__kbuild_default_make_completion}" ]; then
261			"${__kbuild_default_make_completion}" "$@"
262		fi
263		return
264	fi
265
266	# make options with a parameter (copied from the bash-completion project)
267	case ${prev} in
268	--file | --makefile | --old-file | --assume-old | --what-if | --new-file | \
269		--assume-new | -!(-*)[foW])
270		_filedir
271		return
272		;;
273	--include-dir | --directory | -!(-*)[ICm])
274		_filedir -d
275		return
276		;;
277	-!(-*)E)
278		COMPREPLY=($(compgen -v -- "$cur"))
279		return
280		;;
281	--eval | -!(-*)[DVx])
282		return
283		;;
284	--jobs | -!(-*)j)
285		COMPREPLY=($(compgen -W "{1..$(($(_ncpus) * 2))}" -- "$cur"))
286		return
287		;;
288	esac
289
290	local keywords=()
291
292	case ${cur} in
293	-*)
294		# make options (copied from the bash-completion project)
295		local opts
296		opts="$(_parse_help "$1")"
297		COMPREPLY=($(compgen -W "${opts:-$(_parse_usage "$1")}" -- "$cur"))
298		if [[ ${COMPREPLY-} == *= ]]; then
299			compopt -o nospace
300		fi
301		return
302		;;
303	*=*)
304		__kbuild_handle_variable "${cur}" "${srctree}"
305		return
306		;;
307	KBUILD_*)
308		# There are many variables prefixed with 'KBUILD_'.
309		# Display them only when 'KBUILD_' is entered.
310		# shellcheck disable=SC2191 # '=' is appended for variables
311		keywords+=(
312			KBUILD_{CHECKSRC,EXTMOD,EXTMOD_OUTPUT,OUTPUT,VERBOSE,EXTRA_WARN,CLIPPY}=
313			KBUILD_BUILD_{USER,HOST,TIMESTAMP}=
314			KBUILD_MODPOST_{NOFINAL,WARN}=
315			KBUILD_{ABS_SRCTREE,EXTRA_SYMBOLS,KCONFIG}=
316		)
317		;;
318	KCONFIG_*)
319		# There are many variables prefixed with 'KCONFIG_'.
320		# Display them only when 'KCONFIG_' is entered.
321		# shellcheck disable=SC2191 # '=' is appended for variables
322		keywords+=(
323			KCONFIG_{CONFIG,ALLCONFIG,NOSILENTUPDATE,OVERWRITECONFIG}=
324			KCONFIG_{SEED,PROBABILITY}=
325			KCONFIG_WARN_UNKNOWN_SYMBOL=
326			KCONFIG_WERROR=
327		)
328		;;
329	*)
330		# By default, hide KBUILD_* and KCONFIG_* variables.
331		# Instead, display only the prefix parts.
332		keywords+=(KBUILD_ KCONFIG_)
333		;;
334	esac
335
336	if [[ ${cur} != /* && ${cur} != *//* ]]; then
337		local dir srcarch kbuild_file tmp
338		srcarch=$(__kbuild_get_srcarch "${words[@]}")
339
340		# single build
341		dir=${cur}
342		while true; do
343			if [[ ${dir} == */* ]]; then
344				dir=${dir%/*}
345			else
346				dir=.
347			fi
348
349			# Search for 'Kbuild' or 'Makefile' in the parent
350			# directories (may not be a direct parent)
351			if [[ -f ${srctree}/${dir}/Kbuild ]]; then
352				kbuild_file=${srctree}/${dir}/Kbuild
353				break
354			fi
355			if [[ -f ${srctree}/${dir}/Makefile ]]; then
356				kbuild_file=${srctree}/${dir}/Makefile
357				break
358			fi
359
360			if [[ ${dir} == . ]]; then
361				break
362			fi
363		done
364
365		if [[ -n ${kbuild_file} ]]; then
366			tmp=($(__kbuild_tmp_makefile "${kbuild_file}" |
367			       SRCARCH=${srcarch} obj=${dir} src=${srctree}/${dir} \
368			       "${1}" -n -f - 2>/dev/null))
369
370			# Add $(obj)/ prefix
371			if [[ ${dir} != . ]]; then
372				tmp=("${tmp[@]/#/${dir}\/}")
373			fi
374
375			keywords+=("${tmp[@]}")
376		fi
377
378		# *_defconfig and *.config files. These might be grouped into
379		# subdirectories, e.g., arch/powerpc/configs/*/*_defconfig.
380		if [[ ${cur} == */* ]]; then
381			dir=${cur%/*}
382		else
383			dir=.
384		fi
385
386		tmp=($(find "${srctree}/arch/${srcarch}/configs/${dir}" \
387		       "${srctree}/kernel/configs/${dir}" \
388		       -mindepth 1 -maxdepth 1 -type d -printf '%P/\n' \
389		       -o -printf '%P\n' 2>/dev/null))
390
391		if [[ ${dir} != . ]]; then
392			tmp=("${tmp[@]/#/${dir}\/}")
393		fi
394
395		keywords+=("${tmp[@]}")
396	fi
397
398	# shellcheck disable=SC2191 # '=' is appended for variables
399	keywords+=(
400		#
401		# variables (append =)
402		#
403		ARCH=
404		CROSS_COMPILE=
405		LLVM=
406		C= M= MO= O= V= W=
407		INSTALL{,_MOD,_HDR,_DTBS}_PATH=
408		KERNELRELEASE=
409
410		#
411		# targets
412		#
413		all help
414		clean mrproper distclean
415		clang-{tidy,analyzer} compile_commands.json
416		coccicheck
417		dtbs{,_check,_install} dt_binding_{check,schemas}
418		headers{,_install}
419		vmlinux install
420		modules{,_prepare,_install,_sign}
421		vdso_install
422		tags TAGS cscope gtags
423		rust{available,fmt,fmtcheck}
424		kernel{version,release} image_name
425		kselftest{,-all,-install,-clean,-merge}
426
427		# configuration
428		{,old,olddef,sync,def,savedef,rand,listnew,helpnew,test,tiny}config
429		{,build_}{menu,n,g,x}config
430		local{mod,yes}config
431		all{no,yes,mod,def}config
432		{yes2mod,mod2yes,mod2no}config
433
434		# docs
435		{html,textinfo,info,latex,pdf,epub,xml,linkcheck,refcheck,clean}docs
436
437		# package
438		{,bin,src}{rpm,deb}-pkg
439		{pacman,dir,tar}-pkg
440		tar{,gz,bz2,xz,zst}-pkg
441		perf-tar{,gz,bz2,xz,zst}-src-pkg
442	)
443
444	COMPREPLY=($(compgen -W "${keywords[*]}" -- "${cur}"))
445
446	# Do not append a space for variables, subdirs, "KBUILD_", "KCONFIG_".
447	if [[ ${COMPREPLY-} == *[=/] || ${COMPREPLY-} =~ ^(KBUILD|KCONFIG)_$ ]]; then
448		compopt -o nospace
449	fi
450
451} && complete -F _make_for_kbuild make
452