Hi, I've been using pacman for over a year now on DIY-Linux (http://www.diy-linux.org), and to put it simply it's great easy to use and easy to setup. So I thought it was about time I contribute something back... an improved version of makepkg. For the impatient :p see the script attached. makepkg2's use of fakeroot was a bit hackish, it's now very hackish in makepkg3. It also kicks up a few strange bugs (flyspray #6208) because it's being used where it shouldn't be. So I've rewritten/reordered makepkg to reduce the amount of time spent in fakeroot. Specificly it's now only used for... fake_install() (if it exists. See below) build() (if there's no fake_install) tidy_install() - remove docs - move/compress man pages - strip binaries/libraries - remove libtool files - remove empty directories create_package() - create .PKGINFO & .FILELIST - tar it all up everything else is run outside of fakeroot. Over at DIY-Linux we have a patch that adds an extra function to PKGBUILD (fake_install). This way we can prepare, compile, and test the package as the user running makepkg. Then fake_install is run inside fakeroot to install the package. This further reduces the time spent in fakeroot and the possibility of any weird bugs popping up. If the build script doesn't contain a fake_install function the build function is run inside of fakeroot instead (maintaining backwards compatibility with how things work at the moment). Other changes... - reordered the code to reduce the number of if..then..else cases - renamed a lot of variables so they're a bit more descriptive - started to add error codes for the different reasons why makepkg exits - quoted all variables that contain paths so '/home/me/a folder with spaces' doesn't cause errors. (but build scripts are still a point of failure as the majority don't have $startdir in quotes.) - fixed flyspray bug #6468 -- Localized dates for build/install date - added some more error catching - added the ability to choose between usr/man and usr/share/man (var (_MAN_DIR) at the top of the script.) There are still a few things that need fixed, but some discussion is probably required. - Should build deps be removed after a successful build? At the moment deps are only removed if '-r' is used and it removes build deps and runtime deps. An extra option to remove build deps? - Should root be a loud to run makepkg? IMO running makepkg as root is a hanus crime, and anyone foolish enough to do so deserves to have their system burn to the ground as the result of a broken build script. One mistaken key stroke in a build script can result in a successful build or the destruction of a system :p I've added a warning that asks the user if they're sure the want to continue running makepkg as root. With sudo and fakeroot there's no need to run makepkg as root, but thats just my opinion. Andrew #!/bin/bash # # makepkg - make packages compatable for use with pacman # # Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> # Copyright (c) 2005 by Aurelien Foret <orelien@chez.com> # Copyright (c) 2006 by Miklos Vajna <vmiklos@frugalware.org> # Copyright (c) 2005 by Christian Hamar <krics@linuxforum.hu> # Copyright (c) 2006 by Alex Smith <alex@alex-smith.me.uk> # Copyright (c) 2006 by Andras Voroskoi <voroskoi@frugalware.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. # #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # NOTES # ~~~~~~~ # TODO: Rename option names? # --syncdeps > --installdeps # --nodeps > --skipdeps # # TODO: Should root be aloud to run makepkg? # # TODO: Create error codes. # # TODO: Should build deps be removed after build? #____________________________________________________________________ set -e trap 'echo -e "\033[1;31mBANG! \033[1;37mThat didnt sound good. \033[1;33m:(\033[1;0m" >&2' ERR #set -x # For debugging, pipe the output to a file, there's lots of it. _DEBUG_MODE='n' #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Initialize Variables #---------------------------------------------------------------------- _VERSION='3.1.0' _BUG_REPORT_EMAIL='pacman-dev@archlinux.org' _ARG_LIST="$@" # Actions # ~~~~~~~~~ _CLEAN_CACHE='n' _CHECK_DEPS='y' _BUILD_DEPS='n' _SYNC_DEPS='n' _GENERATE_CHKSUM='n' _VALIDATE_CHKSUM='y' _EXTRACT_SOURCE='y' _BUILD_PACKAGE='y' _CLEANUP='n' _REMOVE_DEPS='n' _INSTALL_PACKAGE='n' # Options # ~~~~~~~~~ _FORCE='n' _REPACKAGE='n' _USE_SUDO='n' _LOG_BUILD='n' _USE_COLOR='n' # Miscellaneous # ~~~~~~~~~~~~~~~ _IN_FAKEROOT='n' _PACKAGE_NAME='UNKNOWN' _BUILD_SCRIPT='PKGBUILD' _PKG_EXT='pkg.tar.gz' _MAN_DIR='usr' # For Arch Linux #_MAN_DIR='usr/share' # For DIY-Linux _PACMAN_OPTS='' startdir="$(pwd)" # Exit/Error Codes # ~~~~~~~~~~~~~~~~~~ # TODO: Create exit codes for each different error. E_OK=0 E_UNKNOWN=1 E_USER_ABORT=4 E_INVALID_BUILD_SCRIPT=8 E_MISSING_PROGRAM=16 E_CONFIG_ERROR=32 # For dep checks # ~~~~~~~~~~~~~~~~ . /etc/abs/abs.conf _SRC_ROOT="$ABSROOT" #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # General purpose functions. #---------------------------------------------------------------------- # TODO: Use terminal safe coloring/bolding. See TODO.dan # TODO: Move general functions to an external library file, for all # pacman scripts to use. plain () { if [ $_USE_COLOR = 'y' ]; then echo -e " \033[1;37m$1\033[1;0m" >&2 else echo " $1" >&2 fi } msg () { if [ $_USE_COLOR = 'y' ]; then echo -e "\033[1;32m==>\033[1;37m $1\033[1;0m" >&2 else echo "==> $1" >&2 fi } msg2 () { if [ $_USE_COLOR = 'y' ]; then echo -e " \033[1;34m->\033[1;37m $1\033[1;0m" >&2 else echo " -> $1" >&2 fi } warning () { if [ $_USE_COLOR = 'y' ]; then echo -e "\033[1;33m==> WARNING:\033[1;37m $1\033[1;0m" >&2 else echo "==> WARNING: $1" >&2 fi } error () { if [ $_USE_COLOR = 'y' ]; then echo -e "\033[1;31m==> ERROR:\033[1;37m $1\033[1;0m" >&2 else echo "==> ERROR: $1" >&2 fi } debug () { [ $_DEBUG_MODE = 'y' ] || return if [ $_USE_COLOR = 'y' ]; then echo -e "\033[;35m**> DEBUG:\033[1;37m $1\033[1;0m" >&2 else echo "**> DEBUG: $1" >&2 fi } yn_question () { if [ $_USE_COLOR = 'y' ]; then echo -en " \033[1;1m$1\033[1;0m " >&2 else echo -n " $1 " >&2 fi local answer read answer answer="$(echo $answer | tr [:upper:] [:lower:])" if [ "$answer" = 'y' -o "$answer" = 'yes' ]; then return 0 else return 1 fi } strip_url () { echo "$1" | sed 's|^.*://.*/||g'; } in_array() { local needle=$1 shift 1 [ -z "$1" ] && return 1 for i in $*; do [ "$i" = "$needle" ] && return 0 done return 1 } in_array2 () { local needle=$(echo $1 | tr [:upper:] [:lower:]) local i shift 1 [ -z "$1" ] && return 2 for i in $*; do i=$(echo $i | tr [:upper:] [:lower:]) if [ "$i" = "$needle" ]; then return 0 elif [ "$i" = "!$needle" ]; then return 1 fi done return 2 } check_option () { local ret in_array2 $1 ${options[@]}; ret=$? [ $ret -eq 2 ] && { in_array2 $1 ${OPTIONS[@]}; ret=$?; } [ $ret -eq 2 ] && ret=1 return $ret } check_buildenv () { local ret in_array2 $1 ${BUILDENV[@]}; ret=$? [ $ret -eq -1 ] && ret=1 return $ret } #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Usage functions. #---------------------------------------------------------------------- display_usage () { cat << EOF makepkg version $_VERSION Usage: $0 [options] Actions: -C, --cleancache Clean up source files from the cache. -g, --geninteg Generate integrity checks for source files. -c, --clean Clean up work files after build. -e, --noextract Do not extract source files (use existing src/ dir). -i, --install Install package after successful build. -o, --nobuild Download and extract files only. -R, --repackage Repackage contents of pkg/ without building. -b, --builddeps Build missing dependencies from source -d, --nodeps Skip all dependency checks -r, --rmdeps Remove installed dependencies after a successful build -s, --syncdeps Install missing dependencies with pacman Options: -f, --force Overwrite existing package. -L, --log Log package build process. -m, --nocolor Disable colorized output messages. -p <buildscript> Use an alternate build script (instead of '${_BUILD_SCRIPT}'). If -p is not specified, makepkg will look for '${_BUILD_SCRIPT}'. -S, --usesudo When calling pacman, use sudo. -h, --help Display this help and exit -V, --version Output version information and exit Variables: SRCDEST ... PKGDEST ... Exit Codes: 0 Ok 1 Unknown 4 User abort 8 Invalid build script 16 Missing program 32 Config error These options can be passed to pacman: --noconfirm Do not ask for confirmation when resolving dependencies --noprogressbar Do not show a progress bar when downloading files Report bugs to <${_BUG_REPORT_EMAIL}> EOF } display_version () { echo "makepkg $_VERSION"; } #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # makepkg functions. #---------------------------------------------------------------------- run_build () { if [ "$(type -t $1)" != "function" ]; then error "Unknown function '$1'." exit 1 fi # use distcc if it is requested (check buildenv and PKGBUILD opts) if check_buildenv distcc && [ $(check_option distcc) -ne 1 ]; then [ -d /usr/lib/distcc/bin ] && export PATH="/usr/lib/distcc/bin:$PATH" elif [ $(check_option distcc) -eq 1 ]; then # if it is not wanted, clear the makeflags too export MAKEFLAGS="" fi # use ccache if it is requested (check buildenv and PKGBUILD opts) if check_buildenv ccache && [ $(check_option ccache) -ne 1 ]; then [ -d /usr/lib/ccache/bin ] && export PATH="/usr/lib/ccache/bin:$PATH" fi # clear user-specified makeflags if requested if [ $(check_option makeflags) -eq 1 ]; then export MAKEFLAGS="" fi msg "Running '$1'..." cd "${startdir}/src" # Some locales can break builds. local _LC_ALL="$LC_ALL" ; export LC_ALL='C' local _LANG="$LANG" ; export LANG='C' umask 0022 local ret=0 if [ $_LOG_BUILD = 'y' ]; then local build_log="${startdir}/${_PACKAGE_NAME}-${1}.log" if [ -f "$build_log" ]; then local i=1 while true; do if [ -f "${build_log}.$i" ]; then i=$(($i+1)) else break fi done mv "${build_log}" "${build_log}.$i" fi msg2 "$1 will be logged to $build_log" $1 2>&1 | tee "${build_log}" || ret=${PIPESTATUS[0]} else $1 2>&1 || ret=$? fi export LC_ALL="$_LC_ALL" export LANG="$_LANG" if [ $ret -gt 0 ]; then error "Build FAILED. Aborting" exit 2 # TODO: error code fi } tidy_install () { cd "${startdir}/pkg" msg "Tidying Install..." if ! check_option docs; then msg2 "Removing documentation files..." #fix flyspray bug #5021 rm -rf ${DOC_DIRS[@]} fi local old_man_dir if [ "$_MAN_DIR" = 'usr' ]; then old_man_dir='usr/share' else old_man_dir='usr' fi if [ -d $old_man_dir ]; then msg2 "Moving ${old_man_dir}/man files to /${_MAN_DIR}/man..." mkdir -p ${_MAN_DIR}/man cp -a ${old_man_dir}/man/* ${_MAN_DIR}/man/ rm -rf ${old_man_dir}/man fi if [ -d usr/info ]; then msg2 "Moving /usr/info files to /usr/share/info..." mkdir -p usr/share/info cp -a usr/info/* usr/share/info/ rm -rf usr/info fi [ -e usr/share/info/dir ] && rm -f usr/share/info/dir msg2 "Compressing man pages..." find {${_MAN_DIR}/,usr/local/,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 {${_MAN_DIR},usr/local/,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 if check_option strip; then local file msg2 "Stripping debugging symbols from libraries..." for file in $(LC_ALL=C LANG=C \ find {,usr/,usr/local/,opt/*/}lib -type f -not -name "*.dll" -not -name "*.exe" \ -exec file '{}' \; 2>/dev/null | \ grep 'LSB shared object,' | cut -d : -f 1 ); do strip --strip-debug "${file}" done msg2 "Stripping symbols from binaries..." for file in $(LC_ALL=C LANG=C \ find {,usr/,usr/local/,opt/*/}{bin,sbin} -type f -not -name "*.dll" -not -name "*.exe" \ -exec file '{}' \; 2>/dev/null | \ grep 'LSB executable,' | cut -d : -f 1); do strip --strip-unneeded "${file}" done fi if ! check_option libtool; then msg2 "Removing libtool .la files..." find -type f -name "*.la" -exec rm -f -- '{}' \; fi if ! check_option emptydirs; then msg2 "Removing empty directories..." find -depth -type d -empty -delete; fi } create_package () { cd "${startdir}/pkg" msg "Creating package..." # get some package meta info # flyspray bug 6468 -- Localized dates for build/install date local builddate="$(LC_ALL=C ; LANG=C ; date -u +"%Y-%m-%dT%T%:z")" if [ "$PACKAGER" != "" ]; then local packager="$PACKAGER" else local packager="Arch Linux (http://www.archlinux.org)" fi local size=$(du -sb | awk '{print $1}') # build a filelist - do this first to keep meta files out of the list msg2 "Generating .FILELIST file..." tar -cvf /dev/null * | sort >.FILELIST # write the .PKGINFO file msg2 "Generating .PKGINFO file..." echo "# Generated by makepkg $_VERSION" >.PKGINFO echo "# $(LC_ALL=C; LANG=C; date -u)" >>.PKGINFO echo "pkgname = $pkgname" >>.PKGINFO echo "pkgver = $pkgver-$pkgrel" >>.PKGINFO echo "pkgdesc = $pkgdesc" >>.PKGINFO echo "url = $url" >>.PKGINFO echo "builddate = $builddate" >>.PKGINFO echo "packager = $packager" >>.PKGINFO echo "size = $size" >>.PKGINFO if [ "$CARCH" != "" ]; then echo "arch = $CARCH" >>.PKGINFO fi local it for it in "${license[@]}"; do echo "license = $it" >>.PKGINFO done 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 # TODO: maybe remove this at some point # warn if license array is not present or empty if [ "$license" = "" ]; then warning "Please add a license line to your ${_BUILD_SCRIPT}!" plain "example for GPL'ed software: license=('GPL')." fi local comp_files # check for an install script if [ "$install" != "" ]; then msg2 "Copying install script..." cp "$startdir/$install" .INSTALL comp_files="${comp_files} .INSTALL" fi # do we have a changelog? if [ -f "$startdir/ChangeLog" ]; then msg2 "Copying package change log" cp "$startdir/ChangeLog" .CHANGELOG comp_files="${comp_files} .CHANGELOG" fi # tar it up msg2 "Compressing package..." local pkg_file="$PKGDEST/${_PACKAGE_NAME}.${_PKG_EXT}" comp_files="${comp_files} .PKGINFO .FILELIST" if ! tar -czf $pkg_file $comp_files *; then error "Failed to create package file." exit 1 # TODO: error code fi } #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Dependencie check functions #---------------------------------------------------------------------- checkdeps() { debug "checkdeps() $*" [ $# -eq 0 ] && return pmout=$(pacman $PACMAN_OPTS -T $*) ret=$? if [ $ret -eq 127 ]; then #unresolved deps #strip out the pacman prefix from "requires: xyz" echo $pmout | sed 's|requires:||g' elif [ $ret -ne 0 ]; then error "pacman returned a fatal error ($ret): $pmout" exit 1 fi } handledeps() { debug "handledeps() $*" local R_DEPS_SATISFIED=0 local R_MISSING_DEPS=1 [ $# -eq 0 ] && return $R_DEPS_SATISFIED local deplist="$*" local striplist local dep for dep in $deplist; do depstrip="$(echo $dep | sed -e 's|=.*$||' -e 's|>.*$||' -e 's|<.*$||')" striplist="$striplist $depstrip" done if [ $_BUILD_DEPS = 'n' -a $_SYNC_DEPS = 'n' ]; then return $R_MISSING_DEPS elif [ $_USE_SUDO = 'n' -a $EUID -gt 0 ]; then warning "Cannot auto-install missing dependencies as a normal user without sudo!" plain "Run makepkg as root or with -S to resolve dependencies automatically." return $R_MISSING_DEPS fi if [ $_SYNC_DEPS = 'y' ]; then msg "Installing missing dependencies..." local ret=0 if [ $_USE_SUDO = 'y' ]; then # install missing deps from binary packages (using pacman -S and sudo) sudo pacman $_PACMAN_OPTS -S $striplist || ret=$? else # install missing deps from binary packages (using pacman -S) pacman $_PACMAN_OPTS -S $striplist || ret=$? fi if [ $ret -eq 1 ]; then error "Pacman failed to install missing dependencies." exit 1 # TODO: error code fi elif [ $_BUILD_DEPS = 'y' ]; then msg "Building missing dependencies..." # install missing deps by building them from source. # we look for each package name in $SRCROOT and build it. if [ "$_SRC_ROOT" = "" ]; then error "Source root cannot be found - please make sure it is specified in /etc/abs/abs.conf" exit $E_CONFIG_ERROR fi # TODO: handle version comparators (eg, glibc>=2.2.5) for dep in $striplist; do local candidates="$(find $_SRC_ROOT -type d -name "$dep")" if [ -z "$candidates" ]; then error "Could not find \"$dep\" under $_SRC_ROOT" exit 1 # TODO: error code fi local makepkg_opts='-i -c -b' [ $_REMOVE_DEPS = 'y' ] && makepkg_opts="-r $makepkg_opts" local ret=0 local pkgdir for pkgdir in $candidates; do if [ -f "$pkgdir/$_BUILD_SCRIPT" ]; then cd "$pkgdir" ret=0 PKGBUILD="$PKGBUILD" makepkg $makepkg_opts || ret=$? if [ $ret -eq 0 ]; then continue 2 fi fi done error "Failed to build \"$dep\"" exit 1 # TODO: error code done fi # rerun any additional sh scripts found in /etc/profile.d/ local i for i in /etc/profile.d/*.sh do if [ -x $i ]; then . $i &>/dev/null fi done return $R_DEPS_SATISFIED } resolvedeps() { debug "resolvedeps() $*" local R_DEPS_SATISFIED=0 local R_DEPS_MISSING=1 deplist="$(checkdeps $*)" [ -z "$deplist" ] && return $R_DEPS_SATISFIED if handledeps $deplist; then # check deps again to make sure they were resolved deplist="$(checkdeps $*)" [ -z "$deplist" ] && return $R_DEPS_SATISFIED elif [ $_SYNC_DEPS = 'y' -o $_BUILD_DEPS = 'y' ]; then error "Failed to install all missing dependencies." fi msg "Missing Dependencies:" local dep for dep in $deplist; do msg2 "$dep" done return $R_DEPS_MISSING } # fix flyspray bug #5923 removedeps() { [ $_REMOVE_DEPS = 'n' ] && return [ $_USE_SUDO = 'n' -a $EUID -gt 0 ] && return # runtimedeps and buildtimedeps are set when resolving deps local deplist="$runtimedeps $buildtimedeps" local striplist="" [ -z "$deplist" ] && return local dep for dep in $deplist; do striplist="$striplist $(echo $dep | sed -e 's|=.*$||' -e 's|>.*$||' -e 's|<.*$||')" done msg "Removing installed dependencies..." if [ $_USE_SUDO = 'y' ]; then sudo pacman $_PACMAN_OPTS -Rs $striplist else pacman $_PACMAN_OPTS -Rs $striplist fi # TODO: is error checking (pacman) required/helpful here? } install_package () { [ $_INSTALL_PACKAGE = 'n' ] && return [ $_USE_SUDO = 'n' -a $EUID -gt 0 ] && return local ret=0 msg "Installing package with 'pacman -U'..." if [ $_USE_SUDO = 'y' ]; then sudo pacman $_PACMAN_OPTS -U "${PKGDEST}/${_PACKAGE_NAME}.${_PKG_EXT}" || ret=$? else pacman $_PACMAN_OPTS -U "${PKGDEST}/${_PACKAGE_NAME}.${_PKG_EXT}" || ret=$? fi exit $ret } #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Load config files #---------------------------------------------------------------------- # Preserve enviroment _PKGDEST="${PKGDEST}" _SRCDEST="${SRCDEST}" if [ -r /etc/makepkg.conf ]; then . /etc/makepkg.conf else error "/etc/makepkg.conf not found. cannot continue" exit $E_CONFIG_ERROR fi [ -r ~/.makepkg.conf ] && . ~/.makepkg.conf if check_buildenv color; then _USE_COLOR='y' fi # Environment overrules config file. PKGDEST="${_PKGDEST:-$PKGDEST}"; PKGDEST="${PKGDEST:-$startdir}" # default to $startdir if undefined SRCDEST="${_SRCDEST:-$SRCDEST}"; SRCDEST="${SRCDEST:-$startdir}" # default to $startdir if undefined unset _PKGDEST _SRCDEST #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Parse command line options #---------------------------------------------------------------------- _OPT_SHORT="bcCdefFghiLmop:rRsSV" _OPT_LONG="builddeps,clean,cleancache,nodeps,noextract,force,geninteg,help," _OPT_LONG="${_OPT_LONG}install,log,nocolor,nobuild,rmdeps,repackage,syncdeps," _OPT_LONG="${_OPT_LONG}version,usesudo,noconfirm,noprogressbar" _OPT_TEMP=$(getopt -o $_OPT_SHORT --long $_OPT_LONG -n "$0" -- "$@") eval set -- "$_OPT_TEMP" unset _OPT_SHORT _OPT_LONG _OPT_TEMP while true; do case $1 in # makepkg Options -b|--builddeps) _BUILD_DEPS='y';; -c|--clean) _CLEANUP='y';; -C|--cleancache) _CLEAN_CACHE='y';; -d|--nodeps) _CHECK_DEPS='n';; -e|--noextract) _EXTRACT_SOURCE='n';; -f|--force) _FORCE='y';; -F) _IN_FAKEROOT='y';; -g|--geninteg) _GENERATE_CHKSUM='y';; -i|--install) _INSTALL_PACKAGE='y';; -L|--log) _LOG_BUILD='y';; -m|--nocolor) _USE_COLOR='n';; -o|--nobuild) _BUILD_PACKAGE='n';; -r|--rmdeps) _REMOVE_DEPS='y';; -R|--repackage) _REPACKAGE='y';; -s|--syncdeps) _SYNC_DEPS='y';; -S|--usesudo) _USE_SUDO='y';; -p) _BUILD_SCRIPT="$2"; shift;; # pacman Options --noconfirm) _PACMAN_OPTS="$_PACMAN_OPTS --noconfirm";; --noprogressbar) _PACMAN_OPTS="$_PACMAN_OPTS --noprogressbar";; -h|--help) display_usage; exit $E_OK;; -V|--version) display_version; exit $E_OK;; --) OPTIND=0; shift; break;; *) display_usage; exit $E_UNKNOWN;; esac shift done #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Clean Cache #---------------------------------------------------------------------- if [ $_CLEAN_CACHE = 'y' ]; then #fix flyspray feature request #5223 if [ -n "$SRCDEST" -a "$SRCDEST" != "$startdir" ]; then msg "Cleaning up ALL files from ${SRCDEST}." if yn_question "Are you sure you wish to do this? [Y/n]"; then ret=0 rm "$SRCDEST"/* || ret=$? if [ $ret -ne 0 ]; then error "Problem removing files; you may not have correct permissions in $SRCDEST" exit 1 # TODO: error code else # removal worked msg "Source cache cleaned." exit $E_OK fi else # answer = no msg "No files have been removed." exit $E_OK fi else # $SRCDEST is $startdir, two possibilities error "Source destination must be defined in makepkg.conf." plain "In addition, please run makepkg -C outside of your cache directory." exit $E_CONFIG_ERROR fi fi #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Privilage checks #---------------------------------------------------------------------- if [ $_IN_FAKEROOT = 'n' ]; then if [ $EUID -eq 0 ]; then warning "Running makepkg as root is a \033[1;31mBAD\033[1;37m idea!" plain "" if ! yn_question "Are you sure you want to continue? [N/y]"; then error "Aborting..." exit $E_USER_ABORT fi elif check_buildenv fakeroot; then if ! type -p fakeroot &>/dev/null; then warning "Fakeroot is not installed. Building as an unprivileged user" plain "will result in non-root ownership of the packaged files. Install" plain "the fakeroot package to correctly build as a non-root user." plain "" if ! yn_question "Are you sure you want to continue? [N/y]"; then error "Aborting..." exit $E_USER_ABORT fi fi else warning "Running makepkg as an unprivileged user will result in non-root" plain "ownership of the packaged files. Try using the fakeroot environment by" plain "placing 'fakeroot' in the BUILDENV array in /etc/makepkg.conf." plain "" if ! yn_question "Are you sure you want to continue? [N/y]"; then error "Aborting..." exit $E_USER_ABORT fi fi else if [ -z "$FAKEROOTKEY" ]; then error "Do not use the -F option. This option is only used by makepkg when" plain "it is calling it's self inside fakeroot." plain "" exit 1 # TODO: error code fi fi if [ $_USE_SUDO = 'y' -a ! "$(type -p sudo)" ]; then error "Can not find the sudo binary! Is sudo installed?" exit $E_MISSING_PROGRAM elif [ $EUID -gt 0 -a $_INSTALL_PACKAGE = 'y' ]; then # TODO: Should this be an error and exit? warning "makepkg will not be able to install this package as an unprivilaged user." plain "Use the --usesudo (or -s) option to install this package after it has been" plain "built." plain "" sleep 3 fi #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Load $_BUILD_SCRIPT. #---------------------------------------------------------------------- unset pkgname pkgver pkgrel pkgdesc url license groups provides md5sums force unset replaces depends conflicts backup source install build makedepends unset options noextract if [ ! -r "$_BUILD_SCRIPT" ]; then error "$_BUILD_SCRIPT does not exist." exit $E_INVALID_BUILD_SCRIPT #TODO this is an attempt at a generic way to unset all package specific #variables in a PKGBUILD #else # #this is fun.... we'll unset # for var in $(grep "=" $_BUILD_SCRIPT | sed "s|.*\(\<.*\>\)=.*|\1|g"); do # unset $var # done fi source "$_BUILD_SCRIPT" #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Sanity checks for $_BUILD_SCRIPT. #---------------------------------------------------------------------- # check for no-no's in the build script if [ -z "$pkgver" ]; then error "pkgver is not allowed to be empty." exit $E_INVALID_BUILD_SCRIPT fi if echo "$pkgver" | grep -q '-'; then error "pkgver is not allowed to contain hyphens." exit $E_INVALID_BUILD_SCRIPT fi if [ -z "$pkgrel" ]; then error "pkgrel is not allowed to be empty." exit $E_INVALID_BUILD_SCRIPT fi if echo "$pkgrel" | grep -q '-'; then error "pkgrel is not allowed to contain hyphens." exit $E_INVALID_BUILD_SCRIPT fi if ! in_array $CARCH ${arch[@]}; then error "$pkgname is not available for the '$CARCH' architecture." plain "Note that many packages may need a line added to their $_BUILD_SCRIPT" plain "such as arch=('$CARCH')." exit 1 # TODO: error code fi _PACKAGE_NAME="${pkgname}-${pkgver}-${pkgrel}-${CARCH}" if [ "$install" -a ! -f "$install" ]; then error "install scriptlet ($install) does not exist." exit $E_INVALID_BUILD_SCRIPT fi if [ -f "$PKGDEST/${_PACKAGE_NAME}.${_PKG_EXT}" \ -a $_FORCE = 'n' -a $_GENERATE_CHKSUM = 'n' ]; then if [ $_INSTALL_PACKAGE = 'y' ]; then warning "a package has already been built, installing existing package." installpackage else error "a package has already been built. (use -f to overwrite)" exit 1 # TODO: error code fi fi #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Build package inside fakeroot. #---------------------------------------------------------------------- # Run the bear minimum in fakeroot. # flyspray bug 6208 -- using makepkg with fakeroot gives an error if [ $_IN_FAKEROOT = 'y' ]; then if [ $_REPACKAGE = 'y' ]; then warning "Skipping build..." else if [ "$(type -t fake_install)" != "function" ]; then run_build build else run_build fake_install fi tidy_install fi create_package msg "Leaving fakeroot enviroment." exit $E_OK fi msg "Making package: $pkgname $pkgver-$pkgrel ($(date))" if [ $_REPACKAGE = 'n' ]; then msg "Preparing work area..." if [ -d "${startdir}/src" ]; then msg2 "Removing existing src/ directory..." rm -rf "${startdir}/src" fi if [ -d "${startdir}/pkg" ]; then msg2 "Removing existing pkg/ directory..." rm -rf "${startdir}/pkg" fi fi #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Check dependencies. #---------------------------------------------------------------------- # fix flyspray bug #5973 if [ $_CHECK_DEPS = 'n' -o $_GENERATE_CHKSUM = 'y' -o \ $_BUILD_PACKAGE = 'n' -o $_REPACKAGE = 'y' ]; then if [ $_CHECK_DEPS = 'n' ]; then warning "Skipping dependecy checks." fi elif [ ! "$(type -p pacman)" ]; then warning "pacman was not found in PATH. skipping dependency checks." else deperr=0 unset runtimedeps buildtimedeps msg "Checking Runtime Dependencies..." if resolvedeps ${depends[@]}; then # deplist is a global variable set by resolvedeps runtimedeps="$deplist" else deperr=1 fi msg "Checking Buildtime Dependencies..." if resolvedeps ${makedepends[@]}; then # deplist is a global variable set by resolvedeps buildtimedeps="$deplist" else deperr=1 fi if [ $deperr -eq 1 ]; then error "could not resolve all dependencies." exit 1 # TODO: error code fi unset deperr fi #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Retrieve source. #---------------------------------------------------------------------- msg "Retrieving source files..." cd "${startdir}" mkdir -p src; cd src for netfile in ${source[@]}; do file="$(strip_url "$netfile")" if [ -f "../$file" ]; then msg2 "Found $file in build directory." cp "../$file" . elif [ -f "$SRCDEST/$file" ]; then msg2 "Using cached copy of ${file}." cp "$SRCDEST/$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 $E_CONFIG_ERROR fi ftpclient="$(echo $FTPAGENT | awk {'print $1'})" if [ ! -x "$ftpclient" ]; then error "ftpclient $(basename $ftpclient) is not installed." msg "Aborting..." exit $E_MISSING_PROGRAM 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 # TODO: error code fi msg2 "Downloading ${file}..." ret=0 $FTPAGENT "$netfile" || ret=$? # fix flyspray bug #3289 if [ $ret -gt 0 ]; then error "Failure while downloading $file" msg "Aborting..." #rm "$file" exit 1 # TODO: error code fi if [ -n "$SRCDEST" ]; then ret=0 mkdir -p "$SRCDEST" && cp "$file" "$SRCDEST" || ret=$? if [ $ret -ne 0 ]; then warning "You do not have correct permissions to cache source in $SRCDEST" cp "$file" .. fi else cp "$file" .. fi fi done unset netfile file ftpclient proto ret #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Generate source file checksums. #---------------------------------------------------------------------- if [ $_GENERATE_CHKSUM = 'y' ]; then msg "Generating checksums for source files..." plain "" for integ in ${INTEGRITY_CHECK[@]}; do integ="$(echo $integ | tr [:upper:] [:lower:])" case "$integ" in md5|sha1|sha256|sha384|sha512) : ;; *) error "Invalid integrity algorithm '$integ' specified." exit $E_CONFIG_ERROR;; esac if ! type -p "${integ}sum" &>/dev/null; then error "Can not find the ${integ}sum program." exit $E_MISSING_PROGRAM fi ct=0 numsrc=${#source[@]} echo -n "${integ}sums=(" for netfile in "${source[@]}"; do file="$(strip_url "$netfile")" if [ ! -r "${file}" ]; then plain "" error "Unable to find '${file}' to generate checksum." exit 1 # TODO: error code fi sum=$(${integ}sum "$file" | cut -d ' ' -f 1) [ $ct -gt 0 ] && echo -ne "\t " echo -n "'$sum'" ct=$(($ct+1)) [ $ct -lt $numsrc ] && echo done echo ')' done exit $E_OK fi #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Validate source file checksums. #---------------------------------------------------------------------- if [ $_EXTRACT_SOURCE = 'n' -o $_REPACKAGE = 'y' ]; then warning "Skipping source integrity checks." elif [ $_VALIDATE_CHKSUM = 'y' ]; then for integ in ${INTEGRITY_CHECK[@]}; do integ="$(echo $integ | tr [:upper:] [:lower:])" case "$integ" in md5|sha1|sha256|sha384|sha512) : ;; *) error "Invalid integrity algorithm '$integ' specified." exit $E_CONFIG_ERROR;; esac if ! type -p "${integ}sum" &>/dev/null; then error "Can not find the ${integ}sum program." exit $E_MISSING_PROGRAM fi chksums=($(eval echo \${${integ}sums[@]})) if [ ${#chksums[@]} -ne ${#source[@]} ]; then warning "Integrity checks (${integ}) are missing or incomplete." else msg "Validating source files with ${integ}sums..." errors=0 idx=0 for netfile in "${source[@]}"; do file="$(strip_url "$netfile")" echo -n " $file ... " >&2 if echo "${chksums[$idx]} $file" | ${integ}sum --status -c -; then echo "Passed" >&2 else echo "FAILED" >&2 errors=1 fi idx=$(($idx+1)) done if [ $errors -gt 0 ]; then error "One or more files did not pass the validity check!" exit 1 # TODO: error code fi fi done unset integ chksums errors idx netfile file fi #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Extract sources. #---------------------------------------------------------------------- if [ $_EXTRACT_SOURCE = 'n' -o $_REPACKAGE = 'y' ]; then warning "Skipping source extraction -- using existing src/ tree." else msg "Extracting Sources..." for netfile in "${source[@]}"; do unziphack=0 file="$(strip_url "$netfile")" if in_array "$file" ${noextract[@]}; then #skip source files in the noextract=() array # these are marked explicitly to NOT be extracted continue fi # fix flyspray #6246 file_type="$(file -biz "$file")" cmd='' case "$file_type" in *application/x-tar*) cmd="tar --no-same-owner -xf $file" ;; *application/x-zip*) unziphack=1 cmd="unzip -qqo $file" ;; *application/x-cpio*) cmd="bsdtar -x -f $file" ;; *application/x-gzip*) cmd="gunzip -f $file" ;; *application/x-bzip*) cmd="bunzip2 -f $file" ;; esac if [ "$cmd" != "" ]; then msg2 "$cmd" ret=0 $cmd || ret=$? if [ $ret -ne 0 ]; then # unzip will return a 1 as a warning, it is not an error if [ $unziphack -eq 1 -o $ret -ne 1 ]; then error "Failed to extract $file" msg "Aborting..." exit 1 # TODO: error code fi fi fi done unset netfile file file_type cmd unziphack ret fi #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Build package #---------------------------------------------------------------------- if [ $_BUILD_PACKAGE = 'n' ]; then msg "Sources are ready." exit $E_OK else cd "$startdir" mkdir -p pkg if [ $EUID -eq 0 ]; then warning "It is not a good idea to run makepkg as root." if [ $_REPACKAGE = 'y' ]; then warning "Skipping build." else cd "${startdir}/src" run_build build [ "$(type -t fake_install)" = "function" ] && run_build fake_install tidy_install fi create_package else cd "${startdir}/src" if [ $_REPACKAGE = 'n' -a "$(type -t fake_install)" = "function" ]; then run_build build fi msg "Entering fakeroot environment." cd "${startdir}" ret=0 fakeroot -- $0 -F $_ARG_LIST || ret=$? [ $ret -ne 0 ] && exit $ret unset ret fi fi #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Clean up #---------------------------------------------------------------------- if [ $_CLEANUP = 'y' ]; then msg "Cleaning up..." cd "$startdir" rm -rf src pkg rm -f "${_PACKAGE_NAME}.log*" fi removedeps msg "Finished making: $pkgname ($(date))" install_package exit $E_OK # vim:set ts=2 sw=2 noet: