How to do repeated excution by makefile [duplicate] - makefile

This question already has an answer here:
How To Loop through n Test cases In Makefile?
(1 answer)
Closed last year.
I am using makefile to compile my program. After compiling, I want to do some test automatically and it will take several different files as the input. I am trying to do something like this:
test:
gcc test.c -o test
run:
./test test1.nlb >> test_output.txt
./test test2.nlb >> test_output.txt
...
We may have more test files so I am thinking whether there is a way I can avoid typing all these names. I have tried something like:
FILE = $(wildcard *.nlb)
run:
$(foreach file,$(FILE),./test $(file) >> test_output.txt)
But it didn't work.
Any ideas on how to write such a makefile? Thanks in advance!

Suppose the wildcard expanded to foo.nlb bar.nlb baz.nlb. Then your recipe:
$(foreach file,$(FILE),./test $(file) >> test_output.txt)
would expand to:
./test foo.nlb >> test_output.txt ./test bar.nlb >> test_output.txt ./test baz.nlb >> test_output.txt
which as you can clearly see, is not a valid shell command. Remember, make functions (with some special exceptions) are basically text manipulation functions. They don't run commands, they just massage text.
You have two choices: you can either write your loop using shell syntax, not make syntax:
for file in $(FILE); do ./test $$file >> test_output.txt; done
Or you can add a semicolon into your make foreach loop, to separate the commands:
$(foreach file,$(FILE),./test $(file) >> test_output.txt ;)
^^
which will expand to:
./test foo.nlb >> test_output.txt ; ./test bar.nlb >> test_output.txt ; ./test baz.nlb >> test_output.txt
which is a valid shell script.
Or better, use && as a separator so if a test fails you'll notice it:
$(foreach file,$(FILE),./test $(file) >> test_output.txt &&)

Related

How to compare two shell command output in Makefile?

My Makefile is:
.PHONY: check
check:
ifneq $(shell echo 123), $(shell echo 123)
$(error Not equal)
endif
When I run, I've got the error:
$ make
Makefile:3: *** Not equal. Stop.
But this should happen only when they're different, but they're not. Why?
ifneq cannot be indented. the way you've written it, it's being run via a shell command which means the $(error) is being evaluated first by the make command.
i'm guessing you want the make check to actually run two commands only when make check is invoked, and compare their output. you can do:
.PHONY: check
check:
if [ "`echo 123`" != "`echo 123`" ]; then \
echo "Not equal"; \
exit 1; \
fi
According to GNU Make docs, Conditional Parts cannot be used to control shell commands at the time of execution, since conditionals control what make actually "sees" in the makefile.
So to perform condition during compilation process, shell syntax is preferred, e.g.
SHELL := /bin/bash -e
.PHONY: check
check:
#test "$(shell echo 123)" = "$(shell echo 123)" \
|| { echo Not equal; exit 2; } \
&& { echo Equal; }

Delayed Windows cmd echo with GNU Make environment

I have few simple targets which create some files for me.
Example:
$(MAKE_INA):
#echo Building ASM compilation flags file $(notdir $(MAKE_INA))
#$(foreach i, $(sort $(ASMFLAGS) $(PFLAGS) $(ALL_INC_DIR) $(cppGetPreProcessorDefines)), $(shell echo $i >> $# ))
The target works fine, the file is being created and echo text displayed, but in that order (first the file is build then the echo is shown on cmd.exe console).
I guess that is related somehow with output buffering, but I was not able to find the way to flush the echos immediately.
Any hint? Is it even possible?
I am using Gnu Make 4.0
You are mixing up contexts here.
The first #echo line is a recipe line and is run by the shell when the target runs.
The second $(foreach) line is within the rule but is a make context line and is evaluated by make before running the recipe lines. Within that line $(shell) is also a make command and is run during the make expansion of the recipe instead of being run by the shell at recipe execution time.
To do what you want you can just use:
$(MAKE_INA):
#echo Building ASM compilation flags file $(notdir $(MAKE_INA))
#printf "%s\\n" $(sort $(ASMFLAGS) $(PFLAGS) $(ALL_INC_DIR) $(cppGetPreProcessorDefines)) >> $#
Which does the echoing at recipe execution time (so has the right order) and uses a single call to the printf built-in to output to the file instead of running N calls to echo.
Edit: For Windows cmd.exe compat you need to use echo $i >> $# & as the $(foreach) body so that cmd.exe runs multiple commands correctly.
If you did want to keep the N echo calls then you could use:
$(MAKE_INA):
#echo Building ASM compilation flags file $(notdir $(MAKE_INA))
#$(foreach i, $(sort $(ASMFLAGS) $(PFLAGS) $(ALL_INC_DIR) $(cppGetPreProcessorDefines)), echo $i >> $#; ))
Which has the $(foreach) output echo XXX >> $#; ....; echo ZZZ >> $#; as the recipe line to then execute during recipe execution.

$(eval ) in makefile causing recipe commences before first target error

CFormat:
define Format_File :=
#echo Formatting
ifneq ("$(wildcard $(1))","")
#echo if1
# The default extensions for intermediate files are not used,
# to avoid collisions with backup files open in the editor, etc.
# Save the original source file with an '_X' suffix on the extension.
ifneq("$(wildcard $(1)_X)","")
#echo if2
else
#echo else2
endif
#ren $(1) $(1)_X
# C-Comment is invoked separately, due to pathing limitations
# The redirection is a means to suppress blabbering.
#echo Formatting $(1) . . .
$(CFORMAT_PATH)\Cformat -h$(CFORMAT_PATH) $(1)_X -o$[Base, $(1)].tmp -ino >temp.tmp;
$(CFORMAT_PATH)\Ccomment -h$(CFORMAT_PATH) $[Base, $(1)].tmp -o$(1) >temp.tmp;
else
#echo else1
endif
endef
FormatAll: CFormat
$(foreach loopFile,$(ALL_S_SOURCES),$(eval $(call Format_File,$(loopFile))))
.PHONY: FormatAll
When I replaced eval with info it printed out the function call correctly but every time I try to actually eval the formatter it gives me the error in the title.
Edit: This question was plagued with syntax errors everywhere but following the advice of #MadScientist I was eventually able to get it to work using shell loops.
The shortest answer is, you can't do it that way. A single function like $(foreach ...), or a single variable expansion, can never expand to multiple logical lines in a makefile. That's just not how the make parser works.
Further, $(eval ...) can be used to construct a complete rule but you can't use it to build just part of a rule: before make starts to parse the output of the evaluation it will "close" any rule that's currently being defined (just like you can't put the introduction of a rule in one file and the recipe in another file and use include to include the recipe).
You haven't really explained why you want to do things in just this way. One simple answer is to use shell loops, not makefile loops, like this:
FormatAll: CFormat
#for f in $(ALL_S_SOURCES); do \
echo Formatting; \
if [ -f $$f ]; then \
echo if1; \
if [ -f $${f}_X ]; then \
echo if2; \
else \
echo else2; \
fi; \
ren $$f $${f}_X; \
echo Formatting $$f . . .; \
$(CFORMAT_PATH)\Cformat -h$(CFORMAT_PATH) $F{f}_X -o$[Base, $$f].tmp -ino >temp.tmp; \
$(CFORMAT_PATH)\Ccomment -h$(CFORMAT_PATH) $[Base, $$f].tmp -o$$f >temp.tmp; \
else \
echo else1; \
fi; \
done
I agree with Etan that the $[Base ...] syntax is weird and certainly isn't right.
If you want more details about eval and debugging, you might check out this post and the earlier ones in the series.
The error message is pretty clear: the foreach loop spits out recipe commands before/outside of a target recipe definition.
You can try something like:
all:
$(foreach loopFile,$(ALL_S_SOURCES),$(eval $(call Format_File,$(loopFile))))
.PHONY: all

Makefile as an executable script with shebang?

Is it possible to create an executable script that would be interpreted by make?
I tried this:
#!/usr/bin/env make --makefile=/dev/stdin
main:
#echo Hello!
but it does not work - hangs until press Ctrl-c.
#!/usr/bin/make -f
main:
#echo Hello World!
Is normally all you need in a standard make file. The filename is implicitly passed as the last argument. /dev/stdin here is (usually) the tty. You can do the whole env thing if there's a reason to, but often there's no need.
ajw#rapunzel:~/code/videocc/tools > vi Makefile
ajw#rapunzel:~/code/videocc/tools > chmod a+x Makefile
ajw#rapunzel:~/code/videocc/tools > ./Makefile
Hello World!
The following adds a level of indirection but it's the best solution I've come up with for self-executing makefiles not called "makefile":
#!/bin/sh
exec make -f- "$#" << 'eof'
.PHONY: all
all:
#echo 'hello world!'
I'm trying to collect #! env hacks for each language / program here.

Getting the name of the makefile from the makefile

How to get the name of the makefile in the makefile?
Thanks.
Note:
I would need that because I would like my makefile to call itself, but the makefile is not called Makefile, so I'd like to write something like this:
target:
($MAKE) -f ($MAKEFILENAME) other_target
location = $(CURDIR)/$(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))
WHERE_ART_THOU := $(location)
$(warning $(WHERE_ART_THOU))
I also believe this is GNU make-specific, but I'm not too sure.
(Should you have any questions, refer to amazingly written GNU make manual. But remember, that, just like Makefile, this manual should be read completely before putting the concepts into practice).
I couldn't figure out how it is done easily. As far as I understand, you'll have to do some manual job.
Later I will describe how it could be done and show scripts that introduce current_makefile variable. But I would like to stress an important concept at the first place.
You should understand that if we had some kind of variable current_makefile, that expands to the current makefile name, then it will have to change during the process of reading makefiles. That means that it should be used withinin "immediate" expansion context -- i.e. within commands that are executed during reading the makefile. Most commands, however, are executed after makefiles are read. Therefore, some commands will print the correct value smoothly, while in certain places, where "deferred" expansion is used, it will always expand to the root makefile name.
If you would want to use this variable within rule text, for example, you'll have to do tricks, because rule text always has deferred expansion. So, if your have the rule
rule:
echo In makefile $(current_makefile):
echo Making target $#
it will always print the name of the root makefile. Instead, to force immediate expansion, you will have to create another variable with makefile-specific name (i.e. names of such variables should be different in each makefile):
this_makefile_unique_name := $(current_makefile)
rule:
echo In makefile $(current_makefile):
echo Making target $#
or use eval:.
define make_rule
rule:
echo In makefile $(1):
echo Making target $$#
$(eval $(call make_rule,$(current_makefile)))
If you want to use the name of current makefile for debug purpose only, consider special debugging functions, like warning or info:.
$(warning We're in makefile $(current_makefile))
These functions use "immediate" expansion and will print the correct value.
How to define such a $(current_makefile)?
You have to manually maintain stack of makefile inclusions. When you include a makefile, its name is placed to the top of the stack; when you return from included makefile to the outer one, the topmost name is popped out of stack. This is achieved by inserting special calls to the beginning and the end of makefile:
# Beginning of makefile
$(eval $(makefile_names_push))
#... makefile text
$(warning $(current_makefile))
#...
$(eval $(makefile_names_pop))
#End of file
Now define the functions at the beginning of your root makefile.
lastword=$(word $(words $(1)),$(1))
define makefile_names_push
current_makefile := $$(CURDIR)/$$(call lastword,$$(MAKEFILE_LIST))
makefile_stack :=$$(makefile_stack) $$(current_makefile)
endef
define makefile_names_pop
makefile_stack := $$(filter-out $$(current_makefile),$$(makefile_stack))
current_makefile := $$(call lastword,$$(makefile_stack))
endef
If you're sure your make is new enough (version 3.81+), replace lastword call with builtin function:.
#inctead of $$(call lastword,$$(MAKEFILE_LIST))
$$(lastword $$(MAKEFILE_LIST))
Is it useful?
Totally useless. An only use that might be useful here is to make 100 makefiles that are symlinks to one makefile, the rules in these makefiles depending on their names. But it can be achieved within one makefile and foreach-eval technique described in the manual. So my post was a complete waste of time, though I had some fun :-)
This returns the name of the first Makefile called, i.e. the one at the bottom of the call stack:
MAKEFILE_JUSTNAME := $(firstword $(MAKEFILE_LIST))
MAKEFILE_COMPLETE := $(CURDIR)/$(MAKEFILE_JUSTNAME)
When used in non-cross-recursive situations (e.g. for makedepend), it is just the name of the current makefile.
I wanted to do something similar (for echoing the contents of the Makefile) for when I use Make for managing simple repetitive tasks. I came across this page and found it was exactly what I was after and really useful for my limited understanding of make.
My result after reading this page:
# Makefile - 'make' and 'make help' now echo the makefile.
help:
cat $(lastword $(MAKEFILE_LIST))
start:
sudo -u www /path/to/webapp/myhttpd restart
stop:
sudo kill `cat /path/to/webapp/data/httpd.pid`
A quick excursion to Google suggests this site has the answer.
G'day,
If you make a copy of your original makefile, say makefile_test, and then enter the command:
make -np -f makefile_test 2>&1 | tee output
That will evaluate the makefile and your make environment but not execute any of the commands. Looking through the output file for references to makefile_test will show you what is set in make's environment and where that value is being set.
N.B. This can generate a lot of info! And don't add the -d (debug) switch which will generate tons of additional output about make's decision process but minimal additional info about make's env.
HTH
The solutions here addresses 1) POSIX make with 2) Invoked, non included, makefile in 3) A Unix alike platform.
What the OP asked for:
target:
#pid=$$$$; \
while test `ps -ocomm= $$pid` != make; do \
pid=`ps -oppid= $$pid`; \
done; \
MAKEFILENAME=`ps -oargs= $$pid|sed 's/^.* -f *\([^ ]*\).*$$/\1/'`; \
test -z "$$MAKEFILENAME" -a -f Makefile && MAKEFILENAME=Makefile; \
test -z "$$MAKEFILENAME" -a -f makefile && MAKEFILENAME=makefile; \
export MAKEFILENAME; \
$(MAKE) -e -f $$MAKEFILENAME other_target
The targets depends on the makefile, kind of bloated:
TARGET1_MAKEFILENAME = target1_preamble
all: target1 target2...
target1: $(TARGET1_MAKEFILENAME) other_dependencies...
#test $(TARGET1_MAKEFILENAME) == target1_preamble && exit 0; \
built_instructions_for_target1;
target1_preamble:
#pid=$$$$; \
while test `ps -ocomm= $$pid` != make; do \
pid=`ps -oppid= $$pid`; \
done; \
MAKEFILENAME=`ps -oargs= $$pid|sed 's/^.* -f *\([^ ]*\).*$$/\1/'`; \
test -z "$$MAKEFILENAME" -a -f Makefile && MAKEFILENAME=Makefile; \
test -z "$$MAKEFILENAME" -a -f makefile && MAKEFILENAME=makefile; \
export MAKEFILENAME; \
$(MAKE) -e -f $$MAKEFILENAME target1;
Can be a bit simplified if make is invoked only for all targets.
MAKEFILENAME = invoked_makefile_placeholder
all: target1 target2...
target1: $(MAKEFILENAME) other_dependencies...
#test $(MAKEFILENAME) == invoked_makefile_placeholder && exit 0; \
built_instructions_for_target1;
invoked_makefile_placeholder:
#pid=$$$$; \
while test `ps -ocomm= $$pid` != make; do \
pid=`ps -oppid= $$pid`; \
done; \
MAKEFILENAME=`ps -oargs= $$pid|sed 's/^.* -f *\([^ ]*\).*$$/\1/'`; \
test -z "$$MAKEFILENAME" -a -f Makefile && MAKEFILENAME=Makefile; \
test -z "$$MAKEFILENAME" -a -f makefile && MAKEFILENAME=makefile; \
export MAKEFILENAME; \
$(MAKE) -e -f $$MAKEFILENAME
With the previous approach is trivial to implement a solution for included makefiles based in grep and a unique pattern contained in the makefile.
I never answer when I feel the question got a proper solution.

Resources