|  | # 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 |