How to isolate the version number out of a string in makefile? - shell

I want to obtain the Boost Library Version number 1.58 out of the string "Version: 1.58.0.1ubuntu1" or generally any other version of Boost. This would allow me to compare the current version of boost to my specific version that I need to match. This is what I have so far.
Configure:
if ( test -d $(Boost) )
then
CurrVer=$$(dpkg -s libboost-dev | grep 'Version'
echo $$CurrVer
echo $$CurrVer | tr -cd [:digit:]
else
make DLBoostV1_58
fi
The problem is that I can narrow the string down to the digits 158011 but I can't figure out how to remove the digits 011.
I have read geeksforgeeks website for grep, sed, and awk commands but what got me to this point are, How to extract numbers from a string?, https://askubuntu.com/questions/147474/how-can-i-find-boost-version, https://www.geeksforgeeks.org/tr-command-in-unix-linux-with-examples/nd .
Expecting output: 158
Resulting output: 158011

Supposing you have captured the Boost version message in shell variable CurrVer, you can use the prefix-removal option of parameter expansion to remove the lead text (${CurrVer##* } removes everything up to the last space character), inside an array assignment (a=(...)) with the period (.) as a field separator (IFS=.) to split the version string at the right delimiters. Then you just need to read back the array elements you want.
For example,
ShortVer=$( IFS=.; a=(${CurrVer##* }); echo "${a[0]}.${a[1]}" )
Escaping that for make is left as an exercise.

You can use gmtt which can take apart strings per glob-match:
include gmtt-master/gmtt-master/gmtt.mk
VERSION := Version: 1.58.0.1ubuntu1
VERS-AS-LIST := $(call glob-match,$(VERSION),Version: *.*.*.*ubuntu*)
$(info $(VERS-AS-LIST))
MAJOR := $(word 2,$(VERS-AS-LIST))
MINOR := $(word 4,$(VERS-AS-LIST))
BUGFIX := $(word 6,$(VERS-AS-LIST))
$(info $(MAJOR)-$(MINOR)-$(BUGFIX))
Output:
$ make
Version:§ 1 . 58 . 0 . 1 ubuntu 1
1-58-0
Notice that the first part of the glob pattern was Version: with a space character at the end, which is conserved via the § character (contained in the gmtt variable $(-spacereplace) and removable with the function $(call spc-unmask,_string_))

Related

How to check a text variable length in makefile?

As the title suggests, need to check the length of a text variable in a Make file script
I don't think there's anything built in, but
# XXX BROKEN - see below
len := $(shell printf '%s' '$(VARIABLE)' | wc -c)
should roughly do what you want.
This will fail if there are literal single quotes in the value, though. They can be escaped but doing that correctly is tedious and pesky. Maybe try
len := $(shell printf '%s' '$(subst ','"'"',$(VARIABLE))' | wc -c)
but I'm not sure I have covered all possible bases.
Maybe also notice the difference between wc -c (bytes) and wc -m (characters). They yield the same result for plain ASCII, but different results for other Unicode strings (perhaps also depending on your system encoding).

Makefile: get short version string from version string

How to get the short version string from the version string in Makefile? Suppose the short version string is got by sed 's/\.[0-9][0-9]$//g' $version.
version=v3.2.11
version_short=$(patsubst %.11,%,$(version))
print-% : ; #echo $* = $($*)
all: print-version_short
You don't list GNU make in your tags but your makefile is clearly GNU make so I'll respond in kind.
If you know there are always exactly two "."s it can be done like this:
combine = $(word 1,$1).$(word 2,$1)
short := $(call combine,$(subst ., ,$(version)))
This converts the "." to spaces then calls a user-defined function with the resulting arguments, which puts back together the first two arguments.

remove patterned prefix from list in gnu make

If I have a list of files:
files := xx_foo1.c yy_foo2.c zz_bar1.c aa_bb_bar2.c
Is there any way of removing everything up to the last underscore from the list to get foo1.c foo2.c bar1.c bar2.c?
I was looking into using patsubst, but I would need two%'s -- one for the first part to be ignored, and one for the last part to be kept.
It can be done but it's a little gross. You want something like this:
final := $(foreach F,$(files),$(word $(words $(subst _, ,$F)),$(subst _, ,$F)))
This says, for each element in files we convert the _ to a space, now we can use our per-word functions to manipulate it: extract the last word in the list of words.
ETA
ReAl points out below that this can be simplified using lastword:
final := $(foreach F,$(files),$(lastword $(subst _, ,$F)))
As I see it you are using the underscore as separating character between hierarchical names. GNUmake is well equipped to work with such a scheme if the character is /: file name functions.
So your example should simply boil down to
$(notdir $(subst _,/,$(files))
Use the external program — sed — and enjoy all its power
files := xx_foo1.c yy_foo2.c zz_bar1.c aa_bb_bar2.c
f := `echo $(files) | sed -e "s/[[:alnum:]]*_//g"`
all:
echo $(f)

How to know if a makefile variable is a string of char or numbers?

It probably sounds very elementary but I am unable to find a way to classify a makefile variable into text or number. My pseudocode is like this:
ifeq ($N, 'numeric')
CFLAGS+=-D$N
endif
How to do this? I am using the GNU Make (in cygwin/Windows). I read the make.pdf that comes with it but could not find a way.
Thanks in Advance
EDIT: adopted a suggestion by bobbogo that does not depend on the number of characters to purge.
I assume you use GNU make. Here is a make-only solution, without calling the shell. For performance reasons, depending on your use of it, it can be preferable. Moreover, it does not depend on which shell make uses. Last but not least, it uses recursion and I like recursion:
define PURGE
$(if $(2),$(call PURGE,$(subst $(firstword $(2)),,$(1)),$(filter-out $(firstword $(2)),$(2))),$(1))
endef
DIGITS := 0 1 2 3 4 5 6 7 8 9
define IS_NOT_A_NUMBER
$(call PURGE,$(1),$(DIGITS))
endef
CFLAGS += $(if $(call IS_NOT_A_NUMBER,$(N)),,-D$(N))
all:
$(info N=$(N) => CFLAGS=$(CFLAGS))
Demo:
host> make N=12345
N=12345 => CFLAGS=-D12345
make: 'all' is up to date.
host> make N=foobar
N=foobar => CFLAGS=
make: 'all' is up to date.
Explanation: PURGE is a recursive macro that takes two arguments. The first one ($(1)) is a string to test, the second one ($(2)) is a list of words to match. If $(2) is the empty list PURGE returns $(1). Else, it calls itself with two new parameters:
the value of $(1) where the first word of $(2) has been substituted by nothing,
$(2) from which the first word has been removed
and returns the result. So, if you call PURGE with a string and the list of all digits, it returns the empty string if and only if the string contained only digits.
All make variables are strings. To find out whether a string is in fact a number, you need some elementary text analysis functions. GNU make itself does not offer anything convenient in this area, but you could run a shell command to do the job, perhaps like this:
define is_number
$(shell test '$(1)' -eq '$(1)' 2>/dev/null && echo yes || echo no)
endef
ifeq ($(call is_number, $(N)),yes)
default:
#echo N is a number
else
default:
#echo N is not a number
endif
This results in:
$ make N=5
N is a number
$ make N=string
N is not a number
However, such string processing can be quite unreliable if the string contains special characters.

Makefile: How to apply an equivalent to filter on multiple wildcards

I am writing a Makefile and I get stuck on a filter function limitation.
Indeed, filter takes only one wildcard.
What I would like to do is:
I have a list a files, some matching the regexp blabla, some not. But for this I need 2 wildcards, thus i cannot use filter function.
I would like to split my original list in 2 lists, one containing all the element containing the blabla string (filter equivalent) and the other one containing the not matching one (filter-out equivalent).
thanks for your help.
You can do this without running any external commands. Define the two macros
containing = $(foreach v,$2,$(if $(findstring $1,$v),$v))
not-containing = $(foreach v,$2,$(if $(findstring $1,$v),,$v))
Now you can do
LIST := a_old_tt x_old_da a_new_da q_ty_we
LIST_OLD := $(call containing,old,$(LIST))
LIST_NOT_OLD := $(call not-containing,old,$(LIST))
One of Make's greatest shortcomings is its poor ability to handle regular expressions. The functions filter and filter-out can't find "old" in the middle of a word. I'd suggest this hack:
NOT_OLD = $(shell echo $(LIST) | sed 's/[^ ]*old[^ ]* *//g')
OLD = $(filter-out $(NOT_OLD), $(LIST))
You could take advantage of your shell's more advanced string handling capabilities. Assuming that you have bash, you could use the following in your makefile:
LIST := a_old_tt x_old_da a_new_da q_ty_we
LIST_NOT_OLD := $(shell l=($(LIST)); echo $${l[#]//*old*})
LIST_OLD := $(filter-out $(LIST_NOT_OLD),$(LIST))
You can find an explanation of the bash string replacement mechanism in how to delete elements from an array based on a pattern. The double $ is required to keep the $ sign in the shell invocation.

Resources