diff --git a/src/bmakelib.mk b/src/bmakelib.mk index 06e6ab5..031848e 100644 --- a/src/bmakelib.mk +++ b/src/bmakelib.mk @@ -13,6 +13,9 @@ # limitations under the License. #################################################################################################### +ifndef __bmakelib_bmakelib.mk +__bmakelib_bmakelib.mk := 1 + #################################################################################################### #> # # `bmakelib.MIN_MAKE_VERSION` @@ -135,3 +138,5 @@ bmakelib.FEATURES := error-if-blank.mk default-if-blank.mk timed.mk logged.mk en .PHONY : $(bmakelib.FEATURES:%=$(bmakelib.ROOT)%) include $(bmakelib.FEATURES:%=$(bmakelib.ROOT)%) + +endif # __bmakelib_bmakelib.mk diff --git a/src/default-if-blank.mk b/src/default-if-blank.mk index a0de428..074bac2 100644 --- a/src/default-if-blank.mk +++ b/src/default-if-blank.mk @@ -13,6 +13,9 @@ # limitations under the License. #################################################################################################### +ifndef __bmakelib_default-if-blank.mk +__bmakelib_default-if-blank.mk := 1 + #################################################################################################### #> # # `bmakelib.default-if-blank` @@ -93,3 +96,5 @@ bmakelib.default-if-blank(%) : #################################################################################################### bmakelib.conf.default-if-blank.SILENT ?= $(or $(BMAKELIB_CONF_DEFAULT_IF_BLANK_SILENT),yes) + +endif # __bmakelib_default-if-blank.mk diff --git a/src/dict.mk b/src/dict.mk index 3127878..f8642a2 100644 --- a/src/dict.mk +++ b/src/dict.mk @@ -13,6 +13,9 @@ # limitations under the License. #################################################################################################### +ifndef __bmakelib_dict.mk +__bmakelib_dict.mk := 1 + #################################################################################################### #> # # `bmakelib.dict` @@ -217,3 +220,5 @@ bmakelib.dict.put(%) : define bmakelib.dict.get $(bmakelib.dict.define.__$(1).$(2)) endef + +endif # __bmakelib_dict.mk diff --git a/src/enum.mk b/src/enum.mk index 541f449..2a2c2a7 100644 --- a/src/enum.mk +++ b/src/enum.mk @@ -13,6 +13,9 @@ # limitations under the License. #################################################################################################### +ifndef __bmakelib_enum.mk +__bmakelib_enum.mk := 1 + #################################################################################################### #> # # `bmakelib.enum.define` @@ -162,3 +165,5 @@ $(eval \ ,\ $(error '$($(2))' is not a member of enum '$(1)'))) endef + +endif # __bmakelib_enum.mk diff --git a/src/error-if-blank.mk b/src/error-if-blank.mk index 2a8ba15..49e6d0a 100644 --- a/src/error-if-blank.mk +++ b/src/error-if-blank.mk @@ -13,6 +13,9 @@ # limitations under the License. #################################################################################################### +ifndef __bmakelib_error-if-blank.mk +__bmakelib_error-if-blank.mk := 1 + #################################################################################################### #> # # `bmakelib.error-if-blank` @@ -42,3 +45,5 @@ bmakelib.error-if-blank(%) : $(if $($(*)), \ , \ $(error Provide a value for '$(*)')) + +endif # __bmakelib_error-if-blank.mk diff --git a/src/help.mk b/src/help.mk index 13a0500..441d0c4 100644 --- a/src/help.mk +++ b/src/help.mk @@ -13,6 +13,9 @@ # limitations under the License. #################################################################################################### +ifndef __bmakelib_help.mk +__bmakelib_help.mk := 1 + .PHONY : _bmakelib.help.noop _bmakelib.help.noop : @@ -177,3 +180,5 @@ bmakelib.conf.help.tips ?= $(or $(BMAKELIB_CONF_HELP_TIPS),yes) #################################################################################################### bmakelib.conf.help.show-bmakelib ?= $(or $(BMAKELIB_CONF_HELP_SHOW_BMAKELIB),no) + +endif # __bmakelib_help.mk diff --git a/src/logged.mk b/src/logged.mk index 834dbf5..0ec3cdf 100644 --- a/src/logged.mk +++ b/src/logged.mk @@ -13,6 +13,9 @@ # limitations under the License. #################################################################################################### +ifndef __bmakelib_logged.mk +__bmakelib_logged.mk := 1 + #################################################################################################### #> # # `!!bmakelib.logged` @@ -223,3 +226,6 @@ $(if $(filter yes,$(bmakelib.conf.logged.ECHO_COMMAND)),,@)exec 3>&1 4>&2 $(bmak && $(1) endef + +endif # __bmakelib_logged.mk + diff --git a/src/shell.mk b/src/shell.mk index f83d5b5..02cbd0d 100644 --- a/src/shell.mk +++ b/src/shell.mk @@ -13,6 +13,9 @@ # limitations under the License. #################################################################################################### +ifndef __bmakelib_shell.mk +__bmakelib_shell.mk := 1 + #################################################################################################### #> # # `bmakelib.shell.error-if-nonzero` @@ -123,3 +126,5 @@ endef #################################################################################################### bmakelib.conf.shell.error-if-nonzero.SILENT ?= $(or $(BMAKELIB_CONF_SHELL_ERROR_IF_NONZERO_SILENT),yes) + +endif # __bmakelib_shell.mk diff --git a/src/timed.mk b/src/timed.mk index 6e076f2..4d468fa 100644 --- a/src/timed.mk +++ b/src/timed.mk @@ -13,6 +13,9 @@ # limitations under the License. #################################################################################################### +ifndef __bmakelib_timed.mk +__bmakelib_timed.mk := 1 + #################################################################################################### #> # # `!bmakelib.timed` @@ -161,3 +164,5 @@ bmakelib._%!timed-post : bmakelib._%!timed : bmakelib._%!timed-pre .WAIT % .WAIT bmakelib._%!timed-post $(eval bmakelib.vars.timed.duration.$(*) := \ $(shell perl -E 'printf("%.0f", ($(bmakelib.vars.timed.end-ts.$(*)) - $(bmakelib.vars.timed.begin-ts.$(*))) / 1_000_000)')) + +endif # __bmakelib_timed.mk diff --git a/tests/test_include-guard b/tests/test_include-guard new file mode 100755 index 0000000..0c48bfb --- /dev/null +++ b/tests/test_include-guard @@ -0,0 +1,207 @@ +#!/usr/bin/env bash +#################################################################################################### +# Copyright © Bahman Movaqar +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#################################################################################################### + +set -o pipefail + +test_suite_name=$(basename $BASH_SOURCE) +source ${root_dir}tests/lib.sh + +#################################################################################################### +# when bmakelib.mk is included multiple times, no warnings or errors should be emitted +#################################################################################################### + +function bmakelib_multiple_includes_no_warnings { + local test_case_name=${FUNCNAME[0]} + local actual_value_filename=${test_case_name}.actual.log + bmakelib.test.cat < Makefile +SHELL := /usr/bin/env bash +include \$(bmakelib.ROOT)/bmakelib.mk +include \$(bmakelib.ROOT)/bmakelib.mk +include \$(bmakelib.ROOT)/bmakelib.mk + +.PHONY : test-target + +test-target : + @echo "success" +EOF + + make test-target > $actual_value_filename 2>&1 + + local expected_pattern_filename=${test_case_name}.expected.pattern + bmakelib.test.cat < $expected_pattern_filename +make.+Entering.+ +success +make.+Leaving.+ +EOF + + bmakelib.test.assert_matches $test_case_name $actual_value_filename $expected_pattern_filename \ + || return 1 +} + +#################################################################################################### +# when help.mk is included multiple times, no recipe overwrite warnings should occur +#################################################################################################### + +function help_mk_multiple_includes_no_warnings { + local test_case_name=${FUNCNAME[0]} + local actual_value_filename=${test_case_name}.actual.log + bmakelib.test.cat < Makefile +SHELL := /usr/bin/env bash +include \$(bmakelib.ROOT)/help.mk +include \$(bmakelib.ROOT)/help.mk + +.PHONY : dummy + +dummy : + @echo "help included cleanly" +EOF + + make dummy > $actual_value_filename 2>&1 + + local expected_pattern_filename=${test_case_name}.expected.pattern + bmakelib.test.cat < $expected_pattern_filename +make.+Entering.+ +help included cleanly +make.+Leaving.+ +EOF + + bmakelib.test.assert_matches $test_case_name $actual_value_filename $expected_pattern_filename \ + || return 1 +} + +#################################################################################################### +# when each module is included multiple times, execution should remain idempotent +#################################################################################################### + +function all_individual_modules_multiple_includes { + local test_case_name=${FUNCNAME[0]} + local actual_value_filename=${test_case_name}.actual.log + bmakelib.test.cat < Makefile +SHELL := /usr/bin/env bash +include \$(bmakelib.ROOT)/error-if-blank.mk +include \$(bmakelib.ROOT)/error-if-blank.mk +include \$(bmakelib.ROOT)/default-if-blank.mk +include \$(bmakelib.ROOT)/default-if-blank.mk +include \$(bmakelib.ROOT)/dict.mk +include \$(bmakelib.ROOT)/dict.mk +include \$(bmakelib.ROOT)/enum.mk +include \$(bmakelib.ROOT)/enum.mk +include \$(bmakelib.ROOT)/logged.mk +include \$(bmakelib.ROOT)/logged.mk +include \$(bmakelib.ROOT)/shell.mk +include \$(bmakelib.ROOT)/shell.mk +include \$(bmakelib.ROOT)/timed.mk +include \$(bmakelib.ROOT)/timed.mk + +.PHONY : check-all + +check-all : + @echo "all modules included idempotently" +EOF + + make check-all > $actual_value_filename 2>&1 + + local expected_pattern_filename=${test_case_name}.expected.pattern + bmakelib.test.cat < $expected_pattern_filename +make.+Entering.+ +all modules included idempotently +make.+Leaving.+ +EOF + + bmakelib.test.assert_matches $test_case_name $actual_value_filename $expected_pattern_filename \ + || return 1 +} + +#################################################################################################### +# guard variables must be defined and set to 1 +#################################################################################################### + +function guard_variables_defined { + local test_case_name=${FUNCNAME[0]} + local actual_value_filename=${test_case_name}.actual.log + bmakelib.test.cat < Makefile +SHELL := /usr/bin/env bash +include \$(bmakelib.ROOT)/bmakelib.mk + +.PHONY : check-guards + +check-guards : + @echo "bmakelib: \$(__bmakelib_bmakelib.mk)" + @echo "error-if-blank: \$(__bmakelib_error-if-blank.mk)" + @echo "default-if-blank: \$(__bmakelib_default-if-blank.mk)" + @echo "dict: \$(__bmakelib_dict.mk)" + @echo "enum: \$(__bmakelib_enum.mk)" + @echo "help: \$(__bmakelib_help.mk)" + @echo "logged: \$(__bmakelib_logged.mk)" + @echo "shell: \$(__bmakelib_shell.mk)" + @echo "timed: \$(__bmakelib_timed.mk)" +EOF + + make check-guards > $actual_value_filename 2>&1 + + local expected_pattern_filename=${test_case_name}.expected.pattern + bmakelib.test.cat < $expected_pattern_filename +make.+Entering.+ +bmakelib: 1 +error-if-blank: 1 +default-if-blank: 1 +dict: 1 +enum: 1 +help: 1 +logged: 1 +shell: 1 +timed: 1 +make.+Leaving.+ +EOF + + bmakelib.test.assert_matches $test_case_name $actual_value_filename $expected_pattern_filename \ + || return 1 +} + +#################################################################################################### +# guard variables must not be exposed in make help output +#################################################################################################### + +function guard_variables_not_in_help { + local test_case_name=${FUNCNAME[0]} + local actual_value_filename=${test_case_name}.actual.log + bmakelib.test.cat < Makefile +SHELL := /usr/bin/env bash +include \$(bmakelib.ROOT)/bmakelib.mk + +## Sample target +sample-target : + @echo "sample" +EOF + + make help bmakelib.conf.help.show-bmakelib=yes > $actual_value_filename 2>&1 + + if grep -E '__bmakelib_' $actual_value_filename > /dev/null; then + bmakelib.test.echo.error "${test_case_name}: guard variables found in help output" + return 1 + fi + + return 0 +} + +#################################################################################################### + +bmakelib.test.run_test_case bmakelib_multiple_includes_no_warnings +bmakelib.test.run_test_case help_mk_multiple_includes_no_warnings +bmakelib.test.run_test_case all_individual_modules_multiple_includes +bmakelib.test.run_test_case guard_variables_defined +bmakelib.test.run_test_case guard_variables_not_in_help