[pacman-dev] [PATCH] Provides generation for files (a.k.a. rpm fileattrs for makepkg)

Carson Black uhhadd at gmail.com
Sun Mar 8 20:04:19 UTC 2020


Whenever I'm working in a relatively fresh Arch environment, my work
flow looks like this:

- cmake .. -DFLAGS=blahblahblah
- Wait a few seconds
- Package providing KF5whatever not found
- Try and deduce Arch package name from CMake package name

Often, trying to figure out the relation between the build system package names
and the Arch package names of missing dependencies ends up being the longest
part of whatever task I was working on, which isn't very efficient.

Compare this to me working in an RPM distro:

- cmake .. -DFLAGS=blahblahblah
- Wait a few seconds
- Package providing KF5whatever not found
- dnf install cmake(KF5whatever)

The latter workflow is a lot more efficient, and is exactly what this
commit introduces to packages generated by makepkg.

Every file is iterated over at the end of the build process to generate
additional provides without the packager needing to manually specify
them.

The code is architected in a manner designed to make it trivial to add
new provider autogenerators.

So far, there are autogenerated providers for:
- pkgconfig(package)
- cmake(package)
- desktop files
  * app(desktopfilename)
  * can-open-mimetype(mimetype)

While these automatically generated provides can be used for packaging,
this is intended more for interactive usage rather than an attempt to
change how Arch packaging works.

Signed-off-by: Carson Black <uhhadd at gmail.com>
---
 scripts/makepkg.sh.in | 71 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 70 insertions(+), 1 deletion(-)

diff --git a/scripts/makepkg.sh.in b/scripts/makepkg.sh.in
index ca3e7459..65a3648e 100644
--- a/scripts/makepkg.sh.in
+++ b/scripts/makepkg.sh.in
@@ -57,6 +57,7 @@ CLEANUP=0
 DEP_BIN=0
 FORCE=0
 GENINTEG=0
+GENPROVIDES=1
 HOLDVER=0
 IGNOREARCH=0
 INFAKEROOT=0
@@ -520,6 +521,70 @@ find_libdepends() {
 	(( ${#libdepends[@]} )) && printf '%s\n' "${libdepends[@]}"
 }
 
+pkgconfig_prov() {
+	case $1 in
+		*.pc)
+			directory="`dirname ${1}`"
+			export PKG_CONFIG_PATH="$DIR:$DIR/../../share/pkgconfig"
+			pkg-config --print-provides "$1" 2>/dev/null | while read name _ _; do
+				[ -n "$name" ] || continue
+				echo "pkgconfig($name)"
+			done
+			;;
+	esac
+}
+
+cmake_prov() {
+	case $1 in
+		*.cmake)
+			base="$(basename $1)"
+			case $1 in
+				*Config.cmake)
+					echo "cmake(${base%Config.cmake})"
+					;;
+				*-config.cmake)
+					echo "cmake(${base%-config.cmake})"
+					;;
+			esac
+			;;
+	esac
+}
+
+desktop_prov() {
+	case "$1" in
+		*.desktop)
+			grep -q "Type=Application" "$1" || return
+			grep -q "Exec=" "$1" || return
+			base="$(basename $1)"
+			echo "app(${base%.desktop})"
+
+			mapfile -t -d ";" mimetypes < <(grep MimeType= $1 | cut -d '=' -f 2)
+			for mimetype in "${mimetypes[@]}"; do
+				cleaned=$(echo $mimetype | xargs)
+
+				[[ -z "$cleaned" ]] && continue
+
+				echo "can-open-mimetype($cleaned)"
+			done
+			;;
+	esac
+}
+
+providers=(
+	pkgconfig cmake desktop
+)
+
+find_fileattrs_provides() {
+	local files
+
+	mapfile -t files < <(find "$pkgdir" -type f)
+
+	for file in "${files[@]}"; do
+		for function in "${providers[@]}"; do
+			"$function"_prov "$file"
+		done
+	done
+}
 
 find_libprovides() {
 	local p libprovides missing
@@ -612,12 +677,14 @@ write_pkginfo() {
 
 	mapfile -t provides < <(find_libprovides)
 	mapfile -t depends < <(find_libdepends)
+	(( "$GENPROVIDES" != 0 )) && mapfile -t generated_provides < <(find_fileattrs_provides)
 
 	write_kv_pair "license"     "${license[@]}"
 	write_kv_pair "replaces"    "${replaces[@]}"
 	write_kv_pair "group"       "${groups[@]}"
 	write_kv_pair "conflict"    "${conflicts[@]}"
 	write_kv_pair "provides"    "${provides[@]}"
+	write_kv_pair "provides"    "${generated_provides[@]}"
 	write_kv_pair "backup"      "${backup[@]}"
 	write_kv_pair "depend"      "${depends[@]}"
 	write_kv_pair "optdepend"   "${optdepends[@]//+([[:space:]])/ }"
@@ -980,6 +1047,7 @@ usage() {
 	printf -- "$(gettext "  --nocheck        Do not run the %s function in the %s")\n" "check()" "$BUILDSCRIPT"
 	printf -- "$(gettext "  --noprepare      Do not run the %s function in the %s")\n" "prepare()" "$BUILDSCRIPT"
 	printf -- "$(gettext "  --nosign         Do not create a signature for the package")\n"
+	printf -- "$(gettext "  --noprovidesgen  Do not autogenerate provides")\n"
 	printf -- "$(gettext "  --packagelist    Only list package filepaths that would be produced")\n"
 	printf -- "$(gettext "  --printsrcinfo   Print the generated SRCINFO and exit")\n"
 	printf -- "$(gettext "  --sign           Sign the resulting package with %s")\n" "gpg"
@@ -1029,7 +1097,7 @@ OPT_LONG=('allsource' 'check' 'clean' 'cleanbuild' 'config:' 'force' 'geninteg'
           'help' 'holdver' 'ignorearch' 'install' 'key:' 'log' 'noarchive' 'nobuild'
           'nocolor' 'nocheck' 'nodeps' 'noextract' 'noprepare' 'nosign' 'packagelist'
           'printsrcinfo' 'repackage' 'rmdeps' 'sign' 'skipchecksums' 'skipinteg'
-          'skippgpcheck' 'source' 'syncdeps' 'verifysource' 'version')
+          'skippgpcheck' 'source' 'syncdeps' 'verifysource' 'version' 'noprovidesgen')
 
 # Pacman Options
 OPT_LONG+=('asdeps' 'noconfirm' 'needed' 'noprogressbar')
@@ -1070,6 +1138,7 @@ while true; do
 		--nocheck)        RUN_CHECK='n' ;;
 		--noprepare)      RUN_PREPARE='n' ;;
 		--nosign)         SIGNPKG='n' ;;
+		--noprovidesgen)  GENPROVIDES=0 ;;
 		-o|--nobuild)     BUILDPKG=0 NOBUILD=1 ;;
 		-p)               shift; BUILDFILE=$1 ;;
 		--packagelist)    BUILDPKG=0 PACKAGELIST=1 IGNOREARCH=1;;
-- 
2.25.1


More information about the pacman-dev mailing list