phony-target wildcard - What is the purpose of.PHONY in a makefile?
multiple default (8)
There's also one important tricky treat of ".PHONY" - when a physical target depends on phony target that depends on another physical target:
TARGET1 -> PHONY_FORWARDER1 -> PHONY_FORWARDER2 -> TARGET2
You'd simply expect that if you updated TARGET2, then TARGET1 should be considered stale against TARGET1, so TARGET1 should be rebuild. And it really works this way.
The tricky part is when TARGET2 isn't stale against TARGET1 - in which case you should expect that TARGET1 shouldn't be rebuild.
This surprisingly doesn't work because: the phony target was run anyway (as phony targets normally do), which means that the phony target was considered updated. And because of that TARGET1 is considered stale against the phony target.
all: fileall fileall: file2 filefwd echo file2 file1 >fileall file2: file2.src echo file2.src >file2 file1: file1.src echo file1.src >file1 echo file1.src >>file1 .PHONY: filefwd .PHONY: filefwd2 filefwd: filefwd2 filefwd2: file1 @echo "Produced target file1" prepare: echo "Some text 1" >> file1.src echo "Some text 2" >> file2.src
You can play around with this:
- first do 'make prepare' to prepare the "source files"
- play around with that by touching particular files to see them updated
You can see that fileall depends on file1 indirectly through a phony target - but it always gets rebuilt due to this dependency. If you change the dependency in
fileall does not get rebuilt every time, but only when any of dependent targets is stale against it as a file.
Let's assume you have
install target, which is a very common in makefiles. If you do not use
.PHONY, and a file named
install exists in the same directory as the Makefile, then
make install will do nothing. This is because Make interprets the rule to mean "execute such-and-such recipe to create the file named
install". Since the file is already there, and its dependencies didn't change, nothing will be done.
However if you make the
install target PHONY, it will tell the make tool that the target is fictional, and that make should not expect it to create the actual file. Hence it will not check whether the
install file exists, meaning: a) its behavior will not be altered if the file does exist and b) extra
stat() will not be called.
Generally all targets in your Makefile which do not produce an output file with the same name as the target name should be PHONY. This typically includes
distclean, and so on.
By default, Makefile targets are "file targets" - they are used to build files from other files. Make assumes its target is a file, and this makes writing Makefiles relatively easy:
foo: bar create_one_from_the_other foo bar
However, sometimes you want your Makefile to run commands that do not represent physical files in the file system. Good examples for this are the common targets "clean" and "all". Chances are this isn't the case, but you may potentially have a file named
clean in your main directory. In such a case Make will be confused because by default the
clean target would be associated with this file and Make will only run it when the file doesn't appear to be up-to-date with regards to its dependencies.
These special targets are called phony and you can explicitly tell Make they're not associated with files, e.g.:
.PHONY: clean clean: rm -rf *.o
make clean will run as expected even if you do have a file named
In terms of Make, a phony target is simply a target that is always out-of-date, so whenever you ask
make <phony_target>, it will run, independent from the state of the file system. Some common
make targets that are often phony are:
NOTE: The make tool reads the makefile and checks the modification time-stamps of the files at both the side of ':' symbol in a rule.
In a directory 'test' following files are present:
[email protected]:~/test$ ls hello hello.c makefile
In makefile a rule is defined as follows:
hello:hello.c cc hello.c -o hello
Now assume that file 'hello' is a text file containing some data, which was created after 'hello.c' file. So the modification (or creation) time-stamp of 'hello' will be newer than that of the 'hello.c'. So when we will invoke 'make hello' from command line, it will print as:
make: `hello' is up to date.
Now access the 'hello.c' file and put some white spaces in it, which doesn't affect the code syntax or logic and then save and quit. Now the modification time-stamp of hello.c is newer than that of the 'hello'. Now if you invoke 'make hello', it will execute the commands as:
cc hello.c -o hello
And the file 'hello' (text file) will be overwritten with a new binary file 'hello' (result of above compilation command).
If we use .PHONY in makefile as follow:
.PHONY:hello hello:hello.c cc hello.c -o hello
and then invoke 'make hello', it will ignore if any file present in the pwd named 'hello' and execute the command every time.
Now suppose if no dependencies of target is there in makefile:
hello: cc hello.c -o hello
and 'hello' file is already present in the pwd 'test', then 'make hello' will always show as:
make: `hello' is up to date.
It is a build target that is not a filename.
- means the word "install" doesn't represent a file name in this Makefile;
- means the Makefile has nothing to do with a file called "install" in the same directory.
The best explanation is the GNU make manual itself: 4.6 Phony Targets section.
.PHONY is one of make's Special Built-in Target Names. There are other targets that you may be interested in, so it's worth skimming through these references.
When it is time to consider a .PHONY target, make will run its recipe unconditionally, regardless of whether a file with that name exists or what its last-modification time is.
You may also be interested in make's Standard Targets such as
The Makefile builds the
hello executable if any one of
factorial.cpp changed. The smallest possible Makefile to achieve that specification could have been:
hello: main.cpp hello.cpp factorial.cpp g++ -o hello main.cpp hello.cpp factorial.cpp
- pro: very easy to read
- con: maintenance nightmare, duplication of the C++ dependencies
- con: efficiency problem, we recompile all C++ even if only one was changed
To improve on the above, we only compile those C++ files that were edited. Then, we just link the resultant object files together.
OBJECTS=main.o hello.o factorial.o hello: $(OBJECTS) g++ -o hello $(OBJECTS) main.o: main.cpp g++ -c main.cpp hello.o: hello.cpp g++ -c hello.cpp factorial.o: factorial.cpp g++ -c factorial.cpp
- pro: fixes efficiency issue
- con: new maintenance nightmare, potential typo on object files rules
To improve on this, we can replace all object file rules with a single
OBJECTS=main.o hello.o factorial.o hello: $(OBJECTS) g++ -o hello $(OBJECTS) .cpp.o: g++ -c $< -o [email protected]
- pro: back to having a short makefile, somewhat easy to read
.cpp.o rule defines how to build
$<matches to first dependency, in this case,
[email protected]matches the target, in this case,
The other changes present in the Makefile are:
- Making it easier to changes compilers from g++ to any C++ compiler.
- Making it easier to change the compiler options.
- Making it easier to change the linker options.
- Making it easier to change the C++ source files and output.
- Added a default rule 'all' which acts as a quick check to ensure all your source files are present before an attempt to build your application is made.