Last active
May 8, 2017 16:16
-
-
Save duaneg/86f3a122b66aa341b369 to your computer and use it in GitHub Desktop.
GNU Make modular makefile framework
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#*********************************************************************** | |
# | |
# Build system module framework | |
# | |
# Copyright (c) 2014 Process Systems Enterprise Ltd | |
# | |
# Licensed under GPL v3+ | |
# | |
# This makefile provides definitions which allow use of a modular build system, | |
# where "module" makefiles are isolated from each other. Variables defined in a | |
# makefile are local to that makefile; changed values are reset and new ones | |
# undefined after processing each one. | |
# | |
# It requires GNU make version 3.82 to work properly. If used with 3.81 it will | |
# fall back to clearing variables listed in SCRATCH_VARS and doing a simple | |
# include of the module. No saving/restoring of state will be done. Earlier | |
# versions of GNU make, or other implementations, will likely not work at all. | |
# | |
# The following variables are used to control module processing: | |
# | |
# SCRATCH_VARS: a list of variables which must be undefined at the point the | |
# module is included. They will be undefined/cleared for each | |
# module, even when falling-back on restricted functionality due | |
# to an unsupported make version. | |
# | |
# EXPORT: a list of variables whose value should *not* be cleared/reset after | |
# the module is processed. Variables which are EXPORTed from more than | |
# one module may only be appended to. | |
# | |
# OVERRIDE: a list of variables whose value should *not* be cleared/reset after | |
# the module is processed. Variables listed here may change the value | |
# set by other modules (i.e. they are not append-only, unlike | |
# EXPORT). | |
# | |
# NOTE: The control variables are not treated specially. They will be local to | |
# each module, unless listed in EXPORT or OVERRIDE themselves. However | |
# adding them to EXPORT or OVERRIDE has not been tested and may or may | |
# not work. | |
# | |
# WARNING: Due to the way rules are evaluated any variable used within them | |
# must be exported! | |
# | |
# EXAMPLE USE: | |
# | |
# MODULES := foo bar | |
# SCRATCH_VARS := NAME SRCS CFLAGS INCS LIBS | |
# | |
# include mk/Build.mk | |
# include mk/Modules.mk | |
# $(foreach module,$(MODULES),$(eval $(call IncludeModule,$(module)/Module.mk))) | |
# | |
#*********************************************************************** | |
# The scratch variables should not have a value yet | |
# Confirm that and set their initial value to empty | |
define ConfirmUndefined | |
$$(if $$(strip $$(subst undefined,,$$(origin $(1)))),$$(error Scratch variable $(1) has been defined),) | |
endef | |
$(foreach var,$(SCRATCH_VARS),$(eval $(call ConfirmUndefined,$(var)))) | |
ifdef .FEATURES | |
ifeq (undefine,$(filter undefine,$(.FEATURES))) | |
undefine ConfirmUndefined | |
endif | |
endif | |
$(foreach var,$(SCRATCH_VARS),$(eval $(var):=)) | |
# Variables to exclude from the save/restore/clear cycle | |
# | |
# These are internal to the operation of this file or otherwise special and | |
# must be excluded or things will get very confused. | |
NO_SAVE := _EXPORT _OVERRIDE SaveVariableIfSet RestoreVariable CheckSavedExportedVariable ProcessSavedVariable ClearUnsavedVariable SaveState RestoreState IncludeModule .% | |
# Initially just exclude scratch and internal variables from exports | |
# There are lots of others that should probably be added to this list... | |
# NOTE: Wrap names in quotes so we don't find match on partial words | |
RESERVED_VARS := $(SCRATCH_VARS) $(NO_SAVE) | |
RESERVED_VARS := $(RESERVED_VARS:%="%") | |
# Save a given variable | |
# NOTE: We must use a nested define to handle variables containing newlines | |
define SaveVariable | |
define _SAVE_$(1) | |
$(value $(1)) | |
endef | |
endef | |
# Save a variable's value if it has been set in a file only | |
# | |
# NOTE: Here and elsewhere inside defines we cannot use standard conditionals | |
# as they behave in very obscure and seemingly buggy ways when used | |
# within templates. | |
define SaveVariableIfSet | |
$$(if $$(subst override,,$$(subst file,,$$(origin $(1)))),,$$(eval $$(call SaveVariable,$(1)))) | |
endef | |
# Restore the value of a saved variable | |
define RestoreVariable | |
define $(1) | |
$(value _SAVE_$(1)) | |
endef | |
endef | |
# Check a saved and exported variable has been appended to only | |
# TODO: Support variables declared as redefinable (exported and not append-only) | |
define CheckSavedExportedVariable | |
$$(if $$(findstring ^$$(value _SAVE_$(1)),^$$(value $(1))),,$$(error Saved and exported value $(1) is append-only)) | |
endef | |
# Saved variables that were not exported are restored to their previous value | |
# Saved variables that were exported should have been appended to only | |
# Saved variables that were overriden can have any value | |
define ProcessSavedVariable | |
$$(if $$(findstring "$(1)",$$(_EXPORT)),$$(eval $$(call CheckSavedExportedVariable,$(1))),\ | |
$$(if $$(findstring "$(1)",$$(_OVERRIDE)),,$$(eval $$(call RestoreVariable,$(1))))) | |
undefine _SAVE_$(1) | |
endef | |
# Clear the given variable if it has been set in the file and hasn't had a | |
# value saved or been exported/overriden | |
define ClearUnsavedVariable | |
$$(if $$(strip $$(subst override,,$$(subst file,,$$(origin $(1))))),,\ | |
$$(if $$(strip $$(findstring "$(1)",$$(_EXPORT) $$(_OVERRIDE)) $$(subst undefined,,$$(origin _SAVE_$(1)))),,$$(eval undefine $(1)))) | |
endef | |
# Save value of all other defined variables, excluding a few special ones | |
define SaveState | |
$$(foreach var,$$(filter-out $(NO_SAVE),$$(.VARIABLES)),$$(eval $$(call SaveVariableIfSet,$$(var)))) | |
endef | |
# Check a variable is not on the reserved list | |
define CheckExport | |
$(if $(strip $(findstring "$(2)",$(RESERVED_VARS))),$$(error Module $(1) attempted to export/override reserved variable: $(2)),) | |
endef | |
# Restore to saved state, excluding any exported variables | |
define RestoreState | |
# Wrap each exported/overriden variable name in quotes | |
# NOTE: Need to take care to correctly handle (i.e. not reference) undefined EXPORT/OVERRIDE variables | |
_EXPORT := $$(and $$(filter-out undefined,$$(origin EXPORT)),$$(patsubst %,"%",$$(EXPORT))) | |
_OVERRIDE := $$(and $$(filter-out undefined,$$(origin OVERRIDE)),$$(patsubst %,"%",$$(OVERRIDE))) | |
# Check the module didn't export/override anything it shouldn't | |
# NOTE: Use quoted version defined above so as to correctly handle undefined EXPORT/OVERRIDE variables | |
$$(foreach var,$$(_EXPORT) $$(_OVERRIDE),$$(eval $$(call CheckExport,$(1),$$(patsubst "%",%,$$(var))))) | |
# Clear any variables that have not had values saved and were not exported | |
$$(foreach var,$$(filter-out _SAVE_% $(NO_SAVE),$$(.VARIABLES)),$$(eval $$(call ClearUnsavedVariable,$$(var)))) | |
# Restore or check saved variables | |
$$(foreach var,$$(filter _SAVE_%,$$(.VARIABLES)),$$(eval $$(call ProcessSavedVariable,$$(patsubst _SAVE_%,%,$$(var))))) | |
# Manually clear/reset internal export/override list | |
undefine _EXPORT | |
undefine _OVERRIDE | |
endef | |
# A basic version that clears scratch variables and does an include. | |
# | |
# It will be used if make doesn't support undefine (i.e. version < 3.82). | |
define IncludeModule | |
$$(eval include $(1)) | |
endef | |
ifdef .FEATURES | |
ifeq (undefine,$(filter undefine,$(.FEATURES))) | |
define IncludeModule | |
$$(eval $$(SaveState)) | |
$$(eval include $(1)) | |
$$(eval $$(call RestoreState,$(1))) | |
endef | |
endif | |
endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment