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)