Makefile findstring won't match numerical character - makefile

CLANG_MAJOR_VER = x`${CXX} --version | egrep -i 'clang' | awk '{print $$3}' | awk '{ gsub(/\./, " "); print $$1 }'`xx
PAT = x6
CLANG_NAME = `${CXX} --version | grep -i 'clang' | awk '{print $$1}'`
CLANG_DEF = $(if $(findstring $(PAT),$(CLANG_MAJOR_VER)),abc,def)
all:
#echo $(CLANG_MAJOR_VER)
#echo $(CLANG_NAME)
#echo $(CLANG_DEF)
Result :
x6xx
clang
and if PAT is x or xx, then $(CLANG_DEF) will be abc, if PAT = 6, x6, 6x then $(CLANG_DEF) is def.

Your use of backticks would be fine in a shell script but it is not in a Makefile. The recursive assignments (VAR = VALUE) assign the exact right-hand-side strings to your variables and make expands them recursively when they are needed and only then, that is, in your case, when make expands the recipe of your all rule, just before passing it to the shell. So, if PAT = x6, when CLANG_DEF is expanded you have (decomposed in successive steps):
#echo $(CLANG_DEF)
#echo $(if $(findstring $(PAT),$(CLANG_MAJOR_VER)),abc,def)
#echo $(if $(findstring x6,x`${CXX} --version | egrep -i 'clang' | awk '{print $$3}' | awk '{ gsub(/\./, " "); print $$1 }'`xx),abc,def)
#echo $(if $(findstring x6,x`clang --version | egrep -i 'clang' | awk '{print $3}' | awk '{ gsub(/\./, " "); print $1 }'`xx),abc,def)
#echo $(if ,abc,def)
#echo def
And this does not depend on the result of the evaluation by the shell of:
clang --version | egrep -i 'clang' | awk '{print $3}' | awk '{ gsub(/\./, " "); print $1 }'
because it is not evaluated by the shell, it is only considered as a string by make when expanding $(findstring...)...
But if PAT = x, the recursive expansion becomes:
#echo $(CLANG_DEF)
#echo $(if $(findstring $(PAT),$(CLANG_MAJOR_VER)),abc,def)
#echo $(if $(findstring x,x`${CXX} --version | egrep -i 'clang' | awk '{print $$3}' | awk '{ gsub(/\./, " "); print $$1 }'`xx),abc,def)
#echo $(if $(findstring x,x`clang --version | egrep -i 'clang' | awk '{print $3}' | awk '{ gsub(/\./, " "); print $1 }'`xx),abc,def)
#echo $(if x,abc,def)
#echo abc
Instead of this unusual use of backsticks you could try to use the $(shell...) make function:
CLANG_MAJOR_VER = x$(shell ${CXX} --version | egrep -i 'clang' | awk '{print $$3}' | awk '{ gsub(/\./, " "); print $$1 }')xx
PAT = x6
CLANG_NAME = $(shell ${CXX} --version | grep -i 'clang' | awk '{print $$1}')
CLANG_DEF = $(if $(findstring $(PAT),$(CLANG_MAJOR_VER)),abc,def)
all:
#echo $(CLANG_MAJOR_VER)
#echo $(CLANG_NAME)
#echo $(CLANG_DEF)
And, by the way, your way of retrieving the version major number is not very robust (indeed, it fails on my Mac OS). Something like:
clang --version | sed -n '/version/s/.*version \([0-9]\+\).*/\1/p'
may be a bit better (not %100 sure, tested only on two OSes and two clang versions).

There is a library which can dissect simple (glob) string expressions:
include gmtt/gmtt.mk
define clang--version =
clang version 3.18.240-2ubuntu4 (tags/RELEASE_380/final)
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
endef
parsed-string = $(call glob-match,$(clang--version),clang version *.*.*-* *Target: *Thread model: *InstalledDir: *)
$(info $(parsed-string))
$(info Major: $(word 2,$(parsed-string)) Minor: $(word 4,$(parsed-string)) Bugfix: $(word 6,$(parsed-string)))
Output:
clang§version§ 3 . 18 . 240 - 2ubuntu4 § (tags/RELEASE_380/final) Target:§ x86_64-pc-linux-gnu Thread§model:§ posix InstalledDir:§ /usr/bin
Major: 3 Minor: 18 Bugfix: 240

Related

I have a problem while programming with using shell in makefile

the code is:
# android/device/mediatek/build/core/build_dtboimage.mk
...
my_dtbo_id := 0
define mk_dtboimg_cfg
$(eval name := $$(basename $1)) \
echo "file name:$(name)"; \
$(eval dts_file_name := $(notdir $(name))) \
echo "terminal_name:$$(echo $(dts_file_name) | awk -F '_' '{print $$1}')"; \
$(eval terminal_name := $$(echo $(dts_file_name) | awk -F '_' '{print $$1}')) \
$(eval main_name := $$(echo $(dts_file_name) | awk -F '_' '{print $$3}')) \
echo "terminal_name :$(terminal_name), main_name :$(main_name )"; \
...
and the output is:
file name:out/target/product/k62v1_64_pax/obj/KERNEL_OBJ/arch/arm64/boot/dts/mediatek/A3700_MT6762_V02_V01
terminal_name:A3700
terminal_name:, main_name:
As shown above, the code echo "terminal_name:$$(echo $(dts_file_name) | awk -F '_' '{print $$1}')"; \ can get the right output of A3700, but i don't know why the output of terminal_name and main_name is null.
the right output i expect is terminal_name:A3700, main_name:V02, in other words, i hope terminal_name and main_name can correctly assigned.
What should I do to solve this problem?
thanks!
You're making this far too complicated.
Remove the unnecessary functions and be generous with $:
...
dts_file_name := $$(notdir $$(name))
terminal_name := $$(shell echo $$(dts_file_name) | awk -F '_' '{print $$$$1}')
main_name := $$(shell echo $$(dts_file_name) | awk -F '_' '{print $$$$3}')
$$(info terminal: $$(terminal_name), main: $$(main_name))
...

Echo command containing both double and single quotes

I have this command
cp $(ldd MyApp.out | awk '{print $3}' | sed -E '/^$/d') lib/
and at some point, I want to echo it into a file but a naive approach echo command_above doesn't work.
If I put the command into single quotes, then $3 expands to whitespace.
Is it possible to print that command char-by-char as it is after echo command without any expansion and substitution?
The common approach is to use the << operator to read until some delimiter:
# "cat" just prints what it reads
cat << 'EOF' > output_file
cp $(ldd MyApp.out | awk '{print $3}' | sed -E '/^$/d') lib/
EOF
Use xargs to pass file names list to cp
ldd MyApp.out | awk '$3!=""{print $3}' | xargs -d'\n' -I{} cp {} lib/
For debugging and logging purposes you can use set -x or set -v:
set -v # dump commands below
cp $(ldd MyApp.out | awk '{print $3}' | sed -E '/^$/d') lib/
set +v # stop dumping

Bash - Counter for multiple parameters in file

I created a command, which works, but not exactly as I want. So I would like to upgrade this command to right output.
My command:
awk '{print $1}' ios-example.com.access | sort | uniq -c | sort -nr
Output of my command:
8 192.27.69.191
2 82.202.69.253
Input file:
https://pajda.fit.vutbr.cz/ios/ios-19-1-logs/blob/master/ios-example.com.access.log
Output I need(hashtags instead of numbers):
198.27.69.191 (8): ########
82.202.69.253 (2): ##
cat ios-example.com.access | sort | uniq -c | awk 'ht="#"{for(i=1;i<$1;i++){ht=ht"#"} str=sprintf("%s (%d): %s", $2,$1, ht); print str}'
expecting file with content like:
ipadress1
ipadress1
ipadress1
ipadress2
ipadress2
ipadress1
ipadress2
ipadress1
Using xargs with sh and printf. Comments in between the lines. Live version at tutorialspoint.
# sorry cat
cat <<EOF |
8 192.27.69.191
2 82.202.69.253
EOF
# for each 2 arguments
xargs -n2 sh -c '
# format the output as "$2 ($1): "
printf "%s (%s): " "$2" "$1"
# repeat the character `#` $1 times
seq "$1" | xargs printf "#%.0s"
# lastly a newline
printf "\n"
' --
I think we could shorten that a bit with:
xargs -n2 sh -c 'printf "%s (%s): %s\n" "$2" "$1" $(printf "#%.0s" $(seq $1))' --
or maybe just echo, if the input is sufficiently safe:
xargs -n2 sh -c 'echo "$2 ($1): $(printf "#%.0s" $(seq $1))"' --
You can upgrade your command by adding another awk to the list, or you can just use a single awk for the whole thing:
awk '{a[$1]++}
END { for(i in a) {
printf "%s (%d):" ,i,a[i]
for(j=0;j<a[i];++j) printf "#"; printf "\n"
}
}' file

Shell Output Alignment

How to align this script output.
for instance in `find /bxp/*/*/*/prod/*/apache_*/httpd/htdocs/ -type f -name status.txt` ; do
echo "`hostname`: `ls -ltr | ${instance}` : `cat ${instance}`"
done
Output looks like:
r008abc, /bxp/xip/xip.pentaho-server_pentaho-server-assembly/pentaho.prod.jobengine/prod/xip.pentaho-server_web.partition_0.0.1/apache_5.3.3-2.2.
26/httpd/htdocs/status.txt, Web server is disabled
However i want the output be like:
r008abc| xip - xip.pentaho-server_web.partition_0.0.1 | Web server is disabled
xip - is nothing but the second column of the $instance - xip.pentaho-server_web.partition_0.0.1 is 6th column of the $instance. How can I achieve this. I tried awk command but it was not helpful. Your suggestion is appreciated.
Command I tried
for instance in `find /bxp/*/*/*/prod/*/apache_*/httpd/htdocs/ -type f -name status.txt` ; do
echo "`hostname`: `"ls -ltr | awk -F '/' '{print $3}"' ${instance}` : `cat ${instance}`"
done
something like this oneliner:
find /bxp/*/*/*/prod/*/apache_*/httpd/htdocs/ -type f -name status.txt | awk -F/ -v host=$(hostname) '{cmd="cat \047" $0 "\047"; if ((cmd | getline out) > 0){printf "%s| %s - %s | %s\n", host,$3, $7, out} close(cmd);}'
Explanation of awk-script:
awk -F/ # use / as field separator
-v host=$(hostname) # set var host
'{
cmd="cat \047" $0 "\047" # define cmd
if ((cmd | getline out) > 0) # read output of cmd
printf "%s| %s - %s | %s\n",host,$3,$7,out; # print formatted result
close(cmd);
}'

Using shell in Makefile to find Ubuntu Version

I'm trying make Ubuntu version specific distros of my tool so I want to get the os name and version. I have the following code:
ifeq ($(OS),Windows_NT)
OS_POD+=win
else
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Linux)
OS_VERS := $(shell lsb_release -a 2>/dev/null | grep Description | awk '{ print $2 "-" $3 }')
OS_POD=./dist/linux/$(OS_VERS)
endif
ifeq ($(UNAME_S),Darwin)
OS_POD=./dist/mac
endif
endif
I use the shell one-liner:
lsb_release -a 2>/dev/null | grep Description | awk '{ print $2 "-" $3 }'
Which properly returns Ubuntu-12.04.2 outside of the Makefile, but inside it it returns nothing. That is, the OS_VERS variable is just -.
How can I fix this?
In a Makefile, $ is special. Use $$ where you want the shell to find a dollar.
OS_VERS := $(shell lsb_release -a 2>/dev/null | grep Description | awk '{ print $$2 "-" $$3 }')
You need to escape the $ inside your command.
OS_VERS:=$(shell lsb_release -a 2>/dev/null | grep Description | awk '{ print $$2 "-" $$3 }')
The following sample Makefile prints correctly so it may be a different part of your Makefile.
print:
#echo $(OS_VERS)
OS_VERS:=$(shell lsb_release -a 2>/dev/null | grep Description | awk '{ print $$2 "-" $$3 }')
OS_VERS := $(shell cat /etc/os-release | grep ^NAME | cut -d'=' -f2 | sed 's/\"//gI')

Resources