Version number comparison inside makefile - makefile

In a makefile, I'd like to define a variable specifying whether the current redhat-release is greater than 5.3. (This variable will be passed to gcc as a #define)
So far I've come up with:
# Find out which version of Red-Hat we're running
RH_VER_NUM = $(shell /bin/grep -o [0-9].[0-9] /etc/redhat-release)
RH_GT_5_3 = $RH_VER_NUM > '5.3'
What would be the correct way to define RH_GT_5_3?

GNU Make doesn't contain any string comparisons other than equality, and test can only do less-than/greater-than tests on integers. Split the version number into its integral parts and do the comparison that way. Try this (note also that := is better than = here, as make's lazy evaluation would call your $(shell) commands many more times than required:
RH_VER_MAJOR := $(shell echo $(RH_VER_NUM) | cut -f1 -d.)
RH_VER_MINOR := $(shell echo $(RH_VER_NUM) | cut -f2 -d.)
RH_GT_5_3 := $(shell [ $(RH_VER_MAJOR) -gt 5 -o \( $(RH_VER_MAJOR) -eq 5 -a $(RH_VER_MINOR) -ge 3 \) ] && echo true)
ifeq ($(RH_GT_5_3),true)
CPPFLAGS += -DRH_GT_5_3=1
endif

A little bit shorter solution is:
RH_GT_5_3 := $(shell echo -e "5.4\n$(RH_VER_NUM)"|sort -ct. -k1,1n -k2,2n && echo YES)
This will set RH_GT_5_3 to "YES" if RH_VER_NUM is greater then or equal to 5.4 (so greater then 5.3). Otherwise RH_GT_5_3 will be set to empty.
If multiple version numbers need to be checked we can define a function:
IF_VER_GE = $(shell echo -e "$2\n$1"|sort -ct. -k1,1n -k2,2n && echo YES)
GLIBC := $(word 2,$(shell getconf GNU_LIBC_VERSION))
...
all:
ifeq "$(call IF_VER_GE, $(GLIBC), 2.5)" "YES"
echo "GE"
else
echo "LT"
endif
I used the "$(word 2,..." instead of "$(lastword,..." because the later does not work in make 3.8. And shorter...
... some aeons later
I tried to solve the version comparison with internal makefile functions. I found a project (GNU Make Standard Library (GMSL)), which adds an include to the makefile which implements integer arithmetic. Unfortunately with the common unary numeral system. But comparing version numbers are more complex. As I worked with versions having numbers greater then 100_000 I decided to implement a more general solution. It works with arbitrary number of subversion numbers each with arbitrary digits. Some ideas were borrowed from the GMSL project
It implements the ver.lt function. It returns 'T' if the first version number is less then the second. It returns an empty string otherwise. Of course, the subversion numbers are compared numerically not lexicographically. So 1.20 is greater then 1.3. There is some issues. 1.2 < 1.2.0, 1.0.1 < 1.00.1, 1.9.1 < 1.01.1 (as it is expected that a number starts with a nonzero digit. Except the 0 itself.). I do not want to solve them now.
The solution
It is testAed under gnu make 3.82.90. There are some very long lines as makefile add spaces if the '\' is used. I left some implemented, but not used functions in the code. Maybe I would use better temporary variable names (like GMSL uses _gmsl). Sometimes temporary variables could be left off, but the code would be more cryptic.
.SILENT:
S :=
SP := $S $S
# For non empty strings
#not = $(if $1,$S,T)
#str.ne = $(if $(subst $1,,$2),T,$S)
str.eq = $(if $(subst $1,,$2),$S,T)
str.le = $(call str.eq,$(word 1,$(sort $1 $2)),$1)
#str.ge = $(call str.eq,$(word 1,$(sort $1 $2)),$2)
# Creates a list of digits from a number
mklist = $(eval __tmp := $1)$(foreach i,0 1 2 3 4 5 6 7 8 9,$(eval __tmp := $$(subst $$i,$$i ,$(__tmp))))$(__tmp)
# reverse: $(subst $(SP),,$(list))
#pop = $(wordlist 2, $(words $1), x $1)
#push = $1 $2
shift = $(wordlist 2, $(words $1), $1)
#unshift = $2 $1
num.le = $(eval __tmp1 := $(call mklist,$1))$(eval __tmp2 := $(call mklist,$2))$(if $(call str.eq,$(words $(__tmp1)),$(words $(__tmp2))),$(call str.le,$1,$2),$(call str.le,$(words $(__tmp1)),$(words $(__tmp2))))
#num.ge = $(eval __tmp1 := $(call mklist,$1))$(eval __tmp2 := $(call mklist,$2))$(if $(call str.eq,$(words $(__tmp1)),$(words $(__tmp2))),$(call str.ge,$1,$2),$(call str.ge,$(words $(__tmp1)),$(words $(__tmp2))))
#Strip zeroes from the beginning of a list
list.strip = $(eval __flag := 1)$(foreach d,$1,$(if $(__flag),$(if $(subst 0,,$d),$(eval __flag :=)$d,$S),$d))
#Strip zeroes from the beginning of a number
#num.strip = $(subst $(SP),,$(call list.strip,$(call mklist,$1)))
# temp string: 0 - two number equals, L first LT, G first GT or second is short,
gen.cmpstr = $(eval __Tmp1 := $(subst ., ,$1))$(eval __Tmp2 := $(subst ., ,$2))$(foreach i,$(__Tmp1),$(eval j := $(word 1,$(__Tmp2)))$(if $j,$(if $(call str.eq,$i,$j),0,$(if $(call num.le,$i,$j),L,G)),G)$(eval __Tmp2 := $$(call shift,$(__Tmp2))))$(if $(__Tmp2), L)
ver.lt = $(call str.eq,$(word 1,$(call list.strip,$(call gen.cmpstr,$1,$2))),L)
all:
echo ver.lt,1.20,1.3:$(call ver.lt,1.20,1.3)%
echo ver.lt,1.5.9,1.5:$(call ver.lt,1.5.9,1.5)%
echo ver.lt,1.4.9,1.5:$(call ver.lt,1.4.9,1.5)%
echo ver.lt,1.2,1.2.0:$(call ver.lt,1.2,1.2.0)%
echo ver.lt,1.20.3.4.5,1.10.5:$(call ver.lt,1.20.3.4.5,1.10.5)%
echo ver.lt,1.20.3.4.5,1.0.5:$(call ver.lt,1.20.3.4.5,1.0.5)%
echo ver.lt,1.0,1.0.5:$(call ver.lt,1.0,1.0.5)%
echo ver.lt,1.20,1.10.3:$(call ver.lt,1.20,1.10.3)%
echo ver.lt,1.20,1.30.3::$(call ver.lt,1.20,1.30.3)%
echo ver.lt,1.10.3,1.10.3:$(call ver.lt,1.10.3,1.10.3)%
And the output
ver.lt,1.20,1.3:%
ver.lt,1.5.9,1.5:%
ver.lt,1.4.9,1.5:T%
ver.lt,1.2,1.2.0:T%
ver.lt,1.20.3.4.5,1.10.5:%
ver.lt,1.20.3.4.5,1.0.5:%
ver.lt,1.0,1.0.5:T%
ver.lt,1.20,1.10.3:%
ver.lt,1.20,1.30.3::T%
ver.lt,1.10.3,1.10.3:%
More music
I have found another interesting project called makepp (makepp.sourceforge.net). It enables to implement new functions in perl inside the makefile.

The simplest solution I think of is by using bc:
# Find out which version of Red-Hat we're running
RH_VER_NUM = $(shell /bin/grep -o [0-9].[0-9] /etc/redhat-release)
RH_GT_5_3 = $(shell echo $(RH_VER_NUM)\>=5.3 | bc )
ifeq ($(RH_GT_5_3),1)
CPPFLAGS += -DRH_GT_5_3=1
endif
This will be equal to 1 if bigger than 5.3, and 0 otherwise.
bc is an arbitrary precision calculator language. Here are some documentation about bc:
Official documentation
Man page
Wikipedia

Kudos to TrueY for coming up with a version that doesn't use any expensive shell commands. I have a simpler version that only compares 2 small integers (often you only care about the major version).
The idea is simply
x > y
= y is member of {0,1,2,3,4 ..., x - 1)
The member of operator can be implemented with GNU make's $(filter) and generating the set can be done with $(wordlist)
# returns all integers less than x
LessThanSubset=$(wordlist 1,$(1),0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20)
# if x > y, return 1, empty otherwise
GreaterThan=$(if $(filter $(2),$(call LessThanSubset,$(1))),1)
GreaterOrEqual=$(if $(filter $(2),$(call LessThanSubset,$(1)) $(1)),1)
# example
$(error 5 > 4 = $(call GreaterThan,5,4))

I realize this is an old question, but still. Actually you can resort to lsb_release, which I found to be installed on every single recent RedHat system.
To be precise you'd make use of
lsb_release -sr
inside $(shell ...) and then split at the . like this:
RH_VER:=$(shell lsb_release -sr)
RH_MAJVER:=$(word 1, $(subst ., ,$(RH_VER)))
RH_MINVER:=$(word 2, $(subst ., ,$(RH_VER)))
you now have the major/minor parts of the release version and can check that against whatever you like.
The classic case would be $(filter ...) and the other text functions that GNU make provides.
Nevertheless I agree that a kind of a config.h would make more sense, though Autotools is by no means perfect for all scenarios (there are other build tools that attempt the same, though).

What about using sort function:
ifeq "$(firstword $(sort $(RH_VER_NUM),5.3))" "5.3"
RH_GT_5_3 = 1
endif
(well it would actually test for greater or equal, but you get the idea)

A modern way would be to get the version into make (using one shell invocation), e.g.:
SHELL := /bin/bash
# Define redhat_release as major_version * 100 + minor_version
redhat_release := $(shell [[ "$$(cat /etc/redhat-release)" =~ ([0-9]+)\.([0-9]+) ]] && echo $$(( $${BASH_REMATCH[1]} * 100 + $${BASH_REMATCH[2]} )))
And then have version specific flags:
# Flags for different redhat versions
redhat_flags.604 := abc
redhat_flags.605 := ${redhat_flags.604} def
redhat_flags := ${redhat_flags.${redhat_release}}
And print them out:
$(info redhat_release=${redhat_release})
$(info redhat_flags=${redhat_flags})
Output:
$ make
redhat_release=605
redhat_flags=abc def

Building on TrueY's smart use of sort and making it a re-usable Make function:
version_greater_equal = $(shell if printf '%s\n%s\n' '$(2)' '$(1)' | \
sort -Ct. -k1,1n -k2,2n ; then echo YES; else echo NO; fi )
ifeq (YES,$(call version_greater_equal,${SOME_VERSION},8.2))
...

With the arithmetic capabilities of the GNUmake table toolkit you can write a quite general test - the one thing you need to adapt is the Lisp-like functional style ("function op1,op2,opN"):
include gmtt/gmtt.mk
# Simulate some nasty versioning style:
# X.Y.Z.rN for release versions,
# X.rN-beta.Y.Z for beta versions towards a release
VERSION_NR_REL := $(call glob-match,$(VERSION_NR),*.*.*.r*)
VERSION_NR_BETA := $(call glob-match,$(VERSION_NR),*.r*-beta.*.*)
# glob-match only produces a non-empty result if the match succeeds,
# thus one of the following will be an empty string
$(info Release version: $(VERSION_NR_REL))
$(info Beta version: $(VERSION_NR_BETA))
# we use makes "if" function which sees empty strings as false and everything else as true
BETA_OR_RELEASE := $(if $(VERSION_NR_REL),release,beta)
# react to version numbers on different positions in release and beta
ifeq ($(BETA_OR_RELEASE),beta)
MAJOR_V := $(word 1,$(VERSION_NR_BETA))
MINOR_V := $(word 5,$(VERSION_NR_BETA))
BUGFIX_V := $(word 7,$(VERSION_NR_BETA))
REL_NR := $(word 3,$(VERSION_NR_BETA))
else
MAJOR_V := $(word 1,$(VERSION_NR_REL))
MINOR_V := $(word 3,$(VERSION_NR_REL))
BUGFIX_V := $(word 5,$(VERSION_NR_REL))
REL_NR := $(word 7,$(VERSION_NR_REL))
endif
GT_5_3 := $(if $(or $(call int-gt,$(MAJOR_V),5),\
$(and $(call int-eq,$(MAJOR_V),5),\
$(call int-gt,$(MINOR_V),3))),\
yes,no)
$(info Major: $(MAJOR_V), Minor: $(MINOR_V), Bugfix: $(BUGFIX_V), Release: $(REL_NR), greater than 5.3: $(GT_5_3))
Test with a larger version number:
make VERSION_NR=18.r01-beta.3.5
Output:
Release version:
Beta version: 18 .r 01 -beta. 3 . 5
Major: 18, Minor: 3, Bugfix: 5, Release: 01, greater than 5.3: yes
Test with a version equal to 5.3:
make VERSION_NR=5.3.12.r3
Output:
Release version: 5 . 3 . 12 .r 3
Beta version:
Major: 5, Minor: 3, Bugfix: 12, Release: 3, greater than 5.3: no

I had to compare GLIBC versions, I used:
GLIBC_VERSION := $(shell ldd --version | head -1 | awk '{print $$NF}')
ifeq ($(shell awk -v a="$(GLIBC_VERSION)" -v b="2.35" 'BEGIN{print(a<b)}'), 1)
# do something if GLIBC less than 2.35
endif
this works because awk can handle floating-point comparison and glibc doesn't have a third version number field

Related

How to do if condition inside foreach in Makefile

In Makefile, the pupose is to find a match in a list
#to find the $VER_X which is latest to $MY_VERSION but not greater than $MY_VERSION
VER_1 := 50.0.56.8
VER_2 := 50.0.56.17
VER_3 := 50.0.90.0
#in this case, VER_2 should be the match one
MY_VERSION := 50.0.56.19
I write a function which works fine to compare version string such as a.b.c.d
# function - compare version such as a.b.c.d
version_greater_equal = $(shell if printf '%s\n%s\n' '$(2)' '$(1)' | sort -Ct. -k1,1n -k2,2n -k3,3n -k4,4n ; then echo YES; else echo NO; fi)
The pupose is to find a match which is latest to ${MY_VERSION}
Solution-A and it works.
#solution-A: enumerate via ifeq
#this works, but it is not good idea for a larget number of VER_X
ifeq (YES,$(call version_greater_equal,${MY_VERSION},${VER_1}))
MATCH_ENU=${VER_1}
endif
ifeq (YES,$(call version_greater_equal,${MY_VERSION},${VER_2}))
MATCH_ENU=${VER_2}
endif
ifeq (YES,$(call version_greater_equal,${MY_VERSION},${VER_3}))
MATCH_ENU=${VER_3}
endif
Soluton-B , which doesn't work. How can I fix it?
#solution-2: loop (foreach)
VER_LIST := 50.0.56.8 50.0.56.17 50.0.90.0 50.1.42.3
MATCH_2 := $(foreach each, $(VER_LIST), $(if $(call version_greater_equal,${MY_VERSION},${each})=YES , ${each}))
The log
-->enumerate
MATCH_ENU = 50.0.56.17
-->enumerate
MATCH_2 = 50.0.56.8 50.0.56.17 50.0.90.0 50.1.42.3
Define a new function, which returns the second argument if it's greater-or-equal, or nothing.
version_greater_equal = $(shell if printf '%s\n%s\n' '$(2)' '$(1)' | sort -Ct. -k1,1n -k2,2n -k3,3n -k4,4n ; then echo $(2); fi)
Make a (sorted) list of versions:
VERS := $(VER_1) $(VER_2) $(VER_3) ...
Sift out the one greater than $(MY_VERSION):
LOW_VERS := $(foreach X,$(VERS),$(call version_greater_equal,${MY_VERSION},$(X)))
Take the last (and therefore greatest) of these:
MATCH_ENU := $(lastword $(LOW_VERS))
EDIT: Your Question seemed to imply that the versions would be listed in order (and your Solution A depends on it). But if you want a solution that works without that assumption, here goes.
I will not give a recursive function that returns just the largest value, because recursive functions give me vertigo. And besides, the advice #MadScientist gives me usually contains some horrible trap. So here's a purely iterative solution:
greater = ... function that returns the greater of the two arguments, you write it.
define reduce
X := $(call greater, $(wordlist 1,2,$(X))) $(wordlist 3,$(words $(X)), $(X))
endef
X := $(LOW_VERS)
$(foreach z, $(LOW_VERS), $(eval $(call reduce)))
Now X will contain the version you want.
P.S. Come to think of it, if we're making calls to the shell sort, it's probably easier just to sort the version list that way.

Writing version info to a file using Makefile

I am writing FW version to a file and then reading it while build I my project via Makefile. The SET rule writes the FW version info to the file and GET reads it from it.
When I do
make SET FW_VERSION_MAJOR=1 FW_VERSION_MINOR=2 FW_VERSION_PATCH=3 FW_VERSION_REVISION=4
make GET returns 1.2.3.4
But when I just do
make SET FW_VERSION_MAJOR=1
make GET returns 1...
What I want to achieve is if some one wants to do make SET and want to change only 1 or 2 or 3 parameters out of 4 in the FW version info then my version.h file should be able to retrieve the info left and shouldn't updated with an empty field.
FILE_CONFIG := path/to/version.h
.PHONY: SET GET
SET: VER_MAJOR ?= $(FW_VERSION_MAJOR)
SET: VER_MINOR ?= $(FW_VERSION_MINOR)
SET: VER_PATCH ?= $(FW_VERSION_PATCH)
SET: VER_REVISION ?= $(FW_VERSION_REVISION)
SET:
#echo '#define FW_VERSION_MAJOR $(FW_VERSION_MAJOR)\r\n#define
FW_VERSION_MINOR $(FW_VERSION_MINOR)\r\n#define FW_VERSION_PATCH
$(FW_VERSION_PATCH)\r\n#define FW_VERSION_REVISION $(FW_VERSION_REVISION)'
> $(FILE_CONFIG)
GET:
getnum = $(shell sed -n 's/.*$1 *\([0-9*]\)/\1/p' $(FILE_CONFIG))
FW_VERSION := $(call getnum,MAJOR).$(call getnum,MINOR).$(call
getnum,PATCH).$(call getnum,REVISION)
Not sure I fully understand what you try to achieve but there are several things to fix in your Makefile and examples:
your SET-specific variable definitions (VER_MAJOR...) are useless
your GET rule is empty
your example uses letters-only version numbers while your getnum macro retrieves digits-only version numbers
you must define the FW_VERSION_XXX if you want to use them in a rule when they are not passed on the command line
Try this, maybe:
FILE_CONFIG := version.h
.PHONY: SET GET
SET:
#echo '#define FW_VERSION_MAJOR $(FW_VERSION_MAJOR)\r\n#define FW_VERSION_MINOR $(FW_VERSION_MINOR)\r\n#define FW_VERSION_PATCH $(FW_VERSION_PATCH)\r\n#define FW_VERSION_REVISION $(FW_VERSION_REVISION)' > $(FILE_CONFIG)
GET:
#printf '%s\r\n' '$(FW_VERSION)'
getnum = $(shell sed -n 's/.*$1 *\([0-9*]\)/\1/p' $(FILE_CONFIG))
FW_VERSION_MAJOR := $(call getnum,MAJOR)
FW_VERSION_MINOR := $(call getnum,MINOR)
FW_VERSION_PATCH := $(call getnum,PATCH)
FW_VERSION_REVISION := $(call getnum,REVISION)
FW_VERSION := $(FW_VERSION_MAJOR).$(FW_VERSION_MINOR).$(FW_VERSION_PATCH).$(FW_VERSION_REVISION)
Demo:
$ make SET FW_VERSION_MAJOR=1 FW_VERSION_MINOR=2 FW_VERSION_PATCH=3 FW_VERSION_REVISION=4
$ make GET
1.2.3.4
$ make SET FW_VERSION_REVISION=5
$ make GET
1.2.3.5
Note: we can factorize a bit the FW_VERSION_XXX definitions and also the SET recipe:
FILE_CONFIG := version.h
VERSIONS := MAJOR MINOR PATCH REVISION
.PHONY: SET GET
SET:
#printf '' > $(FILE_CONFIG)
#$(foreach v,$(VERSIONS),$(call setnum,$(v)))
GET:
#printf '%s\r\n' '$(FW_VERSION)'
setnum = printf '\#define FW_VERSION_$1 $(FW_VERSION_$1)\r\n' >> $(FILE_CONFIG);
getnum = $(shell sed -n 's/.*$1 *\([0-9*]\)/\1/p' $(FILE_CONFIG))
$(foreach v,$(VERSIONS),$(eval FW_VERSION_$(v) := $(call getnum,$(v))))
FW_VERSION := $(FW_VERSION_MAJOR).$(FW_VERSION_MINOR).$(FW_VERSION_PATCH).$(FW_VERSION_REVISION)

How Validate Id is correct before creating file using makefile

I need to Validate the ID with pattern (Abbbbb-yyy)
Example :
ID := A12345-789 B98765-123 C58730-417
VARIANT := test1 test2 test3
Build and post processing will generate files depends up on VARIANTS :
`sw_main_test1.hex ,sw_main_test1.hex and sw_main_test1.hex `
.PHONY : SW_TEST
SW_TEST :
if <ID is correct>
cp sw_main_test1.hex --> A12345-789.hex
cp sw_main_test2.hex --> B98765-123.hex
cp sw_main_test3.hex --> C58730-417.hex
I am facing issue in validating the ID with pattern
`Abbbbb-yyy.txt`
Where : A=[A-Z]; b=[0-9]; y=[0-9]
Please let me know how to verify ID is correct using regular expressions inside the Makefile using any tool or utility
In this script, I assume, you get your ID from a file (I called it here someidcontent.txt). Then you could write a script like this (assuming, you only working on Linux).
getID = $(shell cat someidcontent.txt)
all:
if [ "$(getID)" == "1234567890" ]; then \
cp -v output.txt ./delivery/$(getID).txt; \
fi
.PHONY: all
Edit
I made a mistake in my previous script. I did not check, if the ID is correct. Now my newer script does this: I read from a file the ID and check it for correctness. If ID is correct, then some file will be copied into target dir with ID number.
# get ID from a file
getID := $(shell cat someidcontent.txt)
# need a hack for successful checking
idToCheck := $(getID)
# check procedure
checkID := $(shell echo $(idToCheck) | grep "[A-Z][0-9][0-9][0-9][0-9]-[0-9][0-9][0-9]$$")
all:
ifeq "$(checkID)" "$(idToCheck)"
echo found
cp -v output.txt ./delivery/$(idToCheck).txt;
endif
.PHONY: all
Edit 2
Ok, this was a little bit challenging, but I solved it somehow. Maybe there are also other ways to solve this better. In my solution, I assume that the file with IDs and source filenames look like this (in other words, this is the content of my someidcontent.txt):
A2345-678:output1.txt
B3456-123:output.txt
C0987-987:thirdfile.txt
And this is my makefile with comments for additional explanation. I hope, they are sufficient
# retrieve id and filename data from other file
listContent := $(shell cat someidcontent.txt)
# extract only IDs from other files
checkIDs = $(shell echo $(listContent) | grep -o "[A-Z][0-9][0-9][0-9][0-9]-[0-9][0-9][0-9]")
all:
# iterate only over IDs
# first, give me the ID
# second retrieve the filename part for successful copy procedure
# and copy the file to the target dir with ID as filename
#$(foreach x,$(checkIDs), \
echo $(x); \
cp -v $(shell echo $(listContent) | grep -o "$(x):[A-Z0-9a-z\.]*" | sed "s/[-A-Z0-9]*://g") ./delivery/$(x).t$
)
.PHONY: all
You can check simple string patterns quite ok (don't want to say "nicely") from within make:
[A-F] := A B C D E F#
[a-f] := a b c d e f#
[A-Z] := $([A-F]) G H I J K L M N O P Q R S T U V W X Y Z#
[a-z] := $([a-f]) g h i j k l m n o p q r s t u v w x y z#
[0-9] := 0 1 2 3 4 5 6 7 8 9#
######################################################################
##### $(call explode,_stringlist_,_string_)
## Insert a blank after every occurrence of the strings from _stringlist_ in _string_.
## This function serves mainly to convert a string into a list.
## Example: `$(call explode,0 1 2 3 4 5 6 7 8 9,0xl337c0de)` --> `0 xl3 3 7 c0 de`
explode = $(if $1,$(subst $(firstword $1),$(firstword $1) ,$(call explode,$(wordlist 2,255,$1),$2)),$2)
ID := A12345-789 B98765-123 C58730-417 123456+328
############################################################
# $(call check-id,_id-string_)
# Return 'malformed' or the given id
check-id = $(if $(call check-id-1,$(call explode,- $([A-Z]) $([0-9]),$1)),malformed,$1)
check-id-1 = $(strip $(filter-out $([A-Z]),$(wordlist 1,1,$1)) $(filter-out $([0-9]),$(wordlist 2,6,$1)) $(filter-out -,$(word 7,$1)) $(filter-out $([0-9]),$(wordlist 8,10,$1)) )
$(info $(foreach w,$(ID),$(call check-id,$(w))))

Do calculation in the Makefile

I got confused with Makefile. I am trying to run a simple command in the Makefile but it gives me the error "/bin/bash: line 3: :=: command not found". I am using shell to run this makefile
This is my part of my Makefile:
all:
vlog Benchmarks/$(NAME)/Syn/*.v
$(eval tux_number := 1)
$(eval range := 1)
$(eval ssh_log := 255)
echo "Start Range: ${range}"
echo "tux-number: ${tux_number}"
while [[ $$range -le 50 ]] ; do \
ssh -l yazdanbakhsh tux-$(tux_number).cae.wisc.edu exit ; \
echo "range: ${range}" ; \
eval $$range := $$((${range}+1)) ; \
done
Thanks
all:
#range=1; \
while [ $$range -le 10 ] ; \
do echo Range: $$range; \
let range=range+1 ; \
done;
Note that the whitespace in front of #range... is the only TAB.
Just to fix your obvious problems with Makefile syntax, here is an attempt at refactoring your attempt into valid code.
tux_number := 1
ssh_log := 255 # not used anywhere
all:
vlog Benchmarks/$(NAME)/Syn/*.v
echo "Start Range: 1" # This is probably no longer very useful output
echo "tux-number: ${tux_number}"
range=1; while [ $$range -le 50 ] ; do \
ssh -l yazdanbakhsh tux-$(tux_number).cae.wisc.edu exit ; \
echo "range: $$range" ; \
range=$$(expr "$$range + 1); \
done
Notice how tux_number and ssh_log are Makefile variables, while range only exists in the shell which executes the while loop. I have avoided the Bashisms in order to make this portable. (If portability is not important, you might want to refactor it back to Bash syntax and use for ((range=1; range<=50; range++)); do... instead.)
Your use of eval is misguided. As you can see, I simply lifted out the Makefile variables outside the recipe where they don't belong. What you were doing was (1) have Make evaluate the expression range := 1 (which evaluates to itself) and (2) use the output as a shell command in a recipe. Since it's not a valid shell command, you got the syntax error from Bash. Without further ado, I'll just take the easy way out here and say that eval is a complex subject, and until you get more experience with Make, it's probably just best to forget that it exists.
In order to properly make use of Make's facilities, I would make this parallelizable, i.e. split it up into 50 individual targets. This is a bit clumsy (there's probably a better way to define range here), but at least it should illustrate a number of differences to your approach. (If you don't insist on having range count up from 1, making it zero-based would make this a little less clumsy. This exploits the fact that the empty string is harmless in a shell snippet, so we can use it instead of a zero prefix. Again, this could be simplifed if you don't care about the human readability of the range index.)
digits := 0 1 2 3 4 5 6 7 8 9
deca := "" 1 2 3 4
range := $(filter-out ""0,$(foreach d,$(deca),$(foreach i,$(digits),$d$i))) 50
# Or, at the expense of an external process,
# range := $(shell perl -le 'print $$_ for 1..50')
.PHONY: all
all: $(patsubst %,ssh-%,$(range))
.PHONY: ssh-%
ssh-%:
ssh -l yazdanbakhsh tux-$(tux_number).cae.wisc.edu exit
echo "range: $*"
This can be run with something like make -j 5 to execute these in parallel batches of five, for example.
Incidentally, the commented-out $(shell ...) call might be the actual answer to your question, if what you really wanted to do was to use Make to drive an external program to calculate something for you.

gnu make: list the values of all variables (or "macros") in a particular run

How can I list the current value of all variables (also called macros) in a Makefile when running make?
E.g. if this is in the Makefile:
CUR-DIR := $(shell /bin/pwd)
LOG-DIR := $(CUR-DIR)/make-logs
Then I would like it to tell me:
CUR-DIR = /home/johv/src/test
LOG-DIR = /home/johv/src/test/make-logs
GNU make provides .VARIABLES
which holds all global variables' names.
However, this includes built-in variables(like MAKEFLAGS).
If you have to exclude built-in variables, some filtering like the following
might be needed.
The following makefile prints user-defined variables(CUR-DIR, LOG-DIR)
using info:
# Place this line at the top of your Makefile
VARS_OLD := $(.VARIABLES)
# Define your variables
CUR-DIR := $(shell pwd)
LOG-DIR := $(CUR-DIR)/make-logs
# Put this at the point where you want to see the variable values
$(foreach v, \
$(filter-out $(VARS_OLD) VARS_OLD,$(.VARIABLES)), \
$(info $(v) = $($(v))))
Thanks to #Ise Wisteria, condensed down, this shows all variables, useful for large projects with multiple makefiles (Buildroot).
$(foreach v, $(.VARIABLES), $(info $(v) = $($(v))))
output: BR2_GCC_TARGET_TUNE = "cortex-a8" ...
If you get an error like: insufficient number of arguments (1) to function 'addprefix' this project had some broken variables... I trimmed the list of variables to show, only with a prefix BR2_
$(foreach v, $(filter BR2_%,$(.VARIABLES)), $(info $(v) = $($(v))))
I ended up doing it like this:
gmake -pn | grep -A1 "^# makefile"| grep -v "^#\|^--" | sort | uniq > makevars.txt
which gives:
CUR-DIR := /home/johv/src/test
LOG-DIR := /home/johv/src/test/make-logs
MAKEFILE_LIST := Makefile
MAKEFLAGS = pn
SHELL = /bin/sh
VARS_OLD := [...]
gmake -pn is really verbose and looks kinda like this:
# environment
GNOME2_PATH = /usr/local:/opt/gnome:/usr:/usr/local:/opt/gnome:/usr
# automatic
#F = $(notdir $#)
# makefile
SHELL = /bin/sh
# default
RM = rm -f
It's also doable without saving all the .VARIABLES and filtering them out.
Moreover, if one of the original .VARIABLES was modified in your makefile, the two most voted answers won't catch it.
Check out $(origin) function. This target filters out and prints all the variables that were defined in a makefile:
print_file_vars:
$(foreach v, $(.VARIABLES), $(if $(filter file,$(origin $(v))), $(info $(v)=$($(v)))))
I get only a few excess variables this way: CURDIR SHELL MAKEFILE_LIST .DEFAULT_GOAL MAKEFLAGS.
One can replace file with environment or command line to print the respective kinds of variables.
There are a lot of good answers here, but you're going to have problems using $($(v)) if some of your variables are of the recursive flavor. This is why you should use $(value $(v)).
This variation cleans this up a little bit, sorts variables by name and makes the output a bit more readable.
dump:
$(foreach v, \
$(shell echo "$(filter-out .VARIABLES,$(.VARIABLES))" | tr ' ' '\n' | sort), \
$(info $(shell printf "%-20s" "$(v)")= $(value $(v))) \
)
Thanks to #kevinf for the great idea. I would suggest a minor change to prevent .VARIABLE itself from printing out in the variable list:
$(foreach v, $(filter-out .VARIABLES,$(.VARIABLES)), $(info $(v) = $($(v))))
Thanks to #kevinf for the foreach solution -- if one wants to export this list as a somewhat machine-readable file, one will have a hard time with uneven quotes or newlines when using echo or printf, since Make isn't able to quote the data correctly -- one needs to use the $(file ...) function to write the data to avoid sh/bash complaining about invalid syntax. For example, use this in your rule -- it prints variable name, definition and expanded value:
$(file > $(MAKEFILE_ENV_FILE),)
$(foreach v, $(.VARIABLES), \
$(file >> $(MAKEFILE_ENV_FILE),$(v)) \
$(file >> $(MAKEFILE_ENV_FILE), := $(value $(v))) \
$(file >> $(MAKEFILE_ENV_FILE), == $($(v))) \
$(file >> $(MAKEFILE_ENV_FILE),) \
)
(This will still not allow to always distinguish malicious variables with double newlines from two variables, for this one now add a sufficiently unique separator infront of each Makefile-generated newline just after each comma inside $(file >> NAME,TEXT))
Set MAKEFILE_ENV_FILE to some filename, e.g.:
MAKEFILE_ENV_FILE := $(abspath $(lastword $(MAKEFILE_LIST))).env

Resources