Home / Alt manpages / make(1)

  • make(1)
  • User command
  • linux

Build Repeatable File Workflows with GNU make

GNU make rebuilds a target only when its prerequisites are newer, so you stop retyping the same build commands by hand. You will finish with a small Makefile that does that, plus a dry-run mode and a safe cleanup target. The examples use GNU Make 4.3 from the installed make package, version 4.3-4.1build2; gmake is an alias for the same program on this system.

Allow about twenty minutes. You need a shell, GNU make and a project directory where you can create files. The normal build commands are unprivileged. Do not run a Makefile with sudo just because a recipe failed: make runs shell commands, so a bad or untrusted recipe can change far more than the intended build output.

1. Check the Installed Command

Confirm which executable your shell will use and note its version:

$ command -v make
/usr/bin/make
$ make --version | sed -n '1,2p'
GNU Make 4.3
Built for x86_64-pc-linux-gnu

The command also answers to gmake on this machine. If your system has a different implementation, check its manual before relying on GNU-specific features such as .PHONY or --trace.

Checkpoint

The version command succeeds, and you know which project directory will hold the Makefile.

2. Describe One Target and Its Prerequisite

Create a file named Makefile in the project directory. A rule has a target, a colon, zero or more prerequisites, and indented recipe lines. That indentation before printf must be a tab, not spaces:

.PHONY: report

report: input.txt
	printf 'input is ready\n' > report.txt

That first example hides a deliberate mismatch, useful for spotting the pattern: the target is named report, but the recipe creates report.txt. Match the names in a real rule. Use this working version instead:

.PHONY: report

report.txt: input.txt
	printf 'input is ready\n' > report.txt

Create a harmless input file, then ask make to build the target:

$ printf '%s\n' 'source data' > input.txt
$ make report.txt
printf 'input is ready\n' > report.txt
$ cat report.txt
input is ready

Make compares modification times: it builds a missing target, or rebuilds an existing one when a prerequisite is newer. If the target is already newer, a second make report.txt just prints make: 'report.txt' is up to date. and skips the recipe.

3. Add an Aggregate Target

A named aggregate target lets one command build several outputs. It needs no file of its own, so mark it phony, since phony targets are always considered out of date:

.PHONY: all clean

all: report.txt

report.txt: input.txt
	printf 'input is ready\n' > report.txt

clean:
	rm -f report.txt

Now make with no target uses the first one, all, after checking for default file names in this order: GNUmakefile, makefile, then Makefile. Running make all explicitly is clearer in scripts and avoids confusion when a directory has more than one makefile.

Safety checkpoint

make clean deletes the generated file. Read every cleanup recipe before you run it, especially one with variables or wildcards. This particular example is reversible, since running make again just rebuilds from input.txt.

4. Preview Work Before You Allow It

Use --dry-run, or -n, to print recipes without running them:

$ make --dry-run all
printf 'input is ready\n' > report.txt

A dry run is a review step, not proof the real command will succeed: make still reads the makefile and resolves prerequisites. If the output shows a surprising path, stop and inspect the rule before running ordinary make.

For a more focused view of why a target is stale, use the GNU-specific trace option:

$ make --trace report.txt
Makefile:6: update target 'report.txt' due to: input.txt
printf 'input is ready\n' > report.txt

Line numbers and exact wording vary with the makefile. What matters is which prerequisite triggered the rebuild.

5. Pass a Value Through a Variable

Variables keep repeated values in one place. A command-line assignment overrides a := assignment in the file, unless the makefile itself uses a special override:

NAME := default

.PHONY: greet
greet:
	printf 'hello, %s\n' '$(NAME)'
$ make greet
printf 'hello, %s\n' 'default'
hello, default
$ make greet NAME=reader
printf 'hello, %s\n' 'reader'
hello, reader

Quote values in the recipe when they are data. Treat command-line variables as input, never as trusted shell syntax: do not drop an unreviewed value into eval, a shell command substitution, or a destructive path.

6. Check Whether Work Is Needed

Question mode, --question or -q, runs no recipes and prints nothing. It returns zero when the requested targets are up to date, one when make would rebuild them, and two for an error:

$ make --question report.txt
$ printf 'status: %s\n' "$?"
status: 0

Use it in a script when the distinction matters:

if make --question report.txt; then
    printf '%s\n' 'report is current'
else
    status=$?
    if [ "$status" -eq 1 ]; then
        printf '%s\n' 'report needs rebuilding'
    else
        printf 'make check failed with status %s\n' "$status" >&2
        exit "$status"
    fi
fi

Common Traps

  • "No rule to make target". Usually a missing prerequisite that no rule can create. Check the spelling, current directory and file permissions.
  • "missing separator". A recipe line started with spaces instead of a tab. Display the file with something that makes tabs visible, or retype the indentation.
  • Make rebuilds every single time. Check the output path and timestamps, and confirm a recipe actually creates the target its rule names. Also check for a stray .PHONY: correct for an action like clean, wrong for a generated file whose timestamp should drive rebuilding.
  • One target fails, others should still run. make --keep-going or -k continues with other possible targets. It does not repair the failed one, and it can leave a partial build, so review generated files before trusting them.

Done means

  • Checked the version. You confirmed the installed GNU make version.
  • Wrote a working rule. A Makefile names a target and its prerequisite, with tab-indented recipes.
  • Proved the freshness check. A second build skips an unchanged target, while a newer prerequisite triggers a rebuild.
  • Previewed before running. --dry-run reviewed the commands first.
  • Kept cleanup safe. The cleanup target is explicit, understood and recoverable from the source input.
  • Used question mode where it fits. A script can test freshness with -q and the exit status.