[makefile] What is the purpose of .PHONY in a Makefile?

What does .PHONY mean in a Makefile? I have gone through this, but it is too complicated.

Can somebody explain it to me in simple terms?

This question is related to makefile phony-target

The answer is


.PHONY: install
  • 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.

I often use them to tell the default target not to fire.

superclean: clean andsomethingelse

blah: superclean

clean:
   @echo clean

%:
   @echo catcher $@

.PHONY: superclean

Without PHONY, make superclean would fire clean, andsomethingelse, and catcher superclean; but with PHONY, make superclean won't fire the catcher superclean.

We don't have to worry about telling make the clean target is PHONY, because it isn't completely phony. Though it never produces the clean file, it has commands to fire so make will think it's a final target.

However, the superclean target really is phony, so make will try to stack it up with anything else that provides deps for the superclean target — this includes other superclean targets and the % target.

Note that we don't say anything at all about andsomethingelse or blah, so they clearly go to the catcher.

The output looks something like this:

$ make clean
clean

$ make superclean
clean
catcher andsomethingelse

$ make blah 
clean
catcher andsomethingelse
catcher blah

It is a build target that is not a filename.


The special target .PHONY: allows to declare phony targets, so that make will not check them as actual file names: it will work all the time even if such files still exist.

You can put several .PHONY: in your Makefile :

.PHONY: all

all : prog1 prog2

...

.PHONY: clean distclean

clean :
    ...
distclean :
    ...

There is another way to declare phony targets : simply put :: without prerequisites :

all :: prog1 prog2

...

clean ::
    ...
distclean ::
    ...

The :: has other special meanings, see here, but without prerequisites it always execute the recipes, even if the target already exists, thus acting as a phony target.


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.

Consider:

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 from filefwd to file, now fileall does not get rebuilt every time, but only when any of dependent targets is stale against it as a file.


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 all and clean.


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.

Example

In a directory 'test' following files are present:

prerit@vvdn105:~/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 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 any file present in the pwd 'test' and execute the command every time.

Now suppose, that 'hello' target has no dependencies declared:

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.

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 all, install, clean, distclean, and so on.