#!/bin/sh # abuild - build apk packages (light version of makepkg) # Copyright (c) 2008-2015 Natanael Copa # # Distributed under GPL-2 # program_version=@VERSION@ sysconfdir=@sysconfdir@ abuildrepo_base=@abuildrepo@ datadir=@datadir@ abuild_path=$(readlink -f $0) if ! [ -f "$datadir/functions.sh" ]; then echo "$datadir/functions.sh: not found" >&2 exit 1 fi . "$datadir/functions.sh" # defaults BUILD_BASE="build-base" : ${FAKEROOT:="fakeroot"} : ${SUDO_APK:="abuild-apk"} : ${APK:="apk"} : ${ADDUSER:="abuild-adduser"} : ${ADDGROUP:="abuild-addgroup"} apk_opt_wait="--wait 30" # run optional log command for remote logging logcmd() { ${ABUILD_LOG_CMD:-true} "$@" return 0 } # we override the default msg, warning and error as we want the pkgname msg() { [ -n "$quiet" ] && return 0 local prompt="$GREEN>>>${NORMAL}" local fake="${FAKEROOTKEY:+${BLUE}*${NORMAL}}" local name="${STRONG}${subpkgname:-$pkgname}${NORMAL}" printf "${prompt} ${name}${fake}: %s\n" "$1" >&2 } warning() { local prompt="${YELLOW}>>> WARNING:${NORMAL}" local fake="${FAKEROOTKEY:+${BLUE}*${NORMAL}}" local name="${STRONG}${subpkgname:-$pkgname}${NORMAL}" printf "${prompt} ${name}${fake}: %s\n" "$1" >&2 } error() { local prompt="${RED}>>> ERROR:${NORMAL}" local fake="${FAKEROOTKEY:+${BLUE}*${NORMAL}}" local name="${STRONG}${subpkgname:-$pkgname}${NORMAL}" printf "${prompt} ${name}${fake}: %s\n" "$1" >&2 logcmd "ERROR: $pkgname: $1" } cross_compiling() { test "$CBUILD" != "$CHOST" -a -n "$CBUILDROOT" } cleanup() { local i= set_xterm_title "" if [ -n "$CLEANUP_FILES" ]; then rm -f $CLEANUP_FILES fi for i; do case $i in pkgdir) msg "Cleaning up pkgdir"; rm -rf "$pkgbasedir";; srcdir) msg "Cleaning up srcdir"; rm -rf "$srcdir";; deps) if [ -z "$install_after" ] && [ -n "$uninstall_after" ]; then msg "Uninstalling dependencies..." $SUDO_APK del --quiet $apk_opt_wait $uninstall_after if cross_compiling; then $SUDO_APK del --root "$CBUILDROOT" \ --quiet $apk_opt_wait \ $uninstall_after fi fi ;; esac done } die() { error "$@" cleanup $ERROR_CLEANUP exit 1 } spell_error() { die "APKBUILD contains '$1'. It should be '$2'" } # check if apkbuild is basicly sane default_sanitycheck() { local i= j= suggestion= 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 --quiet "$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" if [ $(echo "$pkgdesc" | wc -c) -gt 128 ]; then die "pkgdesc is too long" fi is_function package || warning "Missing package() function in APKBUILD" if [ -n "$replaces_priority" ] \ && ! echo $replaces_priority | egrep -q '^[0-9]+$'; then die "replaces_priority must be a number" fi # check so no package names starts with - for i in $pkgname $subpackages; do case $i in -*) die "${i%:*} is not a valid package name";; esac done # check if CARCH, CBUILD, CHOST and CTARGET is set [ -z "$CHOST" ] && die "Please set CHOST in /etc/abuild.conf" if [ -z "$CARCH" ]; then die "Please fix CHOST, or set CARCH in abuild.conf" fi for i in $install; do local n=${i%.*} local suff=${i##*.} case "$suff" in pre-install|post-install|pre-upgrade|post-upgrade|pre-deinstall|post-deinstall);; *) die "$i: unknown install script suffix" esac if ! subpackages_has "$n" && [ "$n" != "$pkgname" ]; then die "$i: install script does not match pkgname or any subpackage" fi [ -e "$startdir/$i" ] || die "install script $i is missing" for j in chown chmod chgrp; do if grep -q $j "$startdir"/$i; then warning "$i: found $j" warning2 "Permissions should be fixed in APKBUILD package()" fi done done for i in $triggers; do local f=${i%=*} local p=${f%.trigger} [ "$f" = "$i" ] && die "$f: triggers must contain '='" [ "$p" = "$f" ] && die "$f: triggers scripts must have .trigger suffix" if ! subpackages_has "$p" && [ "$p" != "$pkgname" ]; then die "$p: trigger script does not match pkgname or any subpackage" fi [ -e "$startdir"/$f ] || die "trigger script $f is missing" done if [ -n "$source" ]; then for i in $source; do if install_has "$i"; then warning "You should not have \$install in source" continue fi case "$i" in *::*) i=${i%%::*};; https://*) makedepends_has wget && warning "wget no longer need to be in makedepends when source has https://" ;; esac list_has ${i##*/} $md5sums $sha256sums $sha512sums \ || die "${i##*/} is missing in checksums" # verify that our source does not have git tag version # name as tarball (typicallly github) if is_remote "$i" && [ "${i#*::}" = "$i" ]; then case ${i##*/} in v$pkgver.tar.*|$pkgver.tar.*) die "source ${i##*/} needs to be renamed to avoid possible collisions" ;; esac fi done fi # verify that things listed in checksum also is listed in source local algo= for algo in md5 sha256 sha512; do eval set -- \$${algo}sums while [ $# -gt 1 ]; do local file="$2" shift 2 source_has $file || die "$file exists in ${algo}sums but is missing in source" done done # common spelling errors [ -n "$depend" ] && spell_error depend depends [ -n "$makedepend" ] && spell_error makedepend makedepends [ -n "$pkguser" ] && spell_error pkguser pkgusers [ -n "$pkggroup" ] && spell_error pkggroup pkggroups [ -n "$subpackage" ] && spell_error subpackage subpackages check_maintainer || die "Provide a valid RFC822 maintainer address" makedepends_has 'g++' && warning "g++ should not be in makedepends" return 0 } sanitycheck() { default_sanitycheck } sumcheck() { local algo="$1" sums="$2" local dummy f endreturnval originalparams origin file # get number of checksums set -- $sums local numsums=$(( $# / 2 )) set -- $source if [ $# -ne $numsums ]; then die "Number of ${algo}sums($numsums) does not correspond to number of sources($#)" fi fetch || return 1 msg "Checking ${algo}sums..." cd "$srcdir" || return 1 IFS=$'\n' endreturnval=0 for src in $sums; do origin=$1; shift echo "$src" | ${algo}sum -c if [ $? -ne 0 ]; then endreturnval=1 is_remote $origin || continue echo "Because the remote file above failed the ${algo}sum check it will be deleted." echo "Rebuilding will cause it to re-download which in some cases may fix the problem." file=`echo "$src" | sed 's/.*[ \t\n]\(.*\)/\1/'` echo "Deleting: $file" rm $file fi done unset IFS return $endreturnval } # for compatibility md5check() { warning "'md5check' is deprecated. Use 'verify' instead" sumcheck md5 "$md5sums" } # verify checksums verify() { local verified=false algo= for algo in sha512 sha256 sha1 md5; do local sums= eval sums=\"\$${algo}sums\" if [ -z "$sums" ] || [ -z "$source" ]; then continue fi sumcheck "$algo" "$sums" || return 1 verified=true break done if [ -n "$source" ] && ! $verified; then die "Use 'abuild checksum' to generate/update the checksum(s)" fi return 0 } # verify upstream sources sourcecheck() { local uri for uri in $source; do is_remote $uri || continue case "$uri" in saveas-*://*) uri=${uri#saveas-} uri=${uri%/*} ;; *::*) uri=${uri##*::} ;; esac wget -q -s "$uri" || return 1 done return 0 } uri_fetch() { local uri="$1" [ -n "$quiet" ] && opts="-s" mkdir -p "$SRCDEST" abuild-fetch -d "$SRCDEST" "$uri" } is_remote() { case "${1#*::}" in http://*|ftp://*|https://*|saveas-*://*) return 0;; esac return 1 } filename_from_uri() { local uri="$1" local filename="${uri##*/}" # $(basename $uri) case "$uri" in *::*) filename=${uri%%::*};; esac echo "$filename" } # try download from file from mirror first uri_fetch_mirror() { local uri="$1" if [ -n "$DISTFILES_MIRROR" ]; then if is_remote "$DISTFILES_MIRROR"; then uri_fetch "$DISTFILES_MIRROR"/$(filename_from_uri $uri)\ && return 0 else cp "$DISTFILES_MIRROR"/$(filename_from_uri $uri) \ "$SRCDEST" && return 0 fi fi uri_fetch "$uri" } default_fetch() { local s mkdir -p "$srcdir" for s in $source; do if is_remote "$s"; then uri_fetch_mirror "$s" || return 1 ln -sf "$SRCDEST/$(filename_from_uri $s)" "$srcdir"/ else ln -sf "$startdir/$s" "$srcdir/" fi done } fetch() { default_fetch } # verify that all init.d scripts are openrc runscripts initdcheck() { local i line for i in $source; do case $i in *.initd) line=$(head -n 1 "$srcdir"/$i);; *) continue ;; esac case "$line" in *sbin/openrc-run) ;; *sbin/runscript) warning "$i is not an openrc #!/sbin/openrc-run" ;; *) error "$i is not an openrc #!/sbin/openrc-run" return 1 ;; esac done } # unpack the sources default_unpack() { local u if [ -z "$force" ]; then verify || return 1 initdcheck || return 1 fi mkdir -p "$srcdir" for u in $source; do local s="$SRCDEST/$(filename_from_uri $u)" case "$s" in *.tar) msg "Unpacking $s..." tar -C "$srcdir" -xf "$s" || return 1;; *.tar.gz|*.tgz) msg "Unpacking $s..." tar -C "$srcdir" -zxf "$s" || return 1;; *.tar.bz2) msg "Unpacking $s..." tar -C "$srcdir" -jxf "$s" || return 1;; *.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;; esac done } unpack() { default_unpack } # cleanup source and package dir clean() { msg "Cleaning temporary build dirs..." rm -rf "$srcdir" rm -rf "$pkgbasedir" } # cleanup fetched sources cleancache() { local s for s in $source; do if is_remote "$s"; then s=$(filename_from_uri $s) msg "Cleaning downloaded $s ..." rm -f "$SRCDEST/$s" fi done } listpkgnames() { local i for i in $pkgname $subpackages; do echo ${i%:*} done for i in $linguas; do echo $pkgname-lang-$i done } cleanpkg() { local i getpkgver || return 1 msg "Cleaning built packages..." for i in $(listpkgnames); do local p="${i%:*}-$pkgver-r$pkgrel" rm -f "$PKGDEST/$p.apk" "$PKGDEST/$p.src.tar.gz" \ "$abuildrepo"/$p.apk "$abuildrepo"/*/$p.apk done # remove given packages from index update_abuildrepo_index } # clean all packages except current cleanoldpkg() { local i j getpkgver || return 1 msg "Cleaning all packages except $pkgver-r$pkgrel..." for i in $(listpkgnames); do local pn=${i%:*} for j in "$PKGDEST"/$pn-[0-9]*.apk ; do [ "$j" = "$PKGDEST/$pn-$pkgver-r$pkgrel.apk" ] \ && continue rm -f "$j" "$abuildrepo"/*/${j##*/} done done update_abuildrepo_index return 0 } mkusers() { local i for i in $pkggroups; do if ! getent group $i >/dev/null; then msg "Creating group $i" $ADDGROUP -S $i || return 1 fi done for i in $pkgusers; do if ! getent passwd $i >/dev/null; then local gopt= msg "Creating user $i" if getent group $i >/dev/null; then gopt="-G $i" fi $ADDUSER -S -D -H $gopt $i || return 1 fi done } # helper to update config.sub to a recent version update_config_sub() { local changed=false find . -name config.sub | while read f; do if ! ./$f armv6-alpine-linux-muslgnueabihf 2>/dev/null; then msg "Updating $f" cp "$datadir"/${f##*/} "$f" || return 1 changed=true else msg "No update needed for $f" fi # pipe subshell will return status of last command $changed done return $? } runpart() { local part=$1 [ -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 } prepare() { : } build() { : } # generate a simple tar.gz package of pkgdir targz() { cd "$pkgdir" || return 1 mkdir -p "$PKGDEST" 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 } postcheck() { local dir="$1" name="$2" i= msg "Running postcheck for $name" # checking for FHS compat if ! options_has "!fhs"; then for i in "$dir"/srv/* "$dir"/usr/local/* "$dir"/opt/*; do if [ -e "$i" ]; then error "Packages must not put anything under /srv, /usr/local or /opt" return 1 fi done if [ -d "$dir"/usr/var ]; then error "Found /usr/var, localstatedir is most likely wrong" return 1 fi fi # remove *.la files if libtool is not set if ! options_has "libtool"; then find "$dir" -name '*.la' -type f -delete fi # look for /usr/lib/charset.alias if [ -e "$dir"/usr/lib/charset.alias ] \ && ! options_has "charset.alias"; then error "Found /usr/lib/charset.alias" return 1 fi # look for /usr/share/doc if [ -e "$dir"/usr/share/doc ] \ && ! is_doc_pkg "$name"; then warning "Found /usr/share/doc but package name doesn't end with -doc" fi # look for /usr/share/man if [ -e "$dir"/usr/share/man ]; then if ! is_doc_pkg "$name"; then warning "Found /usr/share/man but package name doesn't end with -doc" fi # check for uncompressed man pages i=$(find "$dir"/usr/share/man -name '*.[0-9]' -type f | sed "s|^$dir|\t|") if [ -n "$i" ]; then error "Found uncompressed man pages:" echo "$i" return 1 fi fi # check directory permissions i=$(find "$dir" -type d -perm -777 | sed "s|^$dir|\t|") if [ -n "$i" ]; then warning "World writeable directories found:" echo "$i" fi # check so we dont have any suid root binaries that are not PIE i=$(find "$dir" -type f -perm +6000 \ | xargs scanelf --nobanner --etype ET_EXEC \ | sed "s|ET_EXEC $dir|\t|") if [ -n "$i" ]; then error "Found non-PIE files that has SUID:" echo "$i" return 1 fi # test suid bit on executable if ! options_has "suid"; then i=$(find "$dir" \( -perm -u+s -o -perm -g+s \) -a -type f \ -a -perm -o+x) if [ -n "$i" ]; then error "Found executable files with SUID bit set:" echo "$i" return 1 fi fi # test for textrels if ! options_has "textrels"; then local res="$(scanelf --recursive --textrel --quiet "$dir")" if [ -n "$res" ]; then error "Found textrels:" echo "$res" return 1 fi fi return 0 } pre_split() { if [ -z "$subpkgname" ]; then return 0 fi # the subpackages should not inherit those form main package provides="" install_if="" apkbuild_arch="$arch" } prepare_subpackages() { local i cd "$startdir" for i in $subpackages; do local func=$(get_split_func $i) # call abuild recursively, setting subpkg{dir,name} msg "Running split function $func..." local dir="$pkgbasedir/${i%:*}" name="${i%:*}" ( subpkgdir="$dir" subpkgname="$name" \ $0 pre_split $func prepare_package \ && postcheck "$dir" "$name" ) || return 1 done postcheck "$pkgdir" "$pkgname" || return 1 # post check for /usr/share/locale if [ -d "$pkgdir"/usr/share/locale ]; then warning "Found /usr/share/locale" warning2 "Maybe add \$pkgname-lang to subpackages?" fi } default_lang() { pkgdesc="Languages for package $pkgname" install_if="$pkgname=$pkgver-r$pkgrel lang" arch="noarch" local dir for dir in ${langdir:-/usr/share/locale}; do mkdir -p "$subpkgdir"/${dir%/*} mv "$pkgdir"/"$dir" "$subpkgdir"/"$dir" || return 1 done } lang() { default_lang } default_lang_subpkg() { if [ -z "$lang" ]; then error "lang is not set" return 1 fi pkgdesc="$pkgname language pack for $lang" arch="noarch" install_if="$pkgname=$pkgver-r$pkgrel lang-$lang" local dir for dir in ${langdir:-/usr/share/locale}; do mkdir -p "$subpkgdir"/$dir mv "$pkgdir"/$dir/$lang* \ "$subpkgdir"/$dir/ \ || return 1 done } lang_subpkg() { default_lang_subpkg } prepare_language_packs() { for lang in $linguas; do lang="$lang" \ subpkgname="$pkgname-lang-$lang" \ subpkgdir="$pkgbasedir"/$subpkgname \ $0 lang_subpkg prepare_package || return 1 done } # echo '-dirty' if git is not clean git_dirty() { if [ $(git status -s "$startdir" | wc -l) -ne 0 ]; then echo "-dirty" fi } # echo last commit hash id git_last_commit() { git log --format=oneline -n 1 "$startdir" | awk '{print $1}' } get_maintainer() { if [ -z "$maintainer" ]; then maintainer=$(awk -F': ' '/\# *Maintainer/ {print $2}' "$APKBUILD") # remove surrounding whitespace maintainer=$(echo "$maintainer" | xargs) fi } check_maintainer() { get_maintainer if [ -z "$maintainer" ]; then warning "No maintainer" else # try to check for a valid rfc822 address case "$maintainer" in *[A-Za-z0-9]*\ \<*@*.*\>) ;; *) return 1 ;; esac fi } prepare_metafiles() { getpkgver || return 1 local name=${subpkgname:-$pkgname} [ -z "${name##* *}" ] && die "package name contains spaces" local dir=${subpkgdir:-$pkgdir} local pkg="$name-$pkgver-r$pkgrel.apk" local pkginfo="$controldir"/.PKGINFO local sub [ ! -d "$dir" ] && die "Missing $dir" cd "$dir" mkdir -p "$controldir" local builddate=$(date -u "+%s") local size=$(du -sk | awk '{print $1 * 1024}') local parch="$CARCH" # we need to wait with setting noarch til our build infra can handle it # if [ "$arch" = "noarch" ]; then # parch="noarch" # fi # save arch incase subpackages set it different than main pkg if [ "${apkbuild_arch:-$arch}" != "$arch" ]; then echo "$arch" > "$controldir"/.arch fi echo "# Generated by $(basename $0) $program_version" >"$pkginfo" if [ -n "$FAKEROOTKEY" ]; then echo "# using $($FAKEROOT -v)" >> "$pkginfo" fi echo "# $(date -u)" >> "$pkginfo" cat >> "$pkginfo" </dev/null ; then msg "Script found. busybox added as a dependency for $pkg" deps="$deps busybox" break fi done fi # store last_commit in global var so we only call git once if [ -z "$last_commit" ]; then last_commit="$(git_last_commit)$(git_dirty)" fi echo "commit = $last_commit" >> "$pkginfo" get_maintainer if [ -n "$maintainer" ]; then echo "maintainer = $maintainer" >> "$pkginfo" fi if [ -n "$replaces_priority" ]; then echo "replaces_priority = $replaces_priority" >> "$pkginfo" fi echo "license = $license" >> "$pkginfo" for i in $replaces; do echo "replaces = $i" >> "$pkginfo" done for i in $deps; do echo "depend = $i" >> "$pkginfo" done for i in $provides; do echo "provides = $i" >> "$pkginfo" done for i in $triggers; do local f=${i%=*} local dirs=${i#*=} [ "${f%.trigger}" != "$name" ] && continue echo "triggers = ${dirs//:/ }" >> "$pkginfo" done if [ -n "$install_if" ]; then echo "install_if = $(echo $install_if)" >> "$pkginfo" fi local metafiles=".PKGINFO" for i in $install $triggers; do local f=${i%=*} local n=${f%.*} if [ "$n" != "$name" ]; then continue fi script=${f#$name} msg "Adding $script" cp "$startdir/$f" "$controldir/$script" || return 1 chmod +x "$controldir/$script" metafiles="$metafiles $script" done echo $metafiles | tr ' ' '\n' > "$controldir"/.metafiles } prepare_trace_rpaths() { local dir=${subpkgdir:-$pkgdir} local etype= soname= file= sover= [ "$arch" = "noarch" ] && return 0 options_has "!tracedeps" && return 0 # lets tell all the places we should look for .so files - all rpaths scanelf --quiet --recursive --rpath "$dir" \ | sed -e 's/[[:space:]].*//' -e 's/:/\n/' | sort -u \ >"$controldir"/.rpaths if grep -q -x '/usr/lib' "$controldir"/.rpaths; then warning "Redundant /usr/lib in rpath found" fi if grep '^/home/' "$controldir"/.rpaths; then error "Has /home/... in rpath" return 1 fi } # search for broken symlinks so we later can pull in proper depends prepare_symlinks() { local dir="${subpkgdir:-$pkgdir}" options_has "!tracedeps" && return 0 cd "$dir" || return 1 find -type l | while read symlink; do if ! [ -e "$symlink" ]; then echo "$symlink $(readlink $symlink)" \ >> "$controldir"/.symlinks fi done } prepare_pkgconfig_provides() { local dir="${subpkgdir:-$pkgdir}" options_has "!tracedeps" && return 0 cd "$dir" || return 1 for i in usr/lib/pkgconfig/*.pc; do if ! [ -e "$i" ]; then continue fi local f=${i##*/} local v=$(PKG_CONFIG_PATH="$dir"/usr/lib/pkgconfig pkg-config \ --modversion ${f%.pc}) echo "${f%.pc}=${v:-0}" >> "$controldir"/.provides-pc done } # check if dir has arch specific binaries dir_has_arch_binaries() { local dir="$1" # if scanelf returns something, then we have binaries [ -n "$(scanelf -R "$dir" | head -n 1)" ] && return 0 # look for static *.a [ -n "$(find "$dir" -type f -name '*.a' | head -n 1)" ] && return 0 return 1 } # returns true if this is the -dev package is_dev_pkg() { test "${subpkgname%-dev}" != "$subpkgname" } # returns true if this is the -doc package is_doc_pkg() { test "${1%-doc}" != "$1" } # check that noarch is set if needed archcheck() { options_has "!archcheck" && return 0 if dir_has_arch_binaries "${subpkgdir:-$pkgdir}"; then [ "$arch" != "noarch" ] && return 0 error "Arch specific binaries found so arch must not be set to \"noarch\"" return 1 elif [ "$arch" != "noarch" ] && ! is_dev_pkg; then # we dont want -dev package go to noarch warning "No arch specific binaries found so arch should probably be set to \"noarch\"" fi return 0 } prepare_package() { msg "Preparing ${subpkgname:+sub}package ${subpkgname:-$pkgname}..." stripbin prepare_metafiles \ && prepare_trace_rpaths \ && prepare_symlinks \ && prepare_pkgconfig_provides \ || return 1 archcheck } pkginfo_val() { local key="$1" local file="$2" awk -F ' = ' "\$1 == \"$key\" {print \$2}" "$file" } # find real path to so files real_so_path() { local so="$1" shift while [ $# -gt 0 ]; do [ -e "$1"/$so ] && realpath "$1/$so" && return 0 shift done error "$so: path not found" return 1 } # search rpaths and /usr/lib /lib for given so files find_so_files() { local rpaths=$(cat "$1") shift while [ $# -gt 0 ]; do real_so_path "$1" /usr/lib /lib $rpaths || return 1 shift done return 0 } subpkg_provides_prefixed_so() { [ -n "$sonameprefix" ] && grep -q -w "^$sonameprefix$1" \ "$pkgbasedir"/.control.*/.provides-so 2>/dev/null } subpkg_provides_so() { grep -q -w "^$1" "$pkgbasedir"/.control.*/.provides-so 2>/dev/null } subpkg_provides_pc() { grep -q -w "^${1%%[<>=]*}" "$pkgbasedir"/.control.*/.provides-pc \ 2>/dev/null } trace_apk_deps() { local name="$1" local dir="$2" local i= found= autodeps= deppkgs= missing= msg "Tracing dependencies..." # add pkgconfig if usr/lib/pkgconfig is found if [ -d "$pkgbasedir"/$name/usr/lib/pkgconfig ] \ && ! grep -q '^depend = pkgconfig' "$dir"/.PKGINFO; then autodeps="$autodeps pkgconfig" fi # special case for libpthread: we need depend on libgcc if [ "$CLIBC" = "uclibc" ] && [ -f "$dir"/.needs-so ] \ && grep -q -w '^libpthread.so.*' "$dir"/.needs-so \ && ! grep -q -w "^depend = libgcc" "$dir"/.PKGINFO; then autodeps="$autodeps libgcc" msg " added libgcc (due to libpthread)" fi [ -f "$dir"/.needs-so ] && for i in $(cat "$dir"/.needs-so); do # first check if its provided by same apkbuild grep -q -w "^$sonameprefix$i" "$dir"/.provides-so 2>/dev/null && continue if subpkg_provides_prefixed_so "$i"; then autodeps="$autodeps so:$sonameprefix$i" elif subpkg_provides_so "$i" || cross_compiling \ || $APK info --quiet --installed "so:$i"; then autodeps="$autodeps so:$i" else missing="$missing $i" fi done # find all packages that holds the so files if [ -f "$dir"/.rpaths ]; then local so_files=$(find_so_files "$dir"/.rpaths $missing) \ || return 1 deppkgs=$($APK info --quiet --who-owns $so_files) || return 1 fi for found in $deppkgs; do if grep -w "^depend = ${found}$" "$dir"/.PKGINFO >/dev/null ; then warning "You can remove '$found' from depends" continue fi autodeps="$autodeps $found" done # symlink targets for i in $(sort -u "$dir"/.symlinks-needs 2>/dev/null); do autodeps="$autodeps $i" done # pkg-config depends for i in $(sort -u "$dir"/.needs-pc 2>/dev/null); do if subpkg_provides_pc "$i" || cross_compiling \ || $APK info --quiet --installed "pc:$i"; then local provider=$(apk search --quiet "pc:$i") if list_has "$provider" $depends_dev; then warning "$provider should be removed from depends_dev" fi autodeps="$autodeps pc:$i" else warning "Could not find any provider for pc:$i" local pcfile=/usr/lib/pkgconfig/"${i%%[<>=]*}".pc if [ -e "$pcfile" ]; then local owner=$($APK info --quiet --who-owns $pcfile) warning "${owner:-package providing $pcfile} needs to be rebuilt" fi fi done echo "# automatically detected:" >> "$dir"/.PKGINFO if [ -f "$dir"/.provides-so ]; then sed 's/^\(.*\) \([0-9].*\)/provides = so:\1=\2/' "$dir"/.provides-so \ >> "$dir"/.PKGINFO fi if [ -f "$dir"/.provides-pc ]; then sed 's/^/provides = pc:/' "$dir"/.provides-pc | sort -u \ >> "$dir"/.PKGINFO fi [ -z "$autodeps" ] && return 0 for i in $autodeps; do echo "depend = $i" done | sort -u >> "$dir"/.PKGINFO # display all depends sed -n '/^depend =/s/depend = /\t/p' "$dir"/.PKGINFO >&2 } find_scanelf_paths() { local datadir="$1" local paths="$datadir/lib:$datadir/usr/lib" i= rpaths= if [ -n "$ldpath" ]; then paths="$paths:$(echo "${datadir}${ldpath}" | sed "s|:|:$datadir|g")" fi # search in all rpaths for rpaths in "$pkgbasedir"/.control.*/.rpaths; do [ -f "$rpaths" ] || continue while read i; do local dir="${datadir}${i}" IFS=: if [ -d "$dir" ] && ! list_has "$dir" $paths; then paths="$paths:${dir}" fi unset IFS done < "$rpaths" done echo "$paths" } scan_shared_objects() { local name="$1" controldir="$2" datadir="$3" local opt= i= if [ "$arch" = "noarch" ] && ! [ -e "$controldir"/.arch ]; then return 0 fi # allow spaces in paths IFS=: set -- $(find_scanelf_paths "$datadir") unset IFS # sanity check, verify that each path is prefixed with datadir for i; do if [ "${i#$datadir}" = "$i" ]; then error "Internal error in scanelf paths" return 1 fi done if options_has "ldpath-recursive"; then opt="--recursive" fi msg "Scanning shared objects" # lets tell all the .so files this package provides in .provides-so scanelf --nobanner --soname $opt "$@" | while read etype soname file; do # if soname field is missing, soname will be the filepath sover=0 if [ -z "$file" ]; then file="$soname" soname=${soname##*/} fi # we only want shared libs case $soname in *.so|*.so.[0-9]*|*.c32);; *) continue;; esac case "$file" in *.so.[0-9]*) sover=${file##*.so.};; *.so) # filter out sonames with version when file does not # have version case "$soname" in *.so.[0-9]*) if options_has "sover-namecheck"; then continue fi esac ;; esac list_has "$soname" $somask && continue echo "$sonameprefix$soname $sover" # use awk to filter out dupes that has sover = 0 done | awk '{ if (so[$1] == 0) so[$1] = $2; } END { for (i in so) print(i " " so[i]); }' \ | sort -u > "$controldir"/.provides-so # verify that we dont have any duplicates local dupes="$(cut -d' ' -f1 "$controldir"/.provides-so | uniq -d)" if [ -n "$dupes" ]; then die "provides multiple versions of same shared object: $dupes" fi # now find the so dependencies scanelf --nobanner --recursive --needed "$datadir" | tr ' ' ':' \ | awk -F ":" '$2 != "" && ($1 == "ET_DYN" || $1 == "ET_EXEC") {print $2}' \ | sed 's:,:\n:g' | sort -u \ | while read soname; do # only add files that are not self provided grep -q -w "^$sonameprefix$soname" "$controldir"/.provides-so \ || list_has "$soname" $somask \ || echo $soname done > "$controldir"/.needs-so } # normalize a path string normalize_path() { local oifs="$IFS" pathstr= i= IFS='/' set -- $1 for i; do case "$i" in "."|"") continue;; "..") pathstr="${pathstr%%/${pathstr##*/}}";; *) pathstr="${pathstr}/$i";; esac done echo "$pathstr" } # find which package provides file that symlink points to scan_symlink_targets() { local name="$1" dir="$2" datadir="$3" local symfile= targetpath= cd "$datadir" for symfile in "$pkgbasedir"/.control.*/.symlinks; do local d="${symfile%/.symlinks}" if ! [ -e "$symfile" ] || [ "$d" = "$dir" ]; then continue fi while read symlink target; do if [ "${target#/}" = "$target" ]; then target="${symlink%/*}/$target" fi targetpath="$datadir"/$(normalize_path "$target") if [ -e "$targetpath" ] || [ -L "$targetpath" ]; then echo "$name=$pkgver-r$pkgrel" \ >> "$d"/.symlinks-needs fi done < "$symfile" done } #find pkg-config dependencies scan_pkgconfig_depends() { local provides_pc="$1" controldir= name= datadir= [ -e "$provides_pc" ] || return 0 controldir="${provides_pc%/*}" name="$(pkginfo_val pkgname "$controldir"/.PKGINFO)" datadir="$pkgbasedir"/$name for i in $(sort -u "$provides_pc"); do PKG_CONFIG_PATH="$datadir"/usr/lib/pkgconfig pkg-config \ --print-requires \ --print-requires-private ${i%=*} \ | sed -E 's/\s*([<>=]+)\s*/\1/' \ | while read pc; do # only add files that are not self provided if ! grep -q -w "^${pc%%[<>=]*}" "$provides_pc"; then echo "$pc" >> "$controldir"/.needs-pc fi done done } # read size in bytes from stdin and show as human readable human_size() { awk '{ split("B KB MB GB TB PB", type) for(i=5; y < 1; i--) y = $1 / (2**(10*i)) printf("%.1f %s\n", y, type[i+2]) }' } create_apks() { local file= dir= name= ver= apk= datadir= size= getpkgver || return 1 mkdir -p "$PKGDEST" if ! options_has "!tracedeps"; then for file in "$pkgbasedir"/.control.*/.PKGINFO; do dir="${file%/.PKGINFO}" name="$(pkginfo_val pkgname $file)" datadir="$pkgbasedir"/$name subpkgname=$name scan_shared_objects "$name" "$dir" "$datadir" scan_symlink_targets "$name" "$dir" "$datadir" done for file in "$pkgbasedir"/.control.*/.provides-pc; do scan_pkgconfig_depends "$file" done fi for file in "$pkgbasedir"/.control.*/.PKGINFO; do dir="${file%/.PKGINFO}" name=$(pkginfo_val pkgname $file) ver=$(pkginfo_val pkgver $file) size=$(pkginfo_val size $file | human_size) apk=$name-$ver.apk datadir="$pkgbasedir"/$name subpkgname=$name trace_apk_deps "$name" "$dir" || return 1 msg "Package size: ${size}" msg "Compressing data..." ( cd "$datadir" # data.tar.gz set -- * if [ "$1" = '*' ]; then touch .dummy set -- .dummy fi tar --xattrs -c "$@" | abuild-tar --hash | gzip -9 >"$dir"/data.tar.gz msg "Create checksum..." # 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 cd "$dir" tar -c $(cat "$dir"/.metafiles) | abuild-tar --cut \ | gzip -9 > control.tar.gz abuild-sign -q control.tar.gz || exit 1 msg "Create $apk" # create the final apk cat control.tar.gz data.tar.gz > "$PKGDEST"/$apk ) done subpkgname= } clean_abuildrepo() { local apk cd "$abuildrepo" || return 1 # remove compat symlink for d in "$abuildrepo/$CARCH"; do [ -L "$d" ] && rm "$d" done # remove broken links from abuildrepo for apk in *.apk */*.apk; do if [ -L "$apk" ] && [ ! -f "$apk" ]; then rm -f "$apk" fi done } mklinks_abuildrepo() { [ -n "$REPODEST" ] && return 0 local apk mkdir -p "$abuildrepo"/$CARCH cd "$abuildrepo" || return 1 # create links for this package for apk in $(listpkg); do [ -f "$PKGDEST"/$apk ] || continue ln -sf "$PKGDEST"/$apk "$abuildrepo"/$CARCH/$apk done } build_abuildrepo() { local d apk _build=build if ! is_function package; then # if package() is missing then build is called from rootpkg _build=true fi if ! apk_up2date || [ -n "$force" ]; then # check early if we have abuild key abuild-sign --installed || return 1 logcmd "building ${abuildrepo##*/}/$pkgname-$pkgver-r$pkgrel" sanitycheck && builddeps && clean && fetch && unpack \ && prepare && mkusers && $_build && rootpkg \ && cleanup $CLEANUP \ || return 1 fi update_abuildrepo_index } update_abuildrepo_index() { clean_abuildrepo mklinks_abuildrepo cd "$abuildrepo" local index=$CARCH/APKINDEX.tar.gz msg "Updating the cached abuild repository index..." local sign=".SIGN.RSA.${SIGN_PUBLIC_KEY##*/}" local oldindex= if [ -f "$index" ]; then oldindex="--index $index" fi $APK index --quiet $oldindex --output "$index".unsigned \ --description "$repo $(cd $startdir && git describe)" \ --rewrite-arch $CARCH \ $CARCH/*.apk || exit 1 msg "Signing the index..." abuild-sign -q "$index".unsigned || exit 1 mv "$index".unsigned "$index" chmod 644 "$index" } # predefined splitfunc doc default_doc() { depends="$depends_doc" pkgdesc="$pkgdesc (documentation)" arch=${arch_doc:-"noarch"} install_if="docs $pkgname=$pkgver-r$pkgrel" local i for i in doc man info html sgml licenses gtk-doc ri help; do if [ -d "$pkgdir/usr/share/$i" ]; then mkdir -p "$subpkgdir/usr/share" mv "$pkgdir/usr/share/$i" "$subpkgdir/usr/share/" fi done # compress man pages local previnode= prevname= mandir="$subpkgdir"/usr/share/man [ -d "$mandir" ] && find "$subpkgdir"/usr/share/man \ -type f \( -name \*.[0-9n] -o -name \*.[0-9][a-z]* \) \ -exec stat -c "%i %n" {} \; | sort -n \ | while read inode name; do if [ "$inode" = "$previnode" ]; then # update hard link rm "$name" ln "$prevname".gz "$name".gz else gzip -9 "$name" fi previnode="$inode" prevname="$name" done [ -d "$mandir" ] && find "$subpkgdir"/usr/share/man \ -type l \( -name \*.[0-9n] -o -name \*.[0-9][a-z]* \) \ | while read symlink; do ln -s $(readlink $symlink).gz "$symlink".gz rm "$symlink" done rm -f "$subpkgdir/usr/share/info/dir" # remove if empty, ignore error (not empty) rmdir "$pkgdir/usr/share" "$pkgdir/usr" 2>/dev/null return 0 } doc() { default_doc } # predefined splitfunc doc default_dbg() { local f binfiles=$(scanelf -R "$pkgdir" | grep ET_DYN | sed "s:$pkgdir\/::g" | sed "s:ET_DYN ::g") for f in $binfiles; do srcdir=$(dirname $pkgdir/$f) srcfile=$(basename $pkgdir/$f) dstdir=$(dirname $subpkgdir/usr/lib/debug/$f.debug) dstfile=$(basename $subpkgdir/usr/lib/debug/$f.debug) if [ ! -d $dstdir ] ; then mkdir -p $dstdir fi cd $srcdir objcopy --only-keep-debug $srcfile $dstfile objcopy --add-gnu-debuglink=$dstfile $srcdir/$srcfile mv $dstfile $dstdir strip $srcfile done return 0 } dbg() { default_dbg } # predefined splitfunc dev default_dev() { local i= j= depends="$depends_dev" pkgdesc="$pkgdesc (development files)" cd "$pkgdir" || return 0 local libdirs=usr/ [ -d lib/ ] && libdirs="lib/ $libdirs" 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]*\ usr/share/qt*/mkspecs \ usr/lib/qt*/mkspecs \ usr/lib/cmake \ $(find . -name include -type d) \ $(find $libdirs -name '*.[acho]' \ -o -name '*.prl' 2>/dev/null); do if [ -e "$pkgdir/$i" ] || [ -L "$pkgdir/$i" ]; then d="$subpkgdir/${i%/*}" # dirname $i mkdir -p "$d" mv "$pkgdir/$i" "$d" rmdir "$pkgdir/${i%/*}" 2>/dev/null fi done # 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 return 0 } dev() { default_dev } # predefined splitfunc libs default_libs() { pkgdesc="$pkgdesc (libraries)" local dir= file= for dir in lib usr/lib; do for file in "$pkgdir"/$dir/lib*.so.[0-9]*; do [ -f "$file" ] || continue mkdir -p "$subpkgdir"/$dir mv "$file" "$subpkgdir"/$dir/ done done } libs() { default_libs } is_function() { type "$1" 2>&1 | head -n 1 | egrep -q "is a (shell )?function" } do_fakeroot() { if [ -n "$FAKEROOT" ]; then $FAKEROOT -- "$@" else "$@" fi } # build and package in fakeroot rootpkg() { local _package=package if ! is_function package; then # if package() is missing then run 'build' in fakeroot instead warning "No package() function in APKBUILD" _package=build fi cd "$startdir" rm -rf "$pkgdir" [ -n "$FAKEROOT" ] && msg "Entering fakeroot..." do_fakeroot "$abuild_path" $color_opt \ $_package \ prepare_subpackages \ prepare_language_packs \ prepare_package \ create_apks } srcpkg() { getpkgver || return 1 local p="$pkgname-$pkgver-$pkgrel" local prefix="${startdir##*/}" local i files="$prefix/APKBUILD" for i in $source; do files="$files $prefix/$(filename_from uri $i)" done mkdir -p "$PKGDEST" msg "Creating source package $p.src.tar.gz..." (cd .. && tar -zcf "$PKGDEST/$p.src.tar.gz" $files) } # return true if arch is supported or noarch check_arch() { list_has $CARCH $arch || [ "$arch" = "noarch" ] || [ "$arch" = "all" ] } # return true if libc is not masked in options check_libc() { ! list_has "!libc_$CLIBC" $options } # check if package is up to date apk_up2date() { getpkgver || return 1 local pkg="$PKGDEST/$pkgname-$pkgver-r$pkgrel.apk" local i s cd "$startdir" 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/$(filename_from_uri $i)" else s="$startdir/${i##*/}" fi if [ "$s" -nt "$pkg" ]; then return 1 fi done return 0 } abuildindex_up2date() { local i getpkgver || return 1 local dir="$abuildrepo"/$CARCH local apk="${pkgname%:*}-$pkgver-r$pkgrel.apk" local idx="$dir"/APKINDEX.tar.gz local file="$dir"/$apk # check if index is missing [ -f "$idx" ] || return 1 # if link or file is missing, then we need update abuildrepo index [ -f "$file" ] || return 1 # if file exists and is newer than index, then we need update index [ "$file" -nt "$idx" ] && return 1 return 0 } up2date() { check_arch || return 0 check_libc || return 0 apk_up2date && abuildindex_up2date } # rebuild package and abuildrepo index if needed abuildindex() { up2date && return 0 build_abuildrepo } # source all APKBUILDs and output: # 1) origin of package # 2) all dependencies # the output is i in a format easy parseable for awk parse_aports_makedepends() { # lets run this in a subshell since we source all APKBUILD here ( aportsdir=$(realpath ${APKBUILD%/APKBUILD}/..) for i in $aportsdir/*/APKBUILD; do # no forks in this loop or it will be painfully slow! pkgname= subpackages= depends= makedepends= . $i dir=${i%/APKBUILD} deps= # filter out conflicts from deps and version info for j in $depends $makedepends; do case "$j" in !*) continue;; esac deps="$deps ${j%%[<>=]*}" done for j in $pkgname $subpackages; do echo "o ${j%%:*} $dir" set -- $deps echo -n "d ${j%%:*} $1" shift while [ $# -gt 0 ]; do echo -n ",$1" shift done echo done done ) } trace_makedepends() { local deps= i= # strip versions from deps for i in "$@"; do deps="$deps ${i%%[<>=]*}" done [ -z "$deps" ] && return 0 ( parse_aports_makedepends if [ -z "$upgrade" ]; then # list installed pkgs and prefix with 'i ' $APK info --quiet | sort | sed 's/^/i /' fi ) | 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 pkg= i= missing= local hostdeps= builddeps= installed_hostdeps= installed_builddeps= [ -n "$nodeps" ] && return 0 msg "Analyzing dependencies..." # add depends unless it is a subpackage or package itself if cross_compiling && [ -n "$makedepends_build" -o -n "$makedepends_host" ]; then builddeps="$makedepends_build" for i in $BUILD_BASE; do [ "$pkgname" = "${i%%[<>=]*}" ] && continue subpackages_has ${i%%[<>=]*} || builddeps="$builddeps $i" done hostdeps="$makedepends_host" for i in $depends; do [ "$pkgname" = "${i%%[<>=]*}" ] && continue subpackages_has ${i%%[<>=]*} || hostdeps="$hostdeps $i" done else builddeps="$makedepends" for i in $BUILD_BASE $depends; do [ "$pkgname" = "${i%%[<>=]*}" ] && continue subpackages_has ${i%%[<>=]*} || builddeps="$builddeps $i" done fi installed_builddeps=$($APK info --installed $builddeps) [ -n "$hostdeps" ] && installed_hostdeps=$($APK info --root "$CBUILDROOT" --installed $hostdeps) # find which deps are missing for i in $builddeps; do if [ "${i#\!}" != "$i" ] && $APK info --quiet --installed "${i#\!}"; then error "Conflicting package installed: ${i#\!}" elif ! deplist_has $i $installed_builddeps || [ -n "$upgrade" ]; then missing="$missing $i" fi done for i in $hostdeps; do if [ "${i#\!}" != "$i" ] && $APK info --quiet --installed --root "$CBUILDROOT" "${i#\!}"; then error "Conflicting package installed: ${i#\!}" elif ! deplist_has $i $installed_hostdeps || [ -n "$upgrade" ]; then missing="$missing $i" fi done 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 --simulate run first to detect missing deps # apk-tools --virtual is no goot at reporting those. $SUDO_APK add --repository "$abuildrepo" $apk_opt_wait \ --simulate --quiet $builddeps || return 1 $SUDO_APK add --repository "$abuildrepo" $apk_opt_wait \ --virtual .makedepends-$pkgname $builddeps || return 1 if [ -n "$hostdeps" ]; then $SUDO_APK add --root "$CBUILDROOT" --repository "$abuildrepo" $apk_opt_wait \ --simulate --quiet $hostdeps || return 1 $SUDO_APK add --root "$CBUILDROOT" --repository "$abuildrepo" $apk_opt_wait \ --virtual .makedepends-$pkgname $hostdeps || return 1 fi return 0 fi if [ -n "$CBUILDROOT" ]; then error "Recursive rebuilding is not supported when cross compiling." return 1 fi [ -z "$recursive" ] && return 1 # find dependencies that are installed but missing in repo. for i in $builddeps; do local m=$($APK search --repository "$abuildrepo" ${i%%[<>=]*}) if [ -z "$m" ]; then missing="$missing $i" fi done for i in $(trace_makedepends $missing); do # i = pkg:dir local dir=${i#*:} local pkg=${i%:*} # ignore if dependency is in other repo [ -d "$dir" ] || continue # check if dep is blacklisted if list_has $pkg $ABUILD_BLACKLIST; then error "$pkg is blacklisted" return 1 fi # break circular 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 done $SUDO_APK add --upgrade --repository "$abuildrepo" \ $apk_opt_wait \ --virtual .makedepends-$pkgname $builddeps } # replace the md5sums in the APKBUILD checksum() { local s files [ -z "$source" ] && [ -n "${md5sums}${sha256sums}${sha512sums}" ] \ && msg "Removing checksums from APKBUILD" sed -i -e '/^md5sums="/,/"\$/d; /^md5sums=''/,/''\$/d' "$APKBUILD" sed -i -e '/^sha512sums="/,/"\$/d; /^sha512sums=''/,/''\$/d' "$APKBUILD" sed -i -e '/^sha256sums="/,/"\$/d; /^sha256sums=''/,/''\$/d' "$APKBUILD" [ -z "$source" ] && return 0 fetch for s in $source; do files="$files $(filename_from_uri $s)" done # for compatibility/backporting reasons we still add md5sum msg "Updating the md5sums in APKBUILD..." md5sums="$(cd "$srcdir" && md5sum $files)" || die "md5sum failed" echo "md5sums=\"$md5sums\"" >>"$APKBUILD" msg "Updating the sha256sums in APKBUILD..." sha256sums="$(cd "$srcdir" && sha256sum $files)" \ || die "sha256sum failed" echo "sha256sums=\"$sha256sums\"" >>"$APKBUILD" msg "Updating the sha512sums in APKBUILD..." sha512sums="$(cd "$srcdir" && sha512sum $files)" \ || die "sha512sum failed" echo "sha512sums=\"$sha512sums\"" >>"$APKBUILD" } stripbin() { local bin if options_has "!strip" || [ "$arch" = "noarch" ]; then return 0 fi 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 ::' \ | while read filename; do XATTR=$(getfattr --match="*" --dump "${filename}") ${CROSS_COMPILE}strip "${filename}" [ -n "$XATTR" ] && (echo "$XATTR" | setfattr --restore=-) done } # simply list target apks listpkg() { local name getpkgver || return 1 for name in $(listpkgnames) ; do echo "$name-$pkgver-r$pkgrel.apk" done } source_has() { local i for i in $source; do [ "$1" = "${i##*/}" ] && return 0 [ "$1" = "${i%%::*}" ] && return 0 done return 1 } subpackages_has() { local i for i in $subpackages; do [ "$1" = "${i%:*}" ] && return 0 done return 1 } subpackage_types_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 i=${i%%[<>=]*} [ "$needle" = "$i" ] && return 0 [ "$needle" = "!$i" ] && return 1 done return 1 } options_has() { list_has "$1" $options } depends_has() { deplist_has "$1" $depends } makedepends_has() { deplist_has "$1" $makedepends } md5sums_has() { list_has "$1" $md5sums } install_has() { list_has "$1" $install } # install package after build post_add() { getpkgver || return 1 local pkgf="$PKGDEST/$1-$pkgver-r$pkgrel.apk" local deps i if ! subpackages_has $1 && [ "$1" != "$pkgname" ]; then die "$1 is not built by this APKBUILD" fi # recursively install dependencies that are provided by this APKBUILD deps=$($APK index "$pkgf" 2>/dev/null | awk -F: '$1=="D" { print $2 }') for i in $deps; do if subpackages_has $i || [ "$i" = "$pkgname" ]; then post_add $i || return 1 fi done $SUDO_APK add $apk_opt_wait --upgrade "$pkgf" \ || die "Failed to install $1" } deps() { local builddeps i builddeps="$makedepends" for i in $depends; do [ "$pkgname" = "${i%%[<>=]*}" ] && continue subpackages_has ${i%%[<>=]*} || builddeps="$builddeps $i" done $SUDO_APK add $apk_opt_wait --repository "$abuildrepo" \ --virtual .makedepends-$pkgname \ $builddeps } undeps (){ $SUDO_APK del $apk_opt_wait .makedepends-$pkgname if cross_compiling; then $SUDO_APK del --root "$CBUILDROOT" $apk_opt_wait \ .makedepends-$pkgname fi } # compat installdeps() { deps; } uninstalldeps() { undeps; } index() { update_abuildrepo_index; } all() { if ! [ -n "$force" ]; then check_arch if [ $? -ne 0 ]; then echo "Package not available for the target architecture ($CARCH). Aborting." return 0 fi check_libc || return 0 fi if up2date && [ -z "$force" ]; then msg "Package is up to date" else build_abuildrepo fi } # This abuild hook will checkout an svn or git repository by specifying # $svnurl or $giturl in APKBUILD. You can checkout a specific branch in # git by adding -b $branch in $giturl. $reporev will select the correct # commit, revision or tag for you. If you specify $disturl your distfile # will automatically be uploaded with rsync to the url provided. # Base version defaults to 0 except if specified by $verbase. snapshot() { # check if we setup vars correctly [ -z "$disturl" ] && warning "Missing disturl in APKBUILD, auto uploading disabled." [ -z "$svnurl" ] && [ -z "$giturl" ] && die "Missding repository url in APKBUILD!" [ -n "$svnurl" ] && [ -n "$giturl" ] && die "You can only use a single repository!" local _date=$(date +%Y%m%d) local _format="tar.gz" # remove any repositories left in srcdir abuild clean mkdir -p "$srcdir" && cd "$srcdir" # clone git repo and archive if [ -n "$giturl" ]; then local _version=${verbase:-0}_git${_date} command -v git >/dev/null || \ die "Missing git! Install git to support git clone." [ -z "$reporev" ] && local _rev="HEAD" && local _depth="--depth=1" msg "Creating git snapshot: $pkgname-$_version" git clone $_depth --bare $giturl $pkgname-$_version || return 1 git --git-dir $pkgname-$_version archive \ --format=$_format \ -o $pkgname-$_version.$_format \ --prefix=$pkgname-$_version/ $_rev \ || return 1 fi # export svn repo and archive if [ -n "$svnurl" ]; then local _version=${verbase:-0}_svn${_date} command -v svn >/dev/null || \ die "Missing svn! Install subverion to support svn export." [ -n "$reporev" ] && local _rev="-r $reporev" msg "Creating svn snapshot: $pkgname-$_version" svn co $_rev $svnurl $pkgname-$_version || return 1 tar zcf $pkgname-$_version.$_format $pkgname-$_version || return 1 fi # upload to defined distfiles url if [ -n "$disturl" ]; then command -v rsync >/dev/null || \ die "Missing rsync! Install rsync to enable automatic uploads." msg "Uploading to $disturl" rsync --progress -La $pkgname-$_version.$_format \ $disturl || return 1 cd "$startdir" # set the pkgver to current date and update checksum sed -i -e "s/^pkgver=.*/pkgver=${_version}/" \ APKBUILD || return 1 abuild checksum fi } usage() { echo "$program $program_version" cat << EOF usage: $program [options] [-i PKG] [-P REPODEST] [-p PKGDEST] [-s SRCDEST] [cmd] ... $program [-c] -n PKGNAME[-PKGVER] Options: -A Print CARCH and exit -c Enable colored output -d Disable dependency checking -f Force specified cmd, even if they are already done -F Force run as root -h Show this help -i Install PKG after successful build -k Keep built packages, even if APKBUILD or sources are newer -K Keep buildtime temp dirs and files (srcdir/pkgdir/deps) -m Disable colors (monochrome) -p Set package destination directory -P Set PKGDEST to REPODEST//\$CARCH, where repo is the parents dir name -q Quiet -r Install missing dependencies from system repository (using sudo) -R Recursively build and install missing dependencies (using sudo) -s Set source package destination directory -u Recursively build and upgrade all dependencies (using sudo) Commands: build Compile and install package into \$pkgdir checksum Generate checksum to be included in APKBUILD clean Remove temp build and install dirs cleancache Remove downloaded files from \$SRCDEST cleanoldpkg Remove binary packages except current version cleanpkg Remove already built binary and source package deps Install packages listed in makedepends and depends fetch Fetch sources to \$SRCDEST and verify checksums index Regenerate the APKINDEX for abuildrepo listpkg List target packages package Create package in \$PKGDEST prepare Apply patches rootpkg Run 'package', the split functions and create apks as fakeroot sanitycheck Basic sanity check of APKBUILD snapshot Create a \$giturl or \$svnurl snapshot and upload to \$disturl sourcecheck Check if remote source package exists upstream srcpkg Make a source package undeps Uninstall packages listed in makedepends and depends unpack Unpack sources to \$srcdir up2date Compare target and sources dates verify Verify checksums EOF exit 0 } APKBUILD="${APKBUILD:-./APKBUILD}" unset force unset recursive while getopts "AcdfFhi:kKimnp:P:qrRs:u" opt; do case $opt in 'A') echo "$CARCH"; exit 0;; 'c') enable_colors color_opt="-c";; 'd') nodeps=1;; 'f') force="-f";; 'F') forceroot="-F";; 'h') usage;; 'i') install_after="$install_after $OPTARG";; 'k') keep="-k";; 'K') CLEANUP="" ERROR_CLEANUP="" ;; 'm') disable_colors color_opt="-m";; 'n') die "Use newapkbuild to create new aports";; 'p') PKGDEST=$OPTARG;; 'P') REPODEST=$OPTARG;; 'q') quiet="-q";; 'r') install_deps="-r";; 'R') recursive="-R";; 's') SRCDEST=$OPTARG;; 'u') upgrade="-u" recursive="-R";; esac done shift $(( $OPTIND - 1 )) # check so we are not root if [ "$(whoami)" = "root" ] && [ -z "$FAKEROOTKEY" ]; then [ -z "$forceroot" ] && die "Do not run abuild as root" FAKEROOT= fi # find startdir [ -f "$APKBUILD" ] || die "Could not find $APKBUILD (PWD=$PWD)" APKBUILD=$(readlink -f "$APKBUILD") startdir="${APKBUILD%/*}" srcdir=${srcdir:-"$startdir/src"} pkgbasedir=${pkgbasedir:-"$startdir/pkg"} repo=${startdir%/*} repo=${repo##*/} SRCDEST=${SRCDEST:-$startdir} PKGDEST=${PKGDEST:-$startdir} # set a default CC : ${CC:=gcc} export CC cd "$startdir" || die . "$APKBUILD" # If REPODEST is set then it will override the PKGDEST if [ -n "$REPODEST" ]; then PKGDEST="$REPODEST/$repo/$CARCH" # for recursive action export REPODEST abuildrepo="$REPODEST"/$repo else abuildrepo="$abuildrepo_base"/$repo fi # if we want build debug package if [ -n "$DEBUG" ] || subpackage_types_has "dbg"; then CFLAGS="$CFLAGS -g" CXXFLAGS="$CXXFLAGS -g" options="$options !strip" fi # If we are handling a sub package then reset subpackages and install if [ -n "$subpkgname" ]; then origsubpackages="$subpackages" subpackages= fi pkgdir="$pkgbasedir/$pkgname" controldir="$pkgbasedir"/.control.${subpkgname:-$pkgname} trap 'die "Aborted by user"' INT set_xterm_title "abuild: $pkgname" if [ -z "$1" ]; then set all fi while [ $# -gt 0 ]; do runpart $1 shift done for i in $install_after; do post_add $i done cleanup