Glossary
Make
Make describes what must be produced, what the result depends on, and how to produce it. That model can drive a traditional incremental build or provide a consistent set of short commands for tests, analysis, and local development.
Short definition
Makefile rules connect targets, inputs, and commands.
GNU Make reads a Makefile whose rules consist of a target, prerequisites, and a recipe. The target is often a file to be produced, a prerequisite is an input or another required target, and the recipe is the set of commands that creates the output. Make is not limited to C or C++; the same model can generate documentation, frontend assets, and other file-based outputs.
For file targets, Make normally compares modification times. It runs the recipe when the target is missing or older than any prerequisite, allowing it to rebuild only the affected parts of a build. Make does not understand the meaning of commands inside a recipe: the Makefile author must describe the correct inputs, outputs, and dependencies.
A target can name an action such as test instead of a file. Such a target is declared .PHONY so it still runs if a file with the same name appears in the directory. Used this way, Make is a thin layer over the command line, not a new shell, package manager, or complete CI/CD system.
The problem it solves
Repeatable commands and only the work that is needed.
A project does not have to run every step manually after every change or maintain a long set of instructions for each developer. Make can capture dependencies and expose the same entry points locally and in automation.
- rebuild only files whose inputs have changed
- provide consistent commands for tests, static analysis, generation, or local startup
- express task ordering through prerequisites instead of sequencing steps by hand
- hide a reasonable number of tool flags behind a readable project target
- share the same Makefile between developers and the CI pipeline
- run independent recipes in parallel when their dependencies are described correctly
Practical example
Consistent test and static-analysis commands for a PHP project
This short Makefile does not produce files. The test and analyse targets therefore belong in .PHONY: make test should run PHPUnit every time and must not be blocked by an unrelated file named test. Recipe lines begin with a tab, as required by traditional Makefile syntax.
This is a documentation example. Make does not install PHPUnit or PHPStan; the project must actually provide them in its vendor directory. In a real repository, targets should match the checks that repository supports. Merely adding a name to a Makefile does not create a check.
Makefile
.PHONY: test analyse
test:
php vendor/bin/phpunit
analyse:
php vendor/bin/phpstan analyse
How it works
Requested target → dependencies → freshness check → recipe.
Make traverses the rule graph from the requested target. Declared relationships and timestamps decide what happens in a traditional file build, while a phony target deliberately represents an action to run.
- Read the Makefile Make loads rules, variables, and directives. If no goal is specified, it normally selects the first suitable target as the default goal, so the order of the first rule has a practical effect.
- Expand prerequisites It first finds what the requested target needs. A prerequisite can be an existing file or another target that must be considered first.
- Check timestamps A file target is out of date when it does not exist or is older than any normal prerequisite. Make does not track content automatically, so an omitted input can leave a stale result in place.
- Run the recipe A shell executes the required commands. A recipe must actually create or update its promised output when it succeeds; Make does not understand the commands or their side effects.
- Keep the result or repeat the action A file target remains as evidence of freshness for the next run. A .PHONY target represents no such file and runs again whenever it is explicitly requested.
Key concepts
A Makefile is a small declarative graph with recipes that produce targets.
The most important distinction is whether a rule models a real output or merely names an action.
Target
The result requested by the user or another rule. It is often a generated file, but it can also be the name of an action. A target is therefore not always a real file.
Prerequisite
An input or intermediate target that must be ready before the target. For an ordinary prerequisite, a newer timestamp also means that a file target needs to be rebuilt.
Recipe
One or more command lines passed by Make to the shell. In the default model, separate recipe lines run in separate shells, so a directory change or shell variable does not automatically carry over to the next line without the appropriate syntax.
.PHONY
A special declaration for targets that represent actions. It prevents a collision with a same-named file and makes clear that the file’s existence should not determine whether the action runs.
Variables
Variables help share commands, paths, and flags. They should remain readable and must not conceal which inputs change the result or which environment a recipe expects.
Parallel builds
With the -j option, GNU Make can run independent recipes concurrently. This is safe only when the graph contains the real dependencies and the individual steps do not contend for the same outputs.
Benefits, limitations, and common mistakes
A simple team interface, but no automatic understanding of the project.
Benefits
- the timestamp model can substantially shorten builds with many real outputs
- short targets standardise common commands across local development and CI
- the prerequisite graph expresses both ordering and opportunities for parallel work
- Make is a mature, small tool available in many Unix development environments
Limitations and common mistakes
- an omitted prerequisite can cause a stale file to be reused
- an action target without .PHONY can stop working when a file with the same name appears
- spaces where a recipe requires a tab lead to a confusing syntax error
- parallel execution exposes hidden dependencies or multiple recipes writing the same output
- extensive shell logic, platform-specific conditions, and recursive Make can make the build hard to understand
- GNU Make and other Make implementations are not automatically available on every operating system
When it makes sense
When a project needs readable targets or a real dependency-based build.
Make works well for a project with a handful of recurring commands when the team accepts its syntax and keeps targets as a thin layer. In a PHP application, it can standardise tests, analysis, local containers, and documentation. CI/CD still manages the runner, permissions, secrets, artefacts, and deployment conditions; Make only executes some of the project steps.
A specialised build system or task orchestrator may suit a complex cross-platform build, a dynamic graph, or a large monorepo better. Make is not a package manager: it can call Composer, npm, or Yarn, but does not manage registries, package versions, or lockfiles itself. A shell script is more direct for a short, strictly sequential procedure with no dependency graph.
What to consider
A rule must truthfully describe its inputs, output, and execution.
A Makefile is production automation code. Success should mean the same thing on a developer machine and in a clean environment.
- declare actions that do not produce a same-named file as .PHONY
- list every input that can change a real file output
- keep recipes short and move complex logic into a testable script or a specialised tool
- verify parallel execution instead of assuming an order not expressed by prerequisites
- account for differences in shells, paths, and available programs across supported systems
- do not hide command failures, and run CI targets in a clean environment described in version control
Common questions
Make without common misconceptions
Is Make only for C and C++?
No. It is historically associated with compiling those languages, but recipes can run any available command and rules can model any file-based output.
Is every target a file?
No. A target often represents a file, but it may also name an action. Action targets such as test or analyse are declared .PHONY so a same-named file does not determine whether they run.
How is Make different from a shell script?
A shell script usually describes a sequence of commands. Make also models targets and their prerequisites, decides what is out of date, and can run independent work in parallel; the shell normally still executes the recipes themselves.
Does Make replace a package manager or CI/CD?
No. It can invoke an installation or check, but it does not manage packages, registries, a CI runner, permissions, secrets, or a deployment workflow.
Why does a recipe sometimes not run?
A file target may be up to date according to timestamps, or a same-named file may exist for an action target. Check the declared prerequisites and use .PHONY correctly for actions.
How I work with automated checks
Project commands should be repeatable and clear in CI too.
In PHP projects, I connect tests, static analysis, and other quality gates so developers and automation run the same verified steps.