DISCLOSURE: This report was created with the help of an LLM (Claude Opus 5). I did review the relevant specs, test results and steered the LLM to make a hopefully useful and complete report.
Summary
$^ omits a file that the rule names as its own prerequisite, when that file is also a prerequisite of another target updated earlier in the same run.
In the cases below the file is written once in the rule's own prerequisite list — a direct prerequisite — and is also reachable through one of that rule's other prerequisites — an indirect one. It is the direct prerequisite that disappears from $^.
The two are not duplicates of each other. $^'s subject is the target's own list, and there the file appears once. $+, which is $^ without duplicate removal, keeps it in every case; a recipe using $^ therefore receives fewer arguments than the rule names.
Direct and indirect are used here for brevity and are not the standard's terms. The standard's own definition of prerequisite is per-target, and is quoted below.
Implementations run
|
|
| pdpmake 2.0.4 |
Built from the release tarball, github.com/rmyorston/pdpmake, with clang. Reports This build supports: non-POSIX extensions, POSIX 2024, POSIX 2017, and behaves identically in the default mode and under --posix |
| GNU Make 4.2.1 |
For comparison |
Results
| Case |
Expected $^ |
pdpmake 2.0.4 |
GNU Make 4.2.1 |
| 1 |
first a b |
first a |
first a b |
| 2 |
a b |
a b |
a b |
| 3 |
a b first |
a b first |
a b first |
| 4 |
first a b |
first a |
first a b |
| 5 |
first a b |
first a b |
first a b |
$+ matched the expected value in every case, in both implementations.
What the standard requires
From IEEE Std 1003.1-2024 (POSIX.1-2024), XCU make, EXTENDED DESCRIPTION, Internal Macros (https://pubs.opengroup.org/onlinepubs/9799919799/utilities/make.html#tag_20_76_13_08). The section first defines its term:
In the definitions that follow, the word prerequisite refers to one of:
- An explicit prerequisite specified in the makefile for a particular target
- An implicit prerequisite generated as a result of locating an appropriate inference rule and corresponding file that matches the suffix of the target
Both are per-target: a file is a prerequisite of the target whose rule names it, or of the target an inference rule matched. Being a prerequisite of some other target does not make it one of this target's.
Then the macro:
The $^ macro shall evaluate to the list of prerequisites for the current target, with any duplicates (except the first) removed. It shall be evaluated for both target and inference rules. If the list of prerequisites of the target contains any .WAIT special targets, the results of expanding $^ are unspecified.
For example, in a makefile target rule to build prog from file1.o, file2.o, and file3.o, and regardless of which prerequisites prog is out-of-date with respect to, $^ represents file1.o, file2.o, and file3.o.
And the macro that differs only in the removal:
The $+ macro shall be equivalent to $^, except that duplicates shall not be removed; all prerequisites shall appear in the order they were listed in the makefile.
No case below uses .WAIT.
The cases
Each case is complete: run the setup, write the makefile, run make. Recipe lines are indented with a tab. Each case was run on its own in an empty directory.
Cases 1 and 4 show the defect. Cases 2, 3 and 5 each differ from case 1 in exactly one respect and are correct.
Case 1
Setup:
Makefile:
all: first a b
@echo "caret [$^]"
@echo "plus [$+]"
first: b
@true
Expected:
caret [first a b]
plus [first a b]
pdpmake 2.0.4:
caret [first a]
plus [first a b]
GNU Make 4.2.1:
caret [first a b]
plus [first a b]
b is a direct prerequisite of all, written once. It is also an indirect one, through first. The direct one is what $^ loses.
Case 2
Differs from case 1 in that the rule sharing b is one that is never updated: other is not the default target and nothing depends on it.
Setup:
Makefile:
all: a b
@echo "caret [$^]"
@echo "plus [$+]"
other: b
@true
Expected:
pdpmake 2.0.4:
GNU Make 4.2.1:
Case 3
Differs from case 1 in the order of all's prerequisites alone: first is listed last. The set is the same.
Setup:
Makefile:
all: a b first
@echo "caret [$^]"
@echo "plus [$+]"
first: b
@true
Expected:
caret [a b first]
plus [a b first]
pdpmake 2.0.4:
caret [a b first]
plus [a b first]
GNU Make 4.2.1:
caret [a b first]
plus [a b first]
Case 4
Differs from case 1 in that b does not exist at the start and is built by a rule of its own.
Setup:
Makefile:
all: first a b
@echo "caret [$^]"
@echo "plus [$+]"
first: b
@true
b:
@touch b
Expected:
caret [first a b]
plus [first a b]
pdpmake 2.0.4:
caret [first a]
plus [first a b]
GNU Make 4.2.1:
caret [first a b]
plus [first a b]
Case 5
Differs from case 1 in that first has no prerequisites, so the two rules share nothing. first is still listed first and still updated.
Setup:
Makefile:
all: first a b
@echo "caret [$^]"
@echo "plus [$+]"
first:
@true
Expected:
caret [first a b]
plus [first a b]
pdpmake 2.0.4:
caret [first a b]
plus [first a b]
GNU Make 4.2.1:
caret [first a b]
plus [first a b]
DISCLOSURE: This report was created with the help of an LLM (Claude Opus 5). I did review the relevant specs, test results and steered the LLM to make a hopefully useful and complete report.
Summary
$^omits a file that the rule names as its own prerequisite, when that file is also a prerequisite of another target updated earlier in the same run.In the cases below the file is written once in the rule's own prerequisite list — a direct prerequisite — and is also reachable through one of that rule's other prerequisites — an indirect one. It is the direct prerequisite that disappears from
$^.The two are not duplicates of each other.
$^'s subject is the target's own list, and there the file appears once.$+, which is$^without duplicate removal, keeps it in every case; a recipe using$^therefore receives fewer arguments than the rule names.Direct and indirect are used here for brevity and are not the standard's terms. The standard's own definition of prerequisite is per-target, and is quoted below.
Implementations run
This build supports: non-POSIX extensions, POSIX 2024, POSIX 2017, and behaves identically in the default mode and under--posixResults
$^first a bfirst afirst a ba ba ba ba b firsta b firsta b firstfirst a bfirst afirst a bfirst a bfirst a bfirst a b$+matched the expected value in every case, in both implementations.What the standard requires
From IEEE Std 1003.1-2024 (POSIX.1-2024), XCU
make, EXTENDED DESCRIPTION, Internal Macros (https://pubs.opengroup.org/onlinepubs/9799919799/utilities/make.html#tag_20_76_13_08). The section first defines its term:Both are per-target: a file is a prerequisite of the target whose rule names it, or of the target an inference rule matched. Being a prerequisite of some other target does not make it one of this target's.
Then the macro:
And the macro that differs only in the removal:
No case below uses
.WAIT.The cases
Each case is complete: run the setup, write the makefile, run
make. Recipe lines are indented with a tab. Each case was run on its own in an empty directory.Cases 1 and 4 show the defect. Cases 2, 3 and 5 each differ from case 1 in exactly one respect and are correct.
Case 1
Setup:
Makefile:Expected:
pdpmake 2.0.4:
GNU Make 4.2.1:
bis a direct prerequisite ofall, written once. It is also an indirect one, throughfirst. The direct one is what$^loses.Case 2
Differs from case 1 in that the rule sharing
bis one that is never updated:otheris not the default target and nothing depends on it.Setup:
Makefile:Expected:
pdpmake 2.0.4:
GNU Make 4.2.1:
Case 3
Differs from case 1 in the order of
all's prerequisites alone:firstis listed last. The set is the same.Setup:
Makefile:Expected:
pdpmake 2.0.4:
GNU Make 4.2.1:
Case 4
Differs from case 1 in that
bdoes not exist at the start and is built by a rule of its own.Setup:
Makefile:Expected:
pdpmake 2.0.4:
GNU Make 4.2.1:
Case 5
Differs from case 1 in that
firsthas no prerequisites, so the two rules share nothing.firstis still listed first and still updated.Setup:
Makefile:Expected:
pdpmake 2.0.4:
GNU Make 4.2.1: