Makefile directory rule dependency rebuild - makefile

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.

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

Is my understanding correct for the first rule of the makefile?

I'm learning make, and try to understand the following makefile from Prerequisite-Types
OBJDIR := objdir
OBJS := $(addprefix $(OBJDIR)/,foo.o bar.o baz.o)
$(OBJDIR)/%.o : %.c
$(COMPILE.c) $(OUTPUT_OPTION) $<
all: $(OBJS)
$(OBJS): | $(OBJDIR)
$(OBJDIR):
mkdir $(OBJDIR)
The first rule confuses me. It's to be firstly applied. Since $(OBJDIR) is not there, the last rule will be applied to mkdir objdir. Then, since there's nothing in the newly created directory, there's no stem.o, and correspondingly, no stem.c So the prerequisites and recipes seem to be meaningless.
The only thing that the first rule does is to make a directory, which seems to be unreal.
The first rule is a pattern rule, so it cannot be the default rule; it will not be the first applied unless you specify e.g. make thing.o.
The first rule might not do what you expect. The recipe is $(COMPILE.c) $(OUTPUT_OPTION) $<, but you don't assign a value to OUTPUT_OPTION in the makefile; unless you provide a value from outside (e.g. make thing.o OPTION_OUTPUT=...) this recipe has no special instructions about where to put the file it builds, it has never heard of objdir/, and will use the compiler's default which is probably the working directory.
The last rule will build objdir (if objdir does not already exist, and if Make invokes that rule). The command make objdir will work perfectly. If you try to build one of the object files listed in OBJS, Make will construct the directory (if the directory does not exit) -- not because it needs a place to put the file, but because $(OBJDIR) is a prerequisite of every member of $(OBJS), according to the third rule. It will not construct the directory if you try to build objdir/thing.o, because $(OBJDIR) is not a prerequisite of that target. You think Make should build it because it is obviously needed? Well, Make isn't that smart.
The first rule has a prerequisite pattern %.c, and the recipe looks for the source file in the working directory, not in any newly constructed subdirectory. If there is no such source file in the working directory, Make will not run that rule.
I don't really follow your logic.
The first rule in your makefile is this:
all: $(OBJS)
so the default goal for this makefile (if you don't specify one on the command line) is all.
all depends on all the .o files. All the .o files have an order-only dependency on the directory. So, first the directory is created, then the .o files are created, then all is done (there is no recipe here to create a program or library or anything out of those .o files).

copying only changed files between directories using Makefile

I read this: Makefile: Copying files with a rule but couldn't do it.
To make it simple, suppose I have two directories dir1 and dir2. Under dir1 I have three files: rabbit.c, tiger.c and bus.c .
I made Makefile like this:
dir2/rabbit.c:dir1/rabbit.c
dir2/tiger.c:dir1/tiger.c
dir2/bike.c:dir1/bike.c
dir2/%:
cp -f $< $#
I specified the prerequisites in three separate lines and specified the unified recipe for the three targets. I expected when I touch any file under dir1, make will copy that file to dir2. But this happend only for rabbit.c. What is wrong?
ADD(after selecting an answer) :
After realizing what's wrong by Takkat's answer, I fixed it and later modified it further and I think this is the correct simplest Makefile for this case.
.PHONY:all
LIST:=rabbit.c tiger.c bike.c
DSTFILES:=$(addprefix dir2/, $(LIST))
all: $(DSTFILES)
dir2/%:dir1/%
cp -f $< $#
Make chooses a default target in your makefile and, unless you specify differently on the command line, it builds just that target (and all prerequisites required to build that target).
The default target in a makefile is, by default, the first explicit target listed.
So in your makefile the first rule is:
dir2/rabbit.c:dir1/rabbit.c
so the first explicit target is dir2/rabbit.c, so that's all make builds.
If you want to build multiple targets by default, you need a first target that lists all the "real" targets as prerequisites; put this line first in your makefile:
all: dir2/rabbit.c dir2/tiger.c dir2/bike.c
and it will work. It's often considered good practice to declare targets like this, which don't relate to real files on the disk, as phony:
.PHONY: all

How to make a target in make that is itself named 'makefile'?

Summary: I'm dealing with a make script that generates (and optionally 'makes') a makefile. Historically it used a make make "phony target" to do so. I want to change this to make makefile because it seems more coherent and representative of what's going on. But when I change it and switch to the .FORCE idiom so it will be generated dependent on an artificial phony target, it seems the makefile is created 4 extra times for a reason I do not understand.
Details: The way the script works is that you can write either:
make -f makefile.boot
or:
make -f makefile.boot make
In the first case, it assumes you want to use the rules in makefile.boot to generate a platform-specific makefile, and then run make on that file. In the second case it assumes you only want to create the makefile but not execute it.
Here is a stripped down version of makefile.boot in make make terms that works:
top: make
$(MAKE)
make:
#echo "Pretending to generate makefile..."
cp makefile.fake makefile
The makefile we "generate" wants to be a superset of makefile.boot. It wants to be able to do the make make generation process as well, but its top target is an actual build. So makefile.fake contains
top: product
make:
#echo "Pretending to generate makefile..."
cp makefile.fake makefile
product:
#echo "Pretending to make build product..."
echo "Build Product" >> product
It works, but I had a thought:
"make make" is confusing to read, and it would be clearer if it was "make makefile" instead
An immediate problem with that is when you have a real target instead of a phony one, then if the file exists and has no dependencies it won't get rebuilt. I wanted this makefile to be created every time you did make makefile or make -f makefile.boot makefile. So I used the .FORCE idiom to depend on a phony target. Updated makefile.boot:
.FORCE
top: makefile
$(MAKE)
makefile: .FORCE
#echo "Pretending to generate makefile..."
cp makefile.fake makefile
And an updated makefile.fake:
.FORCE:
top: product
makefile: .FORCE
#echo "Pretending to generate makefile..."
cp makefile.fake makefile
product:
#echo "Pretending to make build product..."
echo "Build Product" >> product
Which seems all well and good, but it now runs the makefile generation five times:
/test$ make -f makefile.boot
Pretending to generate makefile...
cp makefile.fake makefile
make
make[1]: Entering directory '/test'
Pretending to generate makefile...
cp makefile.fake makefile
Pretending to generate makefile...
cp makefile.fake makefile
Pretending to generate makefile...
cp makefile.fake makefile
Pretending to generate makefile...
cp makefile.fake makefile
Pretending to make build product...
echo "Build Product" >> product
make[1]: Leaving directory '/test'
The first one I want, and seems like the only one I asked for. Where are all the other calls coming from? What's triggering the four additional requests for makefile? Or in the absence of understanding, is there an alternative way of achieving my intent?
Do recall that makefile is a magic target in many make implementations, including GNU Make.
If the target makefile exists, then make will remake the makefile before doing anything else, and when it's finished it'll restart processing with the new makefile. That means that your makefile target may be run even if you don't ask for it.
Since the default action when you do make -f makefile.boot is to run make, then that's at least two potential runs of the makefile target's actions right there, before it's even looked at the product target. I can't quite make this add up to five runs, but I'd lay money that it's this special behaviour that's causing the unexpected repeats.

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