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

Eli Schwartz eschwartz at archlinux.org
Sun Mar 8 21:46:55 UTC 2020


On 3/8/20 4:04 PM, Carson Black wrote:
> 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.

Did you actually try this?

It's worth noting that makepkg forbids depends/provides names that
contain characters outside of the following permitted range:

[[:alnum:]+_. at -]

So if I added this to an existing PKGBUILD:

depends=("cmake(KF5Foo)")

then run

$ makepkg --printsrcinfo
==> ERROR: depends contains invalid characters: '()'

If the idea is to use these in PKGBUILDs, then we would need an update
to libmakepkg/lint_pkgbuild/pkgname.sh.in

So if this patch were to be accepted as-is, one would still NOT be able
to use the automatically generated provides for packaging.

And it feels very wrong to generate provides metadata that is forbidden
from being used.

> 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}`"

As I mentioned before, it's bad practice to use the deprecated ``
syntax, modern shellscripts should always use $()

> +			export PKG_CONFIG_PATH="$DIR:$DIR/../../share/pkgconfig"
> +			pkg-config --print-provides "$1" 2>/dev/null | while read name _ _; do

What is the "$DIR" variable?

Why are you exporting PKG_CONFIG_PATH instead of defining it only for
the pkg-config execution?

Why is stderr being redirected to /dev/null? pkg-config exits 1 without
output, if a nonexistent dependency is specified, so the while loop will
exit without running the "do" section even once..

pkg-config is part of archlinux's base-devel group, but pacman isn't
archlinux exclusive. If you want to add a dependency on an external
program, it should at least be documented in the beginning of makepkg.sh.in:

# makepkg uses quite a few external programs during its execution. You
# need to have at least the following installed for makepkg to function:

And it might be a good idea to add a check in libmakepkg/executable/ as
well.

> +				[ -n "$name" ] || continue

When will this be empty?

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

Why do either of these conditions matter if the file provides a
mimetype? Surely that check alone is sufficient?

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

What is "cleaned", and how is the xargs program "cleaning" it?

> +
> +				[[ -z "$cleaned" ]] && continue

When will this be set but empty?

> +				echo "can-open-mimetype($cleaned)"
> +			done
> +			;;
> +	esac
> +}
> +
> +providers=(
> +	pkgconfig cmake desktop
> +)
> +
> +find_fileattrs_provides() {
> +	local files
> +
> +	mapfile -t files < <(find "$pkgdir" -type f)

Instead of storing it in a "files" variable first, I'd actually
recommend using a while loop...

> +
> +	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 ;;

This is unreproducible, the public metadata of the package now depends
on command-line options.

Also, no official repository packages would be built with manual
command-line options, so you'd never be able to use the results. How
does this help you if it only applies to packages you personally build?

>  		-o|--nobuild)     BUILDPKG=0 NOBUILD=1 ;;
>  		-p)               shift; BUILDFILE=$1 ;;
>  		--packagelist)    BUILDPKG=0 PACKAGELIST=1 IGNOREARCH=1;;
> 


-- 
Eli Schwartz
Bug Wrangler and Trusted User

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 1601 bytes
Desc: OpenPGP digital signature
URL: <https://lists.archlinux.org/pipermail/pacman-dev/attachments/20200308/ad67cc8f/attachment-0001.sig>


More information about the pacman-dev mailing list