Makefile always builds even when no changes - makefile

I have the following Makefile for running pdflatex on tex source files:
MAKEFLAGS += --warn-undefined-variables
deps := mydoc.tex mydoc.cls
mydoc.pdf: $(deps)
.PHONY: build
build: $(deps)
pdflatex mydoc.tex mydoc.pdf
.PHONY: build
clean: ## Delete misc
rm -f mydoc.out mydoc.pdf mydoc.aux mydoc.log
When I run make build it always runs pdflatex even though mydoc.tex has not changed.
My understanding is make build should say there is nothing to do if mydoc.tex has not changed. What am I doing wrong?

First, you've declared the target build to be .PHONY. The entire point of a phony target is that it will always be considered out of date and its recipe will be invoked. So, of course the recipe is always run.
Even if you remove the .PHONY though, the recipe will always be run. You say make should do nothing is mydoc.tex has not changed... well, how can make know that mydoc.tex has not changed? Not changed compared to what? Make doesn't have its own database that tells it the last time it ran, and what all the timestamps on the files were at some time in the past. It simply relies on comparing timestamps of files on the filesystem as they exist now.
When you write a rule it tells make, if any of the prerequisites have a newer modification time than the target, then the target is out of date and the recipe should be run to bring it up to date.
So if you write a rule build: mydoc.tex make will look to see if the prerequisite mydoc.tex is newer than the target build. Since there is no file build and one is never created, mydoc.tex will always be considered newer than a non-existent file, and the recipe will always be run.
You need to be sure that the target of the rule is the file that is updated by the recipe. Best practice is to ensure that every recipe you write (that updates a file) updates the file contained in the $# automatic variable:
mydoc.pdf: $(deps)
pdflatex mydoc.tex $#

Related

Makefile ignores existing directory

I have Makefile:
MAKEFILE_DIR:=$(dir $(realpath $(lastword $(MAKEFILE_LIST))))
DIR:=$(MAKEFILE_DIR)/some/where
.PHONY: load install
load: $(DIR)
some-actions...
$(DIR): install
another-actions...
when I run make load I see that $(DIR) is treated as missing and $(DIR) goal is called, so install is called and another-actions too. This happens each time. Where is the error? How to fix it so $(DIR) will be called only when it does not exist?
PS. I had idea that mtime of the $(DIR) changes, may be it's changing after each "make" run. I checked it with stat, seems it does not change.
Every .PHONY target is treated as if it does not exist. Therefore, it's always rebuilt. Every time any prerequisite is rebuilt, the target (in this case, $(DIR)) IS ALSO REBUILT.
How to fix it
Either drop out install from $(DIR) (in fact, I dont' understand why install should be done before creating dir?), or make it an order-only prerequisite (which means exactly that it never triggers rebuilding of its target).
I had idea that mtime of the $(DIR) changes, may be it's changing after each "make" run. I checked it with stat, seems it does not change.
But it can change. So usually directories are made order-only prerequisites (although it seems irrelevant in this particular case).
Yes, the reason is phony dependency of $(DIR). It will be tested every time and becauseit's phony then it will be re-making. This can be avoided with:
define install
commands-of-install...
endef
$(DIR): # no more install dep here
$(call install)
another-actions...
So $(DIR) is just directory without prerequisites, so it will be re-making only it does not exist.

Makefile directory rule dependency rebuild

I got Makefile like this
all: sub-dir my-rule
sub-dir:
$(MAKE) -C $#
my-rule: sub-dir
g++ main.cpp
.PHONY sub-dir
So basically I want to wait for sub-dir to finish before building my-rule but my-rule is rebuilt everytime - even if there where no changes in sub-dir.
How can I make it to wait for sub-dir and rebuild my-rule only when there were changes in sub-dir?
When you write a rule like:
my-rule: sub-dir
the target (my-rule) will be rebuilt if the prerequisite (sub-dir) is newer. Make doesn't care whether the target or prerequisite are files are directories, only what their last modified time is.
Your makefile has many issues. The simplest one is that you never create a target my-rule, so as far as make is concerned that target is always out of date (non-existent == out of date).
You have to write your rule like this so that the recipe updates the target:
my-rule: sub-dir
g++ main.cpp -o $#
Of course change my-rule if that's not the program you want to create.
Second, directory modification times are updated when the directory changes. The directory changes when something in that directory is renamed, added, or removed. So if you invoke the sub-make and the sub-make renames, adds, or removes something in that directory then the my-rule target will be out of date. If nothing is renamed, added, or removed, then my-rule will NOT be out of date.
In general you almost never want to list a directory as a prerequisite. Instead, you should list the targets that the sub-make creates as the prerequisite, like this (supposing the sub-make creates libfoo.):
sub-dir/libfoo.a: FORCE
$(MAKE) -C $(#D)
FORCE:
my-rule: sub-dir/libfoo.a
...
The FORCE rule is there to force the sub-make to be invoked even if sub-dir/libfoo.a already exists.

Make file possible accidental order-only dependency?

I am pulling out my hair trying to debug an issue with make. It seems like make is randomly treating certain prerequisites as order-only prerequisites, resulting in them being left out of the static library target that depends on them. Most of the time the build works find but occasionaly some .cpp files are built but not included in the .a. When i run Make with --debug I see the following output for the suspect prerequisites.
Prerequisite `blah.o' is newer than target `/path/to/foo.a`
Prerequisite `blah1.o' is newer than target `/path/to/foo.a`
Prerequisite `blah2.o' is newer than target `/path/to/foo.a`
No need to remake target `/path/to/foo.a'
For all of the prereqs that do make it into the .a the last line is "Must remate target /path/to/foo.a" as I would expect.
Because make is invoked in several subdirectories, target /path/to/foo.a is updated several times. We are not running make in parallel so I don't think updates to the file are stomping each other. It seems that make is deliberately not updating the .a file despite the fact that the .o's are newer. The recipe to make foo.a is as follows:
$(OBJLIB): $(OBJS)
$(AR) $(ARFLAGS) $(OBJLIB) $?
Where ARFLAGS=rv and OBJLIB would be /path/to/foo.a.
Am i right in thinking that the .o files are being treated as order-only dependencies? Is there something else I'm missing here? I am using $(info) to output the contents of OBJLIB and OBJS and there are no errant pipe ('|') characters making their way into the variable contents that would induce order-only dependencies.
Unfortunately the answer had nothing to do with make. As far as I can tell the filesystem is the real culprit. Several people were experiencing success with the build but I was not. The difference between our systems which were using a common build environment was that I was building on an ext3 filesystem while they were using an ext4 filesystem.
Since ext3 does not support sub-1s timestamps (ext4 does) in some cases when the rule was invoked with only a few CPP files they were being compiled in the same second that the archive was updated by a previous invocation and everything was ending up with the same timestamps. Copying the directory over to an ext4 filesystem fixed the issue.
The real fix is to write a proper set of make rules but at least we have an answer as to why it was working for everyone but me.
You mentioned several updates to the .a file because make is invoked in different subdirectories. Probably the message
No need to remake target `/path/to/foo.a'
comes from one subdirectory, and is newer- from another. Consider building the lib out of all objects in one step.
Try this instead.
$(OBJLIB): $(OBJS)
$(AR) $(ARFLAGS) $(OBJLIB) $^
Your problem is that the variable $? is a list of dependencies that are newer than the target, while $^ is a list of all dependencies.
Also, you can use $# for to be more idiomatic.
$(OBJLIB): $(OBJS)
$(AR) $(ARFLAGS) $# $^

making all rules depend on the Makefile itself

When I change a Makefile, its rules may have changed, so they should be reevaluated, but make doesn't seem to think so.
Is there any way to say, in a Makefile, that all of its targets, no matter which, depend on the Makefile itself?
(Regardless of its name.)
I'm using GNU make.
This looks like one more simple, useful, logical thing that Make should be able to do, but isn't.
Here is a workaround. If the clean rule is set up correctly, Make can execute it whenever the makefile has been altered, using an empty dummy file as a marker.
-include dummy
dummy: Makefile
#touch $#
#$(MAKE) -s clean
This will work for most targets, that is targets that are actual files and that are removed by clean, and any targets that depend on them. Side-effect targets and some PHONY targets will slip through the net.
Since GNU make version 4.3 it is now possible with the use of those two special variable:
.EXTRA_PREREQS
To add new prerequisite to every target
MAKEFILE_LIST
To get the path of the make file
To have every target depend on the current make file:
Put near the top of the file (before any include since it would affect the MAKEFILE_LIST) the following line:
.EXTRA_PREREQS:= $(abspath $(lastword $(MAKEFILE_LIST)))
To have every target depend on the current make file and also the make files which were included
Put the following line at the end of your file:
.EXTRA_PREREQS+=$(foreach mk, ${MAKEFILE_LIST},$(abspath ${mk}))
The only answer I know to this is to add makefile explicitly to the dependencies. For example,
%.o: %.c makefile
$(CC) $(CFLAGS) -c $<

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?
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
Now make clean will run as expected even if you do have a file named clean.
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: all, install, clean, distclean, TAGS, info, check.
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.
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.
.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.
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.
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.
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.
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

Resources