Makefile ignores existing directory - makefile

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.

Related

Makefile always builds even when no changes

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 $#

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 keeps building the wrong target with VPATH

From the docs, regarding VPATH:
After processing the prerequisites, the target may or may not need
to be rebuilt:
a. If the target does _not_ need to be rebuilt, the path to the
file found during directory search is used for any
prerequisite lists which contain this target. In short, if
'make' doesn't need to rebuild the target then you use the
path found via directory search.
b. If the target _does_ need to be rebuilt (is out-of-date), the
pathname found during directory search is _thrown away_, and
the target is rebuilt using the file name specified in the
makefile. In short, if 'make' must rebuild, then the target
is rebuilt locally, not in the directory found via directory
search.
Illustrating the above, with the following makefile - which is basically the same situation as section b. above - where the vpath-ized target turns outs to be out-of-date, we write the Makefile, as:
$(shell rm -rf all D)
$(shell mkdir D)
$(shell touch D/all)
VPATH = D
all: phony
echo '$#'
.SILENT: D/all
.PHONY: phony
Running, I get:
echo 'D/all'
D/all
Now, given that above quote that:
"if the target does need to be rebuilt (is out-of-date), the pathname found during directory search is thrown away, and the target is rebuilt using the file name specified in the makefile"
Why, then does Make build D/all, as evident by the output of the command, and not the original (before it was vpath-ized) target?
Simply make builds D/all because you have specified .SECONDARY: D/all which prevents make from "automatically deleting", i.e. throwing away, the pathname of D/all.
If you remove (or comment out) .SECONDARY: D/all and run make --debug on
$(shell rm -rf all D)
$(shell mkdir D)
$(shell touch D/all)
VPATH = D
all: phony
echo '$#'
.PHONY: phony
the output should be similar to
Reading makefiles...
Updating goal targets....
File 'phony' does not exist.
Must remake target 'phony'.
Successfully remade target file 'phony'.
Prerequisite 'phony' of target 'all' does not exist.
Must remake target 'all'.
Ignoring VPATH name 'D/all'.
echo 'all'
all
Successfully remade target file 'all'.
Here we can clearly see make now throws away the pathname of D/all and rebuilds all locally as per the documentation you've quoted.
.SECONDARY is documented in Special Targets in the GNU Make Documentation.

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 $<

Resources