Conditional statements using Make - makefile

I'm having a problem with conditional statements in make. Existing questions on SO only work on the highest level in the Makefile.
Here's what I have:
.PHONY: all
all: bar/*.o
bar/%.o: foo/%.cc
ifeq (,$(wildcard $(#D)))
#echo $(wildcard $(#D))
endif
There are two empty files under foo, a.cc and b.cc. The folder bar exists.
Here's the output:
PS C:\Users\cbrown2\Src> mingw32-make
bar
bar
It enters the ifeq statement, even though $(wildcard $(#D)) gives bar.
What gives?

You cannot use conditionals this way. When make evaluates your conditional automatic variables don't have a value yet. So make will see:
bar/%.o: foo/%.cc
#echo $(wildcard $(#D))
because $(#D) expands as the empty string. And when make will pass the recipe to the shell it will first expand $(#D) which, this time, has a value: bar.
One side note that has nothing to do with your problem: when bar does not contain all object files yet,
all: bar/*.o
will not do what you probably want. What you probably want is:
SRC := $(wildcard foo/*.cc)
OBJ := $(patsubst foo/%.cc,bar/%.o,$(SRC))
all: $(OBJ)

Related

variable doesn't get exanded for patsubst in makefile

I'm trying to write a rule that would replace a set of .c files with .o files for a variable.
To make sure it works, I'm echoing their values.
the .c files are obtained from the current working directory via a shell wildcard
sources := *.c
However, nothing gets printed out for the .o files, as 'sources' doesn't seem to get expanded for this, whether I do with using patsubst or the handier $(src:%.c=%.o).
On the other hand, if I set the value of 'manually', everything works as expected, e.g.
sources := source1.c source2.c source3.c
The makefile:
sources := *.c
srcs = one.c two.c
objects := $(sources:.c=.o)
objs := $(patsubst %.c, %.o, $(srcs))
prints:
#echo sources
#echo $(sources)
#echo objects
#echo $(objects)
#echo $(objs)
Output:
sources
source1.c source2.c source3.c source4.c
objects
*.o
one.o two.o
As you can see, 'objects' (based on the wildcard-using 'sources') is a no-go, but 'objs' (based on the manally-set 'srcs' works as expected).
What am I missing. I know it must have to do with when or how the expansion takes place.
Make is not the shell. This line:
sources := *.c
sets the make variable sources to the literal string *.c. Then this line:
objects := $(sources:.c=.o)
sets objects to the literal string *.o.
Then this line:
#echo $(sources)
Sends echo *.c to the shell, and the SHELL expands the *.c to the list of files that match that glob.
Then this line:
#echo $(objects)
sends echo *.o to the shell, and since there are no files that match the *.o glob, it just prints the glob itself *.o.
If you want to get a list of files in make you have to use the wildcard function:
sources := $(wildcard *.c)
Also, you have fallen prey to two different common errors people make when they are trying to debug makefiles:
First, you should never, EVER use # in your recipe lines to hide the commands. This is like trying to debug your makefile with a blindfold on. Seeing what make is sending to the shell gives you absolutely vital information as to what is happening here. I personally don't use # even after my makefile is working, except for some trivial operations, and I definitely never use it while trying to debug.
Second, if you want to see the contents of make variables in recipes you should always use single quotes around them. If you leave them unquoted or use double quotes, then the shell will come along and mess with those values (depending on what the values are) and you won't be able to see what they really are. Either use '' around them or else use a make function like $(info ...) to see their value rather than a shell program like echo.
If you had written your test makefile like this:
prints:
echo sources
echo '$(sources)'
echo objects
echo '$(objects)'
echo '$(objs)'
it would have been more immediately obvious what the problem was.

How do I check values of variables from an included makefile?

../Makefile
BAR=sometext
Makefile
include ../Makefile
FOO=othertext
ifneq ($(BAR),)
FOOBAR=$(FOO) $(BAR)
else
FOOBAR=$(FOO)
endif
all:
#echo $(FOOBAR)
This will print "othertext" regardless of whether I have BAR defined in ../Makefile. Why is that? If i have BAR defined locally, it will print "othertext sometext" which is what I want. But I'm stuck using an include in my situation.
I've tried various scenarios using $(value $(BAR)) $(strip $(BAR)), etc but I can't seem to get this to work how I want.
You could do this other way around. In Makefile:
FOO=othertext
BAR=$(FOO)
include ../Makefile
all:
#echo $(BAR)
and ../Makefile
BAR+=sometext

Pass argument of call to another function in Makefile

I would like a way to take the argument to a call invocation in a Makefile rule and pass that to a builtin function, in this case wildcard.
This doesn't seem to work:
MODULE_OBJS = $(patsubst %.cc, %.o, $(wildcard $(1)/*.cc))
lib%.a: $(call MODULE_OBJS, %)
#echo $# : $^
In this case I would expect make libfoo.a to print a list of .o files corresponding to the .cc files found under foo/, but nothing is printed. The parameter is getting there because if I append $(1) to the end of MODULE_OBJS the value of % gets printed, but it seems to be lost when getting passed into wildcard.
You need to understand that make will execute $(call MODULE_OBJS, %) before it has even begun building the dependency tree, you cannot accomplish this with a pattern rule. You could use eval hackery but there's a case to made against trying to be too clever with make.
Something like the following is easy enough to maintain
MODULE_OBJS = $(patsubst %.cc, %.o, $(wildcard $(1)/*.cc))
libfoo.a: $(call MODULE_OBJS, foo)
lib%.a:
#echo $#: $^
but after wrestling with clever ways of generating library and binary dependencies I prefer simply listing them explicitly.
I got what I wanted with some hacking of the eval rule:
EXCLUDE_MODULES = obj
MODULES = $(filter-out $(EXCLUDE_MODULES), $(patsubst %/, %, $(wildcard */)))
define MODULE_RULE
lib$(MODULE).a: $(patsubst %.cc, obj/%.o, $(wildcard $(MODULE)/*.cc))
#echo $# : $^
endef
$(foreach MODULE, $(MODULES), $(eval $(MODULE_RULE)))
This allows you to call make libfoo.a and get out a list of all the .o's corresponding with the .cc's in that subdirectory.
For those curious, I uploaded a complete example here.
The Metaprogramming Make articles were a useful resource here.

filter function in Makefile

Is there a way to pick up the target name using automatic variable.
SOURCES = $(wildcard *.c)
dummytgt: $(OBJ)/tier.o
$(GCC) $(CFLAGS) -c $(filter $#,$(SOURCES)).c -o $#
I do not want to mention the filename as input but would want to use the filter function to get the .c file which is same as target name. make throws an error no input files
It's helpful to have a look at how make parses this:
SOURCES = $(wildcard *.c)
dummytgt: $(OBJ)/tier.o
$(GCC) $(CFLAGS) -c $(filter $#,$(SOURCES)).c -o $#
First off,
it will read the makefile, defining and expanding macros as it goes.
SOURCES = $(wildcard *.c) means that ${SOURCES} is a lazy variable with definition $(wildcard *.c).
Lazy? Yes, these recursive variables (as the make manual has it) only expand their right-hand side when they are themselves expanded.
Make needs the dependencies as it reads the file, so $(OBJ) is expanded.
Let's assume that the expansion of ${OBJ} is objs (say).
The shell command block remains as a single lazy variable.
It's important to note that these are not expanded until make decides that it wants to build dmmytgt.
You could have written this to exactly the same effect:
dummytgt: objs/tier.o
$(GCC) $(CFLAGS) -c $(filter $#,$(wildcard *.c)).c -o $#
For this fragment to work,
the file objs/tier.o must already exist.
Let's assume it does.
Make now has all it needs to build dummytgt (according to your makefile),
so now it expands the command block.
$(wildcard *.c) expands to 1.c 2.c (say).
$# is dummytgt
$(filter dummytgt,1.c 2.c) is of course empty (and always will be!)
${GCC} is gcc (say)
${CFLAGS} is empty (say)
Thus the shell gets
gcc -c .c -o dummytgt
Presumably gcc complains that there is no file called .c.
The resulting error stops make's execution.
A few thing not to like here:
$(wildcard ) is only for hacky one-liner makefiles IMHO.
dummytgt requires objs/tier.o, but its build instructions never reference it.
Your $(filter ) always expands to nothing.
$(filter $#.c,$(SOURCES))
But I don't see why you don't use
$#.c
Or better still, make it a prerequisite.

Why does this makefile target specific variable not expand as expected?

I have the following simplified makefile and I'm trying to set different paths based on different targets. Unfortunately, I'm not getting the results that I expect. This is with make version 3.81.
.SECONDEXPANSION:
all: Debug32
# Object directory set by target
Debug32: OBJDIR = objdir32
#OBJDIR = wrongdirectory
# ObjDir is empty here. :(
OBJS = $(addprefix $(OBJDIR)/,DirUtil.o)
$(OBJDIR)/%.o : %.cpp
echo Compile: $#
Debug32: $(OBJS)
$(OBJS): | $(OBJDIR)
$(OBJDIR):
echo mkdir $(OBJDIR) - $#
The results are as follows with no setting of OBJDIR:
echo Compile: /DirUtil.o
If I uncomment the "OBJDIR = wrongdirectory" line, I'll get the following results, which are confusing since I see both values of the variable where I think I should only see one:
echo mkdir objdir32 - wrongdirectory -
echo Compile: wrongdirectory/DirUtil.o
I'm assuming that the variables are not being expanded when I think they should, but I can't figure out how to alter this behavior.
From the GNU info manual
Variables and functions in all parts of a makefile are expanded when
read, except for in recipes, the right-hand sides of variable
definitions using '=', and the bodies of variable definitions using
the 'define' directive.
The target-specific variable only applies within recipes. Within
$(OBJS): | $(OBJDIR)
and
$(OBJDIR):
it is getting the global variable.
So working through what happens when you run make Debug32, it sees the contents of OBJS as a prerequisite, which leads to the first rule above. $(OBJDIR) has already been substituted with the global value, and this matches the target-name in the second rule which has also been substituted the same way.
However, when we get to the recipe:
echo mkdir $(OBJDIR) - $#
$(OBJDIR) has not been substituted yet, so it gets the target-specific variable value.
A working version
.SECONDEXPANSION:
all: Debug32
# Object directory set by target
Debug32: OBJDIR = objdir32
OBJDIR = wrongdirectory
Debug32: OBJS = $(addprefix $(OBJDIR)/,obj.o)
OBJS = wrongobjs
Debug32: $$(OBJS)
echo OBJS are $(OBJS)
echo OBJDIR is $(OBJDIR)
%/obj.o: | %
touch $#
OBJDIRS = objdir32 wrongdirectory anotherdirectory
$(OBJDIRS):
# mkdir $(OBJDIR)
mkdir $#
The main change is using $$ in this line:
Debug32: $$(OBJS)
With only a single $, I get the error message
make: *** No rule to make target `wrongobjs', needed by `Debug32'. Stop.
However, with the $$, I get
echo OBJS are objdir32/obj.o
OBJS are objdir32/obj.o
echo OBJDIR is objdir32
OBJDIR is objdir32
The use of secondary expansion has allowed accessing the target-specific variable in the prerequisites.
The other change is that I made OBJS a target-specific variable (because it is). In order to have a rule to build OBJS whatever its value, I had to use a pattern rule:
%/obj.o: | %
To avoid having a separate line for each object file, you could do the following instead:
OBJ_BASENAMES=obj.o obj2.o obj3.o
$(addprefix %/,$(OBJ_BASENAMES)): | %
touch $# # Replace with the proper recipe
The line containing the addprefix macro expands to
%/obj.o %/obj2.o %/obj3.o: | %
Then running make anotherdirectory/obj2.o creates a directory called "anotherdirectory" first, and creates a file called "obj2.o" within it.
Note all possible directories have to be listed in OBJDIRS. There's no way to collect all the rule-specific values of OBJDIR, so listing them is the best choice. The alternative is a % : rule to build any directory, which be capable of matching and building any target, which could be risky. (If you abandon the use of target-specific variables, there is another way of getting a list of directories that could be built: use variables with predictable names like Debug32_OBJDIR instead, and generate a list of their values using make functions.)
Alternatively, a generic rule that doesn't require listing the object files:
SOURCE=$(basename $(notdir $#)).cpp
DIR=$(patsubst %/,%,$(dir $#))
%.o: $$(SOURCE) | $$(DIR)
touch $# # Replace with proper recipe
There is no feature to read a rule in the context of every target, substituting in target-specific variables and acquiring a new rule for each target. Generic rules cannot be written in this way using target-specific variables.
a good way to handle
%/obj.o: | %
touch $#
OBJDIRS = objdir32 wrongdirectory anotherdirectory
$(OBJDIRS):
mkdir $#
is:
%/.:
mkdir -p $#
.SECONDEXPANSION:
then later you can just write, for any target that may need a directory
target: prerequisites | $$(#D)/.
normal recipe for target

Resources