Browse Source

build: introduce GLUON_FEATURES

To reduce the number of packages that need to be listed in
GLUON_SITE_PACKAGES, this adds a new variable GLUON_FEATURES. Sets of
packages are enabled automatically based on the combination of listed
feature flags.

Site-specified package feeds can provide their own feature flag
definitions.
Matthias Schiffer 6 years ago
parent
commit
6ca841bad5
7 changed files with 216 additions and 29 deletions
  1. 7 1
      Makefile
  2. 45 0
      docs/dev/feature-flags.rst
  3. 1 0
      docs/index.rst
  4. 23 23
      docs/site-example/site.mk
  5. 38 5
      docs/user/site.rst
  6. 26 0
      package/features
  7. 76 0
      scripts/features.sh

+ 7 - 1
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)))

+ 45 - 0
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

+ 1 - 0
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

+ 23 - 23
docs/site-example/site.mk

@@ -1,29 +1,29 @@
 ##	gluon site.mk makefile example
 
 ##	GLUON_SITE_PACKAGES
-#		specify Gluon/LEDE packages to include here
-
-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
+#		Specify Gluon features/packages to enable;
+#		Gluon will automatically enable a set of packages
+#		depending on the combination of features listed
+
+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

+ 38 - 5
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

+ 26 - 0
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'

+ 76 - 0
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