From f629b71a0f8084f42baa235546dfaade4a7a7b97 Mon Sep 17 00:00:00 2001 From: David Bauer <mail@david-bauer.net> Date: Sun, 5 Nov 2023 16:08:07 +0100 Subject: [PATCH] build: implement flexible site-selection system Implement a flexible system for handling site-defined features as well as packages. This system is inspired by the existing feature-system and allows for a more flexible approach for selecting specific packages for devices. Features are now defined in a `features` file in the site-root. The same goes for packages. These files are sequentially evaluated and the device-package list is evaluated for each device independently. Signed-off-by: David Bauer <mail@david-bauer.net> --- docs/site-example/image-customization | 20 ++++++ docs/site-example/site.mk | 28 --------- scripts/image_customization_lib.lua | 89 +++++++++++++++++++++++++++ scripts/target_config_lib.lua | 40 +++++++----- 4 files changed, 132 insertions(+), 45 deletions(-) create mode 100644 docs/site-example/image-customization create mode 100644 scripts/image_customization_lib.lua diff --git a/docs/site-example/image-customization b/docs/site-example/image-customization new file mode 100644 index 000000000..709987352 --- /dev/null +++ b/docs/site-example/image-customization @@ -0,0 +1,20 @@ +packages({'iwinfo'}) + +features({ + 'autoupdater', + 'ebtables-filter-multicast', + 'ebtables-filter-ra-dhcp', + 'ebtables-limit-arp', + 'mesh-batman-adv-15', + 'mesh-vpn-fastd', + 'respondd', + 'status-page', + 'web-advanced', + 'web-wizard' +}) + +if not device_class('tiny') then + features({ + 'wireless-encryption-wpa3' + }) +end diff --git a/docs/site-example/site.mk b/docs/site-example/site.mk index 30671b181..e50d404da 100644 --- a/docs/site-example/site.mk +++ b/docs/site-example/site.mk @@ -1,33 +1,5 @@ ## gluon site.mk makefile example -## GLUON_FEATURES -# 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 \ - ebtables-limit-arp \ - mesh-batman-adv-15 \ - mesh-vpn-fastd \ - respondd \ - status-page \ - web-advanced \ - web-wizard - -GLUON_FEATURES_standard := \ - wireless-encryption-wpa3 - -## GLUON_SITE_PACKAGES -# Specify additional Gluon/OpenWrt 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 := iwinfo - ## DEFAULT_GLUON_RELEASE # version string to use for images # gluon relies on diff --git a/scripts/image_customization_lib.lua b/scripts/image_customization_lib.lua new file mode 100644 index 000000000..6808383d8 --- /dev/null +++ b/scripts/image_customization_lib.lua @@ -0,0 +1,89 @@ +local M = {} + +local function collect_keys(t) + local ret = {} + for v in pairs(t) do + table.insert(ret, v) + end + return ret +end + +local function evaluate_device(files, env, dev) + local selections = {} + local funcs = {} + local device_disabled = false + + local function add_elements(element_type, element_list) + for _, element in ipairs(element_list) do + if not selections[element_type] then + selections[element_type] = {} + end + + selections[element_type][element] = true + end + end + + function funcs.features(features) + add_elements('feature', features) + end + + function funcs.packages(packages) + add_elements('package', packages) + end + + function funcs.device(device_names) + assert( + type(device_names) == 'table', + 'Incorrect use of device(): pass a list of device names as argument') + + for _, device_name in ipairs(device_names) do + if device_name == dev.image then + return true + end + end + + return false + end + + function funcs.target(target, subtarget) + assert( + type(target) == 'string', + 'Incorrect use of target(): pass a target name as first argument') + + if target ~= env.BOARD then + return false + end + + if subtarget and subtarget ~= env.SUBTARGET then + return false + end + + return true + end + + function funcs.device_class(class) + return dev.options.class == class + end + + -- Evaluate the feature definition files + for _, file in ipairs(files) do + local f, err = loadfile(file) + if not f then + error('Failed to parse feature definition: ' .. err) + end + setfenv(f, funcs) + f() + end + + return { + selections = selections, + device_disabled = device_disabled, + } +end + +function M.get_selection(selection_type, files, env, dev) + local eval_result = evaluate_device(files, env, dev) + return collect_keys(eval_result.selections[selection_type] or {}) +end + +return M diff --git a/scripts/target_config_lib.lua b/scripts/target_config_lib.lua index ef487f061..6895e9c2b 100644 --- a/scripts/target_config_lib.lua +++ b/scripts/target_config_lib.lua @@ -1,5 +1,6 @@ local lib = dofile('scripts/target_lib.lua') local feature_lib = dofile('scripts/feature_lib.lua') +local image_customization_lib = dofile('scripts/image_customization_lib.lua') local env = lib.env local target = env.GLUON_TARGET @@ -86,10 +87,6 @@ END_MAKE lib.escape(var))) end -local function site_packages(image) - return split(site_vars(string.format('$(GLUON_%s_SITE_PACKAGES)', image))) -end - local function feature_packages(features) local files = {'package/features'} for _, feed in ipairs(feeds) do @@ -102,20 +99,30 @@ local function feature_packages(features) return feature_lib.get_packages(files, features) end --- This involves running a few processes to evaluate site.mk, so we add a simple cache -local class_cache = {} -local function class_packages(class) - if class_cache[class] then - return class_cache[class] - end +local function site_specific_packages(dev_info) + local image_custoization = env.GLUON_SITEDIR .. '/image-customization' + + local site_packages = {} + local feature_inherited_pkgs = {} - local features = site_vars(string.format('$(GLUON_FEATURES) $(GLUON_FEATURES_%s)', class)) - features = compact_list(split(features), false) + if file_exists(image_custoization) then + local site_features + + -- First read enabled features from site + site_features = image_customization_lib.get_selection('feature', {image_custoization}, env, dev_info) + site_features = compact_list(site_features, false) + + -- Create List from packages inherited from features + feature_inherited_pkgs = feature_packages(site_features) + + -- Read list of packages from site + site_packages = image_customization_lib.get_selection('package', {image_custoization}, env, dev_info) + end - local pkgs = feature_packages(features) - pkgs = concat_list(pkgs, split(site_vars(string.format('$(GLUON_SITE_PACKAGES) $(GLUON_SITE_PACKAGES_%s)', class)))) + -- Concat feature-packages with site-packages + local pkgs = concat_list(feature_inherited_pkgs, site_packages) - class_cache[class] = pkgs + -- Negations for the resulting package-list are dealt with in the calling function return pkgs end @@ -192,9 +199,8 @@ for _, dev in ipairs(lib.devices) do end handle_pkgs(lib.target_packages) - handle_pkgs(class_packages(dev.options.class)) handle_pkgs(dev.options.packages or {}) - handle_pkgs(site_packages(dev.image)) + handle_pkgs(site_specific_packages(dev)) local profile_config = string.format('%s_DEVICE_%s', openwrt_config_target, dev.name) lib.config( -- GitLab