#!/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=0.9 # read config MAKEAPK_CONF=${MAKEAPK_CONF:-/etc/abuild.conf} [ -f "$MAKEAPK_CONF" ] && . "$MAKEAPK_CONF" startdir="$PWD" srcdir=${srcdir:-"$startdir/src"} pkgdir=${pkgdir:-"$startdir/pkg"} pkgrel=0 # defaults SRCDEST=${SRCDEST:-$startdir} PKGDEST=${PKGDEST:-$startdir} default_cmds="sanitycheck builddeps clean fetch md5check unpack rootpkg" #colors if [ -n "$USE_COLORS" ]; then NORMAL="\033[1;0m" STRONG="\033[1;1m" RED="\033[1;31m" GREEN="\033[1;32m" YELLOW="\033[1;33m" BLUE="\033[1;34m" fi # functions msg() { local prompt="$GREEN>>>${NORMAL}" local fake="${FAKEROOTKEY:+${BLUE}(fakeroot)${NORMAL} }" local name="${STRONG}${subpkgname:-$pkgname}${NORMAL}" [ -z "$quiet" ] && printf "${prompt} ${fake}${name}: $@\n" >&2 } warning() { local prompt="${YELLOW}>>> WARNING: ${NORMAL}" local fake="${FAKEROOTKEY:+${BLUE}(fakeroot)${NORMAL} }" local name="${STRONG}${subpkgname:-$pkgname}${NORMAL}" printf "${prompt} ${fake}${name}: $@\n" >&2 } error() { local prompt="${RED}>>> ERROR: ${NORMAL}" local fake="${FAKEROOTKEY:+${BLUE}(fakeroot)${NORMAL} }" local name="${STRONG}${subpkgname:-$pkgname}${NORMAL}" printf "${prompt} ${fake}${name}: $@\n" >&2 } set_xterm_title() { if [ "$TERM" = xterm ]; then printf "\033]0;$1\007" >&2 fi } cleanup() { set_xterm_title "" } 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 } 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 } # note: this must run in a subshell find_aport() { local i msg "Searching for $1 in aports tree..." cd ../.. for i in */*/APKBUILD; do pkgrel= . $i local j for j in $pkgname $subpackages; do if [ "${j%%:*}" = "$1" ]; then echo "$PWD/${i%/APKBUILD}/$1-$pkgver-r$pkgrel.apk" return 0 fi done done return 1 } # recursively build and install dependencies builddeps() { local dep pkg for dep in $depends $makedepends; do [ -z "$upgrade" ] && apk_info -e $dep && continue [ -z "$recursive" ] && die "Missing dependency $dep. Use -r or -u to build recursively" pkg=$(find_aport $dep) if [ -z "$pkg" ]; then # try install from system repo as fallback apk_add ${upgrade:+-u} $dep && continue die "Failed to find dependency $dep" fi # recursively build deps msg "Entering ${pkg%/*}" cd ${pkg%/*} $0 -r ${upgrade:+-u} || return 1 sudo apk_add ${upgrade:+-u} $pkg 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 } usage() { echo "$(basename $0) $abuild_ver" echo "usage: $0 [options] [cmd] ..." echo "Options:" echo " -h Show this help" echo " -f Force specified cmd, even if they are already done" 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 " package Create package in \$PKGDEST" echo " rootpkg Run '$0 build package' as fakeroot" echo " clean Remove temp build and install dirs" 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}" while getopts "hfqru" opt; do case $opt in 'h') usage;; 'f') force=1;; '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 cleanup