# bash completion for GNU make -*- shell-script -*- proc _make_target_extract_script { local mode="$1" shift local prefix="$1" local prefix_pat=$[ printf "%s\n" $prefix | \ sed 's/[][\,.*^$(){}?+|/]/\\&/g] local basename=$(prefix##*/) local dirname_len=$shExpr(' ${#prefix} - ${#basename} ') if [[ $mode == -d ]] { # display mode, only output current path component to the next slash local output="\2" } else { # completion mode, output full path to the next slash local output="\1\2" } cat << """ 1,/^# * Make data base/ d; # skip any makefile output /^# * Finished Make data base/,/^# * Make data base/{ d; # skip any makefile output } /^# * Variables/,/^# * Files/ d; # skip until files section /^# * Not a target/,/^$/ d; # skip not target blocks /^$(prefix_pat)/,/^$/! d; # skip anything user dont want # The stuff above here describes lines that are not # explicit targets or not targets other than special ones # The stuff below here decides whether an explicit target # should be output. /^# * File is an intermediate prerequisite/ { s/^.*$//;x; # unhold target d; # delete line } /^$/ { # end of target block x; # unhold target /^$/d; # dont print blanks s|^\(.\{$(dirname_len)\}\)\(.\{$(#basename)\}[^:/]*/\{0,1\}\)[^:]*:.*$|$(output)|p; d; # hide any bugs } # This pattern includes a literal tab character as \t is not a portable # representation and fails with BSD sed /^[^# :%]\{1,\}:/ { # found target block /^\.PHONY:/ d; # special target /^\.SUFFIXES:/ d; # special target /^\.DEFAULT:/ d; # special target /^\.PRECIOUS:/ d; # special target /^\.INTERMEDIATE:/ d; # special target /^\.SECONDARY:/ d; # special target /^\.SECONDEXPANSION:/ d; # special target /^\.DELETE_ON_ERROR:/ d; # special target /^\.IGNORE:/ d; # special target /^\.LOW_RESOLUTION_TIME:/ d; # special target /^\.SILENT:/ d; # special target /^\.EXPORT_ALL_VARIABLES:/ d; # special target /^\.NOTPARALLEL:/ d; # special target /^\.ONESHELL:/ d; # special target /^\.POSIX:/ d; # special target /^\.NOEXPORT:/ d; # special target /^\.MAKE:/ d; # special target """ # don't complete with hidden targets unless we are doing a partial completion if [[ -z "${prefix_pat}" || "${prefix_pat}" = */ ]] { cat << """ /^$(prefix_pat)[^a-zA-Z0-9]/d # convention for hidden tgt """ } cat << """ h; # hold target d; # delete line } """ } proc _make { local cur prev words cword split _init_completion -s || return local file makef makef_dir=( "-C" "." ) makef_inc i match $prev { with -f|--file|--makefile|-o|--old-file|--assume-old|-W|--what-if|\ --new-file|--assume-new _filedir return 0 with -I|--include-dir|-C|--directory|-m _filedir -d return 0 with -E setglobal COMPREPLY = ''( $( compgen -v -- "$cur" ) ) return 0 with --eval|-D|-V|-x return 0 with --jobs|-j setglobal COMPREPLY = ''( $( compgen -W "{1..$(( $(_ncpus)*2 ))}" -- "$cur" ) ) return 0 } $split && return 0 if [[ "$cur" == -* ]] { local opts="$[ _parse_help $1]" [[ $opts ]] || setglobal opts = $[ _parse_usage $1] setglobal COMPREPLY = ''( $( compgen -W "$opts" -- "$cur" ) ) [[ $COMPREPLY == *= ]] && compopt -o nospace } elif [[ $cur == *=* ]] { setglobal prev = $(cur%%=*) setglobal cur = $(cur#*=) local diropt [[ ${prev,,} == *dir?(ectory) ]] && setglobal diropt = '-d' _filedir $diropt } else { # before we check for makefiles, see if a path was specified # with -C/--directory for (( i=0; i < ${#words[@]}; i++ )); do if [[ ${words[i]} == -@(C|-directory) ]]; then # eval for tilde expansion eval makef_dir=( -C "${words[i+1]}" ) break fi done # before we scan for targets, see if a Makefile name was # specified with -f/--file/--makefile for (( i=0; i < ${#words[@]}; i++ )); do if [[ ${words[i]} == -@(f|-?(make)file) ]]; then # eval for tilde expansion eval makef=( -f "${words[i+1]}" ) break fi done # recognise that possible completions are only going to be displayed # so only the base name is shown local mode=-- if sh-expr ' COMP_TYPE != 9 ' { setglobal mode = '-d' # display-only mode } local reset=$[ set +o | command grep -F posix]; set +o posix # <(...) setglobal COMPREPLY = ''( $( LC_ALL=C \ make -npq __BASH_MAKE_COMPLETION__=1 \ "${makef[@]}" "${makef_dir[@]}" .DEFAULT 2>/dev/null | \ sed -nf <(_make_target_extract_script $mode "$cur") ) ) $reset if [[ $mode != -d ]] { # Completion will occur if there is only one suggestion # so set options for completion based on the first one [[ $COMPREPLY == */ ]] && compopt -o nospace } } } && complete -F _make make gmake gnumake pmake colormake # ex: ts=4 sw=4 et filetype=sh