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 functionz
: 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 database2>/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:
^[a-zA-Z0-9]
: Line must start with a letter or number[^$#\/\t=]*
: Followed by any characters except:$
- Variable references (e.g.,$(VAR)
)#
- Comments/
- Path separators (pattern rules)\t
- Tab characters (recipe lines)=
- Variable assignments
:
- Target definition marker (the colon after a target name)([^=]|$)
- 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:
split($1,A,/ /)
: Split the pre-colon part on spaces (for multiple targets)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
compdef: command not found
: Make sure you have the initialization lines (autoload
andcompinit
)No completions shown: Check that:
- You’re in a directory with a Makefile
- The Makefile is readable
- Your targets follow standard naming conventions
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.