Build Repeatable File Workflows with GNU make
You will finish with a small Makefile that rebuilds a target only when its prerequisites are newer, supports a dry run, and offers a safe cleanup target. The examples use GNU Make 4.3 from the installed make package, version 4.3-4.1build2. On this system, gmake is an alias for the same program.
The route
Jump straight to the step you need, or tick off Done means at the end.
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 merely because a recipe failed: make executes 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 record 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 accepts the name gmake on this machine. If your system has another 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 contain 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. The indentation before printf must be a tab, not spaces:
.PHONY: report
report: input.txt
printf 'input is ready\n' > report.txt
This first example has a deliberate mismatch that is useful for spotting the model: the target is named report, but the recipe creates report.txt. Use matching names in a real rule. Replace it with this working version:
.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 target when a prerequisite is newer. If the target is already newer, a second make report.txt prints make: 'report.txt' is up to date. and does not run the recipe.
3. Add an aggregate target
A named aggregate target lets you build several outputs with one command. It does not need a file of its own, so mark it phony. 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 target, all, after reading the default file names in this order: GNUmakefile, makefile, then Makefile. Running make all is clearer in scripts and avoids confusion when a directory contains more than one makefile.
Safety checkpoint
make clean deletes the generated file. Read every cleanup recipe before running it, especially when variables or wildcards are involved. The example is reversible by running make again, because input.txt remains.
4. Preview work before allowing it
Use --dry-run, also written as -n, to print recipes without executing them:
$ make --dry-run all
printf 'input is ready\n' > report.txt
A dry run is a review step, not proof that the eventual command will succeed. Make still reads the makefile and resolves prerequisites. If the output contains a surprising path, stop and inspect the rule before using ordinary make.
For a more focused explanation of decisions, 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
The line number and wording can vary with the exact makefile. The useful information is which prerequisite caused the target to be considered stale.
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 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, not as trusted shell syntax. Do not place an unreviewed value in 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 this 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
7. Diagnose the common failures
No rule to make target usually means a prerequisite is missing and no rule can create it. Check the spelling, current directory and the file's permissions. A recipe that starts with spaces instead of a tab can fail with missing separator; display the file with a tool that makes tabs visible, or retype the indentation.
If make rebuilds every time, inspect the output path and timestamps. A recipe must create the target named by its rule. Also check whether the target is marked .PHONY; that is correct for actions such as clean, but wrong for a generated file whose timestamp should control rebuilding.
If one independent target fails, make --keep-going or -k continues with other possible targets. It does not repair the failed target, and it can leave a partial build. Review generated files before using them.
Done means
- The installed GNU make version was checked.
- A Makefile names a target and its prerequisite, with tab-indented recipes.
- A second build skips an unchanged target, while a newer prerequisite triggers a rebuild.
--dry-runis used to review commands before they run.- Cleanup is explicit, understood and recoverable by rebuilding from the source input.
- Question mode and exit status are used when a script needs to test freshness.