Why makefile requests folder for generated file - makefile

I wrote a makefile to compile for generated file
%.o: %.cc
g++ -c $< -o $#
default: gen main.o
gen:
touch main.cc
And got
$ make default
touch main.cc
make: *** No rule to make target 'main.o', needed by 'default'. Stop.
But if I add a folder for the generated .cc and .o files, it works
obj/%.o: src/%.cc
g++ -c $< -o $#
default: gen obj/main.o
gen:
touch src/main.cc
Why the folder obj and src are requested

They're not. This behavior comes from make's optimizations and your Makefile not being sincere about what it is doing.
When make reads a directory, it hashes all the directory contents and performs future file lookups in this hash. When make starts for the first time, there is no main.cc file, so the directory hash does not contain an entry for main.cc. Since your Makefile generates this file, but does not declare it, make does not know that there is a target that generates main.cc file and when it looks up in the hash, the file is not there. Therefore your implicit rule is rejected as impossible:
$ make -dr default
...
Considering target file 'default'.
File 'default' does not exist.
Looking for an implicit rule for 'default'.
No implicit rule found for 'default'.
Considering target file 'gen'.
File 'gen' does not exist.
Finished prerequisites of target file 'gen'.
Must remake target 'gen'.
touch main.cc
Putting child 0x55de3daaa6a0 (gen) PID 6075 on the chain.
Live child 0x55de3daaa6a0 (gen) PID 6075
Reaping winning child 0x55de3daaa6a0 PID 6075
Removing child 0x55de3daaa6a0 PID 6075 from chain.
Successfully remade target file 'gen'.
Considering target file 'main.o'.
File 'main.o' does not exist.
Looking for an implicit rule for 'main.o'.
Trying pattern rule with stem 'main'.
Trying implicit prerequisite 'main.cc'.
Trying pattern rule with stem 'main'.
Trying implicit prerequisite 'main.cc'.
Looking for a rule with intermediate file 'main.cc'.
Avoiding implicit rule recursion.
No implicit rule found for 'main.o'.
Finished prerequisites of target file 'main.o'.
Must remake target 'main.o'.
make: *** No rule to make target 'main.o', needed by 'default'. Stop.
If you strace this invocation, you will notice two things: a) a getdents64 call that reads directory contents (used for lookup and hash) and b) lack of stat for main.cc at all, meaning that there is no check for file existence on the disk, since its presence was resolved from cache:
$ strace make -r default
...
stat(".", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
openat(AT_FDCWD, ".", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 3
fstat(3, {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
getdents64(3, /* 3 entries */, 32768) = 80
getdents64(3, /* 0 entries */, 32768) = 0
close(3) = 0
...
stat("gen", 0x7fff4f66a3d0) = -1 ENOENT (No such file or directory)
stat("main.o", 0x7fff4f66a460) = -1 ENOENT (No such file or directory)
... <-- locale-related calls for error message
write(2, "make: *** No rule to make target"..., 71make: *** No rule to make target 'main.o', needed by 'default'. Stop.
Now if you rerun the same command it will succeed, because main.cc file is already created and available when the directory is cached:
$ make -dr default
...
Considering target file 'default'.
File 'default' does not exist.
Looking for an implicit rule for 'default'.
No implicit rule found for 'default'.
Considering target file 'gen'.
File 'gen' does not exist.
Finished prerequisites of target file 'gen'.
Must remake target 'gen'.
touch main.cc
Putting child 0x555646acc6a0 (gen) PID 6079 on the chain.
Live child 0x555646acc6a0 (gen) PID 6079
Reaping winning child 0x555646acc6a0 PID 6079
Removing child 0x555646acc6a0 PID 6079 from chain.
Successfully remade target file 'gen'.
Considering target file 'main.o'.
File 'main.o' does not exist.
Looking for an implicit rule for 'main.o'.
Trying pattern rule with stem 'main'.
Trying implicit prerequisite 'main.cc'.
Found an implicit rule for 'main.o'.
Considering target file 'main.cc'.
Looking for an implicit rule for 'main.cc'.
No implicit rule found for 'main.cc'.
Finished prerequisites of target file 'main.cc'.
No need to remake target 'main.cc'.
Finished prerequisites of target file 'main.o'.
Must remake target 'main.o'.
g++ -c main.cc -o main.o
Putting child 0x555646ad0690 (main.o) PID 6080 on the chain.
Live child 0x555646ad0690 (main.o) PID 6080
Reaping winning child 0x555646ad0690 PID 6080
Removing child 0x555646ad0690 PID 6080 from chain.
Successfully remade target file 'main.o'.
Finished prerequisites of target file 'default'.
Must remake target 'default'.
Successfully remade target file 'default'.
If you play by the rules and write your Makefile correctly, declaring the files that are being created, make will run as expected:
$ cat Makefile
%.o: %.cc
g++ -c $< -o $#
default: main.o
main.cc:
touch $#
$ make default
touch main.cc
g++ -c main.cc -o main.o

Related

patsubst's returned value when no pattern found

I'm trying to send "sub-make" commands to a subdirectory. I have a Parent Directory with a "top_level" Makefile and a child directory with its own Makefile.
My parent Makefile has a target line as follow :
target%:
make -C sub_dir $(patsubst target-%,%,$#)
I can do in the parent folder:
make target-clean && make target-all
It will be interpreted as :
make -C sub_dir clean && make -C sub_dir all
I want to be able to do:
make target
but in this case, I get :
make -C sub_dir target.o
I was expecting that while "patsubst" does not find the pattern, it will return either nothing or the tested expression. But it returns this "target.o".
Can someone explain it to me ? How can I manage to get nothing ?
I tried these expressions without success:
make -C sub_dir $(patsubst target%,%,$#)
make -C sub_dir $(patsubst -%,%,$(patsubst target%,%,$#))
make -C sub_dir $($(patsubst -%,%,$(patsubst target%,%,$#)):.o=)
The last one is tricky, it gives:
make -C sub_dir
make[1]: Entering directory /home/aurelien/Documents/Projects/parent/sub_dir'
make[1]: 'subtarget' is up to date.
make[1]: Leaving directory '/home/aurelien/Documents/Projects/parent/sub_dir'
cc target.o -o target
cc: target.o: No such file or directory
cc: no input files
make: *** [target] Error 1
The target% pattern matches but only with at least one character for the %, not zero. From the GNU make manual:
A pattern rule contains the character ‘%’ (exactly one of them) in the
target; otherwise, it looks exactly like an ordinary rule. The target
is a pattern for matching file names; the ‘%’ matches any nonempty
substring, while other characters match only themselves.
So target does not match and make uses its implicit rules to build target: build target.o and then target from target.o. When trying to find a way to build target.o, the pattern rule matches and the recipe:
make -C sub_dir $(patsubst target-%,%,$#)
is expanded. But as target.o does not match target-%, patsubst does nothing and your recipe becomes:
make -C sub_dir target.o
The problem is that a pattern must match one or more characters. So, this pattern:
target%:
does not match the target name target, because there are no characters left over to match the pattern.
So, make looks for another implicit rule that can be used to build target and it finds the built-in rule:
% : %.o
so it tries to build target.o. This target will match the pattern target% with a stem of .o, so it tries to use your rule to build that.
To specifically answer your question, any word that does not match the substitution pattern in patsubst will be passed through without changing it.

How to write a makefile for fortran with multiple executables files

based on this question A Makefile with Multiple Executables I am trying to write a fortran version.
The source code is divided in several *.f90 files. So I tried to use the idea quoted before, here's an excerpt of my makefile:
PROGRAM := Mpois Mkvz
# Definitions
COMPILER := gfortran -O3 -ffast-math
LIBS := -fbounds-check -lm
# Directories of object code
OBJDIR = objects
SOURCES_pois := BORDERS.f90 CONVERGENCE.f90 FILESIO.f90 LBM.f90 Main_pois.f90
OBJECTS_pois := BORDERS.o CONVERGENCE.o FILESIO.o LBM.o Main_pois.o
SOURCES_kvz := BORDERS.f90 CONVERGENCE.f90 FILESIO.f90 LBM.f90 Main_KVZ.f90
OBJECTS_kvz := BORDERS.o CONVERGENCE.o FILESIO.o LBM.o Main_KVZ.o
# Linking
Mpois: $(OBJECTS_pois)
$(COMPILER) $^ -o $# $(LIBS)
# Compiling
$(OBJECTS_pois): $(OBJDIR)/%.o: %.f90
$(COMPILER) -c $< -o $#
# Linking
Mkvz: $(OBJECTS_kvz)
$(COMPILER) $^ -o $# $(LIBS)
# Compiling
$(OBJECTS_kvz): $(OBJDIR)/%.o: %.f90
$(COMPILER) -c $< -o $#
clean:
rm -f $(OBJDIR)/*.o
The idea was that when invoking
make
in a linux terminal the results should be two different executable files. Nevertheless I receive the following message:
makefile:21: target `BORDERS.o' doesn't match the target pattern
makefile:21: target `CONVERGENCE.o' doesn't match the target pattern
makefile:21: target `FILESIO.o' doesn't match the target pattern
makefile:21: target `LBM.o' doesn't match the target pattern
makefile:21: target `Main_pois.o' doesn't match the target pattern
makefile:29: target `BORDERS.o' doesn't match the target pattern
makefile:30: warning: overriding commands for target `BORDERS.o'
makefile:22: warning: ignoring old commands for target `BORDERS.o'
makefile:29: target `CONVERGENCE.o' doesn't match the target pattern
makefile:30: warning: overriding commands for target `CONVERGENCE.o'
makefile:22: warning: ignoring old commands for target `CONVERGENCE.o'
makefile:29: target `FILESIO.o' doesn't match the target pattern
makefile:30: warning: overriding commands for target `FILESIO.o'
makefile:22: warning: ignoring old commands for target `FILESIO.o'
makefile:29: target `LBM.o' doesn't match the target pattern
makefile:30: warning: overriding commands for target `LBM.o'
makefile:22: warning: ignoring old commands for target `LBM.o'
makefile:29: target `Main_KVZ.o' doesn't match the target pattern
gfortran -O3 -ffast-math -c -o BORDERS.o
gfortran: fatal error: no input files; unwilling to write output files
compilation terminated.
make: *** [BORDERS.o] Error 4
Looking after hints and advice I salute you.-

Right way to use VPATH/vpath?

I am trying to use my Makefile (Make for Windows) by adding source paths to vpath/VPATH. This seems trivial but for some reason I am unable to get it to work
My directory structure is like this:
├── Makefile
├── out\
└── src\
└── hello.cpp
My Makefile is:
TGT=out
OBJ=hello.o
VPATH=src
# vpath %.cpp src
all: $(TGT)\app.exe
$(TGT)\app : $(TGT)\$(OBJ)
g++ $^ -o $#
$(TGT)\%.o : %.cpp
g++ -Wall -Wextra -Werror -c $<
changing to vpath didn't help me. I seem to have something fundamentally wrong here. The error I see is:
make: *** No rule to make target `out\hello.o', needed by `out\app'. Stop.
EDIT: debug output from make -d
Considering target file `all'.
File `all' does not exist.
No implicit rule found for `all'.
Considering target file `out\app'.
File `out\app' does not exist.
Considering target file `out\hello.o'.
File `out\hello.o' does not exist.
Looking for an implicit rule for `out\hello.o'.
Trying pattern rule with stem `hello'.
Looking for a rule with intermediate file `out\hello.cpp'.
Avoiding implicit rule recursion.
Trying pattern rule with stem `hello.cpp'.
No implicit rule found for `out\hello.o'.
Finished prerequisites of target file `out\hello.o'.
Must remake target `out\hello.o'.
As MadScientist points out you should avoid backslashes as they have odd results like this, had you used forward slashes throughout your Makefile you wouldn't have had this issue, that said it is possible to work around them.
There are a few things wrong here:
You haven't posted the same Makefile you're using again, the first rule after all should have $(TGT)\app.exe as a target.
A backslash before % in a pattern rule will turn it into a literal %, escape the backslash
You forgot to tell gcc where to output the object file
Once you've fixed all of this you should find vpath works as expected, the complete fixed Makefile is
TGT=out
OBJ=hello.o
vpath %.cpp src
all: $(TGT)\app.exe
$(TGT)\app.exe : $(TGT)\$(OBJ)
g++ $^ -o $#
$(TGT)\\%.o : %.cpp
g++ -Wall -Wextra -Werror -c $< -o $#

Match patten rule before explicit rule

I'm trying to generically add some behaviour to every target in a Makefile, without modifying the targets.
My current attempt is thus:
%: $*
#echo 'Logging $* target'
.PHONY: test
test:
#echo 'Inside explicit test target'
When I run make test, I'd like to match the % pattern rule, which would execute test as a prerequisite ($* expanding to the pattern stem), and then log the target that was run.
$ make test
Inside explicit test target
Logging test target
Instead, what happens is that make test matches the explicit test target (presumably since it's a closer match):
$ make test
Inside explicit test target
How can I get this to work, without changing the explicit test target?
EDIT:
Another attempt...
.SECONDEXPANSION:
%: $$*
#echo 'Logging $* target'
results in
$ make test
make: Circular Makefile <- Makefile dependency dropped.
inside actual test target
I appears from your own answer, which has beaten me to the punch, that
you're concerned only to trigger a preliminary action for targets that are
mentioned on the commandline - $(MAKECMDGOALS). From the posting I took
it that you wanted such an action for "every target in a Makefile", which
would include all targets that are prerequisite to the commandline targets or,
if there are no commandline targets, to the default target.
Anyhow, you may still be interested in a solution to the more general problem.
You want a preliminary action to be executed before the recipe for every target.
Your question is: how to match a patten rule before explicit rule?
This is an XY way of posing the problem, because make will consult pattern
rules to find a way of making a target only if you don't give it an explicit
recipe. You know, for example, that make has a pre-defined pattern rule for
making an .o file from a .c file. Even so, if my makefile is:
test.o:
#echo $#
then make prints test.o, without any attempt to find test.c and compile it.
And if my make file is:
test.o: test.c
#echo $#
test.c:
#echo $#
then make prints:
test.c
test.o
needing no resort to the pattern rule. But if my makefile is:
test.o: test.c
Then make says:
make: *** No rule to make target 'test.c', needed by 'test.o'. Stop
So you can't do what you're after in the way your question supposes,
because the preliminary action you want to provoke from the pattern
rule could be provoked only if there were no other action for the target.
In that case the reasons for the failures of your two posted attempts are fairly academic,
and you may wish to scroll to The Chase.
In your first attempt, with:
%: $*
#echo 'Logging $* target'
The pattern rule - which is unemployed by make test - is equivalent to:
%:
#echo 'Logging $* target'
because $* only assumes a value in the recipe, not in the pattern rule. You
can make this pattern rule be employed by making any target for which the
makefile does not provide a recipe, e.g. make nonsuch will print Logging nonsuch target;
but that is of no use.
The second attempt, with:
.SECONDEXPANSION:
%: $$*
#echo 'Logging $* target'
does the right thing to create the rule you intend to create. But the
meaning of that rule is:
<target>: <target>
#echo 'Logging <target> target'
making every target to which this rule is applied a prerequisite of itself.
Inevitably this will result in a circular dependency error for all such targets.
As you saw, this circularity does not affect the your test target because
it has an explicit recipe and does not employ the rule. But it does provoke
the surprising error:
make: Circular Makefile <- Makefile dependency dropped.
That happens because the first target that make automatically considers is
the makefile itself. Unlike the test target, you have no recipe for
the makefile; so the pattern rule applies to it, making the makefile dependent
on itself.
The Chase
You can achieve what you want by a different approach. In a actual project
it is more than likely that in any makefile you can compute a list of
all possible targets. From this you can generate a corresponding list of
auxiliary targets, say, target => target.prelim, where the
sole purpose of target.prelim is to provoke, when it should and not
otherwise, the required preliminary action for target; and you can get make
to generate a list of order-only rules, target: | target.prelim,
for each target, such that target.prelim will not be considered in determining whether target
must be made, but will be made before target whenever target needs to be made.
Here is an illustration:
SRCS := main.c foo.c
OBJS := $(SRCS:.c=.o)
TARGETS := all prog $(OBJS)
PRELIMS := $(patsubst %,%.prelim,$(TARGETS))
define prelim_rule =
$(1): | $(1).prelim
endef
$(foreach target,$(TARGETS),$(eval $(call prelim_rule,$(target))))
.PHONY: all
all: prog
prog: $(OBJS)
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $# $(OBJS) $(LIBS)
clean:
rm -f $(OBJS) $(PRELIMS) prog
%.prelim:
#echo "Logging target $(#:%.prelim=%)"
#touch $#
And a sample session:
$ make
Logging target all
Logging target main.o
cc -c -o main.o main.c
Logging target foo.o
cc -c -o foo.o foo.c
Logging target prog
cc -o prog main.o foo.o
$ make
make: Nothing to be done for 'all'.
$ make clean
rm -f main.o foo.o all.prelim prog.prelim main.o.prelim foo.o.prelim prog
$ make main.o
Logging target main.o
cc -c -o main.o main.c
$ make main.o
make: 'main.o' is up to date.
$ # A prelim can't out-date its target...
$ touch main.o.prelim
$ make main.o
make: 'main.o' is up to date.
I realise that this isn't answering my question as asked, but it has the effect I want - executing a shell command as late in the Makefile processing as possible.
MYVAR?=foo
.PHONY: test
test:
#echo 'Inside test target'
LOG=$(shell echo 'Logging $(MAKECMDGOALS), myvar=$(MYVAR)' > log)
.SECONDEXPANSION:
force: $$(LOG)
LOG is a deferred variable, so is not expanded until Make evaluates the prerequisite list of the force target.
In a single Makefile, the .SECONDEXPANSION: part is not needed, since the force target is evaluated after MYVAR is set.
However, if I move the LOG variable and force variable into a sub-makefile, it would be easy to include subMakefile before the MYVAR?= line - which would not work.
By specifying .SECONDEXPANSION for force, the reliance on ordering is removed.

How can I use a pattern rule to add prerequisites like I can to define variables?

I have the following Makefile:
all: foo/bar/baz
foo/%:
#echo $(VAR)
cp $#.in $#
# This works
foo/bar/%: VAR := Hello world
# This doesn't
foo/bar/%: foo/bar/%.in
foo/bar/baz.in:
touch $#
When I run it, the output is
Hello world
cp foo/bar/baz.in foo/bar/baz
cp: cannot stat ‘foo/bar/baz.in’: No such file or directory
Makefile:4: recipe for target 'foo/bar/baz' failed
make: *** [foo/bar/baz] Error 1
In other words, the pattern-specific variable rule works, but the equivalent syntax to declare an extra prerequisite doesn't. What should I do instead?
The real use case is for copying headers before a build. I wrote
obj/subdir/%.o: CPPFLAGS += -Igen/include
obj/subdir/%.o: | gen/include
gen/include:
# Copy the headers
but the headers don't get copied.
You cannot do this. Pattern rules must define all prerequisite patterns when the rule is created; they cannot be added later.
Writing a pattern rule with no recipe deletes the pattern rule.

Resources