Makefile: Parametrizable recepie command in pattern rule - makefile

Assume the following makefile
objects = $(wildcard *.in)
outputs := $(objects:.in=.txt)
%.txt: %.in
some-command $# $<
compile: $(outputs)
This works as expected.
Now I want to add another target called (for example) upgrade that should do the same thing as compile but pass additional options to some-command (possibly depending on environment variables, but that is out of scope to this question).
The only 'solution' I've found so far has been recursively invoking the same makefile and passing the additional options via env variables. But that seems like a pretty ugly hack.
Is what I'm after possible in make (GNU is fine, this doesn't have to be portable) or am I just going about this the wrong way?

Target-specific variable value:
%.txt: %.in
#echo some-command $(SOME_OPTIONS) $# $<
compile: $(outputs)
upgrade: SOME_OPTIONS:=whatever
upgrade: compile

Related

Makefile stops running at the middle [duplicate]

Hopefully this is a very simple question. I have a makefile pattern rule that looks like this:
%.so : %.f %.pyf
f2py -c -L${LAPACK_DIR} ${GRASPLIBS} -m $* $^ ${SOURCES} --opt='-02' --f77flags='-fcray-pointer' >> silent.txt
I want the makefile to build a number of .so files, so I tried to get it to build two files (radgrd_py.so and lodiso_py.so) by doing this:
radgrd_py.so lodiso_py.so:
%.so : %.f %.pyf
f2py -c -L${LAPACK_DIR} ${GRASPLIBS} -m $* $^ ${SOURCES} --opt='-02' --f77flags='-fcray-pointer' >> silent.txt
and then tried this:
radgrd_py.so:
lodiso_py.so:
%.so : %.f %.pyf
f2py -c -L${LAPACK_DIR} ${GRASPLIBS} -m $* $^ ${SOURCES} --opt='-02' --f77flags='-fcray-pointer' >> silent.txt
But in each case, it only builds the first target that I specify. If I run 'make radgrd_py.so' it works fine, I'm just not sure how to specify a list of files that need to be built so I can just run 'make'.
The usual trick is to add a 'dummy' target as the first that depends on all targets you want to build when running a plain make:
all: radgrd_py.so lodiso_py.so
It is a convention to call this target 'all' or 'default'. For extra correctness, let make know that this is not a real file by adding this line to your Makefile:
.PHONY: all
Best way is to add:
.PHONY: all
.DEFAULT: all
all: radgrd_py.so lodiso_py.so
Explanations:
make uses the first target appearing when no .DEFAULT is specified.
.PHONY informs make that the targets (a coma-separated list, in fact) don't create any file or folder.
all: as proposed by schot

Using % steam inside variables of your Makefiles prerequisites

I often find myself wanting to refer to the stem not in the recipe but int the prerequisites themselves.
For example here I was playing around with some python code that scans the .cpp and .hpp files of the executable source code, in a recursive fashion, to detect what objects it depends from. The script itself works pretty well but I can't figure out how to connect it with the makefile since the input varies.
$(TESTS): bin/tests/%_a : bin/obj/%.o $(foreach var, $(shell python3 ./autoInc.py ./src/lib/%.cpp), bin/obj/$(var).o)
#echo "#---------------------------"
#echo "# Linking $# "
$(CC) -o $# $^
(Here the makefile executes ./autoInc.py ./src/lib/%.cpp without substitution)
This is the form:
.SECONDEXPANSION:
$(TESTS): %_a : $$(foreach var, $$(shell whatever $$*.cpp), $$(var).o)
...
I advise you to get it working with a very simple toy rule, before trying to incorporate your python.

Makefile and computed variable names

I got the following Makefile with several compilers, and I would like to invoke them in a loop through the variable cc:
cc_x64=x86_64-linux-gnu-gcc
cc_mips=mips-linux-gnu-gcc
all:
for arch in "x64" "mips" ; do\
cc="cc_$$arch";\
$($(cc)) some_file -o some_bin
By $($(cc)), I am trying to substitute $(cc) with cc_xxx, and in turn, substitute it with the actual command I am trying to execute. This is called a computed variable name in GNU Make's documentation: https://www.gnu.org/software/make/manual/html_node/Computed-Names.html
For some reason, I cannot get this to work. What am I missing ?
You can't cross shell/make boundaries like that. You are trying, in the shell context, to create and evaluate a make variable on-the-fly. That can't work.
You either need to do all the work in make or in the shell. Export those make variables to the shell and then something like this should work:
all:
for arch in x64 mips; do\
cc=cc_$$arch;\
${!cc} some_file -o some_bin.$$arch;\
done
But it would probably be better to do this in a more make idiomatic way.
Which would look something more like this (untested):
all: $(addprefix some_file.,x86 mips)
some_file.%: some_file
$(cc_$(*)) $^ -o $#
If I felt compelled to use a loop, I'd do it like this:
COMPS = x86_64-linux-gnu-gcc mips-linux-gnu-gcc
all:
for comp in $(COMPS); do\
$$comp some_file -o some_bin; \
done
Your question is "What am I missing?" The answer is, you don't realize, that a Makefile works differently than a shell script. You are trying put a shell script on a Makefile. It is like you trying to put a saddle on a cow. Both you and the cow will be unhappy with the result.
The way you are trying to do it, you don't need Make. Just use the shell script you have there under "all:" and forget about Make. Why try to put a saddle on a cow?
If you do want to learn how to use Make, then please read more carefully the Make manual, and especially study the examples given there. Then you will understand the difference between shell scripts and Makefiles, and everything will be clearer.
I will show you how to do what you want, in the correct way that Makefiles are designed to work. But please, do study the manual more carefully.
RESULTS := $(addprefix some_bin., x86_64 mips)
.PHONY: all
all: $(RESULTS)
$(RESULTS): some_file Makefile
$(patsubst .%,%-linux-gnu-gcc,$(suffix $#)) $< -o $#

How to specify --no-print-directory within the Makefile itself

I'd like to run my makefile without the -w flag turned on by the recursive make calls.
The flag to do that is --no-print-directory on the make command line.
Is it possible to specify that flag within the makefile itself?
I plan to make this flag dependent on a VERBOSE mode, perhaps something like
$(if $(VERBOSE),,MAKEFLAGS += no-print-directory))
Thanks,
Dan
Yes, just appending --no-print-directory to MAKEFLAGS should be enough, but you have to do that with conditional directives, not with conditional functions:
ifndef VERBOSE
MAKEFLAGS += --no-print-directory
endif
You can include the .SILENT: special target in the calling makefile. For example, here's your toplevel makefile:
all:
$(MAKE) -f sub.mk foo
.SILENT:
and the submake makefile, sub.mk:
foo:
#echo done
Note that .SILENT is considered obsolete, so it may not be around forever, and also note that including that in your makefile also has the effect of suppressing command echo, just as if you had put # before every command in the makefile.

Makefile. How to exclude one particular file from compilation?

I am trying to exclude main.cpp file from the list of files to be compiled defined by the rule below:
$(TMPDIRPATH)%.o: %.cpp
#echo compile $<
ifneq ($(notdir $<), main.cpp)
#$(COMPILE.cpp) $(OUTPUT_OPTION) $<
endif
This 'ifneq' condition always evaluates to true, which is bizarre. What am I doing wrong? Is there a better way to exlude one file from an explicit rule?
Why don't you try using the filter-out text function if you're using GNU Make.
Returns all whitespace-separated words in text that do not match any of the pattern words, removing the words that do match one or more. This is the exact opposite of the filter function.
For example, given:
objects=main1.o foo.o main2.o bar.o
mains=main1.o main2.o
the following generates a list which contains all the object files not in ‘mains’:
$(filter-out $(mains),$(objects))
That isn't the best way to do it, but if you do it along these lines, write it as a shell condition, not using GNU make conditionals:
$(TMPDIRPATH)%.o: %.cpp
#echo compile $<
#if [ $(notdir $<) != main.cpp ]; \
then $(COMPILE.cpp) $(OUTPUT_OPTION) $<; \
fi
The continuation markers (backslashes) are needed. So are the semicolons. The values prefixed with $ will be expanded by make before the shell is invoked to interpret them. You probably don't want the echo where it is, either. You probably need:
$(TMPDIRPATH)%.o: %.cpp
#if [ $(notdir $<) != main.cpp ]; \
then echo compile $<; \
$(COMPILE.cpp) $(OUTPUT_OPTION) $<; \
fi
The way I would expect to do it is with a list of the files to be compiled. Using any wild card mechanism leads to problems when extra files are added - other tests, or stray files that aren't really part of the system.
The comment says "But the GNU Make Manual says ifneq should work".
The ifneq would work if it were positioned correctly, which means 'not indented as part of the commands associated with a rule'. You could, therefore, write something like (an appallingly bad example, but my brain's on the fritz):
ifneq (${CFLAGS}, -Wall)
CFLAGS += -Wall
endif
file1.o: file1.c
${CC} ${CFLAGS} -c $<
But when the ifneq is indented as in the question, it is just a command that actually isn't found on the system when the make runs the shell to process the command.
The ifneq line is evaluated only once, when make starts up and parses the makefile. In that context, $< is empty.
To get different behavior for each of the targets matched by your pattern rule, you could do something like
$(TMPDIRPATH)%.o: %.cpp
#echo compile $<
#$(if $(filter main.cpp,$<),$(COMPILE.cpp) $(OUTPUT_OPTION) $<)
It might help you to think of the difference between ifneq and $(if) in a makefile as like the difference between #if and if() in C code.
Taking a step back, though: If you don't want main.cpp to be compiled by this rule, then you probably want to provide an explicit rule with $(TMPDIRPATH)main.o as its target, which will be preferred to the pattern rule always. Or, if you don't want$(TMPDIRPATH)main.o to get made at all, you should be looking for rules that have it on the right sight of the :, and removing it from there.
Make doesn't really have a good way to handle conditionals within a rule. You could put the conditional in the command, but in this case there's a much cleaner way:
$(TMPDIRPATH)main.o:
#echo compile $< (but not really)
$(TMPDIRPATH)%.o: %.cpp
#echo compile $<
#$(COMPILE.cpp) $(OUTPUT_OPTION) $<
EDIT:
I didn't realize you didn't have a main.cpp. The solution is simple: remove main.cpp as the prerequisite of the main.o rule (I've removed it above). Now the makefile doesn't need it, and won't try to build it.
But you're still running the rule, which means that something is still trying to build main.o, as either an explicit target or a prerequisite of something else. That is a symptom of confusion, which this change to the makefile will not fix. If you tell us more about the situation, maybe we can propose a better solution. What calls for main.o? Do you have a main.o? What target do you specify when you call Make?

Resources