diff --git a/Makefile b/Makefile index 8154c61d..8403981e 100644 --- a/Makefile +++ b/Makefile @@ -77,13 +77,19 @@ list-targets: FORCE GLUON_DEFAULT_PACKAGES := -odhcpd -ppp -ppp-mod-pppoe -wpad-mini gluon-core ip6tables hostapd-mini +GLUON_FEATURE_PACKAGES := $(shell scripts/features.sh '$(GLUON_FEATURES)') +ifneq ($(.SHELLSTATUS),0) +$(error Error while evaluating GLUON_FEATURES) +endif + + GLUON_PACKAGES := define merge_packages $(foreach pkg,$(1), GLUON_PACKAGES := $$(strip $$(filter-out -$$(patsubst -%,%,$(pkg)) $$(patsubst -%,%,$(pkg)),$$(GLUON_PACKAGES)) $(pkg)) ) endef -$(eval $(call merge_packages,$(GLUON_DEFAULT_PACKAGES) $(GLUON_SITE_PACKAGES))) +$(eval $(call merge_packages,$(GLUON_DEFAULT_PACKAGES) $(GLUON_FEATURE_PACKAGES) $(GLUON_SITE_PACKAGES))) GLUON_PACKAGES_YES := $(filter-out -%,$(GLUON_PACKAGES)) GLUON_PACKAGES_NO := $(patsubst -%,%,$(filter -%,$(GLUON_PACKAGES))) diff --git a/docs/dev/feature-flags.rst b/docs/dev/feature-flags.rst new file mode 100644 index 00000000..19f43af7 --- /dev/null +++ b/docs/dev/feature-flags.rst @@ -0,0 +1,45 @@ +Feature flags +============= + +Feature flags provide a convenient way to define package selections without +making it necessary to list each package explicitly. + +The main feature flag definition file is ``package/features``, but each package +feed can provide addition defintions in a file called ``features`` at the root +of the feed repository. + +Each flag *$flag* without any explicit definition will simply include the package +with the name *gluon-$flag* by default. The feature definition file can modify +the package selection in two ways: + +* The *nodefault* function suppresses default of including the *gluon-$flag* + package +* The *packages* function adds a list of packages (or removes, when package + names are prepended with minus signs) when a given logical expression + is satisfied + +Example:: + + nodefault 'web-wizard' + + packages 'web-wizard' \ + 'gluon-config-mode-hostname' \ + 'gluon-config-mode-geo-location' \ + 'gluon-config-mode-contact-info' + + packages 'web-wizard & (mesh-vpn-fastd | mesh-vpn-tunneldigger)' \ + 'gluon-config-mode-mesh-vpn' + +This will + +* Disable the inclusion of a (non-existent) package called *gluon-web-wizard* +* Enable three config mode packages when the *web-wizard* feature is enabled +* Enable *gluon-config-mode-mesh-vpn* when both *web-wizard* and one + of *mesh-vpn-fastd* and *mesh-vpn-tunneldigger* is enabled + +Supported syntax elements of logical expressions are: + +* \& (and) +* \| (or) +* \! (not) +* parentheses diff --git a/docs/index.rst b/docs/index.rst index 7778b23d..53d6c33e 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -33,6 +33,7 @@ Several Freifunk communities in Germany use Gluon as the foundation of their Fre :maxdepth: 2 dev/basics + dev/feature-flags dev/hardware dev/upgrade dev/wan diff --git a/docs/site-example/site.mk b/docs/site-example/site.mk index 8f03eb5e..091fb50d 100644 --- a/docs/site-example/site.mk +++ b/docs/site-example/site.mk @@ -1,29 +1,29 @@ ## gluon site.mk makefile example ## GLUON_SITE_PACKAGES -# specify Gluon/LEDE packages to include here +# Specify Gluon features/packages to enable; +# Gluon will automatically enable a set of packages +# depending on the combination of features listed -GLUON_SITE_PACKAGES := \ - gluon-alfred \ - gluon-respondd \ - gluon-autoupdater \ - gluon-config-mode-autoupdater \ - gluon-config-mode-contact-info \ - gluon-config-mode-geo-location \ - gluon-config-mode-hostname \ - gluon-config-mode-mesh-vpn \ - gluon-ebtables-filter-multicast \ - gluon-ebtables-filter-ra-dhcp \ - gluon-web-admin \ - gluon-web-autoupdater \ - gluon-web-network \ - gluon-web-wifi-config \ - gluon-mesh-batman-adv-15 \ - gluon-mesh-vpn-fastd \ - gluon-radvd \ - gluon-status-page \ - haveged \ - iwinfo +GLUON_FEATURES := + autoupdater \ + ebtables-filter-multicast \ + ebtables-filter-ra-dhcp \ + mesh-batman-adv-15 \ + mesh-vpn-fastd \ + radvd \ + respondd \ + status-page \ + web-advanced \ + web-wizard + +## GLUON_SITE_PACKAGES +# Specify additional Gluon/LEDE packages to include here; +# A minus sign may be prepended to remove a packages from the +# selection that would be enabled by default or due to the +# chosen feature flags + +GLUON_SITE_PACKAGES := haveged iwinfo ## DEFAULT_GLUON_RELEASE # version string to use for images diff --git a/docs/user/site.rst b/docs/user/site.rst index d82cdd95..723bd4a7 100644 --- a/docs/user/site.rst +++ b/docs/user/site.rst @@ -382,15 +382,20 @@ legacy \: package wifi_names = {'wifi_freifunk', 'wifi_freifunk5', 'wifi_mesh', 'wifi_mesh5'}, } -Packages --------- +Build configuration +------------------- -The ``site.mk`` is a Makefile which should define constants +The ``site.mk`` is a Makefile which defines various values involved in the build process of Gluon. +GLUON_FEATURES + Defines a list of features to include. The feature list is used to generate + the default package set. + GLUON_SITE_PACKAGES - Defines a list of packages which should be installed additionally - to the ``gluon-core`` package. + Defines a list of packages which should be installed in addition to the + default package set. It is also possible to remove packages from the + default set by prepending a minus sign to the package name. GLUON_RELEASE The current release version Gluon should use. @@ -407,6 +412,34 @@ GLUON_LANGS List of languages (as two-letter-codes) to be included in the web interface. Should always contain ``en``. +Features +^^^^^^^^ + +Most feature flags enable only a single package that is derived from the flag +name; for example, the flag *mesh-batman-adv-15* will include the package +*gluon-mesh-batman-adv-15*. + +The following flags will add multiple packages: + +* *web-wizard* + + - *gluon-config-mode-hostname* + - *gluon-config-mode-geo-location* + - *gluon-config-mode-contact-info* + - *gluon-config-mode-autoupdater* (if the *autoupdater* feature is enabled) + - *gluon-config-mode-mesh-vpn* (if the *mesh-vpn-fastd* or *mesh-vpn-tunneldigger* feature is enabled) + +* *web-advanced* + + - *gluon-web-admin* + - *gluon-web-network* + - *gluon-web-wifi-config* + - *gluon-web-autoupdater* (if the *autoupdater* feature is enabled) + - *gluon-web-mesh-vpn-fastd* (if the *mesh-vpn-fastd* feature is enabled) + +Site-provided package feeds can define additional feature flags. + + .. _site-config-mode-texts: Config mode texts diff --git a/package/features b/package/features new file mode 100644 index 00000000..265158a0 --- /dev/null +++ b/package/features @@ -0,0 +1,26 @@ +nodefault 'web-wizard' + +packages 'web-wizard' \ + 'gluon-config-mode-hostname' \ + 'gluon-config-mode-geo-location' \ + 'gluon-config-mode-contact-info' + +packages 'web-wizard & autoupdater' \ + 'gluon-config-mode-autoupdater' + +packages 'web-wizard & (mesh-vpn-fastd | mesh-vpn-tunneldigger)' \ + 'gluon-config-mode-mesh-vpn' + + +nodefault 'web-advanced' + +packages 'web-advanced' \ + 'gluon-web-admin' \ + 'gluon-web-network' \ + 'gluon-web-wifi-config' + +packages 'web-advanced & autoupdater' \ + 'gluon-web-autoupdater' + +packages 'web-advanced & mesh-vpn-fastd' \ + 'gluon-web-mesh-vpn-fastd' diff --git a/scripts/features.sh b/scripts/features.sh new file mode 100755 index 00000000..2d35fdee --- /dev/null +++ b/scripts/features.sh @@ -0,0 +1,76 @@ +#!/bin/bash --norc + +set -e +shopt -s nullglob + + +nodefault() { + # We define a function instead of a variable, as variables could + # be predefined in the environment (in theory) + eval "gluon_feature_nodefault_$1() { + : + }" +} + +packages() { + : +} + +for f in package/features packages/*/features; do + . "$f" +done + + +# Shell variables can't contain minus signs, so we escape them +# using underscores (and also escape underscores to avoid mapping +# multiple inputs to the same output) +sanitize() { + local v="$1" + v="${v//_/_1}" + v="${v//-/_2}" + echo -n "$v" +} + +vars= + +for feature in $1; do + if [ "$(type -t gluon_feature_nodefault_${feature})" != 'function' ]; then + echo "gluon-${feature}" + fi + + vars="$vars $(sanitize "$feature")=1" +done + + +nodefault() { + : +} + +packages() { + local cond="$(sanitize "$1")" + shift + + # We only allow variable names, parentheses and the operators: & | ! + if [ "$(expr match "$cond" '.*[^A-Za-z0-9_()&|! ].*')" -gt 0 ]; then + exit 1 + fi + + # Let will return false when the result of the passed expression is 0, + # so we always add 1. This way false is only returned for syntax errors. + local ret="$(env -i $vars bash --norc -ec "let _result_='1+($cond)'; echo -n \"\$_result_\"" 2>/dev/null)" + case "$ret" in + 2) + for pkg in "$@"; do + echo "$pkg" + done + ;; + 1) + ;; + *) + exit 1 + esac +} + +for f in package/features packages/*/features; do + . "$f" +done