From fcf8c4782ddf8bd322a445ac7dde3278899b52f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Harter?= Date: Fri, 17 May 2019 11:41:58 +0200 Subject: [PATCH 1/3] makefiles/utils: function to export variables for a target This allows exporting variables only for some target. It will allow not exporting variables when not needed, and so prevent unnecessary evaluation. --- makefiles/utils/test-variables.mk | 19 +++++++++++++++++++ makefiles/utils/variables.mk | 19 +++++++++++++++++++ tests/build_system_utils/Makefile | 6 +++++- 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 makefiles/utils/test-variables.mk create mode 100644 makefiles/utils/variables.mk diff --git a/makefiles/utils/test-variables.mk b/makefiles/utils/test-variables.mk new file mode 100644 index 0000000000..d0040ff24d --- /dev/null +++ b/makefiles/utils/test-variables.mk @@ -0,0 +1,19 @@ +include variables.mk + +# Timestamp in nanoseconds (to be an integer) +# OSx 'date' does not support 'date +%s%N' so rely on python instead +# It could be OSx specific but we do not have 'OS' defined here to differentiate +date_nanoseconds = $(shell python -c 'import time; print(int(time.time() * 1000000000))') + +EXPORTED_VARIABLES = MY_VARIABLE CURRENT_TIME +MY_VARIABLE = my_variable +# Defered evaluation to the test +CURRENT_TIME = $(call date_nanoseconds) + +$(call target-export-variables,test-exported-variables,$(EXPORTED_VARIABLES)) +test-exported-variables: + $(Q)bash -c 'test "$(MY_VARIABLE)" = "$${MY_VARIABLE}" || { echo ERROR: "$(MY_VARIABLE)" != "$${MY_VARIABLE}"; exit 1; }' + $(Q)bash -c 'test $(PARSE_TIME) -lt $${CURRENT_TIME} || { echo ERROR: $(PARSE_TIME) \>= $${CURRENT_TIME} >&2; exit 1; }' + +# Immediate evaluation for comparing +PARSE_TIME := $(call date_nanoseconds) diff --git a/makefiles/utils/variables.mk b/makefiles/utils/variables.mk new file mode 100644 index 0000000000..6d02061ae8 --- /dev/null +++ b/makefiles/utils/variables.mk @@ -0,0 +1,19 @@ +# Utilities to set variables and environment for targets +# These functions should help replacing immediate evaluation and global 'export' + + +# Target specific export the variables for that target +# +# target-export-variables +# +# Parameters +# target: name of target +# variables: the variables to export +# +# The variable will only be evaluated when executing the target as when +# doing export +target-export-variables = $(foreach var,$(2),$(call _target-export-variable,$1,$(var))) + +# '$1: export $2' cannot be used alone +# By using '?=' the variable is evaluated at runtime only +_target-export-variable = $(eval $1: export $2?=) diff --git a/tests/build_system_utils/Makefile b/tests/build_system_utils/Makefile index 2be3981c8e..22e4af7d2a 100644 --- a/tests/build_system_utils/Makefile +++ b/tests/build_system_utils/Makefile @@ -16,7 +16,8 @@ endef MAKEFILES_UTILS = $(RIOTMAKE)/utils -COMPILE_TESTS = test-ensure_value test-ensure_value-negative +COMPILE_TESTS += test-ensure_value test-ensure_value-negative +COMPILE_TESTS += test-exported-variables # Tests will be run both in the host machine and in `docker` all: build-system-utils-tests @@ -31,3 +32,6 @@ test-ensure_value: test-ensure_value-negative: $(Q)$(call command_should_fail,"$(MAKE)" -C $(MAKEFILES_UTILS) -f test-checks.mk test-ensure_value-negative) + +test-exported-variables: + $(Q)$(call command_should_succeed,"$(MAKE)" -C $(MAKEFILES_UTILS) -f test-variables.mk test-exported-variables) From 2c5eeca47ab9b4bb05971b304a9f5d0404a61a2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Harter?= Date: Fri, 7 Jun 2019 18:10:24 +0200 Subject: [PATCH 2/3] makefiles/utils: function to memoize a variable evaluation This allow deferring a variable evaluation to its usage but still benefit from only evaluating it once on multiple uses. --- makefiles/utils/test-variables.mk | 19 +++++++++++++++++++ makefiles/utils/variables.mk | 14 ++++++++++++++ tests/build_system_utils/Makefile | 4 ++++ 3 files changed, 37 insertions(+) diff --git a/makefiles/utils/test-variables.mk b/makefiles/utils/test-variables.mk index d0040ff24d..36cee1997d 100644 --- a/makefiles/utils/test-variables.mk +++ b/makefiles/utils/test-variables.mk @@ -15,5 +15,24 @@ test-exported-variables: $(Q)bash -c 'test "$(MY_VARIABLE)" = "$${MY_VARIABLE}" || { echo ERROR: "$(MY_VARIABLE)" != "$${MY_VARIABLE}"; exit 1; }' $(Q)bash -c 'test $(PARSE_TIME) -lt $${CURRENT_TIME} || { echo ERROR: $(PARSE_TIME) \>= $${CURRENT_TIME} >&2; exit 1; }' + +MEMOIZED_CURRENT_TIME = $(call memoized,MEMOIZED_CURRENT_TIME,$(call date_nanoseconds)) +MEMOIZED_CURRENT_TIME_2 = $(call memoized,MEMOIZED_CURRENT_TIME_2,$(call date_nanoseconds)) + +PRE_MEMOIZED_TIME := $(call date_nanoseconds) +# Two separate evaluations +REF_CURRENT_TIME_1 := $(MEMOIZED_CURRENT_TIME) +# Strip to detect added whitespaces by function +REF_CURRENT_TIME_2 := $(strip $(MEMOIZED_CURRENT_TIME)) + +test-memoized-variables: + @# The value was only evaluated on first use + $(Q)test $(PRE_MEMOIZED_TIME) -lt $(REF_CURRENT_TIME_1) || { echo ERROR: $(PRE_MEMOIZED_TIME) \>= $(REF_CURRENT_TIME_1) >&2; exit 1; } + @# Both evaluation return the same time and without added whitespace + $(Q)test "$(REF_CURRENT_TIME_1)" = "$(REF_CURRENT_TIME_2)" || { echo ERROR: "$(REF_CURRENT_TIME_1)" != "$(REF_CURRENT_TIME_2)" >&2; exit 1; } + @# The second memoized value was only evaluated when calling the target + $(Q)test $(PARSE_TIME) -lt $(MEMOIZED_CURRENT_TIME_2) || { echo ERROR: $(PARSE_TIME) \>= $(MEMOIZED_CURRENT_TIME_2) >&2; exit 1; } + + # Immediate evaluation for comparing PARSE_TIME := $(call date_nanoseconds) diff --git a/makefiles/utils/variables.mk b/makefiles/utils/variables.mk index 6d02061ae8..ead0099063 100644 --- a/makefiles/utils/variables.mk +++ b/makefiles/utils/variables.mk @@ -2,6 +2,20 @@ # These functions should help replacing immediate evaluation and global 'export' +# Evaluate a deferred variable only once on its first usage +# Uses after that will be as if it was an immediate evaluation +# This can replace using `:=` by default +# +# The goal is to use it for `shell` commands +# +# variable = $(call memoized,,) +# +# Parameters +# variable: name of the variable you set +# value: value that should be set when evaluated +memoized = $2$(eval $1:=$2) + + # Target specific export the variables for that target # # target-export-variables diff --git a/tests/build_system_utils/Makefile b/tests/build_system_utils/Makefile index 22e4af7d2a..dc3afac78b 100644 --- a/tests/build_system_utils/Makefile +++ b/tests/build_system_utils/Makefile @@ -18,6 +18,7 @@ MAKEFILES_UTILS = $(RIOTMAKE)/utils COMPILE_TESTS += test-ensure_value test-ensure_value-negative COMPILE_TESTS += test-exported-variables +COMPILE_TESTS += test-memoized-variables # Tests will be run both in the host machine and in `docker` all: build-system-utils-tests @@ -35,3 +36,6 @@ test-ensure_value-negative: test-exported-variables: $(Q)$(call command_should_succeed,"$(MAKE)" -C $(MAKEFILES_UTILS) -f test-variables.mk test-exported-variables) + +test-memoized-variables: + $(Q)$(call command_should_succeed,"$(MAKE)" -C $(MAKEFILES_UTILS) -f test-variables.mk test-memoized-variables) From 25a1bc48bb7e1f10440a35dacf703b8dcccec9b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Harter?= Date: Fri, 7 Jun 2019 18:17:26 +0200 Subject: [PATCH 3/3] makefiles/utils: include in Makefile.include Import utils functions in Makefile.include to allow using them. --- Makefile.include | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile.include b/Makefile.include index f9ed21b6a1..e9c8d91eb3 100644 --- a/Makefile.include +++ b/Makefile.include @@ -80,6 +80,9 @@ MAKEOVERRIDES += $(foreach v,$(__DIRECTORY_VARIABLES),$(v)=$($(v))) # trailing '/' is important when RIOTPROJECT == CURDIR BUILDRELPATH ?= $(patsubst $(RIOTPROJECT)/%,%,$(CURDIR)/) +# include makefiles utils tools +include $(RIOTMAKE)/utils/variables.mk + # get host operating system OS := $(shell uname)