Newer
Older
# script to build apk packages (light version of makepkg)
# Copyright (c) 2008 Natanael Copa <natanael.copa@gmail.com>
#
# Distributed under GPL-2
#
# Depends on: busybox utilities, fakeroot,
#
abuild_ver=@VERSION@
sysconfdir=@sysconfdir@
abuild_path=$(readlink -f $0)
BUILD_BASE="build-base"
SUDO=${SUDO:-"sudo"}
FAKEROOT=${FAKEROOT:-"fakeroot"}
ABUILD_CONF=${ABUILD_CONF:-"$sysconfdir/abuild.conf"}
default_colors() {
NORMAL="\033[1;0m"
STRONG="\033[1;1m"
RED="\033[1;31m"
GREEN="\033[1;32m"
YELLOW="\033[1;33m"
BLUE="\033[1;34m"
}
monochrome() {
NORMAL=""
STRONG=""
RED=""
GREEN=""
YELLOW=""
BLUE=""
}
#colors
if [ -n "$USE_COLORS" ]; then
default_colors
fi
# functions
msg() {
local prompt="$GREEN>>>${NORMAL}"
local fake="${FAKEROOTKEY:+${BLUE}*${NORMAL}}"
local name="${STRONG}${subpkgname:-$pkgname}${NORMAL}"
[ -z "$quiet" ] && printf "${prompt} ${name}${fake}: $@\n" >&2
}
warning() {
local prompt="${YELLOW}>>> WARNING:${NORMAL}"
local fake="${FAKEROOTKEY:+${BLUE}*${NORMAL}}"
local name="${STRONG}${subpkgname:-$pkgname}${NORMAL}"
printf "${prompt} ${name}${fake}: $@\n" >&2
}
error() {
local prompt="${RED}>>> ERROR:${NORMAL}"
local fake="${FAKEROOTKEY:+${BLUE}*${NORMAL}}"
local name="${STRONG}${subpkgname:-$pkgname}${NORMAL}"
printf "${prompt} ${name}${fake}: $@\n" >&2
}
if [ -z "$install_after" ] && [ -n "$uninstall_after" ]; then
$SUDO $APK del $uninstall_after
# check if apkbuild is basicly sane
sanitycheck() {
msg "Checking sanity of $APKBUILD..."
[ -z "$pkgname" ] && die "Missing pkgname in APKBUILD"
[ -z "${pkgname##* *}" ] && die "pkgname contains spaces"
[ -z "$pkgver" ] && die "Missing pkgver in APKBUILD"
if [ "$pkgver" != "volatile" ] && [ -z "$nodeps" ]; then
$APK version --check -q "$pkgver" ||\
die "$pkgver is not a valid version"
fi
[ -z "$pkgrel" ] && die "Missing pkgrel in APKBUILD"
[ -z "$pkgdesc" ] && die "Missing pkgdesc in APKBUILD"
[ -z "$url" ] && die "Missing url in APKBUILD"
[ -z "$license" ] && die "Missing license in APKBULID"
for i in $install; do
[ -e "$startdir/$i" ] || die "install script $startdir/$i is missing"
done
[ -n "${triggers%%:*}" ] && [ ! -e "$startdir"/${triggers%%:*} ] \
&& die "trigger script $startdir/${triggers%%:*} is missing"
if [ -n "$source" ]; then
for i in $source; do
if install_has "$i"; then
warning "You should not have \$install in source"
continue
fi
md5sums_has ${i##*/} || die "${i##*/} is missing in md5sums"
case "$i" in
https://*) makedepends_has wget || die "wget must be in makedepends when source has https://" ;;
esac
done
fi
if [ -n "$md5sums" ]; then
for i in $(echo "$md5sums" | awk '{ print $2 }'); do
source_has $i || die "$i exists in md5sums but is missing in source"
done
fi
# common spelling errors
[ -n "$depend" ] && die "APKBUILD contains 'depend'. It should be depends"
[ -n "$makedepend" ] && die "APKBUILD contains 'makedepend'. It should be makedepends"
grep '^# Maintainer:' $APKBUILD >/dev/null || warning "No maintainer"
makedepends_has 'g++' && warning "g++ should not be in makedepends"
return 0
}
md5check() {
if [ -z "$source" ]; then
return 0
fi
if [ -z "$md5sums" ]; then
die "Use 'abuild checksum' to generate/update the checksum(s)"
if [ "$(echo $source | wc -l)" -ne "$(echo $md5sums | wc -l)" ]; then
die "Number of md5sums does not correspond to number of sources"
fi
msg "Checking md5sums..."
cd "$srcdir" && echo "$md5sums" | md5sum -c
local d="${uri##*/}" # $(basename $uri)
local opts
[ -n "$quiet" ] && opts="-q"
[ -f "$SRCDEST/$d" ] && return 0
# we need GNU wget for this
case "$uri" in
https://*) opts="--no-check-certificate";;
esac
if [ -f "$SRCDEST/$d.part" ]; then
msg "Partial download found. Trying to resume"
opts="$opts -c"
wget $opts -O "$SRCDEST/$d.part" "$uri" \
&& mv "$SRCDEST/$d.part" "$SRCDEST/$d"
is_remote() {
case "$1" in
return 0;;
esac
return 1
}
# try download from file from mirror first
uri_fetch_mirror() {
local uri="$1"
local d="${uri##*/}" # $(basename $uri)
if [ -n "$DISTFILES_MIRROR" ]; then
if is_remote "$DISTFILES_MIRROR"; then
uri_fetch "$DISTFILES_MIRROR"/$d && return 0
else
cp "$DISTFILES_MIRROR"/$d "$SRCDEST" && return 0
fi
fi
uri_fetch "$uri"
}
default_fetch() {
for s in $source; do
if is_remote "$s"; then
uri_fetch_mirror "$s" || return 1
ln -sf "$SRCDEST/${s##*/}" "$srcdir"/
else
ln -sf "$startdir/$s" "$srcdir/"
fi
fetch() {
default_fetch
}
default_unpack() {
mkdir -p "$srcdir"
for u in $source; do
local s="$SRCDEST/${u##*/}" # $(basename $s)
case "$s" in
*.tar.lzma)
msg "Unpacking $s..."
unlzma -c "$s" | tar -C "$srcdir" -x \
|| return 1;;
*.tar.xz)
msg "Unpacking $s..."
unxz -c "$s" | tar -C "$srcdir" -x || return 1;;
*.zip)
msg "Unpacking $s..."
unzip "$s" -d "$srcdir" || return 1;;
unpack() {
default_unpack
}
msg "Cleaning temporary build dirs..."
}
# cleanup fetched sources
cleancache() {
local s
for s in $source; do
if is_remote "$s"; then
msg "Cleaning downloaded ${s##*/}..."
rm -f "$SRCDEST/${s##*/}"
fi
getpkgver || return 1
msg "Cleaning built packages..."
for i in $pkgname $subpackages; do
local p="${i%:*}-$pkgver-r$pkgrel"
rm -f "$PKGDEST/$p.apk" "$PKGDEST/$p.src.tar.gz" \
# remove given packages from index
# clean all packages except current
cleanoldpkg() {
local i j
getpkgver || return 1
msg "Cleaning all packages except $pkgver-r$pkgrel..."
for i in $pkgname $subpackages; do
for j in "$PKGDEST"/${i%:*}-[0-9]*.apk; do
[ "$j" != "$PKGDEST/${i%:*}-$pkgver-r$pkgrel.apk" ] \
&& rm -f "$j"
done
done
return 0
}
mkusers() {
local i
for i in $pkgusers; do
if ! getent passwd $i >/dev/null; then
msg "Creating user $i"
$SUDO adduser -D -H $i || return 1
fi
done
for i in $pkggroups; do
if ! getent group $i >/dev/null; then
msg "Creating group $i"
$SUDO addgroup $i || return 1
fi
done
}
[ -n "$DEBUG" ] && msg "$part"
$part || die "$part failed"
}
# override those in your build script
getpkgver() {
# this func is supposed to be overridden by volatile packages
if [ "$pkgver" = "volatile" ]; then
error "Please provide a getpkgver() function in your APKBUILD"
return 1
fi
}
# generate a simple tar.gz package of pkgdir
targz() {
cd "$pkgdir" || return 1
tar -czf "$PKGDEST"/$pkgname-$pkgver-r$pkgrel.tar.gz *
}
get_split_func() {
# get the 'func' from "sub-pkg:func"
local func=${1##*:}
# get 'func' from "sub-pkg-func" if there was no :func
[ "$func" = "$1" ] && func=${func##*-}
echo $func
}
Natanael Copa
committed
prepare_subpackages() {
local func=$(get_split_func $i)
# call abuild recursively, setting subpkg{dir,name}
msg "Running split function $func..."
subpkgdir="$pkgbasedir/${i%:*}" subpkgname="${i%:*}" \
Natanael Copa
committed
$0 $func prepare_package || return 1
Natanael Copa
committed
prepare_metafiles() {
getpkgver || return 1
local name=${subpkgname:-$pkgname}
[ -z "${name##* *}" ] && die "package name contains spaces"
local pkg="$name-$pkgver-r$pkgrel.apk"
local pkginfo="$controldir"/.PKGINFO
[ ! -d "$dir" ] && die "Missing $dir"
cd "$dir"
local builddate=$(date -u "+%s")
local size=$(du -sk | awk '{print $1 * 1024}')
echo "# Generated by $(basename $0) $abuild_ver" >"$pkginfo"
echo "# using $($FAKEROOT -v)" >> "$pkginfo"
echo "# $(date -u)" >> "$pkginfo"
cat >> "$pkginfo" <<EOF
pkgdesc = $pkgdesc
url = $url
builddate = $builddate
packager = ${PACKAGER:-"Unknown"}
size = $size
EOF
local i deps
deps="$depends"
if [ "$pkgname" != "busybox" ] && ! depends_has busbox; then
for i in $install ${triggers%%:*}; do
if head -n 1 "$startdir/$i" | grep '^#!/bin/sh' >/dev/null ; then
msg "Script found. busybox added as a dependency for $pkg"
deps="$deps busybox"
break
fi
done
echo "license = $i" >> "$pkginfo"
echo "replaces = $i" >> "$pkginfo"
for i in $deps; do
echo "depend = $i" >> "$pkginfo"
echo "conflict = $i" >> "$pkginfo"
echo "provides = $i" >> "$pkginfo"
echo "backup = $i" >> "$pkginfo"
if [ -n "$triggers" ]; then
echo "triggers = ${triggers#*:}" >> "$pkginfo"
fi
script=${i#$name}
case "$script" in
.pre-install|.post-install|.pre-upgrade|.post-upgrade|.pre-deinstall|.post-deinstall|.trigger)
*) error "$script: Invalid install/trigger script"
cp "$startdir/$i" "$controldir/$script" || return 1
chmod +x "$controldir/$script"
metafiles="$metafiles $script"
done
echo $metafiles | tr ' ' '\n' > "$controldir"/.metafiles
prepare_tracedeps() {
local dir=${subpkgdir:-$pkgdir}
options_has "!tracedeps" && return 0
find -name '*.so' -o -name '*.so.[0-9]*' | sed 's:.*/::' \
>"$controldir"/.provides-so
scanelf -Rn "$dir" | tr ' ' ':' | awk -F ":" '$1 == "ET_DYN" || $1 == "ET_EXEC" {print $2}' \
| sed 's:,:\n:g' | sort | uniq \
| while read i; do
# only add files that are not self provided
grep "^$i$" "$controldir"/.provides-so >/dev/null \
|| echo $i >> "$controldir"/.needs-so
Natanael Copa
committed
prepare_package() {
msg "Preparing ${subpkgname:+sub}package ${subpkgname:-$pkgname}..."
prepare_metafiles && prepare_tracedeps
Natanael Copa
committed
}
pkginfo_val() {
local key="$1"
local file="$2"
awk -F ' = ' "\$1 == \"$key\" {print \$2}" "$file"
}
trace_apk_deps() {
local name="$1"
local dir="$2"
local i j found autodeps=
msg "Tracing dependencies for $name..."
# add pkgconfig if usr/lib/pkgconfig is found
if [ -d "$pkgbasedir"/$name/usr/lib/pkgconfig ] \
&& ! grep -q '^depend = pkgconfig' "$dir"/.PKGINFO; then
msg "Added pkgconfig as dependency"
autodeps="$autodeps pkgconfig"
fi
# special case for libpthread: we need depend on libgcc
if grep -q -w '^libpthread.so.*' "$dir"/.needs-so 2>/dev/null \
&& ! grep -q -w "^depend = libgcc" "$dir"/.PKGINFO; then
autodeps="$autodeps libgcc"
msg "Added libgcc as dependency due to libpthread"
for i in $(cat "$dir"/.needs-so 2>/dev/null); do
found=
# first check if its provide by same apkbuild
for j in "$dir"/../.control.*/.provides-so; do
grep -w "$i" "$j" >/dev/null || continue
found=${j%/.provides-so}
break
done
# check apk db if not provided by a subpackage
if [ -z "$found" ]; then
found=$($APK info -q -W /lib/$i /usr/lib/$i)
fi
if [ -z "$found" ]; then
error "Could not find dependency for $i"
return 1
fi
if grep -w "^depend = ${found}$" "$dir"/.PKGINFO >/dev/null ; then
warning "You can remove '$found' from depends"
continue
fi
list_has "$found" $autodeps || autodeps="$autodeps $found"
msg "Added '$found' as dependency as it has $i"
[ -z "$autodeps" ] && return 0
echo "# automatically detected:" >> "$dir"/.PKGINFO
for i in $autodeps; do
echo "depend = $i" >> "$dir"/.PKGINFO
done
}
Natanael Copa
committed
create_apks() {
local file
getpkgver || return 1
for file in "$pkgbasedir"/.control.*/.PKGINFO; do
Natanael Copa
committed
local dir="${file%/.PKGINFO}"
local name=$(pkginfo_val pkgname $file)
local ver=$(pkginfo_val pkgver $file)
local datadir="$pkgbasedir"/$name
trace_apk_deps "$name" "$dir" || return 1
msg "Creating $apk..."
Natanael Copa
committed
(
cd "$datadir"
# data.tar.gz
set -- *
if [ "$1" = '*' ]; then
touch .dummy
set -- .dummy
fi
tar -c "$@" | abuild-tar --hash | gzip -9 >"$dir"/data.tar.gz
# append the hash for data.tar.gz
local sha256=$(sha256sum "$dir"/data.tar.gz | cut -f1 -d' ')
echo "datahash = $sha256" >> "$dir"/.PKGINFO
# control.tar.gz
tar -c $(cat "$dir"/.metafiles) | abuild-tar --cut \
| gzip -9 > control.tar.gz
abuild-sign -q control.tar.gz || exit 1
# create the final apk
cat control.tar.gz data.tar.gz > "$PKGDEST"/$apk
)
if ! apk_up2date || [ -n "$force" ]; then
sanitycheck && builddeps && clean && fetch && unpack \
&& prepare && mkusers && rootpkg || return 1
mkdir -p "$abuildrepo" || return 1
cd "$abuildrepo"
# remove broken links
for apk in *.apk; do
if [ -L "$apk" ] && [ ! -f "$apk" ]; then
rm -f "$apk"
fi
done
# create links for this package
ln -sf "$PKGDEST"/$apk "$abuildrepo"/$apk
msg "Updating the cached abuild repository index..."
local sign=".SIGN.RSA.${SIGN_PUBLIC_KEY##*/}"
local oldindex=
if [ -f APKINDEX.tar.gz ]; then
oldindex="--index APKINDEX.tar.gz"
fi
$APK index $oldindex --output APKINDEX.tar.gz.unsigned \
--description "$repo $(cd $startdir && git describe)" \
*.apk || exit 1
msg "Signing the index..."
abuild-sign -q APKINDEX.tar.gz.unsigned || exit 1
mv APKINDEX.tar.gz.unsigned APKINDEX.tar.gz
default_doc() {
depends="$depends_doc"
for i in doc man info html sgml licenses gtk-doc; do
if [ -d "$pkgdir/usr/share/$i" ]; then
mkdir -p "$subpkgdir/usr/share"
mv "$pkgdir/usr/share/$i" "$subpkgdir/usr/share/"
rm -f "$subpkgdir/usr/share/info/dir"
# # compress info and man pages
# find "$subpkgdir/usr/share" \( -name '*.info' -o -name '*.info-[1-9]' \
# -o -name '*.[1-9]' \) -exec gzip {} \;
# remove if empty, ignore error (not empty)
rmdir "$pkgdir/usr/share" "$pkgdir/usr" 2>/dev/null
# [ -d "$subpkgdir/usr/share/man" ] && depends="man"
doc() {
default_doc
}
default_mod() {
depends="$kernel $depends_mod"
if [ -d "$pkgdir/lib/$i" ]; then
rm -rf "$subpkgdir/lib"
mkdir -p "$subpkgdir/lib"
mv "$pkgdir/lib/$i" "$subpkgdir/lib"
mod() {
default_mod
}
default_dev() {
depends="$pkgname $depends_dev"
for i in $origsubpackages; do
[ "${i%:*}" = "$subpkgname" ] || depends="$depends ${i%:*}"
done
for i in usr/include usr/lib/pkgconfig usr/share/aclocal\
usr/share/gettext usr/bin/*-config \
usr/share/vala/vapi usr/share/gir-[0-9]*\
$(find -name include -type d) \
$(find usr/ -name '*.[acho]' -o -name '*.la' \
2>/dev/null); do
if [ -e "$pkgdir/$i" ] || [ -L "$pkgdir/$i" ]; then
d="$subpkgdir/${i%/*}" # dirname $i
rmdir "$pkgdir/${i%/*}" 2>/dev/null
# move *.so links needed when linking the apps to -dev packages
for i in lib/*.so usr/lib/*.so; do
if [ -L "$i" ]; then
mkdir -p "$subpkgdir"/"${i%/*}"
mv "$i" "$subpkgdir/$i" || return 1
fi
done
dev() {
default_dev
}
is_function() {
type "$1" 2>&1 | head -n 1 | egrep -q "is a (shell )?function"
}
local do_build=build
cd "$startdir"
if is_function package; then
build || return 1
[ -n "$FAKEROOT" ] && msg "Entering fakeroot..."
$FAKEROOT -- "$abuild_path" $color_opt $do_build \
prepare_subpackages \
prepare_package \
getpkgver || return 1
local p="$pkgname-$pkgver-$pkgrel"
local i files="$prefix/APKBUILD"
for i in $source; do
files="$files $prefix/${i##*/}"
done
mkdir -p "$PKGDEST"
msg "Creating source package $p.src.tar.gz..."
(cd .. && tar -zcf "$PKGDEST/$p.src.tar.gz" $files)
# check if package is up to date
getpkgver || return 1
local pkg="$PKGDEST/$pkgname-$pkgver-r$pkgrel.apk"
for i in $pkgname $subpackages; do
[ -f "$PKGDEST/$pkgname-$pkgver-r$pkgrel.apk" ] || return 1
done
[ -n "$keep" ] && return 0
for i in $source APKBUILD; do
local s
if is_remote "$i"; then
s="$SRCDEST/${i##*/}" # $(basename $i)
else
s="$startdir/${i##*/}"
fi
if [ "$s" -nt "$pkg" ]; then
return 1
fi
done
return 0
}
getpkgver || return 1
for i in $pkgname $subpackages; do
apk="${i%:*}-$pkgver-r$pkgrel.apk"
[ "$abuildrepo"/APKINDEX.tar.gz -nt "$abuildrepo"/$apk ] || return 1
done
return 0
}
up2date() {
apk_up2date && abuildindex_up2date
}
# rebuild package and abuildrepo index if needed
abuildindex() {
up2date && return 0
update_abuildrepo
# source all APKBUILDs and output:
# 1) origin of package
# 2) all dependencies
# the output is i in a format easy parseable for awk
depparse_aports() {
# lets run this in a subshell since we source all APKBUILD here
(
aportsdir=$(realpath ${APKBUILD%/APKBUILD}/..)
for i in $aportsdir/*/APKBUILD; do
pkgname=
subpackages=
depends=
makedepends=
# filter out conflicts from deps and version info
for j in $depends $makedepends; do
case "$j" in
!*) continue;;
esac
for j in $pkgname $subpackages; do
echo -n "d ${j%%:*} $1"
shift
while [ $# -gt 0 ]; do
echo -n ",$1"
shift
done
echo
local deps= i=
# strip versions from deps
for i in "$@"; do
( depparse_aports
if [ -z "$upgrade" ]; then
# list installed pkgs and prefix with 'i '
$APK info -q | sort | sed 's/^/i /'
) | awk -v pkgs="$deps" '
function depgraph(pkg, a, i) {
if (visited[pkg])
return 0;
visited[pkg] = 1;
split(deps[pkg], a, ",");
for (i in a)
depgraph(a[i]);
print pkg ":" origin[pkg];
}
$1 == "i" { visited[$2] = 1 }
$1 == "o" { origin[$2] = $3 }
$1 == "d" { deps[$2] = $3 }
END {
split(pkgs, pkgarray);
for (i in pkgarray)
depgraph(pkgarray[i]);
}
'
}
# build and install dependencies
builddeps() {
local deps= alldeps= pkg= i= dir= ver= missing= installed_deps=
local filtered_deps= conflicts=
[ -n "$nodeps" ] && return 0
Natanael Copa
committed
msg "Analyzing dependencies..."
# add depends unless it is a subpackage or package itself
for i in $BUILD_BASE $depends $makedepends; do
[ "$pkgname" = "${i%%[<>=]*}" ] && continue
subpackages_has ${i%%[<>=]*} || deps="$deps $i"
done
installed_deps=$($APK info -e $deps)
# find which deps are missing
for i in $deps; do
if [ "${i#\!}" != "$i" ]; then
$APK info -q -e "${i#\!}" \
&& conflicts="$conflicts ${i#\!}"
elif ! deplist_has $i $installed_deps || [ -n "$upgrade" ]; then
missing="$missing $i"
fi
done
if [ -n "$conflicts" ]; then
error "Conflicting package(s) installed:$conflicts"
return 1
fi
if [ -z "$install_deps" ] && [ -z "$recursive" ]; then
# if we dont have any missing deps we are done now
[ -z "$missing" ] && return 0
error "Missing dependencies: $missing Use -r to autoinstall or -R to build"
return 1
fi
uninstall_after=".makedepends-$pkgname $uninstall_after"
if [ -n "$install_deps" ] && [ -z "$recursive" ]; then
# make a --simluate run first to detect missing deps
# apk-tools --virtual is no goot at reporting those.
$SUDO $APK add --repository "$abuildrepo" \
--wait 30 \
--simulate --quiet $deps || return 1
$SUDO $APK add --repository "$abuildrepo" \
--wait 30 \
--virtual .makedepends-$pkgname $deps \
&& return 0
[ -z "$recursive" ] && return 1
# find dependencies that are installed but missing in repo.
for i in $deps; do
local m=$($APK search --repository "$abuildrepo" ${i%%[<>=]*})
missing="$missing $i"
fi
done
for i in $(deptrace $missing); do
# i = pkg:dir
local dir=${i#*:}
local pkg=${i%:*}
# ignore if dependency is in other repo
[ -d "$dir" ] || continue
# break cricular deps
list_has $pkg $ABUILD_VISITED && continue
export ABUILD_VISITED="$ABUILD_VISITED $pkg"
msg "Entering $dir"
cd "$dir" && $0 $forceroot $keep $quiet $install_deps \
$recursive $upgrade $color_opt abuildindex || return 1
$SUDO $APK add -u --repository "$abuildrepo" \
--wait 30 \
--virtual .makedepends-$pkgname $deps
# replace the md5sums in the APKBUILD
[ -z "$source" ] && return 0
msg "Updating the md5sums in APKBUILD..."
for s in $source; do
files="$files ${s##*/}"
done
md5sums="$(cd "$srcdir" && md5sum $files)" || die "md5sum failed"
sed -i -e '/^md5sums="/,/"\$/d; /^md5sums=''/,/''\$/d' "$APKBUILD"
echo "md5sums=\"$md5sums\"" >>"$APKBUILD"
options_has "!strip" && return 0
cd "${subpkgdir:-$pkgdir}" || return 1
msg "Stripping binaries"
scanelf --recursive --nobanner --etype "ET_DYN,ET_EXEC" . \
| sed -e 's:^ET_DYN ::' -e 's:^ET_EXEC ::' \
| xargs -r strip
# simply list target apks
listpkg() {
local i
getpkgver || return 1
for i in $pkgname $subpackages; do
echo "${i%:*}-$pkgver-r$pkgrel.apk"
done
}
source_has() {
local i
for i in $source; do
[ "$1" = "${i##*/}" ] && return 0
done
return 1
}
subpackages_has() {
local i
for i in $subpackages; do
[ "$1" = "${i%:*}" ] && return 0
done
return 1
}
list_has() {
local needle="$1"
local i
shift
for i in $@; do
[ "$needle" = "$i" ] && return 0
[ "$needle" = "!$i" ] && return 1
done
return 1
}
# same as list_has but we filter version info
deplist_has() {
local needle="$1"
local i
shift
for i in $@; do
[ "$needle" = "$i" ] && return 0
[ "$needle" = "!$i" ] && return 1
done
return 1
}
options_has() {
list_has "$1" $options
}
depends_has() {
deplist_has "$1" $depends
}