$RodHat_
MOTD

POSIX finally standardized .PHONY. Only took thirty years of everyone using it.

Published by

POSIX finally standardized .PHONY. Only took thirty years of everyone using it.
Photo: AI-generated — no human photographer / RodHat AI Cover

The Open Group published POSIX.1-2024 earlier this year — IEEE Std 1003.1-2024, Issue 8, the first major revision since 2017 — and the coverage mostly focused on the shell and AWK updates. Those are real. What I want to talk about is the make section, because it is the most quietly significant part of the update and approximately nobody wrote about it.

POSIX make has been an embarrassment for a long time. Not wrong — the original specification documented what make looked like circa 1988: suffix-based inference rules, macro expansion, the .SUFFIXES and .PRECIOUS and .DEFAULT special targets, basic recipe syntax. Then the standard more or less stopped. The world kept using make, make kept gaining features, and the POSIX spec sat in amber while every production Makefile in the world accumulated extension syntax that was portable in practice but nowhere in the standard. POSIX make became the document you cited in arguments and never actually used for compliance.

The .PHONY situation

.PHONY declares that a target is a command name, not a filename. The purpose is simple enough to fit in one sentence: it makes make clean run the clean recipe even if someone has created a file called clean in the current directory, which would otherwise convince make that the target is already current and skip the recipe silently.

Without .PHONY, every Makefile with an all: or install: target has a latent bug waiting for the one environment where a file with that name exists. That environment is rarer than a disk full on your build server but not so rare that nobody has hit it. And “the build says nothing to do when there’s clearly something to do and no error message anywhere” is exactly the class of failure that burns an hour before someone thinks to check whether a stale artifact with the target name is sitting in the tree.

BSD make has had .PHONY since the early 90s. GNU make has had it since at least 3.77, which shipped in 1997. pmake, NetBSD make, OpenBSD make — all of them. Every make implementation anyone uses on a non-embedded UNIX system has had .PHONY for somewhere between 25 and 35 years. It was a de facto POSIX feature for three decades before POSIX.1-2024 made it an actual POSIX feature.

POSIX.1-2024 made it an actual POSIX feature.

What else changed

The .PHONY standardization is the emblematic change but not the only one. POSIX.1-2024 also specifies double-colon rules — the :: syntax that lets you attach multiple independent sets of prerequisites and recipes to the same target, each evaluated and run independently. GNU make and BSD make have both supported this for decades but with differences in edge-case behavior that made relying on it genuinely non-portable. The new spec gives it defined semantics.

The inference rule system also gets clarification. The old spec left enough ambiguity around how .SUFFIXES modifications interact with the default suffix list that implementations could and did disagree on ordering. The new spec is tighter. This is the boring kind of correctness that only matters when you’re writing a make from scratch or debugging why two compliant implementations produce different results on the same input — but it matters for those people and for the test suites that will eventually enforce it.

What didn’t change: pattern rules. The % syntax — %.o: %.c — that GNU make uses for generic pattern-based inference is not in POSIX.1-2024. The suffix rule mechanism ($(CC) -c $< -o $@ attached to a .c.o: rule) is still how POSIX make does generalized compilation. I have opinions about this. I will keep them to myself for now.

Why it took thirty years is not actually a mystery

The POSIX standard is maintained by the Austin Group, a joint working group of the IEEE Computer Society and The Open Group. It operates on committee time: proposals, working group discussion, formal balloting, appeals, revision. Getting consensus across BSD vendors, Linux distributors, proprietary UNIX licensees, and embedded systems vendors that all ship make implementations with their own extension histories is not fast.

The make changes weren’t languishing because nobody cared. They were in the Austin Group’s issue tracker for years, debated, refined, and eventually accepted. That process is slow and sometimes maddening, but it’s the process that produces standards rather than the preferences of whichever vendor has the loudest voice this year.

There’s also a legitimate reason not to rush: if you standardize .PHONY in 1998 when BSD make and GNU make still disagree on edge cases — and there were edge cases, around how .PHONY interacts with inference rules and what happens when a .PHONY target appears as a prerequisite of another target — you’ve locked in one interpretation and made one major implementation wrong. Better to let the implementations converge on behavior and then write down what they converged on. Slower, more correct.

The practical consequence of that caution is that anyone writing strictly portable Makefiles from 1997 to 2024 was working without a net. The implementations had converged but the spec hadn’t, which meant “portable” meant “tested against at least GNU and BSD make and known to work on both” rather than “verified against the spec.” Most people just used GNU make and called it a day. Which is fine until you’re on a system that doesn’t have GNU make in the default base, which is every BSD system I have ever administered.

What it means in practice

If you’re on a BSD system, this changes almost nothing operationally. FreeBSD make and OpenBSD make have been doing the right thing for decades; POSIX.1-2024 is the standard catching up to what the implementations already agreed on. The behavior you rely on today is the behavior the spec now endorses.

If you’re writing Makefiles that need to run on BSD base make, GNU make, and any other POSIX-compliant implementation, you now have an actual standard to target rather than the intersection of whatever extensions happen to be in both GNU and BSD implementations. That intersection was large enough in practice but it was always an empirical claim, not a specified one.

The real value is in tooling. Linters, static analyzers, and make compliance test suites can now work from a current specification rather than POSIX 2017 plus a list of known extensions. That’s infrastructure work — invisible until it isn’t.

Same pattern as the Git SHA-256 story: the technical reality existed before the formal standard, the standard is the certification that the community agreed. Both took longer than they should have and both landed correctly when they did. The portability tax is always real — running on more than one implementation always costs something — and a well-maintained spec is one of the few tools that makes it cheaper over time.

.PHONY is in POSIX now. I have been using it since before most of you had root on anything. Thirty years late. Correct. Filed under: better than never, barely.

Sources

  1. POSIX.1-2024 — The Open Group Base Specifications Issue 8 — The Open Group
  2. POSIX.1-2024 make utility specification — The Open Group