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