Makefile and computed variable names - makefile

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 $#

Related

Makefile: Parametrizable recepie command in pattern rule

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

Automated testing with makefile

I'm extremely new to makefiles and just spent a full day trying to figure out how to automate my testing. For this project I have one program, main.c and it accepts as parameters an input and an output file. The input file is of the form "test-{filler}.txt" so an example file could be "test-invalid-opcode.txt". The output file would be of the form "out-{filler}.txt", so it would be of the form "out-invalid-opcode.txt". I would then want to compare the output with the correct output saved in "correct-{filler}.txt", so this would be "correct-invalid-opcode.txt". Each test would therefore have an input, output, and correct output. I would want to check if the output and correct output have any differences and I would want this to run on every test file with the prefix "test-". I read through a lot of the makefile spec and a lot of different examples, but I'm really confused as to how to handle this. Any help would be really appreciated.
Here is what I have to run one test:
CC=gcc
CFLAGS=-o
main.o: main.c
$(CC) $(CFLAGS) main.o $<
.PHONY: test
test: main.o test-provided.txt test-out.cs
./program test-provided.txt test-out.txt
diff -q test-out.txt out-provided.txt
The layout of what I want the automatic test to be is something like
.PHONY: autotest
autotest: program
$(foreach <file with "test-" prefix> run:
main.o <test-name> <out-name>
diff -q <correct-name> <out-name>
But I really am lost on how to go about implementing that.
Thank you and sorry I couldn't post more code. I tried a lot of different things, but none of them were even close enough to be worth posting.
Edit to show working final version:
program: main.c
$(CC) $(CFLAGS) $a $<
TEST_INPUTS := $(wildcard test-*.txt)
.PHONY: autotest $(TEST_INPUTS)
autotest: $(TEST_INPUTS)
$(TEST_INPUTS): test-%.txt: program
echo '' > out-$*.txt
./$< $# out-$*.txt
diff -q correct-$*.txt out-$*.txt
In general using loops with make is not very "make-ish". A makefile is an entire language fundamentally based on iteration and recursion so trying to do "extra" iteration inside a recipe is often redundant.
If you need to iterate over something, most especially when that something is files, you should attempt to work with make by taking advantage of its target/prerequisite organization. Above you give an algorithm:
$(foreach <file with "test-" prefix> run:
main.o <test-name> <out-name>
diff -q <correct-name> <out-name>
which is perfectly suited to make's default behavior. To translate this into a makefile you write a rule which invokes a single iteration of the loop, then use prerequisites to run it for the files you want. Something like this:
TEST_INPUTS := $(wildcard test-*.txt)
.PHONY: autotest $(TEST_INPUTS)
autotest: $(TEST_INPUTS)
$(TEST_INPUTS): test-%.txt: program
$< $# test-out.txt
diff -q test-out.txt out-$*.txt
Not only is this much more make-like but it has other advantages: for example you can run make test-provided.txt and it will run just that one test instead of all the tests.
You have to use static pattern rules here not normal pattern rules because .PHONY targets can't work with pattern rules.

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.

Change make's working directory without long command line

I would like to change the working directory of a makefile.
(Extraneous info: I have a legacy makefile that I mostly want to reuse, though many targets and generated deps files make assume that the working directory will not be different. My idea is to create a makefile for my newer project, which is in a different directory, and include the old one, and set the working directory to the old directory.)
I easily can do this from the command line with
make -f /path/to/new/makefile -C /path/to/old/makefile
The users of this makefile would like not to type that out every time.
Attempt 1
Use MAKEFLAGS in the makefile itself. But neither of these seem to have any effect. (I understand why -f couldn't have an effect; I'm really wondering about -C.)
I've looked at http://www.gnu.org/software/make/manual/html_node/Options-Summary.html, but I can't find anything about what is allowed in MAKEFLAGS and what isn't.
Attempt 2
Create a makefile2 with the new targets
include path/to/old/makefile
foo: bar
and then makefile passes everything through
%:
$(MAKE) -f $(abspath makefile2) -C path/to/old/makfele /$*
I don't get nice autocompletion, parallel jobs don't work, and debug options (dry run) doesn't work.
So
(1) Why doesn't -C work MAKEFLAGS (it does work, but I made a mistake; it doesn't work, and it is documented; it doesn't work, and it is not documented but it is intentional; it doesn't work, and it is a bug)?
(2) Is there a better way of change a makefile's working directory?
Some things are wrong here :
make -f /path/to/new/makefile -C /path/to/old/makefile
The -f options specifies the name of the Makefile to be found when searched in the directory specified with -C (or the current directory if not provided). So it is more :
make -C /path/to/old/Makefile -f name_of_old_makefile
If the name is simply Makefile or makefile, there is no need to provide the -foption.
The MAKEFLAGS variable does not contains -f or -C in the called sub-Makefile.
To be able to pass multiple targets to another makefile, you need the MAKECMDGOALS variable.
Ultimately, all you have to do in your new Makefile is to have someting like this :
all:
$(MAKE) $(MAKEFLAGS) -C /path/to/old/Makefile -f old_Makefile_name $#
%:
$(MAKE) $(MAKEFLAGS) -C /path/to/old/Makefile -f old_Makefile_name $(MAKECMDGOALS)

How to trace a recursive make?

I need to work on a system that uses automake tools and makes recursively.
'make -n' only traces the top level of make.
Is there a way to cause make to execute a make -n whenever he encounters a make command?
Use $(MAKE) to call your submakefiles, instead of using make. That should work. Check out How the MAKE variable works in the manual. Here's a quick example:
Makefile:
all:
#$(MAKE) -f Makefile2
Makefile2:
all:
#echo Makefile2
Command line:
$ make
Makefile2
$ make -n
make -f Makefile2
echo Makefile2
$
Does your recursive makefile look like this:
foo:
make -C src1
make -C src2
Or like this:
foo:
${MAKE} -C src1
${MAKE} -C src2
I think you need to use the second style if you want flags passed to child make processes. Could be your problem.
Setting the environment variable "MAKEFLAGS" to "n" may do what you need.
There are some more advanced tricks for tracing make commands here:
http://www.cmcrossroads.com/ask-mr-make/6535-tracing-rule-execution-in-gnu-make
The simplest of these tricks comes down to adding SHELL="sh -x" to your make command (running without "-n" in that case).

Resources