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

Eli Schwartz eschwartz at archlinux.org
Sun Mar 8 06:12:22 UTC 2020


On 3/7/20 11:12 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

$ pacman -S pkgfile; pkgfile -u; pkgfile -v KF5whatever.cmake
extra/kwhatever 1.0-1     /usr/lib/cmake/KF5Whatever/KF5Whatev
erConfig.cmake

Alternatively, pacman -Fy; pacman -F provides similar functionality to
pkgfile, these days.

> - 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)
>   * can-open-file-extension(filetype)
> 
> 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.

I consider this rpm functionality to be an anti-feature, there are far
too many ways to depend on the exact same thing and none of it is
opt-in. Also, given there are very simple, intuitive tools like pkgfile
or pacman -F, I don't see why such complexity is needed.

> Signed-off-by: Carson Black <uhhadd at gmail.com>
> ---
>  scripts/makepkg.sh.in | 108 ++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 108 insertions(+)
> 
> diff --git a/scripts/makepkg.sh.in b/scripts/makepkg.sh.in
> index ca3e7459..10703a91 100644
> --- a/scripts/makepkg.sh.in
> +++ b/scripts/makepkg.sh.in
> @@ -520,6 +520,111 @@ find_libdepends() {
>  	(( ${#libdepends[@]} )) && printf '%s\n' "${libdepends[@]}"
>  }
>  
> +pkgconfig_fileattr() {
> +	case $1 in
> +		--generate-provides)
> +			case $2 in
> +				*.pc)
> +					directory="`dirname ${2}`"
> +					export PKG_CONFIG_PATH="$DIR:$DIR/../../share/pkgconfig"
> +					pkg-config --print-provides "$2" 2>/dev/null | while read name _ _; do
> +						[ -n "$name" ] || continue
> +						echo "pkgconfig($name)"
> +					done
> +					;;
> +			esac
> +			;;
> +		--generate-depends)
> +			;;
> +	esac
> +}
> +
> +cmake_fileattr() {
> +	case $1 in
> +		--generate-provides)
> +			case $2 in
> +				*.cmake)
> +					base="$(basename $2)"
> +					case $2 in
> +						*Config.cmake)
> +							echo "cmake(${base%Config.cmake})"
> +							;;
> +						*-config.cmake)
> +							echo "cmake(${base%-config.cmake})"
> +							;;
> +					esac
> +					;;
> +			esac
> +			;;
> +		--generate-depends)
> +			;;
> +	esac
> +}
> +
> +desktop_file_attr() {
> +	case $1 in
> +		--generate-provides)
> +			case "$2" in
> +				*.desktop)
> +					grep -q "Type=Application" "$2" || return
> +					grep -q "Exec=" "$2" || return
> +					base="$(basename $2)"
> +					echo "app(${base%.desktop})"
> +
> +					IFS=';'
> +					mimetypes=`grep MimeType= $2 | cut -d '=' -f 2`
> +					for mimetype in $mimetypes; do
> +						echo "can-open-mimetype($mimetype)"
> +
> +						IFS=' '

... did you really just overwrite IFS (twice), then not restore it?

> +						filetypes=`grep -v ^\# /etc/mime.types | grep $mimetype | cut -d ' ' | xargs`
> +						for filetype in $filetypes; do

That is a lot of grep and sed, using a deprecated shell construct (you
should never use ``, always use $() instead) and if you're going to
build a list of multiple things then iterate over it, use a real
datatype like arrays, then iterate over "${filetypes[@]}" -- and mind
your quoting.

This would mean you no longer overwrote IFS -- using arrays means you'd
have to properly split them upfront (mapfile has options to specify
custom delimiters, so that works too).

> +							echo "can-open-file-extension($filetype)"
> +						done
> +					done
> +					;;
> +			esac
> +			;;
> +		--generate-depends)
> +			;;
> +	esac
> +}
> +
> +check_pkg_dir() {
> +	if [[ ! -d $pkgdir ]]; then
> +		error "$(gettext "Missing %s directory.")" "\$pkgdir/"
> +		plain "$(gettext "Aborting...")"
> +		exit $E_MISSING_PKGDIR
> +	fi
> +}

I don't even understand what the purpose of this function is supposed to
be, since $pkgdir is a directory created by makepkg and cannot
(normally) fail to exist.

In the event that a user-created PKGBUILD deletes it, create_package()
performs the exact same check after all user-provided functions are run,
after which it cd's into that directory, and then, from within that
directory, executes the write_pkginfo function which you want to extend.

> +find_fileattrs_provides() {
> +	check_pkg_dir
> +
> +	for file in `find $pkgdir`; do

There is a lot of existing, example code in makepkg which operates on
all files in the pkgdir. It is completely unacceptable to do
word-splitting like this, because filenames can and do contain
whitespace. There are 143 existing packages in the official repositories
that will die on this, including core/linux-firmware and... cmake itself
(in /usr/share/cmake-3.16/Help/generator/).

The common bash programming methodology for iterating over filenames
looks like this:

while IFS= read -rd '' filename; do
    ...
done < <(find "$pkgdir" -type f -print0)


Note also that "$pkgdir" itself must be quoted.

> +		for function in $(declare -F); do

... no. Just no.

Under no circumstances will we be iterating over every shell function
*ever* and checking if they exist. You know exactly which ones exist,
run them manually. This is far, far too leaky as-is.

Another method would be as demonstrated in scripts/libmakepkg/tidy.sh by
registering functions in the arrays tidy_remove and tidy_modify. This is
the pluginized approach to extending something.

> +			case $function in
> +				*_fileattr)
> +					$function --generate-provides $file
> +					;;
> +			esac
> +		done
> +	done
> +}
> +
> +find_fileattrs_depends() {
> +	check_pkg_dir
> +
> +	for file in `find $pkgdir`; do
> +		for function in $(declare -F); do
> +			case $function in
> +				*_fileattr)
> +					$function --generate-depends $file
> +					;;
> +			esac
> +		done
> +	done
> +}

It seems like your proposed code has this being dead functionality?

>  find_libprovides() {
>  	local p libprovides missing
> @@ -613,6 +718,9 @@ write_pkginfo() {
>  	mapfile -t provides < <(find_libprovides)
>  	mapfile -t depends < <(find_libdepends)
>  
> +	mapfile -t provides < <(find_fileattrs_provides)
> +	mapfile -t depends < <(find_fileattrs_depends)

This seems like you are deleting all existing provides and depends, and
then recreating them with only the autogenerated fileattrs ones?

If you look at the existing find_lib* functions, they actually rebuild
the existing provides/depends arrays, taking explicit care to return the
existing ones, but in-place modifying any "libfoo.so" style dependencies
with additional versioning metadata.

Perhaps you meant to append, not overwrite? You could start adding at
the end like this: mapfile -t -O ${#provides[@]} provides

>  	write_kv_pair "license"     "${license[@]}"
>  	write_kv_pair "replaces"    "${replaces[@]}"
>  	write_kv_pair "group"       "${groups[@]}"
> 


-- 
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/3790bf24/attachment.sig>


More information about the pacman-dev mailing list