#!/bin/sh # script to build apk packages (light version og makepkg) # Copyright (c) 2008 Natanael Copa # # Distributed under GPL-2 # # Depends on: busybox utilities, fakeroot, # abuild_ver=1.0 # read config MAKEAPK_CONF=${MAKEAPK_CONF:-/etc/abuild.conf} [ -f "$MAKEAPK_CONF" ] && . "$MAKEAPK_CONF" # source functions # if abuild was not run from PATH, then look for func lib at same location if [ -z "$FUNCLIB" ]; then FUNCLIB="${0##/*}/functions.sh" [ -f "$FUNCLIB" ] || FUNCLIB=/usr/share/abuild/functions.sh fi if ! [ -f "$FUNCLIB" ]; then echo "$FUNCLIB: not found" >&2 exit 1 fi . "$FUNCLIB" startdir="$PWD" srcdir=${srcdir:-"$startdir/src"} pkgdir=${pkgdir:-"$startdir/pkg"} pkgrel=0 # defaults SRCDEST=${SRCDEST:-$startdir} PKGDEST=${PKGDEST:-$startdir} BUILD_BASE="binutils gcc make patch uclibc-dev" default_cmds="sanitycheck builddeps clean fetch md5check unpack rootpkg" set_xterm_title() { if [ "$TERM" = xterm ]; then printf "\033]0;$1\007" >&2 fi } cleanup() { set_xterm_title "" if [ -z "$install_after" ] && [ -n "$uninstall_after" ]; then sudo apk_delete $uninstall_after fi } die() { error "$@" cleanup exit 1 } # check if apkbuild is basicly sane sanitycheck() { msg "Checking sanity of $APKBUILD..." [ -z "$pkgname" ] && die "Missing pkgname in APKBUILD" [ -z "${pkgname##* *}" ] && die "pkgname contains spaces" [ -z "$pkgver" ] && die "Missing pkgver in APKBUILD" [ "${pkgver##[0-9]}" == "$pkgver" ] && \ die "pkgver does not start with a digit" [ -z "$pkgrel" ] && warning "Missing pkgrel in APKBUILD. Using pkgrel=0" [ -z "$pkgdesc" ] && die "Missing pkgdesc in APKBUILD" [ -z "$url" ] && die "Missing url in APKBUILD" [ -z "$license" ] && die "Missing license in APKBULID" if [ "$(echo $source | wc -l)" -ne "$(echo $md5sums | wc -l)" ]; then die "Number of md5sums does not correspond to number of sources" fi # common spelling errors [ -n "$depend" ] && die "APKBUILD contains 'depend'. It should be depends" [ -n "$makedepend" ] && die "APKBUILD contains 'makedepend'. It should be makedepends" return 0 } md5check() { if [ -z "$source" ]; then return 0 fi if [ -z "$md5sums" ]; then die "Use 'abuild checksum >>$APKBUILD' to generate a checksum" fi if [ "$(echo $source | wc -l)" -ne "$(echo $md5sums | wc -l)" ]; then die "Number of md5sums does not correspond to number of sources" fi fetch msg "Checking md5sums..." cd "$srcdir" && echo "$md5sums" | md5sum -c } uri_fetch() { local uri="$1" local d="${s##*/}" # $(basename $s) local opts [ -n "$quiet" ] && opts="-q" [ -f "$SRCDEST/$d" ] && return 0 mkdir -p "$SRCDEST" if [ -f "$SRCDEST/$d.part" ]; then msg "Partial download found. Trying to resume" opts="$opts -c" fi msg "Fetching $uri" wget $opts -O "$SRCDEST/$d.part" "$uri" \ && mv "$SRCDEST/$d.part" "$SRCDEST/$d" } is_remote() { case "$1" in http://*|ftp://*) return 0;; esac return 1 } fetch() { local s mkdir -p "$srcdir" for s in $source; do if is_remote "$s"; then uri_fetch "$s" || return 1 ln -sf "$SRCDEST/${s##*/}" "$srcdir"/ else ln -sf "$startdir/$s" "$srcdir/" fi done } # unpack the sources unpack() { local u md5check mkdir -p "$srcdir" for u in $source; do local s="$SRCDEST/${u##*/}" # $(basename $s) case "$s" in *.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;; esac done } # cleanup source and package dir clean() { msg "Cleaning temporary build dirs..." rm -rf "$srcdir" rm -rf "$pkgdir" local i for i in $subpackages; do rm -rf "$pkgdir-$(get_split_func $i)" done } # cleanup fetched sources cleancache() { local s for s in $source; do if is_remote "$s"; then msg "Cleaning downloaded ${s##*/}..." rm -f "$SRCDEST/${s##*/}" fi done } cleanpkg() { local i msg "Cleaning built packages..." for i in $pkgname $subpackages; do local p="$i-$pkgver-r$pkgrel" rm -f "$PKGDEST/$p.apk" "$PKGDEST/$p.src.tar.gz" done } # clean all packages except current cleanoldpkg() { local i j msg "Cleaning all packages except $pkgver-r$pkgrel..." for i in $pkgname $subpackages; do for j in "$PKGDEST"/${i%:*}-[0-9]*.apk; do [ "$j" != "$PKGDEST/${i%:*}-$pkgver-r$pkgrel.apk" ] \ && rm -f "$j" done done return 0 } runpart() { local part=$1 [ -n "$DEBUG" ] && msg "$part" $part || die "$part failed" } # override those in your build script build() { die "No build() function found in $APKBUILD" } 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 } subpkg() { if [ -z "$subpackages" ]; then return 0 fi 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..." subpkgdir="$startdir/pkg-$func" subpkgname="${i%:*}" \ $0 $func package || return 1 done } package_apk() { local name=${subpkgname:-$pkgname} [ -z "${name##* *}" ] && die "package name contains spaces" local dir=${subpkgdir:-$pkgdir} local pkg="$name-$pkgver-r$pkgrel.apk" local sub [ ! -d "$dir" ] && die "Missing $dir" cd "$dir" msg "Creating ${subpkgname:+sub}package $pkg..." local builddate=$(date -u "+%s") local size=$(du -sk | awk '{print $1 * 1024}') echo "# Generated by $(basename $0) $abuild_ver" >.PKGINFO if [ -n "$FAKEROOTKEY" ]; then echo "# using $(fakeroot -v)" >> .PKGINFO fi echo "# $(date -u)" >> .PKGINFO cat >> .PKGINFO <>.PKGINFO done for i in $replaces; do echo "replaces = $i" >>.PKGINFO done for i in $depends; 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 $backup; do echo "backup = $i" >>.PKGINFO done local metafiles=".PKGINFO" if [ -n "$install" ]; then cp "$srcdir/$install" "$dir/.INSTALL" || return 1 chmod +x "$dir/.INSTALL" metafiles="$metafiles .INSTALL" fi # for i in pre-install post-install pre-deinstall post-deinstall; do # [ -f ../$i ] && cp ../$i "$db"/ # done set * [ "$1" = '*' ] && set -- ( cd "$dir" && tar -zcf "$PKGDEST/$pkg" $metafiles $@ ) } package() { stripbin package_apk } # predefined splitfunc doc doc() { depends="" local i for i in doc man info html sgml; do if [ -d "$pkgdir/usr/share/$i" ]; then mkdir -p "$subpkgdir/usr/share" mv "$pkgdir/usr/share/$i" "$subpkgdir/usr/share/" fi done rm -f "$subpkgdir/usr/share/info/dir" # # compress info and man pages # find "$subpkgdir/usr/share" \( -name '*.info' -o -name '*.info-[1-9]' \ # -o -name '*.[1-9]' \) -exec gzip {} \; # remove if empty, ignore error (not empty) rmdir "$pkgdir/usr/share" "$pkgdir/usr" 2>/dev/null # [ -d "$subpkgdir/usr/share/man" ] && depends="man" return 0 } # predefined splitfunc mod mod() { depends="$kernel" for i in firmware modules; do if [ -d "$pkgdir/lib/$i" ]; then rm -rf "$subpkgdir/lib" mkdir -p "$subpkgdir/lib" mv "$pkgdir/lib/$i" "$subpkgdir/lib" fi done } # predefined splitfunc dev dev() { depends="$pkgname" cd "$pkgdir" || return 0 for i in cd $(find usr/lib -name '*.a' -o \ -name '*.la' -o -name '*.o' 2>/dev/null) \ usr/include usr/lib/pkgconfig usr/share/aclocal \ usr/bin/*-config ; 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 } # build and package in fakeroot rootpkg() { cd "$startdir" msg "Entering fakeroot..." fakeroot $0 build subpkg package } srcpkg() { local p="$pkgname-$pkgver-$pkgrel" local prefix="${startdir##*/}" local i files="$prefix/APKBUILD" for i in $source; do files="$files $prefix/${i##*/}" done mkdir -p "$PKGDEST" msg "Creating source package $p.src.tar.gz..." (cd .. && tar -zcf "$PKGDEST/$p.src.tar.gz" $files) } # check if package is up to date up2date() { local pkg="$PKGDEST/$pkgname-$pkgver-r$pkgrel.apk" local i s cd "$startdir" [ -f "$pkg" ] || return 1 for i in $source APKBUILD; do local s if is_remote "$i"; then s="$SRCDEST/${i##*/}" # $(basename $i) else s="$startdir/${i##*/}" fi if [ "$s" -nt "$pkg" ]; then return 1 fi done return 0 } # 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} for j in $pkgname $subpackages; do echo "o ${j%%:*} $dir" set -- $depends $makedepends echo -n "d ${j%%:*} $1" shift while [ $# -gt 0 ]; do echo -n ",$1" shift done echo done done ) } deptrace() { ( depparse_aports if [ -z "$upgrade" ]; then # list installed pkgs and prefix with 'i ' apk_info | sed 's/-[0-9].*//; s/^/i /' fi ) | awk -v pkgs="$BUILD_BASE $depends $makedepends" ' function depgraph(pkg, a, i) { if (visited[pkg]) return 0; visited[pkg] = 1; split(deps[pkg], a, ","); for (i in a) depgraph(a[i]); print pkg ":" origin[pkg]; } $1 == "i" { visited[$2] = 1 } $1 == "o" { origin[$2] = $3 } $1 == "d" { deps[$2] = $3 } END { split(pkgs, pkgarray); for (i in pkgarray) depgraph(pkgarray[i]); } ' } # build and install dependencies builddeps() { local deps alldeps pkg i dir ver msg "Building dependencies..." deps="$BUILD_BASE $depends $makedepends" if [ -z "$recursive" ]; then for i in $deps; do apk_info -e $i || die "Missing dependency $i. Use -r to build recursively" done return 0 fi for i in $(deptrace); do # i = pkg:dir local dir=${i#*:} local pkg=${i%:*} # break circular deps [ $(realpath "$dir") = $(realpath "${APKBUILD%/*}") ] \ && continue msg "Entering $dir" cd "$dir" || return 1 $0 -i $pkg || return 1 uninstall_after="$pkg $uninstall_after" done } checksum() { local s files fetch for s in $source; do files="$files ${s##*/}" done md5sums="$(cd "$srcdir" && md5sum $files)" || die "md5sum failed" echo "md5sums=\"$md5sums\"" } stripbin() { local bin dirs=${STRIP_DIRS:-bin lib sbin usr/bin usr/lib usr/sbin} cd "${subpkgdir:-$pkgdir}" || return 1 msg "Stripping binaries" find $dirs -type f 2>/dev/null | while read bin; do local opt= case "$(file -biz "$bin")" in */x-sharedlib*|*/x-archive*) strip --strip-debug "$bin";; */x-executable*) strip "$bin";; esac done return 0 } # simply list target apks listpkg() { local i for i in $pkgname $subpackages; do echo "${i%:*}-$pkgver-r$pkgrel.apk" done } usage() { echo "$(basename $0) $abuild_ver" echo "usage: $0 [options] [-i PKG] [cmd] ..." echo "Options:" echo " -f Force specified cmd, even if they are already done" echo " -h Show this help" echo " -i Install PKG after successul build" echo " -q Quiet" echo " -r Recursively build and install missing dependencies (using sudo)" echo " -u Recursively build and upgrade dependencies (using sudo)" echo "" echo "Commands:" echo " checksum Generate checksum to be included in $APKBUILD" echo " fetch Fetch sources to \$SRCDEST and verify checksums" echo " sanitycheck Basic sanity check of APKBUILD" echo " md5check Check md5sums" echo " unpack Unpack sources to \$srcdir" echo " build Compile and install package into \$pkgdir" echo " listpkg List target packages" echo " package Create package in \$PKGDEST" echo " rootpkg Run '$0 build package' as fakeroot" echo " clean Remove temp build and install dirs" echo " cleanoldpkg Remove binary packages except current version" echo " cleanpkg Remove already built binary and source package" echo " cleancache Remove downloaded files from \$SRCDEST" echo " srcpkg Make a source package" echo " up2date Compare target and sources dates" echo "" exit 0 } APKBUILD="${APKBUILD:-./APKBUILD}" unset force unset recursive while getopts "fhi:qru" opt; do case $opt in 'f') force=1;; 'h') usage;; 'i') install_after="$install_after $OPTARG";; 'q') quiet=1;; 'r') recursive=1;; 'u') upgrade=1 recursive=1;; esac done shift $(( $OPTIND - 1 )) # source the buildfile [ -f "$APKBUILD" ] || die "Could not find $APKBUILD (PWD=$PWD)" . "$APKBUILD" # If we are handling a sub package then reset subpackages if [ -n "$subpkgname" ]; then subpackages= fi trap 'die "Aborted by user"' INT set_xterm_title "abuild: $pkgname" if [ -z "$1" ]; then if up2date && [ -z "$force" ]; then msg "Package is up to date" else set $default_cmds fi fi while [ $# -gt 0 ]; do runpart $1 shift done for i in $install_after; do sudo apk_add -s -u $PKGDEST/$i-$pkgver-r$pkgrel.apk \ || die "Failed to install $i" done cleanup