new file mode 100644
@@ -0,0 +1,21 @@
+# Common class for customization packages, used by dpkg-customization.bbclass
+# and image-customizations.bbclass.
+
+LIST_VARIABLES += "CUSTOMIZATIONS CUSTOMIZATION_VARS CUSTOMIZATION_VARS_PREFIXED"
+
+CUSTOMIZATIONS ?= ""
+CUSTOMIZATIONS[doc] = "List of customization packages to be installed in images."
+
+CUSTOMIZATION_VARS ?= "${DISTRO} ${MACHINE}"
+CUSTOMIZATION_VARS[doc] = "List of variables that should be added to customization package names."
+CUSTOMIZATION_VARS_IMAGE ?= "${IMAGE}"
+CUSTOMIZATION_VARS:append = " ${@d.getVar('CUSTOMIZATION_VARS_IMAGE') if d.getVar('CUSTOMIZATION_FOR_IMAGES').strip() else ''}"
+
+CUSTOMIZATION_VARS_PREFIXED ?= "${DISTRO}"
+CUSTOMIZATION_VARS_PREFIXED[doc] = "List of variables from CUSTOMIZATION_VARS that should be prefixed rather than suffixed to customization package names."
+
+CUSTOMIZATION_FOR_IMAGES ?= ""
+CUSTOMIZATION_FOR_IMAGES[doc] = "List of images that should install the customizations in CUSTOMIZATIONS"
+
+CUSTOMIZATION_PREFIX ?= "${@'-'.join(var for var in d.getVar('CUSTOMIZATION_VARS').split() if var in d.getVar('CUSTOMIZATION_VARS_PREFIXED'))}"
+CUSTOMIZATION_SUFFIX ?= "${@'-'.join(var for var in d.getVar('CUSTOMIZATION_VARS').split() if var not in d.getVar('CUSTOMIZATION_VARS_PREFIXED'))}"
new file mode 100644
@@ -0,0 +1,37 @@
+inherit dpkg-raw customization-base
+
+PRE_CUSTOMIZATION_PN := "${PN}"
+FILESEXTRAPATHS:prepend := "${THISDIR}/${BPN}:"
+PN =. "${@d.getVar('CUSTOMIZATION_PREFIX') + '-' if d.getVar('CUSTOMIZATION_PREFIX') else ''}"
+PN .= "${@'-' + d.getVar('CUSTOMIZATION_SUFFIX') if d.getVar('CUSTOMIZATION_SUFFIX') else ''}"
+
+BBCLASSEXTEND = "${@' '.join(f'dpkg-customization:{image}' for image in d.getVar('CUSTOMIZATION_FOR_IMAGES').split())}"
+
+python customization_virtclass_handler() {
+ orig_pn = d.getVar('PRE_CUSTOMIZATION_PN')
+
+ d = e.data
+ extend = d.getVar('BBEXTENDCURR') or ''
+ variant = d.getVar('BBEXTENDVARIANT') or ''
+ if extend != 'dpkg-customization' or variant == '':
+ d.appendVar('PROVIDES', f' {orig_pn}')
+ d.setVar('IMAGE', '')
+ return
+
+ vars = (d.getVar('CUSTOMIZATION_VARS', expand=False) or '').split()
+ if '${IMAGE}' not in vars:
+ return
+
+ images = (d.getVar('CUSTOMIZATION_FOR_IMAGES') or '').split()
+ if variant not in images:
+ return
+
+ d.setVar('IMAGE', variant)
+ if not d.getVar('BPN').endswith(f'-{variant}'):
+ d.appendVar('BPN', f'-{variant}')
+ d.appendVar('PROVIDES', f' {orig_pn}-{variant}')
+ d.appendVar('OVERRIDES', f':{variant}')
+}
+addhandler customization_virtclass_handler
+customization_virtclass_handler[eventmask] = "bb.event.RecipePreFinalise"
+
new file mode 100644
@@ -0,0 +1,30 @@
+inherit customization-base
+
+def customization_packages(d):
+ customizations = d.getVar('CUSTOMIZATIONS').split()
+ if not customizations:
+ return ''
+
+ # Use image-specific customization if enabled for this image
+ images = (d.getVar('CUSTOMIZATION_FOR_IMAGES') or '').split()
+ image = d.getVar('BPN')
+ if not images or image not in images:
+ d.setVar('IMAGE', '')
+ else:
+ d.setVar('IMAGE', image)
+
+ prefix = d.getVar('CUSTOMIZATION_PREFIX')
+ if prefix:
+ prefix += '-'
+
+ suffix = d.getVar('CUSTOMIZATION_SUFFIX')
+ if suffix:
+ suffix = '-customization-' + suffix
+ else:
+ suffix = '-customization'
+
+ customizations = [ prefix + package + suffix for package in customizations ]
+ return ' '.join(customizations)
+
+CUSTOMIZATION_PACKAGES = "${@ customization_packages(d) }"
+IMAGE_INSTALL:append = " ${CUSTOMIZATION_PACKAGES}"
@@ -80,6 +80,7 @@ inherit image-tools-extension
inherit image-postproc-extension
inherit image-locales-extension
inherit image-account-extension
+inherit image-customizations
# Extra space for rootfs in MB
ROOTFS_EXTRA ?= "64"