linux-yocto/scripts/bash-completion/make
Masahiro Yamada eb47ee0181 kbuild: add Kbuild bash completion
Kernel build commands can sometimes be long, particularly when
cross-compiling, making them tedious to type and prone to mistypes.

This commit introduces bash completion support for common variables
and targets in Kbuild.

For installation instructions, please refer to the documentation in
Documentation/kbuild/bash-completion.rst.

The following examples demonstrate how this saves typing.

[Example 1] a long command line for cross-compiling

  $ make A<TAB>
   -> completes 'A' to 'ARCH='

  $ make ARCH=<TAB>
   -> displays all supported architectures

  $ make ARCH=arm64 CR<TAB>
   -> completes 'CR' to 'CROSS_COMPILE='

  $ make ARCH=arm64 CROSS_COMPILE=<TAB>
   -> displays installed toolchains

  $ make ARCH=arm64 CROSS_COMPILE=aa<TAB>
   -> completes 'CROSS_COMPILE=aa' to 'CROSS_COMPILE=aarch64-linux-gnu-'

  $ make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- def<TAB>
   -> completes 'def' to 'defconfig'

[Example 2] a single build target

  $ make f<TAB>
   -> completes 'f' to 'fs/'

  $ make fs/<TAB>
   -> displays objects and sub-directories in fs/

  $ make fs/xf<TAB>
   -> completes 'fs/xf' to 'fs/xfs/'

  $ make fs/xfs/l<TAB>
   -> completes 'fs/xfs/l' to 'fs/xfs/libxfs/xfs_'

  $ make fs/xfs/libxfs/xfs_g<TAB>
   -> completes 'fs/xfs/libxfs/xfs_g' to 'fs/xfs/libxfs/xfs_group.o'

This does not aim to provide a complete list of variables and targets,
as there are too many. However, it covers variables and targets used
in common scenarios, and I hope this is useful enough.

Signed-off-by: Masahiro Yamada <masahiroy@kernel.org>
Reviewed-by: Nicolas Schier <n.schier@avm.de>
Tested-by: Nicolas Schier <n.schier@avm.de>
2025-03-15 21:22:52 +09:00

11 KiB

SPDX-License-Identifier: GPL-2.0-only

bash completion for GNU make with kbuild extension -- shell-script --

Load the default completion script for make. It is typically located at

/usr/share/bash-completion/completions/make, but we do not rely on it.

__kbuild_load_default_make_completion() { local -a dirs=("${BASH_COMPLETION_USER_DIR:-${XDG_DATA_HOME:-$HOME/.local/share}/bash-completion}/completions") local ifs=$IFS IFS=: dir compfile this_dir

for dir in ${XDG_DATA_DIRS:-/usr/local/share:/usr/share}; do
        dirs+=("$dir"/bash-completion/completions)
done
IFS=$ifs

this_dir="$(realpath "$(dirname "${BASH_SOURCE[0]}")")"

for dir in "${dirs[@]}"; do
	if [[ ! -d ${dir} || ${dir} = "${this_dir}" ]]; then
		continue
	fi

	for compfile in make make.bash _make; do
		compfile=$dir/$compfile
		# Avoid trying to source dirs; https://bugzilla.redhat.com/903540
		if [[ -f ${compfile} ]] && . "${compfile}" &>/dev/null; then

			__kbuild_default_make_completion=$(
				# shellcheck disable=SC2046 # word splitting is the point here
				set -- $(complete -p make)

				while [[ $# -gt 1 && "$1" != -F ]]; do
					shift
				done

				if [[ "$1" = -F ]]; then
					echo "$2"
				fi
			)

			return
		fi
	done
done

}

__kbuild_load_default_make_completion

__kbuild_handle_variable() { local var=${1%%=*} local cur=${cur#"${var}"=} local srctree=$2 local keywords=()

case $var in
ARCH)
	# sub-directories under arch/
	keywords+=($(find "${srctree}/arch" -mindepth 1 -maxdepth 1 -type d -printf '%P\n'))
	# architectures hard-coded in the top Makefile
	keywords+=(i386 x86_64 sparc32 sparc64 parisc64)
	;;
CROSS_COMPILE)
	# toolchains with a full path
	local cross_compile=()
	local c c2
	_filedir

	for c in "${COMPREPLY[@]}"; do
		# eval for tilde expansion
		# suppress error, as this fails when it contains a space
		eval "c2=${c}" 2>/dev/null || continue
		if [[ ${c} == *-elfedit && ! -d ${c2} && -x ${c2} ]]; then
			cross_compile+=("${c%elfedit}")
		fi
	done

	# toolchains in the PATH environment
	while read -r c; do
		if [[ ${c} == *-elfedit ]]; then
			keywords+=("${c%elfedit}")
		fi
	done < <(compgen -c)

	COMPREPLY=()
	_filedir -d

	# Add cross_compile directly without passing it to compgen.
	# Otherwise, toolchain paths with a tilde do not work.
	# e.g.)
	#   CROSS_COMPILE=~/0day/gcc-14.2.0-nolibc/aarch64-linux/bin/aarch64-linux-
	COMPREPLY+=("${cross_compile[@]}")
	;;
LLVM)
	# LLVM=1 uses the default 'clang' etc.
	keywords+=(1)

	# suffix for a particular version. LLVM=-18 uses 'clang-18' etc.
	while read -r c; do
		if [[ ${c} == clang-[0-9]* ]]; then
			keywords+=("${c#clang}")
		fi
	done < <(compgen -c)

	# directory path to LLVM toolchains
	_filedir -d
	;;
KCONFIG_ALLCONFIG)
	# KCONFIG_ALLCONFIG=1 selects the default fragment
	keywords+=(1)
	# or the path to a fragment file
	_filedir
	;;
C | KBUILD_CHECKSRC)
	keywords+=(1 2)
	;;
V | KBUILD_VERBOSE)
	keywords+=({,1}{,2})
	;;
W | KBUILD_EXTRA_WARN)
	keywords+=({,1}{,2}{,3}{,c}{,e})
	;;
KBUILD_ABS_SRCTREE | KBUILD_MODPOST_NOFINAL | KBUILD_MODPOST_WARN | \
	CLIPPY | KBUILD_CLIPPY | KCONFIG_NOSILENTUPDATE | \
	KCONFIG_OVERWRITECONFIG | KCONFIG_WARN_UNKNOWN_SYMBOL | \
	KCONFIG_WERROR )
	keywords+=(1)
	;;
INSTALL_MOD_STRIP)
	keywords+=(1 --strip-debug --strip-unneeded)
	;;
O | KBUILD_OUTPUT | M | KBUILD_EXTMOD | MO | KBUILD_EXTMOD_OUTPUT | *_PATH)
	# variables that take a directory.
	_filedir -d
	return
	;;
KBUILD_EXTRA_SYMBOL | KBUILD_KCONFIG | KCONFIG_CONFIG)
	# variables that take a file.
	_filedir
	return
esac

COMPREPLY+=($(compgen -W "${keywords[*]}" -- "${cur}"))

}

Check the -C, -f options and 'source' symlink. Return the source tree we are

working in.

__kbuild_get_srctree() { local words=("$@") local cwd makef_dir

# see if a path was specified with -C/--directory
for ((i = 1; i < ${#words[@]}; i++)); do
	if [[ ${words[i]} == -@(C|-directory) ]]; then
		# eval for tilde expansion.
		# suppress error, as this fails when it contains a space
		eval "cwd=${words[i + 1]}" 2>/dev/null
		break
	fi
done

if [[ -z ${cwd} ]]; then
	cwd=.
fi

# see if a Makefile was specified with -f/--file/--makefile
for ((i = 1; i < ${#words[@]}; i++)); do
	if [[ ${words[i]} == -@(f|-?(make)file) ]]; then
		# eval for tilde expansion
		# suppress error, as this fails when it contains a space
		eval "makef_dir=${words[i + 1]%/*}" 2>/dev/null
		break
	fi
done

if [ -z "${makef_dir}" ]; then
	makef_dir=${cwd}
elif [[ ${makef_dir} != /* ]]; then
	makef_dir=${cwd}/${makef_dir}
fi

# If ${makef_dir} is a build directory created by the O= option, there
# is a symbolic link 'source', which points to the kernel source tree.
if [[ -L ${makef_dir}/source ]]; then
	makef_dir=$(readlink "${makef_dir}/source")
fi

echo "${makef_dir}"

}

Get SRCARCH to do a little more clever things

__kbuild_get_srcarch() { local words=("$@") local arch srcarch uname_m

# see if ARCH= is explicitly specified
for ((i = 1; i < ${#words[@]}; i++)); do
	if [[ ${words[i]} == ARCH=* ]]; then
		arch=${words[i]#ARCH=}
		break
	fi
done

# If ARCH= is not specified, check the build marchine's architecture
if [[ -z ${arch} ]]; then
	uname_m=$(uname -m)

	# shellcheck disable=SC2209 # 'sh' is SuperH, not a shell command
	case ${uname_m} in
	arm64 | aarch64*) arch=arm64 ;;
	arm* | sa110)     arch=arm ;;
	i?86 | x86_64)    arch=x86 ;;
	loongarch*)       arch=loongarch ;;
	mips*)            arch=mips ;;
	ppc*)             arch=powerpc ;;
	riscv*)           arch=riscv ;;
	s390x)            arch=s390 ;;
	sh[234]*)         arch=sh ;;
	sun4u)            arch=sparc64 ;;
	*)                arch=${uname_m} ;;
	esac
fi

case ${arch} in
	parisc64)          srcarch=parisc ;;
	sparc32 | sparc64) srcarch=sparc ;;
	i386 | x86_64)     srcarch=x86 ;;
	*)                 srcarch=${arch} ;;
esac

echo "$srcarch"

}

small Makefile to parse obj-* syntax

__kbuild_tmp_makefile() { cat <<'EOF' .PHONY: __default __default: $(foreach m,$(obj-y) $(obj-m) $(obj-),$(foreach s, -objs -y -m -,$($(m:%.o=%$s))) $(m)) EOF echo "include ${1}" }

_make_for_kbuild () { # shellcheck disable=SC2034 # these are set by _init_completion local cur prev words cword split _init_completion -s || return

local srctree
srctree=$(__kbuild_get_srctree "${words[@]}")

# If 'kernel' and 'Documentation' directories are found, we assume this
# is a kernel tree. Otherwise, we fall back to the generic rule provided
# by the bash-completion project.
if [[ ! -d ${srctree}/kernel || ! -d ${srctree}/Documentation ]]; then
	if [ -n "${__kbuild_default_make_completion}" ]; then
		"${__kbuild_default_make_completion}" "$@"
	fi
	return
fi

# make options with a parameter (copied from the bash-completion project)
case ${prev} in
--file | --makefile | --old-file | --assume-old | --what-if | --new-file | \
	--assume-new | -!(-*)[foW])
	_filedir
	return
	;;
--include-dir | --directory | -!(-*)[ICm])
	_filedir -d
	return
	;;
-!(-*)E)
	COMPREPLY=($(compgen -v -- "$cur"))
	return
	;;
--eval | -!(-*)[DVx])
	return
	;;
--jobs | -!(-*)j)
	COMPREPLY=($(compgen -W "{1..$(($(_ncpus) * 2))}" -- "$cur"))
	return
	;;
esac

local keywords=()

case ${cur} in
-*)
	# make options (copied from the bash-completion project)
	local opts
	opts="$(_parse_help "$1")"
	COMPREPLY=($(compgen -W "${opts:-$(_parse_usage "$1")}" -- "$cur"))
	if [[ ${COMPREPLY-} == *= ]]; then
		compopt -o nospace
	fi
	return
	;;
*=*)
	__kbuild_handle_variable "${cur}" "${srctree}"
	return
	;;
KBUILD_*)
	# There are many variables prefixed with 'KBUILD_'.
	# Display them only when 'KBUILD_' is entered.
	# shellcheck disable=SC2191 # '=' is appended for variables
	keywords+=(
		KBUILD_{CHECKSRC,EXTMOD,EXTMOD_OUTPUT,OUTPUT,VERBOSE,EXTRA_WARN,CLIPPY}=
		KBUILD_BUILD_{USER,HOST,TIMESTAMP}=
		KBUILD_MODPOST_{NOFINAL,WARN}=
		KBUILD_{ABS_SRCTREE,EXTRA_SYMBOLS,KCONFIG}=
	)
	;;
KCONFIG_*)
	# There are many variables prefixed with 'KCONFIG_'.
	# Display them only when 'KCONFIG_' is entered.
	# shellcheck disable=SC2191 # '=' is appended for variables
	keywords+=(
		KCONFIG_{CONFIG,ALLCONFIG,NOSILENTUPDATE,OVERWRITECONFIG}=
		KCONFIG_{SEED,PROBABILITY}=
		KCONFIG_WARN_UNKNOWN_SYMBOL=
		KCONFIG_WERROR=
	)
	;;
*)
	# By default, hide KBUILD_* and KCONFIG_* variables.
	# Instead, display only the prefix parts.
	keywords+=(KBUILD_ KCONFIG_)
	;;
esac

if [[ ${cur} != /* && ${cur} != *//* ]]; then
	local dir srcarch kbuild_file tmp
	srcarch=$(__kbuild_get_srcarch "${words[@]}")

	# single build
	dir=${cur}
	while true; do
		if [[ ${dir} == */* ]]; then
			dir=${dir%/*}
		else
			dir=.
		fi

		# Search for 'Kbuild' or 'Makefile' in the parent
		# directories (may not be a direct parent)
		if [[ -f ${srctree}/${dir}/Kbuild ]]; then
			kbuild_file=${srctree}/${dir}/Kbuild
			break
		fi
		if [[ -f ${srctree}/${dir}/Makefile ]]; then
			kbuild_file=${srctree}/${dir}/Makefile
			break
		fi

		if [[ ${dir} == . ]]; then
			break
		fi
	done

	if [[ -n ${kbuild_file} ]]; then
		tmp=($(__kbuild_tmp_makefile "${kbuild_file}" |
		       SRCARCH=${srcarch} obj=${dir} src=${srctree}/${dir} \
		       "${1}" -n -f - 2>/dev/null))

		# Add $(obj)/ prefix
		if [[ ${dir} != . ]]; then
			tmp=("${tmp[@]/#/${dir}\/}")
		fi

		keywords+=("${tmp[@]}")
	fi

	# *_defconfig and *.config files. These might be grouped into
	# subdirectories, e.g., arch/powerpc/configs/*/*_defconfig.
	if [[ ${cur} == */* ]]; then
		dir=${cur%/*}
	else
		dir=.
	fi

	tmp=($(find "${srctree}/arch/${srcarch}/configs/${dir}" \
	       "${srctree}/kernel/configs/${dir}" \
	       -mindepth 1 -maxdepth 1 -type d -printf '%P/\n' \
	       -o -printf '%P\n' 2>/dev/null))

	if [[ ${dir} != . ]]; then
		tmp=("${tmp[@]/#/${dir}\/}")
	fi

	keywords+=("${tmp[@]}")
fi

# shellcheck disable=SC2191 # '=' is appended for variables
keywords+=(
	#
	# variables (append =)
	#
	ARCH=
	CROSS_COMPILE=
	LLVM=
	C= M= MO= O= V= W=
	INSTALL{,_MOD,_HDR,_DTBS}_PATH=
	KERNELRELEASE=

	#
	# targets
	#
	all help
	clean mrproper distclean
	clang-{tidy,analyzer} compile_commands.json
	coccicheck
	dtbs{,_check,_install} dt_binding_{check,schemas}
	headers{,_install}
	vmlinux install
	modules{,_prepare,_install,_sign}
	vdso_install
	tags TAGS cscope gtags
	rust{available,fmt,fmtcheck}
	kernel{version,release} image_name
	kselftest{,-all,-install,-clean,-merge}

	# configuration
	{,old,olddef,sync,def,savedef,rand,listnew,helpnew,test,tiny}config
	{,build_}{menu,n,g,x}config
	local{mod,yes}config
	all{no,yes,mod,def}config
	{yes2mod,mod2yes,mod2no}config

	# docs
	{html,textinfo,info,latex,pdf,epub,xml,linkcheck,refcheck,clean}docs

	# package
	{,bin,src}{rpm,deb}-pkg
	{pacman,dir,tar}-pkg
	tar{,gz,bz2,xz,zst}-pkg
	perf-tar{,gz,bz2,xz,zst}-src-pkg
)

COMPREPLY=($(compgen -W "${keywords[*]}" -- "${cur}"))

# Do not append a space for variables, subdirs, "KBUILD_", "KCONFIG_".
if [[ ${COMPREPLY-} == *[=/] || ${COMPREPLY-} =~ ^(KBUILD|KCONFIG)_$ ]]; then
	compopt -o nospace
fi

} && complete -F _make_for_kbuild make