Makefile variable expansion - makefile

The following is a contrived example Makefile illustrating a problem that I'm having.
release: TYPE := release
FILE = main.cpp
OBJDIR = dist/$(TYPE)
OBJS = $(patsubst %.cpp,$(OBJDIR)/%.o,$(FILE))
release: $(OBJS)
#echo "just created: " $(OBJS)
%.o:
#echo "create $#"
When I run 'make release' the output is:
create dist//main.o
just created: dist/release/main.o
How can I ensure that the $(OBJS) dependency of release target is expanded to dist/release/main.o and not dist//main.o . Also what is the reason for it expanding to dist//main.o?

The reason for it expanding to dist//main.o is that TYPE is a target-specific variable. The value of this kind of variable is only available within the context of a target's recipe (and in other target-specific assignments.
This means the value of TYPE is empty in the prerequisites for that rule.

Related

makefile recipe specific variables and $# evaluation

I'm trying to set up a makefile which consists of a debug and release target.
There are some target specific variables, mainly folder names, which are set properly in the different cases, but do not apply correctly for the $# automatic variable. Folowing code:
SRC_DIR = src
OBJ_ROOT_DIR = obj
REL_DIR = release
DBG_DIR = debug
TGT_DIR = $(REL_DIR)
SRC_LIST = main.cc prime.cc
SRC_SUFF = cc
SRCS = $(patsubst %.cc, $(SRC_DIR)/%.$(SRC_SUFF), $(SRC_LIST))
OBJ_DIR = $(OBJ_ROOT_DIR)/$(TGT_DIR)
OBJS = $(patsubst %.cc, $(OBJ_DIR)/%.o, $(SRC_LIST))
all: $(OBJS)
release: all
debug: TGT_DIR = $(DBG_DIR)
debug: OBJ_DIR = $(OBJ_ROOT_DIR)/$(TGT_DIR)
debug: OBJS = $(patsubst %.cc, $(OBJ_DIR)/%.o, $(SRC_LIST))
debug: $(OBJS)
$(OBJS):$(OBJ_DIR)/%.o: $(SRC_DIR)/%.$(SRC_SUFF)
#echo "TGT_DIR: $(TGT_DIR) OBJ_DIR: $(OBJ_DIR) OBJS: $(OBJS)"
#echo "Compiling $< to $# ..."
#echo "... done !"
#echo "."
Running make release produces the following expected output:
TGT_DIR: release OBJ_DIR: obj/release OBJS: obj/release/main.o obj/release/prime.o
Compiling src/main.cc to obj/release/main.o ...
... done !
.
TGT_DIR: release OBJ_DIR: obj/release OBJS: obj/release/main.o obj/release/prime.o
Compiling src/prime.cc to obj/release/prime.o ...
... done !
Wheras make debug leads to the following unexpected:
TGT_DIR: debug OBJ_DIR: obj/debug OBJS: obj/debug/main.o obj/debug/prime.o
Compiling src/main.cc to obj/release/main.o ...
... done !
.
TGT_DIR: debug OBJ_DIR: obj/debug OBJS: obj/debug/main.o obj/debug/prime.o
Compiling src/prime.cc to obj/release/prime.o ...
... done !
I cannot figure out, why $# doesn't evaluate to the desired debug version in the second case. The variables seem to be set correctly though. How can I achieve to have the correct directory name when executing the recipe?
Thanks in advance!
You have quite a few options / ways to do this.
One quick way is to check the contents of MAKECMDGOALS (these are the words after make e.g. release or debug). Note: there can be more then one - but if your makefile is simple and just has one the following will work:
eg:
ifneq ($(MAKECMDGOALS),debug)
TGT_DIR = $(DBG_DIR)
OBJ_DIR = $(OBJ_ROOT_DIR)/$(TGT_DIR)
OBJS = $(patsubst %.cc, $(OBJ_DIR)/%.o, $(SRC_LIST))
endif
Another option is to use second expansion, I am just going to put a link: secondary expansion
Yet another option is to setup some variables in your rule, then export them and call make again (recursively), perhaps something like this:
eg (incomplete):
# Note you can just export the ones you want but... for ease of the example:
.EXPORT_ALL_VARIABLES
debug: TGT_DIR = $(DBG_DIR)
debug: OBJ_DIR = $(OBJ_ROOT_DIR)/$(TGT_DIR)
debug: OBJS = $(patsubst %.cc, $(OBJ_DIR)/%.o, $(SRC_LIST))
debug:
$(MAKE) do_build
do_build: $(OBJS)
...etc...
Where in your first call to make it setup up some variables in the debug rule. In the second it calls do_build directly and the variables are setup already. Recursive make is generally frowned upon - but I found many times its the easiest way to get certain things done.
I'd probably err towards option 1. for its simplicity. There are even more ways to do this, but it sort of depends how you want to roll - I am just setting out some options that keep your makefile structure more or less the same...

Makefile: order of adding prefix affects how a list of targets is built from a common dependency

Motivation:
I have a C project in which multiple .o files are to be generated from a common file. This main file uses preprocessor directives to conditionally include other .h files as needed, depending on target-specific variables defined in the makefile.
I've written this rule below, but depending on the order in which I apply my variable references I get different outcomes.
One small(ish) change, two different outputs
Consider two versions of code from my Makefile. In version A we have the following snippets:
MAIN_OBJ:= $(MAIN_1) $(MAIN_2) $(MAIN_3) $(MAIN_4)
... omitted non-relevant rules (including an all: rule)
$(OBJECT_DIR)/$(MAIN_1): MFLAG = $(METHOD_1_FLAG)
$(OBJECT_DIR)/$(MAIN_2): MFLAG = $(METHOD_2_FLAG)
$(OBJECT_DIR)/$(MAIN_3): MFLAG = $(METHOD_3_FLAG)
$(OBJECT_DIR)/$(MAIN_4): MFLAG = $(METHOD_4_FLAG)
$(OBJECT_DIR)/$(MAIN_OBJ): $(SOURCE_DIR)/$(DEPENDENT_MAIN)
$(CC) -DUSE_$(MFLAG) $(CFLAGS) -o $# $<
This only successfully builds the first target, $(OBJECT_DIR)/$(MAIN_1). The remaining three never get compiled and make stops there.
Now in version B we redefine MAIN_OBJ so that the directory prefix is included within the target list itself:
MAIN_OBJ:= $(MAIN_1) $(MAIN_2) $(MAIN_3) $(MAIN_4)
MAIN_OBJ:= $(addprefix $(OBJECT_DIR)/,$(MAIN_OBJ)
... omitted non-relevant rules (again)
$(OBJECT_DIR)/$(MAIN_1): MFLAG = $(METHOD_1_FLAG)
$(OBJECT_DIR)/$(MAIN_2): MFLAG = $(METHOD_2_FLAG)
$(OBJECT_DIR)/$(MAIN_3): MFLAG = $(METHOD_3_FLAG)
$(OBJECT_DIR)/$(MAIN_4): MFLAG = $(METHOD_4_FLAG)
$(MAIN_OBJ): $(SOURCE_DIR)/$(DEPENDENT_MAIN)
$(CC) -DUSE_$(MFLAG) $(CFLAGS) -o $# $<
This solution works, and compiles all 4 .o files, each with the proper $(MFLAG) value.
What's happening here?
This is probably a dumb question, but why does Version A only compile one .o file? I recognize version B is a generally better way to write rules.
Let me provide one more example that will perhaps illustrate my confusion.
Say we want to write a much more common type of rule: compiling targets from a list with a pattern rule for finding dependencies.
Doing something similar to Version A wouldn't result in a single .o being successfully generated:
MY_FILES:= $(wildcard $(SOURCE_DIR)/*.c))
MY_OBJ:= $(patsubst $(SOURCE_DIR)/%.c, %.o, $(MY_FILES))
...
$(OBJECT_DIR)/$(MY_OBJ): $(OBJECT_DIR)/%.o: $(SOURCE_DIR)/%.c
$(CC) $(CFLAGS) -o $# $<
Clearly the above is a bad idea, and you should write something like this instead:
MY_FILES:= $(wildcard $(SOURCE_DIR)/*.c))
MY_OBJ:= $(patsubst $(SOURCE_DIR)/%.c, $(OBJECT_DIR)/%.o, $(MY_FILES))
...
$(MY_OBJ): $(OBJECT_DIR)/%.o: $(SOURCE_DIR)/%.c
$(CC) $(CFLAGS) -o $# $<
But my question is this:
Why in this case does adding the directory prefix in the rule itself result in nothing being built, while in version A of my makefile the first target was successfully made?
"Version A" fails because make is just expanding things like you asked it to. A variable reference like this:
$(OBJECT_DIR)/$(MAIN_OBJ): ...
says "expand the variable OBJECT_DIR, then add a "/", then expand the variable MAIN_OBJ". So you get:
$(OBJECT_DIR)/$(MAIN_1) $(MAIN_2) $(MAIN_3) $(MAIN_4): ...
So, only the first one is actually prefixed by the OBJECT_DIR value, not all of them (since you didn't show what the values were for all these variables I didn't complete the expansion).
Secondly, make always builds just the first target that it finds in the makefile (unless you override that with the command line or .DEFAULT). You don't say what the "non-relevant rules" are that you omitted, but unless one of them was an all target or similar that depends on all the MAIN_* targets, make will only build the first one which is the behavior you saw.
ETA Prepending to all words is trivial using various methods; see the GNU make manual.
One option:
$(addprefix $(OBJECT_DIR)/,$(MAIN_OBJ)): ...
Another option:
$(MAIN_OBJ:%=$(OBJECT_DIR)/%): ...
Another option:
$(patsubst %,$(OBJECT_DIR)/%,$(MAIN_OBJ)): ...

generating multiple executables from the same sources

To build multiple executables from the same source, I have to translate every source file with different Compiler Switches. For every variant, I have a set of defines to be set. I want to store the resulting object files into different subfolders. I have a variable, keeping all object file from all variants. Now I have problems to define a proper static rule to build the object files from the sources:
SOURCEEXT=.c
ALL_OBJECT_FILES := abcdefg/cctalkio.o tollcoll/cctalkio.o
source-from-object = $(addsuffix $(SOURCEEXT),$(basename $(notdir $(1))))
$(ALL_OBJECT_FILES): %.o: $(call source-from-object,%.o)
#echo $*.o
when I run make abcdefg/cctalkio.o, I get:
make: *** No rule to make target 'abcdefg/cctalkio.c', needed by 'abcdefg/cctalkio.o'. Schluss.
The same, when I simpify the rule to:
abcdefg/cctalkio.o: %.o: $(call source-from-object,%.o)
#echo $*.o
But when I change the rule to:
abcdefg/cctalkio.o: %.o: $(call source-from-object,abcdefg/cctalkio.o)
#echo $*.o
I get abcdefg/cctalkio.o as Output. So the stem seems to be abcdefg/cctalkio, thus %.o should be the same as abcdefg/cctalkio.o. But why is make behaving different in both cases?
When I "debug" the source-from-object function:
debug:
#echo $(call source-from-object,/abcdefg/cctalkio.o)
I get the expected result cctalkio.c, so it seem like the function is working.
Your $(call) in the prereq is happening immediately and so your function is actually being passed %.o (not the matched result as you expected).
You would have to use something like:
.SECONDEXPANSION:
abcdefg/cctalkio.o: %.o: $$(call source-from-object,%.o)
...
to get what you want I believe.
Alternatively you could probably loop over your object files and statically give them the correct prerequisites and just let the static pattern rule supply the body.

GNU Make: Using the filter function in prerequisites

I want to compile some C++ files and I absolutely have to put all object files in a separate build directory, but stored completely flat, i.e., without any further subdirectories. I know the common solution using VPATH, which goes something like this:
SOURCES = foo/one.cpp \
foo/bar/two.cpp \
foo/bar/sub/three.cpp
OBJDIR = obj
VPATH=$(dir $(SOURCES))
OBJECTS = $(addprefix $(OBJDIR)/, $(notdir $(SOURCES:%.cpp=%.o)))
$(OBJDIR)/%.o : %.cpp
#echo Should compile: $(filter %/$*.cpp, $(SOURCES))
#echo Compiling $<
all: $(OBJECTS)
This example pretty much works: I get three object files one.o, two.o, three.o in the 'obj' subdirectory (you can assume it just exists).
Now here's the catch when using VPATH: If there happens to be a file 'foo/three.cpp', then this will be compiled instead of the 'foo/bar/sub/three.cpp' which is named in the SOURCES variable. And no, I cannot rename either file; this name clash simply exists and I cannot do anything about that.
So my question is: How can I tell Make to only use '.cpp' files which appear in the SOURCES variable? I think the best solution would be to use that 'filter' statement in the target's prerequisite. I think this should be possible using secondary expansion, but I don't know what to do with the '%'. For example, I tried
.SECONDEXPANSION:
$(OBJDIR)/%.o : $$(filter %/$$*.cpp, $(SOURCES))
but that doesn't work.
UPDATE: With the help of tripleee, I managed to get this working using the following:
define make-deps
$(OBJDIR)/$(notdir $(1:%.cpp=%.o)): $1
endef
$(foreach d, $(SOURCES), $(eval $(call make-deps,$d)))
%.o :
#echo Should compile $^ into $#
#echo Compiling $^
I suspect the easiest solution to your problem would be to get rid of VPATH and document each dependency explicitly. This can easily be obtained from your SOURCES definition; perhaps you want to define a function, but it really boils down to this:
obj/one.o: foo/one.cpp
obj/two.o: foo/bar/two.cpp
obj/three.o: foo/bar/sub/three.cpp
The actual rule can remain, only it should no longer contain the dependencies in-line, and you can skip the obj/ subdirectory, because it's declared explicitly in each dependency:
%.o : # Dependencies declared above
#echo Should compile $^ into $#
#echo Compiling $^
I changed the rule to use $^ instead of $< in case you ever have more than a single dependency. This may be right or wrong for your circumstances; revert the change if it's not what you need.
In order to not need to maintain the dependencies by hand, you might want to generate %.d for each %.cpp file. See the GNU Make manual. (I tried to do this by using a define, but it seems you cannot declare dependencies with a foreach loop.)
In response to the question in the comment, this should not affect parallel builds in any way; it merely disambiguates the dependencies where your original Makefile was ambiguous when there were multiple biuld candidates with the same name in the VPATH. There are no new dependencies and no new rules.

Make target specific variables

I am writing small makefile, and I have a problem with target specific variables, I have this piece of code:
FILE_SOURCE := pliki.c wczytaj_plik.c wypisz_plik.c
CONSOLE_SOURCE := wczytaj_konsola.c wypisz_konsola.c
OTHER_SOURCE := suma.c roznica.c iloczyn.c macierz.c
HEADERS := suma.h roznica.h iloczyn.h wypisz.h wczytaj.h macierz.h
DEFINE_OPT =
NAME=macierze
FILE_OBJECTS := $(FILE_SOURCE:.c=.o)
CONSOLE_OBJECTS := $(CONSOLE_SOURCE:.c=.o)
OTHER_OBJECTS := $(OTHER_SOURCE:.c=.o)
finput: HEADERS+=pliki.h
finput: DEFINE_OPT+=-D WEWY_PLIKI
finput: OTHER_OBJECTS+=$(FILE_OBJECTS)
finput cinput: debug $(NAME)
$(NAME): $(OTHER_OBJECTS) main.o
#echo $^
gcc $(CFLAGS) -o $(NAME) $^
debug:
#echo $(OTHER_OBJECTS)
this is a piece that is relevant, when I invoke
make finput
in target debug I get all the .o files but
#echo $^
only produces
suma.o roznica.o iloczyn.o macierz.o main.o
so it is like FILE_OBJECTS were not added, but in gnu make manual:
There is one more special feature of target-specific variables: when you define a target-specific variable that variable value is also in effect for all prerequisites of this target, and all their prerequisites, etc. (unless those prerequisites override that variable with their own target-specific variable value).
So it is a bit weird that $(OTHER_OBJECTS) in $(NAME) don't include $(FILE_OBJECTS), how can I fix this problem?
If you look a couple lines up in the same section of the GNU make manual you quoted from, you will find the following.
As with automatic variables, these values are only available within the context of a target's recipe
This means the target specific value of OTHER_OBJECTS is not available in the prerequisites. (I haven't tried, but perhaps you can use the same workaround as with automatic variables, namely secondary expansion.)

Resources