#!/bin/bash # # makepkg # # Copyright (c) 2002-2004 by Judd Vinet <jvinet@zeroflux.org> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, # USA. # myver='2.9.2' startdir=`pwd` PKGDEST=$startdir USE_COLOR="n" # source Arch's abs.conf if it's present [ -f /etc/abs/abs.conf ] && source /etc/abs/abs.conf # makepkg configuration [ -f /etc/makepkg.conf ] && source /etc/makepkg.conf INFAKEROOT= if [ "$1" = "-F" ]; then INFAKEROOT=1 shift fi ### SUBROUTINES ### plain() { if [ "$USE_COLOR" = "Y" -o "$USE_COLOR" = "y" ]; then echo -e " \033[1;1m$1\033[1;0m" >&2 else echo " $1" >&2 fi } msg() { if [ "$USE_COLOR" = "Y" -o "$USE_COLOR" = "y" ]; then echo -e "\033[1;32m==>\033[1;0m \033[1;1m$1\033[1;0m" >&2 else echo "==> $1" >&2 fi } warning() { if [ "$USE_COLOR" = "Y" -o "$USE_COLOR" = "y" ]; then echo -e "\033[1;33m==> WARNING:\033[1;0m \033[1;1m$1\033[1;0m" >&2 else echo "==> WARNING: $1" >&2 fi } error() { if [ "$USE_COLOR" = "Y" -o "$USE_COLOR" = "y" ]; then echo -e "\033[1;31m==> ERROR:\033[1;0m \033[1;1m$1\033[1;0m" >&2 else echo "==> ERROR: $1" >&2 fi } strip_url() { echo $1 | sed 's|^.*://.*/||g' } checkdeps() { local missdep="" local deplist="" missdep=`pacman -T $*` ret=$? if [ "$ret" != "0" ]; then if [ "$ret" = "127" ]; then msg "Missing Dependencies:" msg "" nl=0 for dep in $missdep; do echo -ne "$dep " >&2 if [ "$nl" = "1" ]; then nl=0 echo -ne "\n" >&2 # add this dep to the list depname=`echo $dep | sed 's|=.*$||' | sed 's|>.*$||' | sed 's|<.*$||'` deplist="$deplist $depname" continue fi nl=1 done msg "" else error "pacman returned a fatal error." exit 1 fi fi echo $deplist } handledeps() { local missingdeps=0 local deplist="$*" local haveperm=0 if [ "`id -u`" = "0" -a "$INFAKEROOT" != "1" ]; then haveperm=1 fi if [ "$deplist" != "" -a $haveperm -eq 1 ]; then if [ "$DEP_BIN" = "1" ]; then # install missing deps from binary packages (using pacman -S) msg "Installing missing dependencies..." pacman -D $deplist if [ "$?" = "127" ]; then error "Failed to install missing dependencies." exit 1 fi # TODO: check deps again to make sure they were resolved elif [ "$DEP_SRC" = "1" ]; then # install missing deps by building them from source. # we look for each package name in $ABSROOT and build it. if [ "$ABSROOT" = "" ]; then error "The ABSROOT environment variable is not defined." exit 1 fi # TODO: handle version comparators (eg, glibc>=2.2.5) msg "Building missing dependencies..." for dep in $deplist; do candidates=`find $ABSROOT -type d -name "$dep"` if [ "$candidates" = "" ]; then error "Could not find \"$dep\" under $ABSROOT" exit 1 fi success=0 for pkgdir in $candidates; do if [ -f $pkgdir/PKGBUILD ]; then cd $pkgdir if [ "$RMDEPS" = "1" ]; then makepkg -i -c -b -r -w $PKGDEST else makepkg -i -c -b -w $PKGDEST fi if [ $? -eq 0 ]; then success=1 break fi fi done if [ "$success" = "0" ]; then error "Failed to build \"$dep\"" exit 1 fi done # TODO: check deps again to make sure they were resolved else missingdeps=1 fi elif [ "$deplist" != "" -a $haveperm -eq 0 ]; then if [ "$DEP_SRC" = "1" -o "$DEP_BIN" = "1" ]; then warning "Cannot auto-install missing dependencies as a normal user!" plain "Run makepkg as root to resolve dependencies automatically." fi missingdeps=1 fi return $missingdeps } usage() { echo "makepkg version $myver" echo "usage: $0 [options]" echo "options:" echo " -b, --builddeps Build missing dependencies from source" echo " -c, --clean Clean up work files after build" echo " -C, --cleancache Clean up source files from the cache" echo " -d, --nodeps Skip all dependency checks" echo " -e, --noextract Do not extract source files (use existing src/ dir)" echo " -f, --force Overwrite existing package" echo " -g, --genmd5 Generate MD5sums for source files" echo " -h, --help This help" echo " -i, --install Install package after successful build" echo " -j <jobs> Set MAKEFLAGS to \"-j<jobs>\" before building" echo " -m, --nocolor Disable colorized output messages" echo " -n, --nostrip Do not strip binaries/libraries" echo " -o, --nobuild Download and extract files only" echo " -p <buildscript> Use an alternate build script (instead of PKGBUILD)" echo " -r, --rmdeps Remove installed dependencies after a successful build" echo " -s, --syncdeps Install missing dependencies with pacman" echo " -w <destdir> Write package to <destdir> instead of the working dir" echo echo " if -p is not specified, makepkg will look for a PKGBUILD" echo " file in the current directory." echo } # Options CLEANUP=0 CLEANCACHE=0 INSTALL=0 GENMD5=0 DEP_BIN=0 DEP_SRC=0 NODEPS=0 FORCE=0 NOEXTRACT=0 NOSTRIP=0 NOBUILD=0 RMDEPS=0 BUILDSCRIPT="./PKGBUILD" ARGLIST=$@ while [ "$#" -ne "0" ]; do case $1 in --clean) CLEANUP=1 ;; --cleancache) CLEANCACHE=1 ;; --syncdeps) DEP_BIN=1 ;; --builddeps) DEP_SRC=1 ;; --nodeps) NODEPS=1 ;; --noextract) NOEXTRACT=1 ;; --install) INSTALL=1 ;; --force) FORCE=1 ;; --nostrip) NOSTRIP=1 ;; --nobuild) NOBUILD=1 ;; --nocolor) USE_COLOR="n" ;; --genmd5) GENMD5=1 ;; --rmdeps) RMDEPS=1 ;; --help) usage exit 0 ;; --*) usage exit 1 ;; -*) while getopts "cCsbdehifgj:mnorp:w:-" opt; do case $opt in c) CLEANUP=1 ;; C) CLEANCACHE=1 ;; b) DEP_SRC=1 ;; d) NODEPS=1 ;; e) NOEXTRACT=1 ;; f) FORCE=1 ;; g) GENMD5=1 ;; h) usage exit 0 ;; i) INSTALL=1 ;; j) export MAKEFLAGS="-j$OPTARG" ;; m) USE_COLOR="n" ;; n) NOSTRIP=1 ;; o) NOBUILD=1 ;; p) BUILDSCRIPT=$OPTARG ;; r) RMDEPS=1 ;; s) DEP_BIN=1 ;; w) PKGDEST=$OPTARG ;; -) OPTIND=0 break ;; *) usage exit 1 ;; esac done ;; *) true ;; esac shift done # convert a (possibly) relative path to absolute cd $PKGDEST 2>/dev/null if [ $? -ne 0 ]; then error "Package destination directory does not exist or permission denied." exit 1 fi PKGDEST=`pwd` cd $OLDPWD if [ "$CLEANCACHE" = "1" ]; then if [ "`id -u`" = "0" -a "$INFAKEROOT" != "1" ]; then msg "Cleaning up source files from the cache." rm -rf /var/cache/pacman/src/* exit 0 else error "You must be root to clean the cache." exit 1 fi fi unset pkgname pkgver pkgrel pkgdesc url license groups provides md5sums force unset replaces depends conflicts backup source install build makedepends umask 0022 if [ ! -f $BUILDSCRIPT ]; then error "$BUILDSCRIPT does not exist." exit 1 fi source $BUILDSCRIPT # check for no-no's if [ `echo $pkgver | grep '-'` ]; then error "pkgver is not allowed to contain hyphens." exit 1 fi if [ `echo $pkgrel | grep '-'` ]; then error "pkgrel is not allowed to contain hyphens." exit 1 fi if [ -f $PKGDEST/${pkgname}-${pkgver}-${pkgrel}.pkg.tar.gz -a "$FORCE" = "0" -a "$GENMD5" = "0" ]; then if [ "$INSTALL" = "1" ]; then warning "a package has already been built, installing existing package." pacman --upgrade $PKGDEST/${pkgname}-${pkgver}-${pkgrel}.pkg.tar.gz exit $? else error "a package has already been built. (use -f to overwrite)" exit 1 fi fi # Enter the fakeroot environment if necessary. This will call the makepkg script again # as the fake root user. We detect this by passing a sentinel option (-F) to makepkg if [ "`id -u`" != "0" ]; then if [ "$USE_FAKEROOT" = "y" -o "$USE_FAKEROOT" = "Y" ]; then if [ `type -p fakeroot` ]; then msg "Entering fakeroot environment" fakeroot -- $0 -F $ARGLIST exit $? else warning "Fakeroot is not installed. Building as an unprivileged user" plain "will result in non-root ownership of the packaged files." plain "Install the fakeroot package to correctly build as a non-root" plain "user." plain "" sleep 1 fi else warning "Running makepkg as an unprivileged user will result in non-root" plain "ownership of the packaged files. Try using the fakeroot" plain "environment. (USE_FAKEROOT=y in makepkg.conf)" plain "" sleep 1 fi fi msg "Making package: $pkgname $pkgver-$pkgrel (`date`)" unset deplist makedeplist if [ `type -p pacman` -a "$NODEPS" = "0" ]; then msg "Checking Runtime Dependencies..." deplist=`checkdeps ${depends[@]}` handledeps $deplist if [ $? -gt 0 ]; then exit 1 fi msg "Checking Buildtime Dependencies..." makedeplist=`checkdeps ${makedepends[@]}` handledeps $makedeplist if [ $? -gt 0 ]; then exit 1 fi elif [ "$NODEPS" = "1" ]; then warning "skipping dependency checks." else warning "pacman was not found in PATH. skipping dependency checks." fi cd $startdir # retrieve sources msg "Retrieving Sources..." mkdir -p src cd $startdir/src for netfile in ${source[@]}; do file=`strip_url $netfile` if [ -f ../$file ]; then msg " Found $file in build dir" cp ../$file . elif [ -f /var/cache/pacman/src/$file ]; then msg " Using local copy of $file" cp /var/cache/pacman/src/$file . else # check for a download utility if [ -z "$FTPAGENT" ]; then error "FTPAGENT is not configured. Check the /etc/makepkg.conf file." msg "Aborting..." exit 1 fi ftpclient=`echo $FTPAGENT | awk {'print $1'}` if [ ! -x $ftpclient ]; then error "ftpclient `basename $ftpclient` is not installed." msg "Aborting..." exit 1 fi proto=`echo $netfile | sed 's|://.*||'` if [ "$proto" != "ftp" -a "$proto" != "http" -a "$proto" != "https" ]; then error "$netfile was not found in the build directory and is not a proper URL." msg "Aborting..." exit 1 fi msg " Downloading $file" $FTPAGENT $netfile 2>&1 if [ ! -f $file ]; then error "Failed to download $file" msg "Aborting..." exit 1 fi if [ "`id -u`" = "0" -a "$INFAKEROOT" != "1" ]; then mkdir -p /var/cache/pacman/src && cp $file /var/cache/pacman/src else cp $file .. fi fi done if [ "$GENMD5" = "0" ]; then if [ "$NOEXTRACT" = "1" ]; then warning "Skipping source extraction -- using existing src/ tree" warning "Skipping source integrity checks -- using existing src/ tree" else # MD5 validation if [ ${#md5sums[@]} -ne ${#source[@]} ]; then warning "MD5sums are missing or incomplete. Cannot verify source integrity." #sleep 1 elif [ `type -p md5sum` ]; then msg "Validating source files with MD5sums" errors=0 idx=0 for netfile in ${source[@]}; do file=`strip_url $netfile` echo -n " $file ... " >&2 echo "${md5sums[$idx]} $file" | md5sum -c - >/dev/null 2>&1 if [ $? -ne 0 ]; then echo "FAILED" >&2 errors=1 else echo "Passed" >&2 fi idx=$(($idx+1)) done if [ $errors -gt 0 ]; then error "One or more files did not pass the validity check!" exit 1 fi else warning "The md5sum program is missing. Cannot verify source files!" sleep 1 fi # extract sources msg "Extracting Sources..." for netfile in ${source[@]}; do unziphack=0 file=`strip_url $netfile` unset cmd case $file in *.tar.gz|*.tar.Z|*.tgz) cmd="tar --use-compress-program=gzip -xf $file" ;; *.tar.bz2) cmd="tar --use-compress-program=bzip2 -xf $file" ;; *.tar) cmd="tar -xf $file" ;; *.zip) unziphack=1 cmd="unzip -qqo $file" ;; *.gz) cmd="gunzip $file" ;; *.bz2) cmd="bunzip2 $file" ;; esac if [ "$cmd" != "" ]; then msg " $cmd" $cmd if [ $? -ne 0 ]; then # unzip will return a 1 as a warning, it is not an error if [ "$unziphack" != "1" -o $? -ne 1 ]; then error "Failed to extract $file" msg "Aborting..." exit 1 fi fi fi done fi else # generate md5 hashes if [ ! `type -p md5sum` ]; then error "Cannot find the md5sum program." exit 1 fi msg "Generating MD5sums for source files" plain "" ct=0 newline=0 numsrc=${#source[@]} for netfile in ${source[@]}; do file=`strip_url $netfile` sum=`md5sum $file | cut -d' ' -f 1` if [ $ct -eq 0 ]; then echo -n "md5sums=(" else if [ $newline -eq 0 ]; then echo -n " " fi fi echo -n "'$sum'" ct=$(($ct+1)) if [ $ct -eq $numsrc ]; then echo ')' else if [ $newline -eq 1 ]; then echo '\' newline=0 else echo -n ' ' newline=1 fi fi done plain "" exit 0 fi if [ "`id -u`" = "0" ]; then # chown all source files to root.root chown -R root.root $startdir/src fi # check for existing pkg directory if [ -d $startdir/pkg ]; then msg "Removing existing pkg/ directory..." rm -rf $startdir/pkg fi mkdir -p $startdir/pkg if [ "$NOBUILD" = "1" ]; then msg "Sources are ready." exit 0 fi # use ccache if it's available [ -d /usr/lib/ccache/bin ] && export PATH=/usr/lib/ccache/bin:$PATH # build msg "Starting build()..." build 2>&1 if [ $? -gt 0 ]; then error "Build Failed. Aborting..." exit 2 fi # remove info/doc files cd $startdir rm -rf pkg/usr/info pkg/usr/share/info rm -rf pkg/usr/doc pkg/usr/share/doc # move /usr/share/man files to /usr/man if [ -d pkg/usr/share/man ]; then mkdir -p pkg/usr/man cp -a pkg/usr/share/man/* pkg/usr/man/ rm -rf pkg/usr/share/man fi # remove /usr/share directory if empty if [ -d pkg/usr/share ]; then if [ -z "`ls -1 pkg/usr/share`" ]; then rm -r pkg/usr/share fi fi # compress man pages msg "Compressing man pages..." find $startdir/pkg/{usr{,/local,/share},opt/*}/man -type f 2>/dev/null | while read i ; do ext="${i##*.}" fn="${i##*/}" if [ "$ext" != "gz" -a "$ext" != "bz2" ]; then # update symlinks to this manpage find $startdir/pkg/{usr{,/local,/share},opt/*}/man -lname "$fn" 2> /dev/null | while read ln ; do rm -f "$ln" ln -sf "${fn}.gz" "${ln}.gz" done # compress the original gzip -9 "$i" fi done cd $startdir # strip binaries if [ "$NOSTRIP" = "0" ]; then msg "Stripping debugging symbols from libraries..." find pkg/{,usr,usr/local,opt/*}/lib -type f -not -name "*.dll" -not -name "*.exe" \ -exec /usr/bin/strip --strip-debug '{}' \; 2>&1 \ | grep -v "No such file" | grep -v "format not recognized" msg "Stripping symbols from binaries..." find pkg/{,usr,usr/local,opt/*}/{bin,sbin} -type f -not -name "*.dll" -not -name "*.exe" \ -exec /usr/bin/strip '{}' \; 2>&1 \ | grep -v "No such file" | grep -v "format not recognized" fi # get some package meta info builddate=`LC_ALL= ; LANG= ; date -u "+%a %b %e %H:%M:%S %Y"` if [ "$PACKAGER" != "" ]; then packager="$PACKAGER" else packager="Arch Linux (http://www.archlinux.org)" fi size=`du -cb $startdir/pkg | tail -n 1 | awk '{print $1}'` # write the .PKGINFO file msg "Generating .PKGINFO file..." cd $startdir/pkg echo "# Generated by makepkg $myver" >.PKGINFO echo -n "# " >>.PKGINFO date >>.PKGINFO echo "pkgname = $pkgname" >>.PKGINFO echo "pkgver = $pkgver-$pkgrel" >>.PKGINFO echo "pkgdesc = $pkgdesc" >>.PKGINFO echo "url = $url" >>.PKGINFO for it in "${license[@]}"; do echo "license = $it" >>.PKGINFO done echo "builddate = $builddate" >>.PKGINFO echo "packager = $packager" >>.PKGINFO echo "size = $size" >>.PKGINFO if [ "$CARCH" != "" ]; then echo "arch = $CARCH" >>.PKGINFO fi for it in "${replaces[@]}"; do echo "replaces = $it" >>.PKGINFO done for it in "${groups[@]}"; do echo "group = $it" >>.PKGINFO done for it in "${depends[@]}"; do echo "depend = $it" >>.PKGINFO done for it in "${conflicts[@]}"; do echo "conflict = $it" >>.PKGINFO done for it in "${provides[@]}"; do echo "provides = $it" >>.PKGINFO done for it in "${backup[@]}"; do echo "backup = $it" >>.PKGINFO done # check for an install script if [ "$install" != "" ]; then msg "Copying install script..." cp $startdir/$install $startdir/pkg/.INSTALL fi # build a filelist msg "Generating .FILELIST file..." cd $startdir/pkg tar cvf /dev/null * | sort >.FILELIST # tar it up msg "Compressing package..." cd $startdir/pkg if [ -f $startdir/pkg/.INSTALL ]; then cmd="tar czvf $PKGDEST/$pkgname-$pkgver-$pkgrel.pkg.tar.gz .PKGINFO .FILELIST .INSTALL *" else cmd="tar czvf $PKGDEST/$pkgname-$pkgver-$pkgrel.pkg.tar.gz .PKGINFO .FILELIST *" fi $cmd | sort >../filelist cd $startdir if [ "$CLEANUP" = "1" ]; then msg "Cleaning up..." rm -rf src pkg filelist fi if [ "$RMDEPS" = "1" -a "`id -u`" = "0" -a "$INFAKEROOT" != "1" ]; then msg "Removing installed dependencies..." pacman -R $makedeplist $deplist fi msg "Finished making: $pkgname (`date`)" if [ "$INSTALL" = "1" -a "`id -u`" = "0" -a "$INFAKEROOT" != "1" ]; then msg "Running pacman --upgrade..." pacman --upgrade $PKGDEST/${pkgname}-${pkgver}-${pkgrel}.pkg.tar.gz exit $? fi exit 0