If you work with Makefiles regularly, you know the frustration of trying to remember all your target names. Wouldn’t it be great if you could just press Tab and see all available targets? In this post, we’ll explore how to enable Makefile target completion in zsh, explain how it works under the hood, and cover some common pitfalls.

Setting Up Completion

Add the following code to your ~/.zshrc:

# Initialize zsh completion system
autoload -Uz compinit
compinit

# Makefile target completion
function _makefile_targets {
    local -a targets
    targets=($(command make -qp 2>/dev/null | awk -F':' '/^[a-zA-Z0-9][^$#\/\t=]*:([^=]|$)/ && !/^Makefile/ {split($1,A,/ /);for(i in A)print A[i]}' | sort -u))
    compadd $targets
}

compdef _makefile_targets make

After adding this code, either restart your terminal or run:

source ~/.zshrc

How It Works

Let’s break down each component to understand what’s happening.

1. Initialization

autoload -Uz compinit
compinit

These lines initialize zsh’s completion system. Without them, you’ll get the error command not found: compdef. The -Uz flags mean:

  • U: Don’t expand aliases when loading the function
  • z: Use zsh-style function loading

2. The Completion Function

function _makefile_targets {
    local -a targets
    targets=($(command make -qp 2>/dev/null | ...))
    compadd $targets
}

This function:

  • Creates a local array variable targets
  • Uses make -qp to query the Makefile (more on this below)
  • Processes the output with awk
  • Adds the results to zsh’s completion system using compadd

3. Make Query

The make -qp command:

  • -q: “Question” mode - don’t actually run any commands
  • -p: Print make’s internal database
  • 2>/dev/null: Suppress error messages

This gives us a complete view of make’s rules, which we then need to parse.

4. The Awk Magic

Let’s dissect the awk command that extracts target names:

awk -F':' '/^[a-zA-Z0-9][^$#\/\t=]*:([^=]|$)/ && !/^Makefile/ {split($1,A,/ /);for(i in A)print A[i]}'

Field Separator

-F':' tells awk to split lines on colons, which is how Makefile targets are defined.

Pattern Matching

The awk pattern combines two conditions:

First pattern /^[a-zA-Z0-9][^$#\/\t=]*:([^=]|$)/ breaks down as:

  1. ^[a-zA-Z0-9]: Line must start with a letter or number
  2. [^$#\/\t=]*: Followed by any characters except:
    • $ - Variable references (e.g., $(VAR))
    • # - Comments
    • / - Path separators (pattern rules)
    • \t - Tab characters (recipe lines)
    • = - Variable assignments
  3. : - Target definition marker (the colon after a target name)
  4. ([^=]|$) - After the colon, either:
    • A non-equals character, or
    • End of line This prevents matching := variable assignments

Second pattern !/^Makefile/:

  • Excludes the “Makefile” target that appears in make -qp output

This combination effectively captures real Makefile targets while excluding special cases and metadata from make’s output.

Processing

For matching lines:

  1. split($1,A,/ /): Split the pre-colon part on spaces (for multiple targets)
  2. for(i in A)print A[i]: Print each target name

5. Registration

compdef _makefile_targets make

This registers our completion function for use with the make command.

Example

Consider this Makefile:

# Build targets
deps:
    @echo "Installing dependencies..."

build: deps
    @echo "Building..."

test: build
    @echo "Testing..."

# Multiple targets on one line
clean dist: build
    @echo "Cleaning..."

# These won't show up in completion:
.PHONY: build test clean dist
ARCH := x86_64
$(BUILD_DIR)/%.o: %.c
    @echo "Compiling..."

With completion enabled, typing make and pressing tab will show:

build  clean  deps  dist  test

The completion system correctly:

  • Includes real targets (deps, build, test, clean, dist)
  • Handles multiple targets on one line
  • Ignores comments, variables, and special targets
  • Ignores pattern rules and recipe lines

Common Issues

  1. compdef: command not found: Make sure you have the initialization lines (autoload and compinit)

  2. No completions shown: Check that:

    • You’re in a directory with a Makefile
    • The Makefile is readable
    • Your targets follow standard naming conventions
  3. Wrong completions: Verify your Makefile syntax:

    • Target lines must use standard colons
    • Target names should start with letters or numbers
    • Use proper spacing around colons

Conclusion

Makefile target completion makes working with complex build systems much more efficient. Instead of memorizing targets or constantly referring to the Makefile, you can quickly see what’s available with a simple Tab press. The completion system is smart enough to handle various Makefile constructs while focusing on the targets you actually want to run.

Remember to check your ~/.zshrc after adding these changes, and consider adding other completion features for tools you frequently use. zsh’s completion system is powerful and can significantly improve your command-line productivity.