#!/bin/sh # abuild - build apk packages (light version of makepkg) # Copyright (c) 2008 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" 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 grep '^# Maintainer:' $APKBUILD >/dev/null || warning "No maintainer" 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 } # convert curl options to wget options and call wget instead of curl wget_fallback() { local wget_opts= outfile= opt= while getopts "C:Lko:s" opt; do case $opt in 'L') ;; # --location. wget does this by default 'f') ;; # --fail. wget does this by default 'C') wget_opts="$wget_opts -c";; # --continue-at 's') wget_opts="$wget_opts -q";; # --silent 'o') wget_opts="$wget_opts -O $OPTARG";; # --output 'k') wget_opts="$wget_opts --no-check-certificate";; #gnu wget esac done shift $(( $OPTIND - 1 )) wget $wget_opts "$1" } uri_fetch() { local uri="$1" local d="${uri##*/}" # $(basename $uri) local opts [ -n "$quiet" ] && opts="-s" local lockfile="$SRCDEST/$d".lock # fix saveas-*://* URIs case "$uri" in # remove 'saveas-' from beginning and # '/filename' from end of URI saveas-*://*) uri="${uri:7:$(expr ${#uri} - 7 - ${#d} - 1)}";; *::*) d=${uri%%::*} uri=${uri#$d::} ;; esac case "$uri" in https://*) opts="-k";; esac mkdir -p "$SRCDEST" CLEANUP_FILES="$CLEANUP_FILES $lockfile" ( flock -n -x 9 || msg "Waiting for ${lockfile##*/}..." flock -x 9 [ -f "$SRCDEST/$d" ] && exit 0 # use exit since its a subshell if [ -f "$SRCDEST/$d.part" ]; then msg "Partial download found. Trying to resume" opts="$opts -C -" fi msg "Fetching $uri" # fallback to wget if curl is missing. useful for bootstrapping local fetcher= if ! [ -x "$(which curl)" ]; then fetcher=wget_fallback else fetcher=curl opts="$opts -L -f -k" fi $fetcher $opts -o "$SRCDEST/$d.part" "$uri" \ && mv "$SRCDEST/$d.part" "$SRCDEST/$d" ) 9>$lockfile local rc=$? rm -f "$lockfile" return $rc } 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 for i in $source; do case $i in *.initd) head -n 1 "$srcdir"/$i | grep -q '/sbin/runscript' \ && continue error "$i is not an openrc #!/sbin/runscript" 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 # look for *.la files i=$(find "$dir" -name '*.la' | sed "s|^$dir|\t|") if [ -n "$i" ] && ! options_has "libtool"; then error "Libtool archives (*.la) files found and \$options has no 'libtool' flag:" echo "$i" return 1 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 # 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 } 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 $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") 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 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 for i in $license; do echo "license = $i" >> "$pkginfo" done for i in $replaces; do echo "replaces = $i" >> "$pkginfo" done for i in $deps; do echo "depend = $i" >> "$pkginfo" done for i in $conflicts; do echo "conflict = $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_tracedeps() { 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 } # 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" } # 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_tracedeps || 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() { grep -q -w "^$1" "$pkgbasedir"/.control.*/.provides-so 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 msg " added pkgconfig (found /usr/lib/pkgconfig)" autodeps="$autodeps pkgconfig" fi # special case for libpthread: we need depend on libgcc if [ -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 "^$i" "$dir"/.provides-so 2>/dev/null && continue if subpkg_provides "$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 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 [ -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= # 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 "$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 "^$soname" "$controldir"/.provides-so \ || list_has "$soname" $somask \ || echo $soname done > "$controldir"/.needs-so } # 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 [ "$arch" != "noarch" ] && ! 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" 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 -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"} 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 find "$subpkgdir"/usr/share/man/*[1-9] \ -type f \! -name \*.gz \! -name \*.bz2 2>/dev/null | xargs -r gzip -9 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="$pkgname=$pkgver-r$pkgrel $depends_dev" pkgdesc="$pkgdesc (development files)" for i in $origsubpackages; do [ "${i%:*}" = "$subpkgname" ] || depends="$depends ${i%:*}=$pkgver-r$pkgrel" done cd "$pkgdir" || return 0 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 lib/ usr/ -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 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= . $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 ) } deptrace() { local deps= i= # strip versions from deps for i in "$@"; do deps="$deps ${i%%[<>=]*}" done [ -z "$deps" ] && return 0 ( depparse_aports 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= conflicts= 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" ]; then $APK info --quiet --installed "${i#\!}" \ && conflicts="$conflicts ${i#\!}" elif ! deplist_has $i $installed_builddeps || [ -n "$upgrade" ]; then missing="$missing $i" fi done for i in $hostdeps; do if [ "${i#\!}" != "$i" ]; then $APK info --quiet --installed --root "$CBUILDROOT" "${i#\!}" \ && conflicts="$conflicts ${i#\!}" elif ! deplist_has $i $installed_hostdeps || [ -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 --simulate run first to detect missing deps # apk-tools --virtual is no goot at reporting those. msg "Installing packages on builder: $builddeps" $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 $(deptrace $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 ::' \ | xargs -r ${CROSS_COMPILE}strip } # 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; } 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 -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: checksum Generate checksum to be included in APKBUILD fetch Fetch sources to \$SRCDEST and verify checksums sanitycheck Basic sanity check of APKBUILD verify Verify checksums unpack Unpack sources to \$srcdir prepare Apply patches build Compile and install package into \$pkgdir listpkg List target packages package Create package in \$PKGDEST rootpkg Run 'package', the split functions and create apks as fakeroot clean Remove temp build and install dirs cleanoldpkg Remove binary packages except current version cleanpkg Remove already built binary and source package cleancache Remove downloaded files from \$SRCDEST srcpkg Make a source package sourcecheck Check if remote source package exists upstream up2date Compare target and sources dates deps Install packages listed in makedepends and depends undeps Uninstall packages listed in makedepends and depends snapshot Create a \$giturl or \$svnurl snapshot and upload to \$disturl EOF exit 0 } APKBUILD="${APKBUILD:-./APKBUILD}" unset force unset recursive while getopts "AcdfFhi:kimnp: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";; '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" 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