[openwrt/openwrt] build: fix apk packaging and ABI-versioning

LEDE Commits lede-commits at lists.infradead.org
Mon Dec 22 01:44:49 PST 2025


robimarko pushed a commit to openwrt/openwrt.git, branch main:
https://git.openwrt.org/18029977f65e11bafaad501399ad42a66d3baa10

commit 18029977f65e11bafaad501399ad42a66d3baa10
Author: George Sapkin <george at sapk.in>
AuthorDate: Thu Nov 20 01:50:03 2025 +0200

    build: fix apk packaging and ABI-versioning
    
    The updated logic for the APK dependencies and provides is as follows:
    
    - If ABI version is defined:
      - package is named `package_name-ABI_version`
      - package implicitly provides
        `package_name-ABI_version=package_version`
        this implies that only one version of a package per ABI can be
        installed at the same time
      - additionally provide `package_name` so multiple packages can be
        looked up by its base name
      - for each `provides`, provide `provide-ABI_version=package_version`
        this implies that only one version of a provide can be installed at
        the same time
    
    - else if ABI version is _not_ defined
      - package is named `package_name`
      - package implicitly provides `package_name=package_version`
        this implies that only one version of a package can be installed at
        the same time
      - if `alternatives` is defined
        - for each `provides`, provide `provide`
          this implies that multiple versions of a provide can be installed
          at the same time
      - else if `alternatives` is _not_ defined
        - for each `provides`, provide `provide=package_version`
          this implies that only one version of a provide can be installed
          at the same time
    
    Both cases a package can be looked up by its base name.
    
    ABI version `alternatives`, `conffiles`, `conffiles_static`, `list` and
    `rusers` files so multiple versions of the same ABI package can be
    installed side by side, and so they don't overwrite each other's
    packaging files.
    
    ABI version `EXTRA_DEPENDS` so dependencies can be correctly looked up
    using the existing OpenWrt semantics without the ABI specified. This is
    needed since ABI-versioned libraries no longer provide
    `package_name=package_version`, so that they can be installed side by
    side.
    
    Remove duplicate dependencies when `EXTRA_DEPENDS` specifies a versioned
    one that is already in `DEPENDS`.
    
    ABI is defined
    ------------------------------------------------------------------------
    
    `libsqlite3` has `PROVIDES` set to `libfake` and has two different ABI
    versions installed. `libfake` is just an example to demonstrate the
    mechanics, as the library can already be depended upon using e.g.
    `libsqlite3-0=3.51.0-r1`. Note the ABI-versioned lists.
    
    ```
    root at OpenWrt:/tmp# apk add --allow-untrusted ./libsqlite3-0-3.51.0-r1.apk
    (1/1) Installing libsqlite3-0 (3.51.0-r1)
    libsqlite3-0-3.51.0-r1.post-install: Executing script...
    OK: 22 MiB in 157 packages
    
    root at OpenWrt:/tmp# apk add --allow-untrusted ./libsqlite3-1-4.00.0-r1.apk
    (1/1) Installing libsqlite3-1 (4.00.0-r1)
    libsqlite3-1-4.00.0-r1.post-install: Executing script...
    OK: 23 MiB in 158 packages
    
    root at OpenWrt:/tmp# apk query --fields name,version,contents,provides libsqlite3-0 libsqlite3-1
    Name: libsqlite3-0
    Version: 3.51.0-r2
    Provides: libfake-0=3.51.0-r2 libsqlite3
    Contents:
      lib/apk/packages/libsqlite3-0.list
      usr/lib/libsqlite3.so.0
      usr/lib/libsqlite3.so.3.51.0
    
    Name: libsqlite3-1
    Version: 4.00.0-r1
    Provides: libfake-1=4.00.0-r1 libsqlite3
    Contents:
      lib/apk/packages/libsqlite3-1.list
      usr/lib/libsqlite3.so.1
      usr/lib/libsqlite3.so.4.00.0
    
    root at OpenWrt:/tmp# ls -lh /usr/lib/libsqlite3.so.*
    lrwxrwxrwx    1 root     root          20 Nov 20 00:23 /usr/lib/libsqlite3.so.0 -> libsqlite3.so.3.51.0
    lrwxrwxrwx    1 root     root          20 Nov 20 00:27 /usr/lib/libsqlite3.so.1 -> libsqlite3.so.4.00.0
    -rwxr-xr-x    1 root     root        1.0M Nov  6 18:19 /usr/lib/libsqlite3.so.3.51.0
    -rwxr-xr-x    1 root     root        1.0M Nov  6 18:19 /usr/lib/libsqlite3.so.4.00.0
    ```
    
    ABI is not defined
    ------------------------------------------------------------------------
    
    Both `avahi-dbus-daemon` and `avahi-nodbus-daemon` provide `avahi-daemon`,
    but have no ABI specified. This results in `avahi-daemon=0.8-r11` provides
    for both packages and only one being able to be installed at the same time:
    
    ```
    root at OpenWrt:/tmp# apk add --allow-untrusted ./avahi-nodbus-daemon-0.8-r11.apk
    (1/4) Installing libavahi-nodbus-support (0.8-r10)
    libavahi-nodbus-support-0.8-r10.post-install: Executing script...
    (2/4) Installing libdaemon (0.14-r5)
    libdaemon-0.14-r5.post-install: Executing script...
    (3/4) Installing libexpat (2.7.3-r1)
    libexpat-2.7.3-r1.post-install: Executing script...
    (4/4) Installing avahi-nodbus-daemon (0.8-r11)
    avahi-nodbus-daemon-0.8-r11.post-install: Executing script...
    23 MiB in 160 packages
    
    root at OpenWrt:/tmp# apk query --fields provides avahi-nodbus-daemon
    Provides: avahi-daemon=0.8-r11
    
    root at OpenWrt:/tmp# apk add --allow-untrusted ./avahi-dbus-daemon-0.8-r11.apk
    ERROR: unable to select packages:
      avahi-dbus-daemon-0.8-r11:
        conflicts: avahi-nodbus-daemon-0.8-r11[avahi-daemon=0.8-r11]
        satisfies: world[avahi-dbus-daemon><Q1R111s+ke9Vf+eCxDHX2BZVUK54Q=]
      avahi-nodbus-daemon-0.8-r11:
        conflicts: avahi-dbus-daemon-0.8-r11[avahi-daemon=0.8-r11]
        satisfies: world[avahi-nodbus-daemon><Q1BAu7nLI2MgRabpveLTGO2ksQz7E=]
    ```
    
    Provides and alternatives
    ------------------------------------------------------------------------
    
    Both `uclient-fetch` and `wget-nossl` provide `wget` and specify
    alternatives, so provides are not versioned and both packages can be
    installed at the same time:
    
    ```
    root at OpenWrt:/tmp# apk query --fields name,version,contents,provides uclient-fetch wget-nossl
    Name: uclient-fetch
    Version: 2025.10.03~dc909ca7-r1
    Provides: wget
    Contents:
      bin/uclient-fetch
      lib/apk/packages/uclient-fetch.alternatives
      lib/apk/packages/uclient-fetch.list
    
    Name: wget-nossl
    Version: 1.25.0-r1
    Provides: gnu-wget wget
    Contents:
      lib/apk/packages/wget-nossl.alternatives
      lib/apk/packages/wget-nossl.list
      usr/libexec/wget-nossl
    
    ```
    
    Fixes: https://github.com/openwrt/openwrt/issues/20582
    Fixes: https://github.com/openwrt/openwrt/issues/20802
    Signed-off-by: George Sapkin <george at sapk.in>
    Link: https://github.com/openwrt/openwrt/pull/20819
    Signed-off-by: Robert Marko <robimarko at gmail.com>
---
 include/package-pack.mk | 79 +++++++++++++++++++++++++++++++++++--------------
 1 file changed, 57 insertions(+), 22 deletions(-)

diff --git a/include/package-pack.mk b/include/package-pack.mk
index c989fdb116..980fe29f69 100644
--- a/include/package-pack.mk
+++ b/include/package-pack.mk
@@ -199,11 +199,49 @@ endif
 	$(call locked,$(CP) $(PKG_BUILD_DIR)/.pkgdir/$(1)/. $(STAGING_DIR_ROOT)/,root-copy)
 	touch $$@
 
-    Package/$(1)/DEPENDS := $$(call mergelist,$$(foreach dep,$$(filter-out @%,$$(IDEPEND_$(1))),$$(dep)$$(call GetABISuffix,$$(dep))))
+    Package/$(1)/DEPENDS := $$(foreach dep,$$(filter-out @%,$$(IDEPEND_$(1))),$$(dep)$$(call GetABISuffix,$$(dep)))
     ifneq ($$(EXTRA_DEPENDS),)
-      Package/$(1)/DEPENDS := $$(EXTRA_DEPENDS)$$(if $$(Package/$(1)/DEPENDS),$$(comma) $$(Package/$(1)/DEPENDS))
+      ifeq ($(CONFIG_USE_APK),)
+        Package/$(1)/DEPENDS := $$(call mergelist,$$(Package/$(1)/DEPENDS))
+        Package/$(1)/DEPENDS := $$(EXTRA_DEPENDS)$$(if $$(Package/$(1)/DEPENDS),$$(comma) $$(Package/$(1)/DEPENDS))
+      else
+        _SEP := __COMMA_SEP__
+        _SPACE := __SPACE_SEP__
+        _DEPENDS := $$(Package/$(1)/DEPENDS)
+        _EXTRA_DEPENDS_ABI :=
+        _DEP_ITEMS := $$(subst $$(_SEP),$$(space),$$(subst $$(space),$$(_SPACE),$$(subst $$(comma),$$(_SEP),$$(EXTRA_DEPENDS))))
+
+        $$(foreach dep,$$(_DEP_ITEMS), \
+          $$(eval _CUR_DEP := $$(subst $$(_SPACE),$$(space),$$(strip $$(dep)))) \
+          $$(eval _PKG_NAME := $$(word 1,$$(_CUR_DEP))) \
+          $$(if $$(findstring $$(paren_left), $$(_PKG_NAME)), \
+            $$(error "Unsupported extra dependency format: no space before '(': $$(_CUR_DEP)")) \
+          ) \
+          $$(eval _ABI_SUFFIX := $$(call GetABISuffix,$$(_PKG_NAME))) \
+          $$(eval _PKG_NAME_ABI := $$(_PKG_NAME)$$(_ABI_SUFFIX)) \
+          $$(eval _VERSION_CONSTRAINT := $$(word 2,$$(_CUR_DEP))) \
+          $$(if $$(_VERSION_CONSTRAINT), \
+            $$(eval _EXTRA_DEP := $$(_PKG_NAME_ABI) $$(_VERSION_CONSTRAINT)), \
+            $$(error "Extra dependencies must have version constraints. $$(_PKG_NAME) seems to be unversioned.") \
+          ) \
+          $$(if $$(_EXTRA_DEPENDS_ABI), \
+            $$(eval _EXTRA_DEPENDS_ABI := $$(_EXTRA_DEPENDS_ABI)$$(comma)$$(_EXTRA_DEP)), \
+            $$(eval _EXTRA_DEPENDS_ABI := $$(_EXTRA_DEP)) \
+          ) \
+          $$(if $$(_DEPENDS), \
+            $$(eval _DEPENDS := $$(filter-out $$(_PKG_NAME_ABI),$$(_DEPENDS))) \
+          ) \
+        )
+
+        _DEPENDS := $$(call mergelist,$$(_DEPENDS))
+        Package/$(1)/DEPENDS := $$(_EXTRA_DEPENDS_ABI)$$(if $$(_DEPENDS),$$(comma) $$(_DEPENDS))
+      endif
+    else
+      Package/$(1)/DEPENDS := $$(call mergelist,$$(Package/$(1)/DEPENDS))
     endif
 
+    $$(info $(1) fused dependencies: $$(Package/$(1)/DEPENDS))
+
 $(_define) Package/$(1)/CONTROL
 Package: $(1)$$(ABIV_$(1))
 Version: $(VERSION)
@@ -320,7 +358,7 @@ else
 		echo "[ -s "\$$$${IPKG_INSTROOT}/lib/functions.sh" ] || exit 0"; \
 		echo ". \$$$${IPKG_INSTROOT}/lib/functions.sh"; \
 		echo 'export root="$$$${IPKG_INSTROOT}"'; \
-		echo 'export pkgname="$(1)"'; \
+		echo 'export pkgname="$(1)$$(ABIV_$(1))"'; \
 		echo "add_group_and_user"; \
 		echo "default_postinst"; \
 		[ ! -f $$(ADIR_$(1))/postinst-pkg ] || sed -z 's/^\s*#!/#!/' "$$(ADIR_$(1))/postinst-pkg"; \
@@ -345,34 +383,34 @@ else
 		echo "[ -s "\$$$${IPKG_INSTROOT}/lib/functions.sh" ] || exit 0"; \
 		echo ". \$$$${IPKG_INSTROOT}/lib/functions.sh"; \
 		echo 'export root="$$$${IPKG_INSTROOT}"'; \
-		echo 'export pkgname="$(1)"'; \
+		echo 'export pkgname="$(1)$$(ABIV_$(1))"'; \
 		echo "default_prerm"; \
 		[ ! -f $$(ADIR_$(1))/prerm-pkg ] || sed -z 's/^\s*#!/#!/' "$$(ADIR_$(1))/prerm-pkg"; \
 	) > $$(ADIR_$(1))/pre-deinstall;
 
 	[ ! -f $$(ADIR_$(1))/postrm ] || sed -zi 's/^\s*#!/#!/' "$$(ADIR_$(1))/postrm";
 
-	if [ -n "$(USERID)" ]; then echo $(USERID) > $$(IDIR_$(1))/lib/apk/packages/$(1).rusers; fi;
-	if [ -n "$(ALTERNATIVES)" ]; then echo $(ALTERNATIVES) > $$(IDIR_$(1))/lib/apk/packages/$(1).alternatives; fi;
-	(cd $$(IDIR_$(1)) && find . -type f,l -printf "/%P\n" | sort > $(TMP_DIR)/$(1).list && mv $(TMP_DIR)/$(1).list $$(IDIR_$(1))/lib/apk/packages/$(1).list)
+	if [ -n "$(USERID)" ]; then echo $(USERID) > $$(IDIR_$(1))/lib/apk/packages/$(1)$$(ABIV_$(1)).rusers; fi;
+	if [ -n "$(ALTERNATIVES)" ]; then echo $(ALTERNATIVES) > $$(IDIR_$(1))/lib/apk/packages/$(1)$$(ABIV_$(1)).alternatives; fi;
+	(cd $$(IDIR_$(1)) && find . -type f,l -printf "/%P\n" | sort > $(TMP_DIR)/$(1).list && mv $(TMP_DIR)/$(1).list $$(IDIR_$(1))/lib/apk/packages/$(1)$$(ABIV_$(1)).list)
 	# Move conffiles to IDIR and build conffiles_static with csums
 	if [ -f $$(ADIR_$(1))/conffiles ]; then \
-		mv -f $$(ADIR_$(1))/conffiles $$(IDIR_$(1))/lib/apk/packages/$(1).conffiles; \
-		for file in $$$$(cat $$(IDIR_$(1))/lib/apk/packages/$(1).conffiles); do \
+		mv -f $$(ADIR_$(1))/conffiles $$(IDIR_$(1))/lib/apk/packages/$(1)$$(ABIV_$(1)).conffiles; \
+		for file in $$$$(cat $$(IDIR_$(1))/lib/apk/packages/$(1)$$(ABIV_$(1)).conffiles); do \
 			[ -f $$(IDIR_$(1))/$$$$file ] || continue; \
 			csum=$$$$($(MKHASH) sha256 $$(IDIR_$(1))/$$$$file); \
-			echo $$$$file $$$$csum >> $$(IDIR_$(1))/lib/apk/packages/$(1).conffiles_static; \
+			echo $$$$file $$$$csum >> $$(IDIR_$(1))/lib/apk/packages/$(1)$$(ABIV_$(1)).conffiles_static; \
 		done; \
 	fi
 
 	# Some package (base-files) manually append stuff to conffiles
 	# Append stuff from it and delete the CONTROL directory since everything else should be migrated
 	if [ -f $$(IDIR_$(1))/CONTROL/conffiles ]; then \
-		echo $$$$(IDIR_$(1))/CONTROL/conffiles >> $$(IDIR_$(1))/lib/apk/packages/$(1).conffiles; \
+		echo $$$$(IDIR_$(1))/CONTROL/conffiles >> $$(IDIR_$(1))/lib/apk/packages/$(1)$$(ABIV_$(1)).conffiles; \
 		for file in $$$$(cat $$(IDIR_$(1))/CONTROL/conffiles); do \
 			[ -f $$(IDIR_$(1))/$$$$file ] || continue; \
 			csum=$$$$($(MKHASH) sha256 $$(IDIR_$(1))/$$$$file); \
-			echo $$$$file $$$$csum >> $$(IDIR_$(1))/lib/apk/packages/$(1).conffiles_static; \
+			echo $$$$file $$$$csum >> $$(IDIR_$(1))/lib/apk/packages/$(1)$$(ABIV_$(1)).conffiles_static; \
 		done; \
 		rm -rf $$(IDIR_$(1))/CONTROL/conffiles; \
 	fi
@@ -394,16 +432,13 @@ else
 	  --info "origin:$(SOURCE)" \
 	  --info "url:$(URL)" \
 	  --info "maintainer:$(MAINTAINER)" \
-	  --info "provides:$$(foreach prov,\
-			$$(filter-out $(1)$$(ABIV_$(1)), \
-			$(PROVIDES)$$(if $$(ABIV_$(1)), \
-				$(1)=$(VERSION) $(foreach provide, \
-					$(PROVIDES), \
-					$(provide)$$(ABIV_$(1))=$(VERSION) \
-				) \
-			) \
-		), \
-		$$(prov) )" \
+	  --info "provides:$$(if $$(ABIV_$(1)), \
+	    $(1) $(foreach provide,$(PROVIDES), $(provide)$$(ABIV_$(1))=$(VERSION)), \
+	    $(if $(ALTERNATIVES), \
+	      $(PROVIDES), \
+	      $(foreach provide,$(PROVIDES), $(provide)=$(VERSION)) \
+	    ) \
+	  )" \
 	  $(if $(DEFAULT_VARIANT),--info "provider-priority:100",$(if $(PROVIDES),--info "provider-priority:1")) \
 	  $$(APK_SCRIPTS_$(1)) \
 	  --info "depends:$$(foreach depends,$$(subst $$(comma),$$(space),$$(subst $$(space),,$$(subst $$(paren_right),,$$(subst $$(paren_left),,$$(Package/$(1)/DEPENDS))))),$$(depends))" \




More information about the lede-commits mailing list