makefile shell and pipe - shell

I need help: the following command line is not working
TESTS := $(shell cat test_cases_file | egrep -v ^\s*(#|$) )
all: $(TESTS)
when I launch:
make all
I get an error something like "call of shell command is not finished."

You're missing quotes in egrep parameter. If I were make I'd too believe that # is the beginning of comment.
Apparently simply adding quotes won't help, you'll need to escape the # too.
edit:
Actually $ has to be escaped too and in case of make it is done using $$
Try this:
TESTS := $(shell egrep -v '^\s*(\#|$$)' test_cases_file)
all: $(TESTS)
Finally, you don't really need to discard blank lines - that won't hurt:
TESTS := $(shell egrep -v '^\s*\#' test_cases_file)
all: $(TESTS)

What you've stumbled upon is probably the fact, that parentheses are not escapeable in GNU Make.
You can work around this problem by using helper script:
TESTS := $(shell ./grep.sh test_cases_file )
all: $(TESTS)
Where grep.sh is:
cat $1 | egrep -v ^\s*(#|$)

Related

addprefix in MAKE doesn't work correctly when -e is used as prefix

I encounter a strange behavior of addprefix with -e. The expected -e for the first word is not added. Here's the makefile to reproduce it.
a := file1 file2 file3
b := $(addprefix -s , $(a))
c := $(addprefix -e , $(a))
all:
#echo $(b)
#echo $(c)
Result:
-s file1 -s file2 -s file3
file1 -e file2 -e file3
Is it a bug or something is missing?
You should never use echo to print any string that is not just simple text that you know contains no starting dash characters. The echo command is not really standardized and there are a lot of versions, some of which accept options and some of which don't.
On your system, the echo command accepts a -e option so when you use echo -e file -e file2 -e file3 echo eats the first -e.
You can use:
all:
#printf '%s\n' '$(b)'
#printf '%s\n' '$(c)'
to be portable. Or you can use make's built-in functions:
all:
$(info $(b))
$(info $(c))

Modifying file extensions using Makefiles

I'm new to Makefiles and I want to modify the extension of a set of files. The following command works on the shell:
for file in path/*.ext1; do j=`echo $file | cut -d . -f 1`;j=$j".ext2";echo mv $file $j; done
However, I'm not sure how to run this in a Makefile. I tried running
$(shell for file in path/*.ext1; do j=`echo $file | cut -d . -f 1`;j=$j".ext2";echo mv $file $j; done)
But this never did what I needed it to do. What do I need to do to make this work on the Makefile? How do I call it in a section?
The immediate answer to your question is that the $ character is special to make: it introduces a make variable. If you want to pass a $ to the shell, you'll have to write two of them: $$.
So, your shell function invocation would have to be written as:
$(shell for file in path/*.ext1; do j=`echo $$file | cut -d . -f 1`;j=$$j".ext2";echo mv $$file $$j; done)
However, this is almost certainly not a good way to do what you want. You don't really describe clearly what you want to do, however. If you just want to have a target in a makefile that can be invoked to make this change, you can use:
fixext:
for file in path/*.ext1; do \
j=`echo $$file | cut -d . -f 1`; \
j=$$j".ext2"; \
echo mv $$file $$j; \
done
Or, taking advantage of some useful shell shortcuts, you could just run:
fixext:
for file in path/*.ext1; do \
echo mv $$file $${file%.*}.ext2; \
done
Now if you run make fixext it will perform those steps.
But, a much more make-like way to do it would be to write a single rule that knows how to rename one file, then use prerequisites to have them all renamed:
TARGETS = $(patsubst %.ext1,%.ext2,$(wildcard path/*.ext1))
fixext: $(TARGETS)
%.ext2 : %.ext1
mv $< $#
Now you can even run make -j5 and do 5 of the move commands in parallel...
you can also add rename blocks at the top of your file eg to change a suffix
output := $(input:.mov=.mp4)
but this won't work inside a make command as far as I can see
check:
output := $(input:.mov=.mp4)
gives
$ input=walkthrough.mov make check
output := walkthrough.mp4
make: output: No such file or directory
make: *** [check] Error 1

makefile: using for and ifneq

I have an objective in my makefile named "cambios" that makes a cvs commit on each file of the project (by separate) and shows the last revision.
Now, I have an auxiliar shellscript that do that, but I'd like to know how I can do it in the makefile. I've created the objective cambios2 that do the same without the auxiliar shellscript, but it has some syntax problems.
makefile:
(...)
TODO= makefile cambiosaux.sh lib/libreria.cc include/libreria.h src/principal.cc
(...)
cambios:
#./cambiosaux.sh "$(TODO)"
cambios2:
#for dir in $(TODO); do \
A = $(cvs commit -m "Incorporando cambios automáticamente." $$dir) \
ifneq ($(A),)
echo $dir ; \
echo "Última revisión:"$(echo $(A) | sed 's/.*new revision: //' | sed 's/;.*//') ; \
endif ; \
done
cambiosaux.sh :
for dir in $1
do
A=$(cvs commit -m "Incorporando cambios automáticamente." $dir)
if [ "$A" != "" ]; then
echo $dir
echo "Última revisión:"$(echo $A | sed 's/.*new revision: //' | sed 's/;.*//')
fi
done
There are some syntax problems in the objective cambios2, but I'm really new on doing makefiles and I really don't know how to solve that problems.
Thanks!
You forgot to escape dollars that are parts of Bash command command substitution, and Make tries to perform variable expansion: $(cvs commit ...), $(echo $(A) ...).
Also you can't assign a Make variable inside a recipe. A = $(cvs commit ...) is illegal, it won't be treated neither as Make assignment nor as Bash. Try to run make with --warn-undefined-variables, I guess it will say lots of interesting details.
Finally ifneq conditional is part of Make language, and it gets interpreted at the very early stage of reading Makefile. Thus you must not indent ifneq and endif with tabs. How Make reads a Makefile chapter gives a good explanation.
To conclude, I would recommend you to leave a separate sh as is and just invoke it from your Makefile. It is not good practice to mix code in two different languages.
Okay, I found the way it works:
CVS: $(TODO)
#for dir in $?; do \
echo $$dir ; \
echo "Última revisión:" $$(cvs commit -m "Incorporando cambios automáticamente." $$dir | grep "new revision" | sed 's/.*new revision: //' | sed 's/;.*//') ; \
done
cambios: CVS

What's wrong with the following GNU make shell variable expansion?

On this line:
GCCVER:=$(shell a=`mktemp` && echo $'#include <stdio.h>\nmain() {printf("%u.%u\\n", __GNUC__, __GNUC_MINOR__);}' | gcc -o "$a" -xc -; "$a"; rm "$a")
I get:
*** unterminated call to function `shell': missing `)'. Stop.
What's wrong with my stupidly circuitous variable?
Update0
$ make --version
GNU Make 3.81
$ bash --version
GNU bash, version 4.2.8(1)-release (x86_64-pc-linux-gnu)
$ uname -a
Linux 2.6.38-10-generic #46-Ubuntu SMP x86_64 GNU/Linux
$ gcc --version
gcc (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2
when using $ for Bash inside a Makefile, you need to double them: $$a for example. I'm not familiar with the notation $' but I'll have to assume you know what you're doing with that. unless it's a Makefile construct, you need to double the dollar sign on that one too.
also, the hash sign # is terminating the shell expansion in Make's evaluation, which is why it never sees the right paren. escaping it helps, but I don't have it working quite right yet.
I'm debugging it by having two steps: first is setting GCCVER to be the list of commands without the enclosing $(shell), then in the 2nd step setting GCCVER := $(shell $(GCCVER)). you might want to try that too, commenting out the $(shell) step when it doesn't work, using export, and making a "set" recipe:
GCCVER := some commands here
#GCCVER := $(shell $(GCCVER)) # expand the commands, commented out now
export # all variables available to shell
set:
set # make sure this is prefixed by a tab, not spaces
Then:
make set | grep GCCVER
[update] this works:
GCCVER := a=`mktemp` && echo -e '\#include <stdio.h>\nmain() {printf("%u.%u\\n", __GNUC__, __GNUC_MINOR__);}' | gcc -o "$$a" -xc -; "$$a"; rm "$$a"
GCCVER := $(shell $(GCCVER))
export
default:
set
jcomeau#intrepid:/tmp$ make | grep GCCVER
GCCVER=4.6
And full circle, having gotten rid of the extra step:
jcomeau#intrepid:/tmp$ make | grep GCCVER; cat Makefile
GCCVER=4.6
GCCVER := $(shell a=`mktemp` && echo -e '\#include <stdio.h>\nmain() {printf("%u.%u\\n", __GNUC__, __GNUC_MINOR__);}' | gcc -o "$$a" -xc -; "$$a"; rm "$$a")
export
default:
set
Using the $' Bash construct:
jcomeau#intrepid:/tmp$ make | grep GCCVER; cat Makefile
GCCVER=4.6
GCCVER := $(shell a=`mktemp` && echo $$'\#include <stdio.h>\nmain() {printf("%u.%u\\n", __GNUC__, __GNUC_MINOR__);}' | gcc -o "$$a" -xc -; "$$a"; rm "$$a")
export
default:
set
Since your system doesn't work the same as mine, I'm going to cop out and say either use reinierpost's suggestion or, alternatively:
GCCVER := $(shell gcc -dumpversion | cut -d. -f1,2)

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