diff --git a/dist/tools/ci/static_tests.sh b/dist/tools/ci/static_tests.sh index e25ac9ace933..aef94ff74dbf 100755 --- a/dist/tools/ci/static_tests.sh +++ b/dist/tools/ci/static_tests.sh @@ -122,6 +122,7 @@ run ./dist/tools/coccinelle/check.sh run ./dist/tools/flake8/check.sh run ./dist/tools/headerguards/check.sh run ./dist/tools/buildsystem_sanity_check/check.sh +run ./dist/tools/feature_resolution/check.sh run ./dist/tools/codespell/check.sh if [ -z "${GITHUB_RUN_ID}" ]; then run ./dist/tools/uncrustify/uncrustify.sh --check diff --git a/dist/tools/feature_resolution/check.sh b/dist/tools/feature_resolution/check.sh new file mode 100755 index 000000000000..20daea1a16b0 --- /dev/null +++ b/dist/tools/feature_resolution/check.sh @@ -0,0 +1,2 @@ +#!/bin/sh +make --silent -C "$(dirname "$0")"/../../../makefiles/tests/feature_resolution diff --git a/makefiles/features_check.inc.mk b/makefiles/features_check.inc.mk index abca2fb644cb..b10ac34aab68 100644 --- a/makefiles/features_check.inc.mk +++ b/makefiles/features_check.inc.mk @@ -1,16 +1,20 @@ # Check if all required FEATURES are provided +# Features that are provided and not blacklisted +FEATURES_USABLE := $(filter-out $(FEATURES_BLACKLIST),$(FEATURES_PROVIDED)) + +# Features that may be used, if provided. FEATURES_OPTIONAL_ONLY := $(sort $(filter-out $(FEATURES_REQUIRED),$(FEATURES_OPTIONAL))) -FEATURES_OPTIONAL_USED := $(sort $(filter $(FEATURES_PROVIDED),$(FEATURES_OPTIONAL_ONLY))) -# Optional features that will not be used because they are not provided -FEATURES_OPTIONAL_MISSING := $(sort $(filter-out $(FEATURES_PROVIDED),$(FEATURES_OPTIONAL_ONLY))) + +# Optional features that end up being used +FEATURES_OPTIONAL_USED := $(sort $(filter $(FEATURES_USABLE),$(FEATURES_OPTIONAL_ONLY))) + +# Optional features that will not be used because they are not provided or blacklisted +FEATURES_OPTIONAL_MISSING := $(sort $(filter-out $(FEATURES_USABLE),$(FEATURES_OPTIONAL_ONLY))) # Features that are used without taking "one out of" dependencies into account FEATURES_USED_SO_FAR := $(sort $(FEATURES_REQUIRED) $(FEATURES_OPTIONAL_USED)) -# Features that are provided and not blacklisted -FEATURES_USABLE := $(filter-out $(FEATURES_BLACKLIST),$(FEATURES_PROVIDED)) - # Additionally required features due to the "one out of" dependencies # Algorithm: # - For each list in FEATURES_REQUIRED_ANY as "item": @@ -47,11 +51,6 @@ FEATURES_USED := $(sort $(FEATURES_REQUIRED) \ $(FEATURES_REQUIRED_ONE_OUT_OF) \ $(FEATURES_OPTIONAL_USED)) -# Used features that conflict when used together -FEATURES_CONFLICTING := $(sort $(foreach conflict,\ - $(FEATURES_CONFLICT),\ - $(call _features_conflicting,$(conflict)))) - # Return conflicting features from the conflict string feature1:feature2 # $1: feature1:feature2 # Return the list of conflicting features @@ -61,6 +60,11 @@ _features_conflicting = $(if $(call _features_used_conflicting,$(subst :, ,$1)), # Return non empty on error _features_used_conflicting = $(filter $(words $1),$(words $(filter $(FEATURES_USED),$1))) +# Used features that conflict when used together +FEATURES_CONFLICTING := $(sort $(foreach conflict,\ + $(FEATURES_CONFLICT),\ + $(call _features_conflicting,$(conflict)))) + # Features that are used by the application but blacklisted by the BSP. # Having blacklisted features may cause the build to fail. FEATURES_USED_BLACKLISTED := $(sort $(filter $(FEATURES_USED), $(FEATURES_BLACKLIST))) diff --git a/makefiles/tests/feature_resolution/Makefile b/makefiles/tests/feature_resolution/Makefile new file mode 100644 index 000000000000..f3c42efa2515 --- /dev/null +++ b/makefiles/tests/feature_resolution/Makefile @@ -0,0 +1,12 @@ +.PHONY: all info-boards-supported + +TESTS := $(patsubst test-%.mk,run-%,$(wildcard test-*.mk)) + +all: $(TESTS) + +info-boards-supported: + @echo none + +run-%: test-%.mk + @echo "Running test $(patsubst run-%,%,$@)" + @$(MAKE) --silent -f $< diff --git a/makefiles/tests/feature_resolution/Makefile.test b/makefiles/tests/feature_resolution/Makefile.test new file mode 100644 index 000000000000..f6046276113d --- /dev/null +++ b/makefiles/tests/feature_resolution/Makefile.test @@ -0,0 +1,23 @@ +include ../../features_check.inc.mk + +FEATURES_VARS := \ + FEATURES_CONFLICTING \ + FEATURES_USED \ + FEATURES_USED_BLACKLISTED \ + FEATURES_MISSING \ + # + +define _expected = + ifneq ($$(EXPECTED_$1),$$($1)) + $$(info EXPECTED_$1 = $$(EXPECTED_$1)) + $$(info $1 = $$($1)) + $$(error $1 incorrectly resolved) + endif +endef + +$(foreach var,$(FEATURES_VARS),$(eval $(call _expected,$(var)))) + + +.PHONY: all +all: + @echo Success diff --git a/makefiles/tests/feature_resolution/README.md b/makefiles/tests/feature_resolution/README.md new file mode 100644 index 000000000000..0f94aab5c1e1 --- /dev/null +++ b/makefiles/tests/feature_resolution/README.md @@ -0,0 +1,15 @@ +Simple Unit Tests for Feature Resolution +======================================== + +This folder contains a Makefile that runs a set of unit tests for the feature resolution logic. +Each unit test is defined in a file named `tests-.mk` in which the inputs and the +expected outputs are defined. Finally, the include `Makefile.test`, which will perform the tests. + +The tests can be run simply by typing `make`. Also, `make static-test` at the root of the repo will +run these tests and, hence, these tests are also executed on each CI run. + +Adding new Test Cases +--------------------- + +Just create a new `tests-.mk` file and start with the contests of an existing +test case and change the inputs and expected outputs accordingly. diff --git a/makefiles/tests/feature_resolution/test-blacklist-failure.mk b/makefiles/tests/feature_resolution/test-blacklist-failure.mk new file mode 100644 index 000000000000..f89a8d1997e7 --- /dev/null +++ b/makefiles/tests/feature_resolution/test-blacklist-failure.mk @@ -0,0 +1,15 @@ +# inputs +FEATURES_OPTIONAL := +FEATURES_REQUIRED := a b +FEATURES_REQUIRED_ANY := +FEATURES_PROVIDED := a b c +FEATURES_BLACKLIST := b +FEATURES_CONFLICT := + +# expected results +EXPECTED_FEATURES_USED := a b +EXPECTED_FEATURES_MISSING := +EXPECTED_FEATURES_USED_BLACKLISTED := b +EXPECTED_FEATURES_CONFLICTING := + +include Makefile.test diff --git a/makefiles/tests/feature_resolution/test-blacklist-optional.mk b/makefiles/tests/feature_resolution/test-blacklist-optional.mk new file mode 100644 index 000000000000..94112cc4529a --- /dev/null +++ b/makefiles/tests/feature_resolution/test-blacklist-optional.mk @@ -0,0 +1,15 @@ +# inputs +FEATURES_OPTIONAL := c +FEATURES_REQUIRED := a b +FEATURES_REQUIRED_ANY := +FEATURES_PROVIDED := a b c +FEATURES_BLACKLIST := c +FEATURES_CONFLICT := + +# expected results +EXPECTED_FEATURES_USED := a b +EXPECTED_FEATURES_MISSING := +EXPECTED_FEATURES_USED_BLACKLISTED := +EXPECTED_FEATURES_CONFLICTING := + +include Makefile.test diff --git a/makefiles/tests/feature_resolution/test-blacklist-success.mk b/makefiles/tests/feature_resolution/test-blacklist-success.mk new file mode 100644 index 000000000000..d4955218eaae --- /dev/null +++ b/makefiles/tests/feature_resolution/test-blacklist-success.mk @@ -0,0 +1,15 @@ +# inputs +FEATURES_OPTIONAL := +FEATURES_REQUIRED := a b +FEATURES_REQUIRED_ANY := +FEATURES_PROVIDED := a b c +FEATURES_BLACKLIST := c +FEATURES_CONFLICT := + +# expected results +EXPECTED_FEATURES_USED := a b +EXPECTED_FEATURES_MISSING := +EXPECTED_FEATURES_USED_BLACKLISTED := +EXPECTED_FEATURES_CONFLICTING := + +include Makefile.test diff --git a/makefiles/tests/feature_resolution/test-complex-failure.mk b/makefiles/tests/feature_resolution/test-complex-failure.mk new file mode 100644 index 000000000000..b371a89156cf --- /dev/null +++ b/makefiles/tests/feature_resolution/test-complex-failure.mk @@ -0,0 +1,15 @@ +# inputs +FEATURES_OPTIONAL := a b +FEATURES_REQUIRED := c d +FEATURES_REQUIRED_ANY := e|f|d a|h|i +FEATURES_PROVIDED := a b c d e f g +FEATURES_BLACKLIST := a +FEATURES_CONFLICT := f:e c:d + +# expected results +EXPECTED_FEATURES_USED := a|h|i b c d +EXPECTED_FEATURES_MISSING := a|h|i +EXPECTED_FEATURES_USED_BLACKLISTED := +EXPECTED_FEATURES_CONFLICTING := c d + +include Makefile.test diff --git a/makefiles/tests/feature_resolution/test-complex-success.mk b/makefiles/tests/feature_resolution/test-complex-success.mk new file mode 100644 index 000000000000..5b15ec093aee --- /dev/null +++ b/makefiles/tests/feature_resolution/test-complex-success.mk @@ -0,0 +1,15 @@ +# inputs +FEATURES_OPTIONAL := a b +FEATURES_REQUIRED := c d +FEATURES_REQUIRED_ANY := e|f|d +FEATURES_PROVIDED := a b c d e f g +FEATURES_BLACKLIST := a +FEATURES_CONFLICT := f:e + +# expected results +EXPECTED_FEATURES_USED := b c d +EXPECTED_FEATURES_MISSING := +EXPECTED_FEATURES_USED_BLACKLISTED := +EXPECTED_FEATURES_CONFLICTING := + +include Makefile.test diff --git a/makefiles/tests/feature_resolution/test-conflict-failure.mk b/makefiles/tests/feature_resolution/test-conflict-failure.mk new file mode 100644 index 000000000000..fcfc9e6661b8 --- /dev/null +++ b/makefiles/tests/feature_resolution/test-conflict-failure.mk @@ -0,0 +1,15 @@ +# inputs +FEATURES_OPTIONAL := +FEATURES_REQUIRED := a b +FEATURES_REQUIRED_ANY := +FEATURES_PROVIDED := a b c d +FEATURES_BLACKLIST := +FEATURES_CONFLICT := a:b c:d + +# expected results +EXPECTED_FEATURES_USED := a b +EXPECTED_FEATURES_MISSING := +EXPECTED_FEATURES_USED_BLACKLISTED := +EXPECTED_FEATURES_CONFLICTING := a b + +include Makefile.test diff --git a/makefiles/tests/feature_resolution/test-conflict-success.mk b/makefiles/tests/feature_resolution/test-conflict-success.mk new file mode 100644 index 000000000000..cdbd738d79f4 --- /dev/null +++ b/makefiles/tests/feature_resolution/test-conflict-success.mk @@ -0,0 +1,15 @@ +# inputs +FEATURES_OPTIONAL := +FEATURES_REQUIRED := a c +FEATURES_REQUIRED_ANY := +FEATURES_PROVIDED := a b c d +FEATURES_BLACKLIST := +FEATURES_CONFLICT := a:b c:d + +# expected results +EXPECTED_FEATURES_USED := a c +EXPECTED_FEATURES_MISSING := +EXPECTED_FEATURES_USED_BLACKLISTED := +EXPECTED_FEATURES_CONFLICTING := + +include Makefile.test diff --git a/makefiles/tests/feature_resolution/test-empty.mk b/makefiles/tests/feature_resolution/test-empty.mk new file mode 100644 index 000000000000..e5df3be88dfa --- /dev/null +++ b/makefiles/tests/feature_resolution/test-empty.mk @@ -0,0 +1,15 @@ +# inputs +FEATURES_OPTIONAL := +FEATURES_REQUIRED := +FEATURES_REQUIRED_ANY := +FEATURES_PROVIDED := +FEATURES_BLACKLIST := +FEATURES_CONFLICT := + +# expected results +EXPECTED_FEATURES_USED := +EXPECTED_FEATURES_MISSING := +EXPECTED_FEATURES_USED_BLACKLISTED := +EXPECTED_FEATURES_CONFLICTING := + +include Makefile.test diff --git a/makefiles/tests/feature_resolution/test-required-any-corner-case.mk b/makefiles/tests/feature_resolution/test-required-any-corner-case.mk new file mode 100644 index 000000000000..6583cba8c2c6 --- /dev/null +++ b/makefiles/tests/feature_resolution/test-required-any-corner-case.mk @@ -0,0 +1,15 @@ +# inputs +FEATURES_OPTIONAL := +FEATURES_REQUIRED := a +FEATURES_REQUIRED_ANY := b +FEATURES_PROVIDED := a b c +FEATURES_BLACKLIST := +FEATURES_CONFLICT := + +# expected results +EXPECTED_FEATURES_USED := a b +EXPECTED_FEATURES_MISSING := +EXPECTED_FEATURES_USED_BLACKLISTED := +EXPECTED_FEATURES_CONFLICTING := + +include Makefile.test diff --git a/makefiles/tests/feature_resolution/test-required-any-fail.mk b/makefiles/tests/feature_resolution/test-required-any-fail.mk new file mode 100644 index 000000000000..137614e95ee3 --- /dev/null +++ b/makefiles/tests/feature_resolution/test-required-any-fail.mk @@ -0,0 +1,15 @@ +# inputs +FEATURES_OPTIONAL := +FEATURES_REQUIRED := +FEATURES_REQUIRED_ANY := d|b|a +FEATURES_PROVIDED := c +FEATURES_BLACKLIST := +FEATURES_CONFLICT := + +# expected results +EXPECTED_FEATURES_USED := d|b|a +EXPECTED_FEATURES_MISSING := d|b|a +EXPECTED_FEATURES_USED_BLACKLISTED := +EXPECTED_FEATURES_CONFLICTING := + +include Makefile.test diff --git a/makefiles/tests/feature_resolution/test-required-any-order.mk b/makefiles/tests/feature_resolution/test-required-any-order.mk new file mode 100644 index 000000000000..d1314debea15 --- /dev/null +++ b/makefiles/tests/feature_resolution/test-required-any-order.mk @@ -0,0 +1,15 @@ +# inputs +FEATURES_OPTIONAL := +FEATURES_REQUIRED := +FEATURES_REQUIRED_ANY := d|b|a +FEATURES_PROVIDED := a b c +FEATURES_BLACKLIST := +FEATURES_CONFLICT := + +# expected results +EXPECTED_FEATURES_USED := b +EXPECTED_FEATURES_MISSING := +EXPECTED_FEATURES_USED_BLACKLISTED := +EXPECTED_FEATURES_CONFLICTING := + +include Makefile.test diff --git a/makefiles/tests/feature_resolution/test-required-any-reuse-optional.mk b/makefiles/tests/feature_resolution/test-required-any-reuse-optional.mk new file mode 100644 index 000000000000..ce5888f11aa3 --- /dev/null +++ b/makefiles/tests/feature_resolution/test-required-any-reuse-optional.mk @@ -0,0 +1,15 @@ +# inputs +FEATURES_OPTIONAL := a +FEATURES_REQUIRED := +FEATURES_REQUIRED_ANY := d|b|a +FEATURES_PROVIDED := a b c +FEATURES_BLACKLIST := +FEATURES_CONFLICT := + +# expected results +EXPECTED_FEATURES_USED := a +EXPECTED_FEATURES_MISSING := +EXPECTED_FEATURES_USED_BLACKLISTED := +EXPECTED_FEATURES_CONFLICTING := + +include Makefile.test diff --git a/makefiles/tests/feature_resolution/test-required-any-reuse-required.mk b/makefiles/tests/feature_resolution/test-required-any-reuse-required.mk new file mode 100644 index 000000000000..24f4ec292f6c --- /dev/null +++ b/makefiles/tests/feature_resolution/test-required-any-reuse-required.mk @@ -0,0 +1,15 @@ +# inputs +FEATURES_OPTIONAL := +FEATURES_REQUIRED := a b +FEATURES_REQUIRED_ANY := c|b +FEATURES_PROVIDED := a b c +FEATURES_BLACKLIST := +FEATURES_CONFLICT := + +# expected results +EXPECTED_FEATURES_USED := a b +EXPECTED_FEATURES_MISSING := +EXPECTED_FEATURES_USED_BLACKLISTED := +EXPECTED_FEATURES_CONFLICTING := + +include Makefile.test diff --git a/makefiles/tests/feature_resolution/test-trivial-failure.mk b/makefiles/tests/feature_resolution/test-trivial-failure.mk new file mode 100644 index 000000000000..89992cfde862 --- /dev/null +++ b/makefiles/tests/feature_resolution/test-trivial-failure.mk @@ -0,0 +1,15 @@ +# inputs +FEATURES_OPTIONAL := +FEATURES_REQUIRED := a b +FEATURES_REQUIRED_ANY := +FEATURES_PROVIDED := a c +FEATURES_BLACKLIST := +FEATURES_CONFLICT := + +# expected results +EXPECTED_FEATURES_USED := a b +EXPECTED_FEATURES_MISSING := b +EXPECTED_FEATURES_USED_BLACKLISTED := +EXPECTED_FEATURES_CONFLICTING := + +include Makefile.test diff --git a/makefiles/tests/feature_resolution/test-trivial-success.mk b/makefiles/tests/feature_resolution/test-trivial-success.mk new file mode 100644 index 000000000000..d11e8038e0e4 --- /dev/null +++ b/makefiles/tests/feature_resolution/test-trivial-success.mk @@ -0,0 +1,15 @@ +# inputs +FEATURES_OPTIONAL := +FEATURES_REQUIRED := a b +FEATURES_REQUIRED_ANY := +FEATURES_PROVIDED := a b c +FEATURES_BLACKLIST := +FEATURES_CONFLICT := + +# expected results +EXPECTED_FEATURES_USED := a b +EXPECTED_FEATURES_MISSING := +EXPECTED_FEATURES_USED_BLACKLISTED := +EXPECTED_FEATURES_CONFLICTING := + +include Makefile.test