Defining variables depending on the target - makefile

I've got a Makefile that looks like:
gator: LIB=-lm
gatorgpu : GPU=-DG
....
STATIC=
ifdef STATIC
$(info CPU static)
endif
But I would like to have something like:
gator: LIB=-lm
gatorgpu : GPU=-DG
....
STATIC=
ifdef STATIC
ifdef GPU
$(info GPU static)
else
$(info CPU static)
endif
endif
So when somebody types $make gatorgpu STATIC=1 or $make gator STATIC=1 then it will compile with static libraries depending on the target, in this case CPU or GPU. Unfortunatelly, STATIC is read however GPU variables is not, so it always goes forCPU static. Is there an elegant way of doing it?

Target specific variables are only available within the corresponding recipes for a reason. Different targets can use a different version of the same variable for its recipe.
Although if you decide to use target specific variables to achieve whatever you're tyring to do, this would work:
gator: export LIB := -lm
gatorgpu : export GPU := -DG
STATIC ?= 0
ACTUAL ?= 0
export STATIC
ifeq ($(ACTUAL),1)
ifeq ($(STATIC), 1)
ifneq ($(GPU),)
$(info GPU static)
else
$(info CPU static)
endif
endif
endif
.gator:
#echo LIB=$(LIB) GPU=$(GPU)
.gatorgpu:
#echo LIB=$(LIB) GPU=$(GPU)
gator:
#$(MAKE) .gator ACTUAL=1
gatorgpu:
#$(MAKE) .gatorgpu ACTUAL=1
PHONY: .gator .gatorgpu gator gatorgpu
This has the overhead of invoking an additional make (per target) with the correct set of exported variables.
The export ensures that the variables are set for any sub-processes run from make.
This is the result I see:
$ make gatorgpu
make[1]: Entering directory `/home/ash/.scratch/make-test'
LIB= GPU=-DG
make[1]: Leaving directory `/home/ash/.scratch/make-test'
$ make gator
make[1]: Entering directory `/home/ash/.scratch/make-test'
LIB=-lm GPU=
make[1]: Leaving directory `/home/ash/.scratch/make-test'
$ make gatorgpu STATIC=1
GPU static
make[1]: Entering directory `/home/ash/.scratch/make-test'
LIB= GPU=-DG
make[1]: Leaving directory `/home/ash/.scratch/make-test'
$ make gator STATIC=1
CPU static
make[1]: Entering directory `/home/ash/.scratch/make-test'
LIB=-lm GPU=
make[1]: Leaving directory `/home/ash/.scratch/make-test'
A couple of minor corrections, although it does not impact the results much:
The ?= (known as conditional variable assignment operator) performs the assignment only when the variable is unassigned. So when you run make mytarget STATIC=1, the variable STATIC would not be assigned 0.
ifeq and ifneq are more robust check compared to ifdef, as you can compare the variable against a specific value you're interested in.
:= should be preferred over the regular = to avoid unnecessary slowdown in make, unless you really have a need for recursively expanded variables.
A better approach would be to refactor your Makefile such a way that you define different target-specific variables or rules just based on the value of STATIC
ifeq ($(STATIC), 1)
gator: LIB := -lm
gatorgpu : GPU := -DG
else
gator: LIB := <SOMETHING_ELSE_HERE>
gatorgpu : GPU := <SOMETHING_ELSE_HERE>
endif
There is also a not-so-recommended approach that involves checking the value of $(MAKECMDGOALS), which contains the target with which make was invoked.
ifeq ($(MAKECMDGOALS),gatorgpu)
GPU := -DG
endif

From your description, I would say the following approach can be used:
ifdef STATIC
gator: LIB:=-static -lstaticlib
gatorgpu: GPU:=-DSTATICDEF
else
gator: LIB:=-lm
gatorgpu: GPU:=-DG
endif
You have not exactly stated what options you want to use or change in the case when STATIC is defined versus when it is not defined, so I have made up some example values.

Related

makefile target-specific changing srcs files

I'm trying to write a Makefile with a rule to make the project with another main.cpp file, because I'm testing my code with different options
I have different versions of the main function, that I put inside differents files : main.cpp, main_1.cpp, main_2.cpp, ..., to test different versions of my code, and they all have the same dependencies
first I was just commenting and un-commenting the Makefile variable MAIN that define the main.cpp file, but I was hoping there is a way to choose the one I want to try with a specific rule ?
I tried something with target-specific variables but it didn't work :
# # # # # # #
# VARIABLES #
# # # # # # #
NAME = my_program
VPATH = srcs
CXX = c++
CXXFLAGS = -I ./headers
OBJS = $(SRCS:%.cpp=%.o)
MAIN = main.cpp
#MAIN = main_1.cpp
SRCS = $(MAIN) file.cpp
# # # # #
# RULES #
# # # # #
all: $(NAME)
# target-specific variables
test-1: MAIN = main_1.cpp
test-1: re
$(NAME) : $(OBJS)
$(CXX) $(OBJS) -o $(NAME)
clean:
rm -f $(OBJS)
fclean: clean
rm -f $(NAME)
re: fclean all
.PHONY : all clean fclean re
the error output for main test_1 is :
c++ -I ./headers -c -o main.o srcs/main.cpp
c++ -I ./headers -c -o file.o srcs/file.cpp
c++ main_1.o Webserv.o -o my_program
c++: error: main_1.o: No such file or directory
Makefile:21: recipe for target 'my_program' failed
make: *** [my_program] Error 1
I think, then, that target-specific is not the right tool for what I'm trying to do.
Does Make provide a way to accomplish that (modifying the list of srcs files when calling a specific rule, and having the compilation working great with the new srcs files) ?
I'm vaguely thinking something like this.
test-%: main_%.cpp file.cpp
Now, make test-1 will produce an executable with that name from main_1.cpp instead of main.cpp, and similarly test-2 from main_2.cpp, etc.
If you have subsequent targets which hardcode my_program which should actually depend on which version you made, this might not be suitable, or at a minimum, you'd have to refactor those to use the current output executable. Similarly, you might want to add test-[1-9] to the files to remove in the clean target (or perhaps add a realclean target to remove them too).
Tangentially, several of your make variables don't seem to serve any immediate purpose. Putting stuff in variables makes sense for things you want to be able to override at compile time, or vaguely for making a general-purpose Makefile which can be applied with only minor modifications across several projects; but in isolation, these seem like unnecessary complexities you should probably avoid for the time being.
Your immediate problem could perhaps be solved by refactoring the dependency chain, but on the whole, I'd recommend keeping it as simple as possible. make already knows how to compile common source formats; all you really need to put in the Makefile are the dependencies which are not trivially obvious and any .PHONY targets, and overrides to select e.g. a specific default action.

.EXPORT_ALL_VARIABLES works only when made 'phony'

The docs provides:
'.EXPORT_ALL_VARIABLES'
Simply by being mentioned as a target, this tells 'make' to export
all variables to child processes by default. *Note Communicating
Variables to a Sub-'make': Variables/Recursion.
However, the following makefiles show that only by making .EXPORT_ALL_VARIABLES a phony target, then and only then, will it have the desired effect on the makefile, i.e. to export ALL variables.
Makefile(version 1) is:
ifeq "$(MAKELEVEL)" "0"
foo=bar
.DEFAULT:;
all: .EXPORT_ALL_VARIABLES
#$(MAKE)
else
all:
#echo 'foo is: $(foo)'
endif
Running, we get:
make[1]: Entering directory '/home/myname'
foo is:
make[1]: Leaving directory '/home/myname'
Makefile(version 2) is:
ifeq "$(MAKELEVEL)" "0"
foo=bar
.DEFAULT:;
all: .EXPORT_ALL_VARIABLES
#$(MAKE)
# This line is added in THIS version.
.PHONY: .EXPORT_ALL_VARIABLES
else
all:
#echo 'foo is: $(foo)'
endif
Running, we get:
make[1]: Entering directory '/home/myname'
foo is: bar
make[1]: Leaving directory '/home/myname'
Now, the only difference between these 2 versions of makefile, is that in the 2nd version, the .EXPORT_ALL_VARIABLES was made phony.
Why is the 'phoniness' needed in order to work?
Simply by being mentioned as a target,
Why is the 'phoniness' needed in order to work?
It's not. You didn't declare .EXPORT_ALL_VARIABLES as a target, you declared it as a prerequisite:
all: .EXPORT_ALL_VARIABLES
That's a prerequisite, not a target. If you declare it as a target:
.EXPORT_ALL_VARIABLES:
then it will work and you won't have to declare it phony.
A more accurate question would be, why does declaring .EXPORT_ALL_VARIABLES as phony work even though it's not declared as a target? It happens because things that are marked phony are assumed to be targets even if they're not explicitly mentioned as such. That may or may not be a bug, depending on how you interpret the intent of .PHONY.
Your questions recently seem to follow a pattern: read the documentation, then write a makefile that does something similar to but not the same as what the documentation says, observe it doesn't work as described, then ask why not.

How can I set long options within a makefile

I am writing a makefile for distribution among students. To ease up their hacking experience, I would like make to warn about uninitialised variables.
I know there is the option --warn-undefined-variables to do just this, and of course, I can add an alias รก la alias make="make --warn-undefined-variables" to my .bashrc. But I would like to set this option within the makefile so students will automatically profit from those warnings too, when they start to extend the makefile.
The logical way to do so would be the MAKEFLAGS variable. However, while it works for short options, I cannot get it to work with --warn-undefined-variables as described in Can make warn me, when I use unset variables?
Makefile:
MAKEFLAGS=--warn-undefined-variables
$(info MAKEFLAGS: $(MAKEFLAGS))
$(info ${BAR})
Call:
$ make
MAKEFLAGS: --warn-undefined-variables
make: *** No targets. Stop.
$ make --warn-undefined-variables
MAKEFLAGS: --warn-undefined-variables
Makefile:3: warning: undefined variable 'BAR'
make: *** No targets. Stop.
When I change the MAKEFLAGS to -d the console is flooded with debug information, so I know MAKEFLAGS is set correctly. Any suggestions?
I have GNU make 4.0 here and I cannot for the life of me get make to honor MAKEFLAGS= --warn-undefined-variables with a straightforward Makefile. However, if I make the Makefile invoke itself, then MAKEFLAGS= --warn-undefined-variables works in the child invocation!
MAKEFLAGS= --warn-undefined-variables
$(info MAKEFLAGS: $(MAKEFLAGS))
$(info $(BAR))
# This prevents a warning if we invoke make without a target...
MAKECMDGOALS?=
all:
ifndef RECURSED
$(MAKE) RECURSED=1 $(MAKECMDGOALS)
else
echo $(FOO)
endif
If I just run make, I get:
MAKEFLAGS: --warn-undefined-variables
make RECURSED=1
make[1]: Entering directory '/tmp/t1'
MAKEFLAGS: --warn-undefined-variables
Makefile:3: warning: undefined variable 'BAR'
Makefile:12: warning: undefined variable 'FOO'
echo
make[1]: Leaving directory '/tmp/t1'
Either I'm borking on something... or there's a bug in make. I'm inclined to think the latter.

Reassign Makefile Parameters

In the current system, there was a ghetto hack to initiate a parallel build for the system. For instance, to call a parallel make required make JOBS=8 instead of make -j8. I have since fixed the makefile, however there are a lot of previous dependencies on the JOBS flags within scripts that call make. I was hoping to recursively call make as a workaround as such:
ifdef JOBS
%:
$(MAKE) $(MAKECMDGOALS) JOBS= -j$(JOBS)
endif
This has some odd behavior though. It will first call make JOBS= -j8 as it should, but after this rule is completed it seems to go on and rebuild everything again that is in $(MAKECMDGOALS).
#Example
TARGETS = lib0 lib1 lib2
ifdef JOBS
%:
$(MAKE) $(MAKECMDGOALS) -j$(JOBS) JOBS=
endif
all: $(TARGETS)
lib%:
#echo g++ $#
Cmd: make JOBS=8.
This is fine for most rules, however it keeps wasting time rebuilding PHONY rules, so this should be fixed. Is there a way to completely reroute a make JOBS=N command to make -j$(JOBS) without executing any other rules redundantly in either scope? (meaning nothing executes except a submake in make JOBS=N, and the submake is only called once with all $(MAKECMDGOALS) at once.)
EDIT: I would also like to avoid large ifdef; else; endif statements, at the very most putting a self contained one at the top of the file. I'm starting to think the solution may require something like that though:
TARGETS = lib0 lib1 lib2
ifdef JOBS
%: unique_make
#echo $# built > /dev/null
.PHONY: unique_make
unique_make:
$(MAKE) $(MAKECMDGOALS) -j$(JOBS) JOBS=
else
all: $(TARGETS)
lib%:
#echo g++ $#
endif
In GNU make, you can modify the make flags within a Makefile. Why not try something like this:
ifdef JOBS
MAKEFLAGS+=-j$(JOBS)
endif

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.

Resources