Related
The documentation (https://www.tcl.tk/man/tcl8.7/TclCmd/Tcl.htm) says:
For instance, “cmd a {*}{b [c]} d {*}{$e f {g h}}” is equivalent to “cmd a b {[c]} d {$e} f {g h}”.
But why is $e in braces after substitution? Where are those coming from (and why aren't the other arguments in the word (f and {g h})?
$e is in braces to stop it from being evaluated. f and {g h} aren't variables or in square brackets like c is, so don't need them.
These examples will also all expand to the same thing:
cmd a {*}[list b {[c]}] d {*}[list {$e} f {g h}]
cmd a {*}[list b {[c]}] d {*}[list \$e f {g h}]
cmd a {*}{b [c]} d {*}"\$e f {g h}"
This is a silly question, but.... with GNU Make:
VAR = MixedCaseText
LOWER_VAR = $(VAR,lc)
default:
#echo $(VAR)
#echo $(LOWER_VAR)
In the above example, what's the correct syntax for converting VAR's contents to lower case? The syntax shown (and everything else I've run across) result in LOWER_VAR being an empty string.
you can always spawn off tr
LOWER_VAR = `echo $(VAR) | tr A-Z a-z`
or
LOWER_VAR = $(shell echo $(VAR) | tr A-Z a-z)
The 'lc' functions you trying to call is from GNU Make Standard Library
Assuming that is installed , the proper syntax would be
LOWER_VAR = $(call lc,$(VAR))
You can do this directly in gmake, without using the GNU Make Standard Library:
lc = $(subst A,a,$(subst B,b,$(subst C,c,$(subst D,d,$(subst E,e,$(subst F,f,$(subst G,g,$(subst H,h,$(subst I,i,$(subst J,j,$(subst K,k,$(subst L,l,$(subst M,m,$(subst N,n,$(subst O,o,$(subst P,p,$(subst Q,q,$(subst R,r,$(subst S,s,$(subst T,t,$(subst U,u,$(subst V,v,$(subst W,w,$(subst X,x,$(subst Y,y,$(subst Z,z,$1))))))))))))))))))))))))))
VAR = MixedCaseText
LOWER_VAR = $(call lc,$(VAR))
all:
#echo $(VAR)
#echo $(LOWER_VAR)
It looks a little clunky, but it gets the job done.
If you do go with the $(shell) variety, please do use := instead of just =, as in LOWER_VAR := $(shell echo $VAR | tr A-Z a-z). That way, you only invoke the shell one time, when the variable is declared, instead of every time the variable is referenced!
To handle capital letters with accents:
LOWER_VAR = $(shell echo $VAR | tr '[:upper:]' '[:lower:]')
Results:
$ VAR="Éclipse"
$ echo $VAR | tr A-Z a-z
Éclipse
$ echo $VAR | tr '[:upper:]' '[:lower:]'
éclipse
I find this slightly cleaner...
$(shell tr '[:upper:]' '[:lower:]' <<< $(VAR))
If Python is installed this runs even on Windows:
$(shell python -c "print('$(VAR)'.lower())")
GNU make doesn't include string functions for case conversion. Thus, there is no lc function defined, by default.
But GNU Make usually comes with GNU Guile support enabled (e.g. this is the case on Fedora 33).
Thus, you can just call a Guile function for converting the case:
VAR = MixedCaseText
LOWER_VAR = $(guile (string-downcase "$(VAR)"))
default:
#echo $(VAR)
#echo $(LOWER_VAR)
Or if you want to encapsulate the Guile call:
VAR = MixedCaseText
LOWER_VAR = $(call to_lower,$(VAR))
define to_lower
$(guile (string-downcase "$(1)"))
endef
default:
#echo $(VAR)
#echo $(LOWER_VAR)
I wrote this while looking for a solution.
It is a bit verbose but believe it explains the steps and keeps really long lines out on the Makefile.
You can easily be modify it to perform any substitution you may want.
Hope it helps someone.
# set the separator for the *_TABLE variables, needed as otherwise `$(addprefix ...)` fails
luc_JOIN ::= ,
# define the upper and lower cased characters
lc_CHARS ::= a b c d e f g h i j k l m n o p q r s t u v w x y z
uc_CHARS ::= A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
# join the above to create the *_TABLE variables (i.e `a,A b,B ...`, `A,a B,b ...`)
lc_TABLE ::= $(join $(uc_CHARS),$(addprefix $(luc_JOIN),$(lc_CHARS)))
uc_TABLE ::= $(join $(lc_CHARS),$(addprefix $(luc_JOIN),$(uc_CHARS)))
# an internal macro to recursively create `$(subst ...)` from provided *_TABLE and string, (e.g. `$(subst a,A,$(subst b,B,...))`)
luc_internal = $(if $1,$$(subst $(firstword $1),$(call luc_internal,$(wordlist 2,$(words $1),$1),$2)),$2)
# the actual macros to $(call ...), which calls the luc_internal with the correct *_TABLE
lc = $(eval lc_RESULT ::= $(call luc_internal,$(lc_TABLE),$1))$(lc_RESULT)
uc = $(eval uc_RESULT ::= $(call luc_internal,$(uc_TABLE),$1))$(uc_RESULT)
# a mixed case value
VAR = SOME text
default:
#echo $(call lc,$(VAR))
#echo $(call uc,$(VAR))
Being impressed by the Eric Melski answer, I was curious how make handles recursion (I'm looking at you C preprocessor). Somewhat more involved, than original answer, but it's fascinating what a 50 years old tool can do. Not saying you should use this code, but I guess you could.
pop2 = $(wordlist 3,$(words $(1)),$(1))
sub1 = $(subst $(word 1,$(1)),$(word 2,$(1)),$(2))
map = $(if $(1),$(call sub1,$(1),$(call map,$(call pop2,$(1)),$(2))),$(2))
upperMap := a A b B c C d D e E f F g G h H i I j J k K l L m M n N o O p P q Q r R s S t T u U v V w W x X y Y z Z
upper = $(call map,$(upperMap),$(1))
lowerMap := A a B b C c D d E e F f G g H h I i J j K k L l M m N n O o P p Q q R r S s T t U u V v W w X x Y y Z z
lower = $(call map,$(lowerMap),$(1))
#Usage:
x := $(call upper,AaBbCcDdEe)
In my makefile, I need to make a variable assignment based on a command line variable value. for example, I do:
make var_1=xxx
where var_1 can have one of say 100 possible values. Based on the value of var_1, I need to assign a value to var_2 in my makefile. I could do:
ifeq ($(var_1), a)
var_2 = A
endif
ifeq ($(var_1), b)
var_2 = B
endif
and so on for all 100 possible combinations of var_1, var_2. Here a,A,b,B represent some strings. How do I do this to avoid 100's of if statements? I was thinking to define two variables:
var_1_values = a b c d
var_2_values = A B C D
I can use $(findstring $(var_1),$(var_1_values)) to see if $(var_1) is among $(var_1_values), but how do I locate the position of $(var_1) among all $(var_1_values)? That position is then to be used to pick the corresponding word inside $(var_2_values).
It's a little kludgey, but if there's a symbol you know won't be in any of the values (such as "_") you could do this:
var_1_values = a b c d
var_2_values = A B C D
# This will be a_A b_B c_C d_D
LIST1 = $(join $(addsuffix _,$(var_1_values)),$(var_2_values))
var_1 := a
# The filter gives a_A, the subst turns it into A
var_2 = $(subst $(var_1)_,,$(filter $(var_1)_%, $(LIST1)))
This can be done quite slickly using recursion inside GNU make functions as follows:
_pos = $(if $(findstring $1,$2),$(call _pos,$1,\
$(wordlist 2,$(words $2),$2),x $3),$3)
pos = $(words $(call _pos,$1,$2))
To use it you would $(call) the pos function with two arguments: the item to find and the list to find it in. For example,
$(call pos,a,a b c d e f g)
$(call pos,e,a b c d e f g)
$(call pos,g,a b c d e f g)
$(call pos,h,a b c d e f g)
$(call pos,e,))
It works by recursing through the $2 argument until it can no longer find the value in $1. Each time it recurses it lops the head off $2 using $(wordlist 2,$(words $2),$2). Each time is recurses it adds an x to the returned string so that there is one x for each position through $2 up to where $1 was found.
It then just uses $(words) to count the length of the return from _pos (the number of xs).
If you use the GMSL project this can be written more slickly write this as:
_pos = $(if $(findstring $1,$2),$(call $0,$1,$(call rest,$2),x $3),$3)
pos = $(words $(call _$0,$1,$2))
Note that I used $0 here which will contain the name of the current function (that's a standard GNU make feature) and the GMSL function rest to lop the head off the list.
There's a smooth way to do this using recursion as follows. First define a function called pos that finds the position of an element in a list and then use $(word) to extract the corresponding element in another list.
Here's pos:
_pos = $(if $(findstring $1,$2),$(call _pos,$1,\
$(wordlist 2,$(words $2),$2),x $3),$3)
pos = $(words $(call _pos,$1,$2))
Read this answer to understand how it works: Makefile: find function which returns position
Now it's easy to define a function that finds an element in a list and the finds the corresponding element in another list.
lookup = $(word $(call pos,$1,$2),$3)
And then try if out like this:
ALPHA := a b c d e f g h i j k l m n o p q r s t u v w x y z
NATO := alpha beta charlie delta echo foxtrot gamma hotel india\
juliet kilo lima mike november oscar papa quebec romeo\
sierra tango uniform victor whisky yankee zulu
to-nato = $(call lookup,$1,$(ALPHA),$(NATO))
To make a to-nato function that converts from a letter of the alphabet to the NATO alphabet.
One way simulate associative containers in make is to use computed variables. E.g.:
var_2.a := A
var_2.b := B
# ...
# lookup
var_2 = ${var_2.${var_1}}
# or, lookup and assign a default value if lookup fails
var_2_or_default = $(or ${var_2.${var_1}},<default-value>)
FAB = macrofab
# NOTE: base starts at 1 instead of 0
none := 1
seeed := 1
oshpark := 2
macrofab := 2
pcbng := 3
#NOTE: String must end with a space for word function to work
SUFFIX_DRILLS := "txt xln drl "
.PHONY: gerber
gerber:
#echo $(FAB) drill suffix is $(word $($(FAB)), $(SUFFIX_DRILLS))
Output of make is:
macrofab drill suffix is xln
This works in mingw gnu make 4.3 :
define get_index
$(shell \
$(eval _c = 1)\
$(eval _b = n n) \
$(foreach x,$(2), \
$(eval _b += n \
$(eval _c = $(words $(_b)) \
$(if $(filter $(1),$(x)), \
$(eval _r = $(_c)))))) \
echo $(_r) \
$(eval _c =) \
$(eval _b =) \
$(eval _r =) \
)
endef
Takes two arguments; the word to search for and the list where to search.
The index is 'returned' by echoing in $(shell ...)
Example:
list = cow horse chicken
index = $(call get_index,horse,$(list))
#echo $(index)
# output: 2
In my makefile, I need to make a variable assignment based on a command line variable value. for example, I do:
make var_1=xxx
where var_1 can have one of say 100 possible values. Based on the value of var_1, I need to assign a value to var_2 in my makefile. I could do:
ifeq ($(var_1), a)
var_2 = A
endif
ifeq ($(var_1), b)
var_2 = B
endif
and so on for all 100 possible combinations of var_1, var_2. Here a,A,b,B represent some strings. How do I do this to avoid 100's of if statements? I was thinking to define two variables:
var_1_values = a b c d
var_2_values = A B C D
I can use $(findstring $(var_1),$(var_1_values)) to see if $(var_1) is among $(var_1_values), but how do I locate the position of $(var_1) among all $(var_1_values)? That position is then to be used to pick the corresponding word inside $(var_2_values).
It's a little kludgey, but if there's a symbol you know won't be in any of the values (such as "_") you could do this:
var_1_values = a b c d
var_2_values = A B C D
# This will be a_A b_B c_C d_D
LIST1 = $(join $(addsuffix _,$(var_1_values)),$(var_2_values))
var_1 := a
# The filter gives a_A, the subst turns it into A
var_2 = $(subst $(var_1)_,,$(filter $(var_1)_%, $(LIST1)))
This can be done quite slickly using recursion inside GNU make functions as follows:
_pos = $(if $(findstring $1,$2),$(call _pos,$1,\
$(wordlist 2,$(words $2),$2),x $3),$3)
pos = $(words $(call _pos,$1,$2))
To use it you would $(call) the pos function with two arguments: the item to find and the list to find it in. For example,
$(call pos,a,a b c d e f g)
$(call pos,e,a b c d e f g)
$(call pos,g,a b c d e f g)
$(call pos,h,a b c d e f g)
$(call pos,e,))
It works by recursing through the $2 argument until it can no longer find the value in $1. Each time it recurses it lops the head off $2 using $(wordlist 2,$(words $2),$2). Each time is recurses it adds an x to the returned string so that there is one x for each position through $2 up to where $1 was found.
It then just uses $(words) to count the length of the return from _pos (the number of xs).
If you use the GMSL project this can be written more slickly write this as:
_pos = $(if $(findstring $1,$2),$(call $0,$1,$(call rest,$2),x $3),$3)
pos = $(words $(call _$0,$1,$2))
Note that I used $0 here which will contain the name of the current function (that's a standard GNU make feature) and the GMSL function rest to lop the head off the list.
There's a smooth way to do this using recursion as follows. First define a function called pos that finds the position of an element in a list and then use $(word) to extract the corresponding element in another list.
Here's pos:
_pos = $(if $(findstring $1,$2),$(call _pos,$1,\
$(wordlist 2,$(words $2),$2),x $3),$3)
pos = $(words $(call _pos,$1,$2))
Read this answer to understand how it works: Makefile: find function which returns position
Now it's easy to define a function that finds an element in a list and the finds the corresponding element in another list.
lookup = $(word $(call pos,$1,$2),$3)
And then try if out like this:
ALPHA := a b c d e f g h i j k l m n o p q r s t u v w x y z
NATO := alpha beta charlie delta echo foxtrot gamma hotel india\
juliet kilo lima mike november oscar papa quebec romeo\
sierra tango uniform victor whisky yankee zulu
to-nato = $(call lookup,$1,$(ALPHA),$(NATO))
To make a to-nato function that converts from a letter of the alphabet to the NATO alphabet.
One way simulate associative containers in make is to use computed variables. E.g.:
var_2.a := A
var_2.b := B
# ...
# lookup
var_2 = ${var_2.${var_1}}
# or, lookup and assign a default value if lookup fails
var_2_or_default = $(or ${var_2.${var_1}},<default-value>)
FAB = macrofab
# NOTE: base starts at 1 instead of 0
none := 1
seeed := 1
oshpark := 2
macrofab := 2
pcbng := 3
#NOTE: String must end with a space for word function to work
SUFFIX_DRILLS := "txt xln drl "
.PHONY: gerber
gerber:
#echo $(FAB) drill suffix is $(word $($(FAB)), $(SUFFIX_DRILLS))
Output of make is:
macrofab drill suffix is xln
This works in mingw gnu make 4.3 :
define get_index
$(shell \
$(eval _c = 1)\
$(eval _b = n n) \
$(foreach x,$(2), \
$(eval _b += n \
$(eval _c = $(words $(_b)) \
$(if $(filter $(1),$(x)), \
$(eval _r = $(_c)))))) \
echo $(_r) \
$(eval _c =) \
$(eval _b =) \
$(eval _r =) \
)
endef
Takes two arguments; the word to search for and the list where to search.
The index is 'returned' by echoing in $(shell ...)
Example:
list = cow horse chicken
index = $(call get_index,horse,$(list))
#echo $(index)
# output: 2
This is a silly question, but.... with GNU Make:
VAR = MixedCaseText
LOWER_VAR = $(VAR,lc)
default:
#echo $(VAR)
#echo $(LOWER_VAR)
In the above example, what's the correct syntax for converting VAR's contents to lower case? The syntax shown (and everything else I've run across) result in LOWER_VAR being an empty string.
you can always spawn off tr
LOWER_VAR = `echo $(VAR) | tr A-Z a-z`
or
LOWER_VAR = $(shell echo $(VAR) | tr A-Z a-z)
The 'lc' functions you trying to call is from GNU Make Standard Library
Assuming that is installed , the proper syntax would be
LOWER_VAR = $(call lc,$(VAR))
You can do this directly in gmake, without using the GNU Make Standard Library:
lc = $(subst A,a,$(subst B,b,$(subst C,c,$(subst D,d,$(subst E,e,$(subst F,f,$(subst G,g,$(subst H,h,$(subst I,i,$(subst J,j,$(subst K,k,$(subst L,l,$(subst M,m,$(subst N,n,$(subst O,o,$(subst P,p,$(subst Q,q,$(subst R,r,$(subst S,s,$(subst T,t,$(subst U,u,$(subst V,v,$(subst W,w,$(subst X,x,$(subst Y,y,$(subst Z,z,$1))))))))))))))))))))))))))
VAR = MixedCaseText
LOWER_VAR = $(call lc,$(VAR))
all:
#echo $(VAR)
#echo $(LOWER_VAR)
It looks a little clunky, but it gets the job done.
If you do go with the $(shell) variety, please do use := instead of just =, as in LOWER_VAR := $(shell echo $VAR | tr A-Z a-z). That way, you only invoke the shell one time, when the variable is declared, instead of every time the variable is referenced!
To handle capital letters with accents:
LOWER_VAR = $(shell echo $VAR | tr '[:upper:]' '[:lower:]')
Results:
$ VAR="Éclipse"
$ echo $VAR | tr A-Z a-z
Éclipse
$ echo $VAR | tr '[:upper:]' '[:lower:]'
éclipse
I find this slightly cleaner...
$(shell tr '[:upper:]' '[:lower:]' <<< $(VAR))
If Python is installed this runs even on Windows:
$(shell python -c "print('$(VAR)'.lower())")
GNU make doesn't include string functions for case conversion. Thus, there is no lc function defined, by default.
But GNU Make usually comes with GNU Guile support enabled (e.g. this is the case on Fedora 33).
Thus, you can just call a Guile function for converting the case:
VAR = MixedCaseText
LOWER_VAR = $(guile (string-downcase "$(VAR)"))
default:
#echo $(VAR)
#echo $(LOWER_VAR)
Or if you want to encapsulate the Guile call:
VAR = MixedCaseText
LOWER_VAR = $(call to_lower,$(VAR))
define to_lower
$(guile (string-downcase "$(1)"))
endef
default:
#echo $(VAR)
#echo $(LOWER_VAR)
I wrote this while looking for a solution.
It is a bit verbose but believe it explains the steps and keeps really long lines out on the Makefile.
You can easily be modify it to perform any substitution you may want.
Hope it helps someone.
# set the separator for the *_TABLE variables, needed as otherwise `$(addprefix ...)` fails
luc_JOIN ::= ,
# define the upper and lower cased characters
lc_CHARS ::= a b c d e f g h i j k l m n o p q r s t u v w x y z
uc_CHARS ::= A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
# join the above to create the *_TABLE variables (i.e `a,A b,B ...`, `A,a B,b ...`)
lc_TABLE ::= $(join $(uc_CHARS),$(addprefix $(luc_JOIN),$(lc_CHARS)))
uc_TABLE ::= $(join $(lc_CHARS),$(addprefix $(luc_JOIN),$(uc_CHARS)))
# an internal macro to recursively create `$(subst ...)` from provided *_TABLE and string, (e.g. `$(subst a,A,$(subst b,B,...))`)
luc_internal = $(if $1,$$(subst $(firstword $1),$(call luc_internal,$(wordlist 2,$(words $1),$1),$2)),$2)
# the actual macros to $(call ...), which calls the luc_internal with the correct *_TABLE
lc = $(eval lc_RESULT ::= $(call luc_internal,$(lc_TABLE),$1))$(lc_RESULT)
uc = $(eval uc_RESULT ::= $(call luc_internal,$(uc_TABLE),$1))$(uc_RESULT)
# a mixed case value
VAR = SOME text
default:
#echo $(call lc,$(VAR))
#echo $(call uc,$(VAR))
Being impressed by the Eric Melski answer, I was curious how make handles recursion (I'm looking at you C preprocessor). Somewhat more involved, than original answer, but it's fascinating what a 50 years old tool can do. Not saying you should use this code, but I guess you could.
pop2 = $(wordlist 3,$(words $(1)),$(1))
sub1 = $(subst $(word 1,$(1)),$(word 2,$(1)),$(2))
map = $(if $(1),$(call sub1,$(1),$(call map,$(call pop2,$(1)),$(2))),$(2))
upperMap := a A b B c C d D e E f F g G h H i I j J k K l L m M n N o O p P q Q r R s S t T u U v V w W x X y Y z Z
upper = $(call map,$(upperMap),$(1))
lowerMap := A a B b C c D d E e F f G g H h I i J j K k L l M m N n O o P p Q q R r S s T t U u V v W w X x Y y Z z
lower = $(call map,$(lowerMap),$(1))
#Usage:
x := $(call upper,AaBbCcDdEe)