Checking if Makefile Targets Exist: Two Approaches Compared

June 15, 2025 • 3 min read

When working with Makefiles in shell scripts, you often need to check if a target exists before attempting to run it. Here are two different approaches, each with distinct advantages depending on your use case.

Approach 1: Text-Based Search with grep

if grep -q "^build:" Makefile; then
  make build
else
  echo "Makefile target 'build' does not exist"
fi

This approach performs a simple text search in the Makefile, looking for lines that start with the target name followed by a colon.

When to use grep:

  • When you want targets to run and fail naturally: If the target has missing dependencies, syntax errors, or other issues, make will execute and report the actual error
  • Simple Makefiles: Works well with straightforward Makefiles where targets are explicitly defined
  • You want to see Make’s error messages: Any issues with the build process will be reported by Make itself
  • Fast text parsing: Very quick for simple target detection

Limitations of grep:

  • Doesn’t detect implicit rules: Won’t find pattern rules like %.o: %.c
  • Misses included files: Targets defined in included Makefiles won’t be detected
  • False positives: Could match comments or other non-target lines that happen to match the pattern
  • No dependency validation: Doesn’t verify if the target can actually be executed

Approach 2: Make’s Query Mode with -q

make -q build 2>/dev/null; exit_code=$?
if (( exit_code < 2 )); then
  make build
else
  echo "Makefile target 'build' does not exist"
fi

This approach uses Make’s built-in query functionality to check target existence and validity.

How -q exit codes work:

  • 0: Target exists and is up to date
  • 1: Target exists but needs to be rebuilt
  • 2: Target doesn’t exist or has errors (missing dependencies, syntax issues, etc.)

When to use make -q:

  • Comprehensive target detection: Finds all types of targets including implicit rules and included files
  • Dependency validation: Ensures the target can actually be built (all dependencies exist)
  • Robust checking: Uses Make’s own parsing logic, so it’s always accurate
  • Complex Makefiles: Essential for Makefiles with includes, pattern rules, or complex dependency chains
  • Pre-flight validation: When you want to avoid running targets that will definitely fail due to missing dependencies

Key Difference in Error Handling:

The crucial distinction is how each approach handles problematic targets:

  • grep approach: Will attempt to run the target even if it has issues, letting Make report the specific error
  • make -q approach: Will detect problematic targets upfront and treat them as “non-existent” from an execution standpoint

Choosing the Right Approach

Use grep when:

  • You have simple, explicit Makefiles
  • You want Make to handle and report its own errors
  • You prefer targets to fail loudly with detailed error messages
  • You need maximum speed for basic target detection

Use make -q when:

  • You’re working with complex Makefiles (includes, pattern rules)
  • You want to validate that targets can actually be executed
  • You need bulletproof target detection
  • You prefer to avoid running targets that will definitely fail

Example Scenario

Consider a Makefile with this target:

build: missing-dependency
	@echo "Building..."

# missing-dependency target is not defined
  • grep approach: Will find “build:” and attempt to run it, then Make will report the missing dependency error
  • make -q approach: Will detect that the target cannot be built (exit code 2) and treat it as non-existent

Both approaches are valid—choose based on whether you want pre-validation (make -q) or natural error reporting (grep).