I create a function rendering input text(argument)
#Color renderning
define GPrint
kColorRed := "\x1b[0;31m"
kColorGreen := "\x1b[0;32m"
kColorEnd := "\x1b[0m"
#echo -e "${kColorGreen}${1}${kColorEnd}"
endef
when I call this funtion
.PHONY : create_odir
create_odir:
$(call GPrint "create output dir")
mkdir ./output_dir
I got this error message
kColorRed := "\x1b[0;31m"
/bin/sh: line 1: kColorRed: command not found
make: *** [../src/sw/makefile:51: create_odir] Error 127
How do I solved it?
Please see below code which will help you fix this issue.
Method 1: Using global variables
# Regular Colors
kColorRed ='\e[0;31m' # Red
kColorGreen ='\e[0;32m' # Green
kColorEnd='\e[0m' # Text Reset
# Color renderning
define GPrint
echo -e ${kColorRed}$(1)${kColorEnd}
echo -e ${kColorGreen}$(1)${kColorEnd}
endef
create_odir:
$(call GPrint ,"create output dir")
Output :
Method 2:
If you want to use local variables inside the function.
# Color renderning
define GPrint
$(eval kColorRed := '\e[0;31m')
$(eval kColorGreen := '\e[0;32m')
$(eval kColorEnd := '\e[0m')
echo -e $(kColorRed)$(1)$(kColorEnd)
echo -e $(kColorGreen)$(1)$(kColorEnd)
endef
create_odir:
$(call GPrint ,"create output dir")
Related
To understand better string variables in a Makefile, I have tried to do this example :
KEYWORD=Nothing
test:
$(call myFunc)
define myFunc
ifeq ($(KEYWORD), "Apple")
echo "You have found the key"
else
echo "Try again"
endif
endef
But when I'm typing
make test KEYWORD="Fork"
It prints errors
syntax error near unexpected token `Fork,'
`ifeq (Fork, "Apple")'
I have also tried :
Put Apple or 'Apple' in ifeq
Put a space or not after "," : ifeq ($(KEYWORD), "Apple")
Run command with KEYWORD=Fork
Did it using shell (if [ ${KEYWORD} -eq "Apple" ])
I'm running out of ideas because I don't understand how Makefille / Shell interpret the assignment KEYWORD="Fork"
Thanks
MadScientist identifies the problem. Perhaps the solution you're looking for is simply evaluating the conditional earlier. eg:
KEYWORD ?= NOTHING
...
ifeq ($(KEYWORD), Apple)
define myFunc
echo "You have found the key"
endef
else
define myFunc
echo "Try again"
endef
endif
First, $(call myfunc) is 100% identical to writing $(myfunc). The call function in make merely expands a variable with some other local values (the arguments) bound first. If you don't provide any local values, then you're just expanding the variable.
Expanding a variable just replaces the variable reference with what it expands to. So writing:
FOO = bar
foo:
echo $(FOO)
is 100% identical to writing:
foo:
echo bar
So in your situation,
test:
$(call myFunc)
is the same as:
test:
$(myFunc)
which is the same as:
test:
ifeq ($(KEYWORD), "Apple")
echo "You have found the key"
else
echo "Try again"
endif
which is why you get the output you did: these are not valid shell commands, but since you've expanded the variable as part of a recipe, they are sent to the shell as part of the recipe.
I have a project which use makefile to control vagrant, I want to put the vagrant parameter into the makefile, such as cpu, memory, ip, hostname, forwarded_port and the like. I find a way that vagrantfile read yaml file to parameterize vagrantfile. So makefile needs a target to read all the user option variables and write them to config.yaml as key-value pairs.
The sample is as follows
# === BEGIN USER OPTIONS ===
BOX_OS ?= fedora
# Box setup
#BOX_IMAGE
# Disk setup
DISK_COUNT ?= 1
DISK_SIZE_GB ?= 25
# VM Resources
MASTER_CPUS ?= 2
MASTER_MEMORY_SIZE_GB ?= 2
NODE_CPUS ?= 2
NODE_MEMORY_SIZE_GB ?= 2
NODE_COUNT ?= 2
# Network
MASTER_IP ?= 192.168.26.10
NODE_IP_NW ?= 192.168.26.
POD_NW_CIDR ?= 10.244.0.0/16
...
...
# === END USER OPTIONS ===
The echo command does achieve it
# Makefile
envInit:
#echo "POD_NW_CIDR : \"$(POD_NW_CIDR)\"" > ${FILECWD}/configs.yaml
But too many variables can be too complex.
Is there a way to bulk read variables and their values and write them to a yml file
I would very appreciate it if you guys can tell me how to achieve it that bulk read variables and their values and write them to a yml file.
Define all user options (along with the default values) as a list, so that they are iterable:
# list of user options with default values
userOptions = \
BOX_OS=2 \
DISK_COUNT=1 \
MASTER_IP=192.168.26.10
# replace each default value with the env value, if any
userOptionValues = $(foreach i, $(userOptions), \
$(word 1, $(subst =, ,$i))=$(or \
$($(word 1, $(subst =, ,$i))), $($(word 1, $(subst =, ,$i))), $(word 2, $(subst =, ,$i))))
# write the yaml file
envInit:
# empty the file
#printf "" > configs.yaml
# write a line for each option
#for i in $(userOptionValues); do \
printf "%s : %s\n" "$$(printf $$i | cut -d= -f1)" "$$(printf $$i | cut -d= -f2)" >> configs.yaml; \
done
#flyx Thank you for you answer, your code does work great. But I seem to have found a more convenient way, and I've partially modified it.
printvars:
#echo$(foreach V,$(sort $(.VARIABLES)), \
$(if $(filter-out environment% default automatic,$(origin $V)),$(info $V: $($V))))
But there is still a gap between achieving the goal.
# the Makefile test file
FILECWD = $(shell pwd)
# === BEGIN USER OPTIONS ===
CLOUD_IP ?= 192.168.79.222
CLOUD_NAME ?= cloud
CLOUD_CPU ?= 6
CLOUD_MEMORY ?= 8
# === END USER OPTIONS ===
printvars:
#echo$(foreach V,$(sort $(.VARIABLES)), \
$(if $(filter-out environment% default automatic,$(origin $V)),$(info $V: $($V))))
make printvars's output contains a number of other variables
$ make printvars
.DEFAULT_GOAL: printvars
CLOUD_IP: 192.168.79.222
CLOUD_MEMORY: 8
CLOUD_NAME: cloud
CURDIR: /testmakecreateyml0930
FILECWD: /testmakecreateyml0930
GNUMAKEFLAGS:
MAKEFILE_LIST: Makefile
MAKEFLAGS:
SHELL: /bin/sh
And it can only be printed and not exported to the yaml file.This is only one step away from success.
I would appreciate it if you could help me modify it to achieve my goal
You can write directly to a file with GNUmakes $(file) function:
define newline :=
$(strip)
$(strip)
endef
space := $(strip) $(strip)#
-never-matching := ¥# character 165, this is used as a list element that should never appear as a real element
option-names = $(subst $(-never-matching),,$(filter $(-never-matching)%,$(subst $(-never-matching)$(space),$(-never-matching),$(-never-matching)$(strip $(subst $(newline), $(-never-matching),$1)))))
# define your user options in as many separate parts as you like, spaces and empty lines included:
define USER_OPTIONS +=
a = spaces are no problem
b = "neither nearly all 'other' characters: 8&)("
endef
define USER_OPTIONS +=
c = baz baf
d = foobar
endef
# make all definition make variables verbatim
$(eval $(USER_OPTIONS))
YAML_FORMAT := $(foreach name,$(call option-names,$(USER_OPTIONS)),$(newline)$(name) : $($(name)))
# write the file. Warning: this happens before any rule is run!
$(file >test.yaml,$(YAML_FORMAT))
$(info $(foreach name,$(call option-names,$(USER_OPTIONS)),<$(name) : $($(name))> ))
The trick lies in the clustering of all relevant user option variables in one multi-line make variable. The function option-names pulls all identifiers from that variable into a separate list.
I took the newline etc. character definitions from the GNUmake table toolkit which has many functions for "programmatic" make.
[HEADS UP] : There are some similar questions that are already present here on stackoverflow but they seem to not completely resolve my issue. Therefore, I am posting this question.
I am trying to write a makefile function that should set a value to a variable that is passed as argument to the function.
So, I am calling this function as -
RESULT :=
$(eval $(call myfunction,RESULT,value,res1,res2))
here 'res1' and 'res2' are two possible resulting values for RESULT and the argument 'value' will be used for some test condition.
Following is my attempt of the definition of myfunction. But it seems that it is not working.
define myfunction
TEST1 := $(shell test `mybinary` -ge 5 && printf "TEST")
TEST2 := $(findstring $(2),$(SOME_SHELL_ENV))
$(info "$(TEST1)")
$(info "$(TEST2)")
ifneq "$$(or $(TEST1),$(TEST2)" ""
LOCAL_RESULT := true
else
LOCAL_RESULT := false
endif
ifeq($(LOCAL_RESULT),true)
$(1) = $(3)
else
$(1) = $(4)
endif
endef
To me it appears that the local variables TEST1 and TEST2 are not even getting set.
Can somebody tell me why my function is not working correctly and what changes do I have to make to resolve the issues?
Quoting eval manual page:
The eval function is very special: [...] The argument to the eval function is expanded, then the results of that expansion are parsed as makefile syntax.
It’s important to realize that the eval argument is expanded twice; first by the eval function, then the results of that expansion are expanded again when they are parsed as makefile syntax. This means you may need to provide extra levels of escaping for “$” characters when using eval.
This happens after $(call) arguments are substituted, so $1 etc are already expanded by the time $(eval) is called, and need not to be $-escaped.
To make variables expand during the last (parsing) stage of $(eval), escape $s of non-numeric variables by doubling them.
define myfunction
TEST1 := $$(shell test `echo 6` -ge 5 && printf "TEST")
TEST2 := $$(findstring $(2),$$(PATH))
$$(info "$$(TEST1)")
$$(info "$$(TEST2)")
ifneq "$$(or $$(TEST1),$$(TEST2))" ""
LOCAL_RESULT := true
else
LOCAL_RESULT := false
endif
ifeq ($$(LOCAL_RESULT), true)
$(1) = $(3)
else
$(1) = $(4)
endif
endef
$(eval $(call myfunction,RESULT,value,res1,res2))
test:
echo "$(LOCAL_RESULT)"
Plus, you had a missing closing brace in $(or).
It's not clear if res1 and res2 are variable names or values; depending on this, the last two assignments need or need not to look like $(1) := $($(4)).
Try to always use eager assignments: :=, for fewer surprises from lazy variable expansion.
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 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