Makefile autocompletion on Mac

This seems to achieve simple bash completions for me on El Capitan:

# .bashrc
function _makefile_targets {
    local curr_arg;
    local targets;

    # Find makefile targets available in the current directory
    targets=''
    if [[ -e "$(pwd)/Makefile" ]]; then
        targets=$( \
            grep -oE '^[a-zA-Z0-9_-]+:' Makefile \
            | sed 's/://' \
            | tr '\n' ' ' \
        )
    fi

    # Filter targets based on user input to the bash completion
    curr_arg=${COMP_WORDS[COMP_CWORD]}
    COMPREPLY=( $(compgen -W "${targets[@]}" -- $curr_arg ) );
}
complete -F _makefile_targets make

Here's how this works:

  • complete -F [function name] [command name] -- this bash builtin register a new completion for [command name] which is generated by the bash function [function name]. So in my code above, if you type make [TAB][TAB] into your shell, you'll trigger the _makefile_targets() function.

  • if [[ -e "$(pwd)/Makefile" ]]; then -- make sure there's a Makefile in the current directory, otherwise don't try a bash completion.

  • grep -oE '^[a-zA-Z0-9_-]+:' Makefile -- filter every line of Makefile using the regex for a target name like "test:". -o means only return the part of the line that matches. For example, given a Makefile target like "test: build package", only "test:" will be returned

  • | sed 's/://' -- taking the grep results, remove the colon from the end of line

  • | tr '\n' ' ' -- smoosh all targets onto one line, separated by one space

Inside a bash completion function, complete sets several environment variables for you. COMP_WORDS is an array of the list of available bash completion choises based on what the user typed. COMP_CWORD is the index of the currently selected word.

Another very magical builtin compgen will take a list of space separately strings and filter them using the currently selected word. I'm not at all clear how that works.

So, the bottom line is that the last two lines in the function filter our list of makefile targets (stored inside $targets) and shoves them into an array COMPREPLY. The bash completion reads and displays COMPREPLY as choices in the shell.


Inspired by:

  1. https://gist.github.com/tlrobinson/1073865
  2. http://www.thegeekstuff.com/2013/12/bash-completion-complete/ (Esp 9.)

This worked for me on Catalina in the standard zsh terminal

  1. Edit the file named .zshrc in you home directory this can be done with this command in the terminal nano ~/.zshrc

  2. Add the following lines

zstyle ':completion:*:*:make:*' tag-order 'targets'

autoload -U compinit && compinit
  1. exit and save by pressing ctrl + x and then save Y