I'm completely stumped on how to do this in a Makefile
Let's say I have a target. Inside the target I have a loop. How do i change a variable to keep track of the iterations?
For example:
COUNTER = 0
target:
(loop){
COUNTER++
echo COUNTER
}
I know that variables in Makefiles are only expanded, and I'm not sure if they can be permanently changed, but there has to be a way to do this, right? :(
Here are some sources that are asking similar questions. It seems like those examples only change the variable temporarily:
How do I perform arithmetic in a makefile?
How to do arithmetic operation in makefile?
Doing simple math in Makefile
Maybe I have to use the eval function somehow?
Maybe I have to append onto a Makefile string a character each time and then use something in the shell to count the characters?
If the variable doesn't have to survive the rule, this should do (I'm assuming bash):
clean:
#n=0 ; \
for x in $(THINGS_TO_BE_DELETED); do \
if [ -f $$x ] ; then \
rm $$x; \
let "n+=1" ; \
fi ; \
done ; \
echo deleted $$n files;
Here is one solution: Write a simple script like this:
#!/bin/bash
count=`cat count.txt`
count=$((count + 1))
echo $count
cat $count > count.txt
Initialize the file by doing
$ echo "0" > count.txt
Then include it as a .PHONY requirement to build whatever you'd like.
This is similar to the accepted answer, but the syntax below should work with a POSIX compliant shell. Quotes should also be used inside of the test.
clean:
#n=0; \
for x in *.a *.b *.c ; do \
if [ -f "$$x" ]; then \
rm "$$x"; \
n=$$((n+1)); \
fi; \
done; \
echo deleted $$n files;
Note: tabs must be used for indentation
Related
I am writing a complicated Makefile which have multiple parts like:
step1:
if [[ ${input} == "delete" ]]; then \
echo "this is a test to delete files"; \
else \
echo "error stop"; \
fi
step2:
rm -f *.txt
test:
make step1
make step2
So if I enter "make test input=delete", it will delete all the .txt files. And if I enter "make test input=none", it would not do anything. I know the simplest way is to combine step1 and step2 as:
test:
if [[ ${input} == "delete" ]]; then \
echo "this is a test to delete files"; \
rm -f *.txt; \
else \
echo "error stop"; \
fi
But my Makefile is so complicated that I have to separate into several parts. Does Makefile support similar features? If yes, what should I look for? Thanks.
You should always use $(MAKE) and never use make directly when invoking a sub-make.
If you do that, then all command-line overrides you provide will be correctly passed to the sub-makes.
BTW, you should not use the bash syntax [[ x == y ]]. If you run this makefile on a system where the default shell is limited to POSIX standard syntax this won't work. You should use POSIX syntax: [ x = y ].
As you use GNU Make you can use its conditionals, e.g.
ifeq:
test:
ifeq ($(input),delete)
echo "this is a test to delete files"
rm -f *.txt
else
echo "error stop"
endif
Or, if you want to control the prerequisites of a target:
step1:
echo "this is a test to delete files"
step2:
rm -f *.txt
# test always depends on step1
test: step1
# test also depends on step2, if input = delete
ifeq ($(input),delete)
test: step2
endif
I'm trying to set env variable from Perl script and using it inside Makefile doesn't work. While dumping ENV hash tree shows variable successfully set.
Makefile code
list_gen: $(TESTBENCH_PATH)/blocks/soc_tb/global/default
perl list_gen.pl; \
$(MAKE) $(RECITAL_PATH)/catalog/catalog.xml
# touch $#
# Generates catalog.xml from .xmls of VIPs
$(RECITAL_PATH)/catalog/catalog.xml: $(VIP_XMLs)
#echo "# generating catalog.xml " ; \
echo "vip list is $(VIP_LIST)" ; \
rpfCatalog --add $(VIP_XMLs); \
for vip in $(VIP_LIST); \
do \
rpfCatalog --add $$TESTBENCH_PATH/common_blocks/$$vip/global/default.xml; \
done
Perl Script code
$ENV{"VIP_LIST"} = $vip_exists;
I don't think there is any elegant way to update environment of make by running any external command (like perl script) from it. Because even a direct way wouldn't work.
/home/user> cat makefile
NUM=100
first:
NUM=200
echo $(NUM)
second:
NUM=200; echo $(NUM)
third:
NUM=200; \
echo $(NUM);
/home/user> make first
NUM=200
echo 100
100
/home/user> make second
NUM=200; echo 100
100
/home/user> make third
NUM=200; \
echo 100;
100
If you are only looking for a perl script to modify it's parent's environment, something like this works (in a very limited way):
/home/user> cat change_env.pl
#!/usr/bin/perl -w
print "NUM=100\n";
/home/user> NUM=1
/home/user> echo $NUM
1
/home/user> eval $(./change_env.pl)
/home/user> echo $NUM
100
Coming to original your question, .. can't you call make after making the changes to your shell environment?
VIP_LIST=$(perl my.pl)
Would save the output of the script in the variable of the parent shell.
HTH
Georg
When you execute your Perl script, do it like $(./myperlscript.pl) on the prompt. What you need to do is
$ENV{VIP_LIST} = $vip_exists;
Drop the quotes
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
I am making a makefile to rename files with a random number in it (I am a newbie in shell script). I don't understand why, but when I run the file $rand is given the value 'ANDOM'. When I run this outside of the makefile it works.
I run this in the Mac os terminal, in case it's helpful.
all: renamefiles
renamefiles:
rand=$RANDOM && mv myfile.css $rand-myfile.css && mv myotherfile.css $rand-myotherfile.css
Wouldn't it be easier/better to use a date/time stamp so that the renamed files are listed in date order?
You need to use two $ signs in the makefile for each $ that you want the shell to see.
Thus:
all: renamefiles
renamefiles:
rand=$$RANDOM && \
mv myfile.css $$rand-myfile.css && \
mv myotherfile.css $$rand-myotherfile.css
Or, with date/time stamps:
all: renamefiles
renamefiles:
time=$$(date +'%Y%m%d-%H%M%S') && \
mv myfile.css $$time-myfile.css && \
mv myotherfile.css $$time-myotherfile.css
To use a random number within one or multiple make variables, the following works fine for me:
FOO="some string with \"$$rand\" in it"
BAR=" you may use it $$rand times."
foobar:
rand=$$$$ && \
echo $(FOO) $(BAR)
You might need to surround a multi-letter macro name with braces (or parentheses), for example
${RANDOM}
$(RANDOM)
ref
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