#!/bin/bash -e # # makepkg - make packages compatable for use with pacman # @configure_input@ # # Copyright (c) 2002-2008 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, see <http://www.gnu.org/licenses/>. # # makepkg uses quite a few external programs during its execution. You # need to have at least the following installed for makepkg to function: # awk, bsdtar (libarchive), bzip2, coreutils, fakeroot, find (findutils), # getopt (util-linux), gettext, grep, gzip, openssl, sed # gettext initialization export TEXTDOMAIN='pacman' export TEXTDOMAINDIR='@localedir@' # file -i does not work on Mac OSX unless legacy mode is set export COMMAND_MODE='legacy' myver='@PACKAGE_VERSION@' confdir='@sysconfdir@' startdir="$PWD" srcdir="$startdir/src" pkgdir="$startdir/pkg" known_options=('strip' 'docs' 'libtool' 'emptydirs' 'zipman' 'ccache' 'distcc' 'makeflags' 'force') readonly -a known_options # Options ASROOT=0 CLEANUP=0 CLEANCACHE=0 DEP_BIN=0 FORCE=0 INFAKEROOT=0 GENINTEG=0 INSTALL=0 NOBUILD=0 NODEPS=0 NOEXTRACT=0 RMDEPS=0 REPKG=0 LOGGING=0 SOURCEONLY=0 IGNOREARCH=0 HOLDVER=0 # Forces the pkgver of the current PKGBUILD. Used by the fakeroot call # when dealing with svn/cvs/etc PKGBUILDs. FORCE_VER="" PACMAN_OPTS= ### SUBROUTINES ### plain() { local mesg=$1; shift if [ -t 2 -a ! "$USE_COLOR" = "n" -a "$(check_buildenv color)" = "y" ]; then printf "\033[1;1m ${mesg}\033[1;0m\n" "$@" >&2 else printf " ${mesg}\n" "$@" >&2 fi } msg() { local mesg=$1; shift if [ -t 2 -a ! "$USE_COLOR" = "n" -a "$(check_buildenv color)" = "y" ]; then printf "\033[1;32m==>\033[1;0m\033[1;1m ${mesg}\033[1;0m\n" "$@" >&2 else printf "==> ${mesg}\n" "$@" >&2 fi } msg2() { local mesg=$1; shift if [ -t 2 -a ! "$USE_COLOR" = "n" -a "$(check_buildenv color)" = "y" ]; then printf "\033[1;34m ->\033[1;0m\033[1;1m ${mesg}\033[1;0m\n" "$@" >&2 else printf " -> ${mesg}\n" "$@" >&2 fi } warning() { local mesg=$1; shift if [ -t 2 -a ! "$USE_COLOR" = "n" -a "$(check_buildenv color)" = "y" ]; then printf "\033[1;33m==> $(gettext "WARNING:")\033[1;0m\033[1;1m ${mesg}\033[1;0m\n" "$@" >&2 else printf "==> $(gettext "WARNING:") ${mesg}\n" "$@" >&2 fi } error() { local mesg=$1; shift if [ -t 2 -a ! "$USE_COLOR" = "n" -a "$(check_buildenv color)" = "y" ]; then printf "\033[1;31m==> $(gettext "ERROR:")\033[1;0m\033[1;1m ${mesg}\033[1;0m\n" "$@" >&2 else printf "==> $(gettext "ERROR:") ${mesg}\n" "$@" >&2 fi } ## # Special exit call for traps, Don't print any error messages when inside, # the fakeroot call, the error message will be printed by the main call. ## trap_exit() { if [ "$INFAKEROOT" = "0" ]; then echo error "$@" fi exit 1 } ## # Clean up function. Called automatically when the script exits. ## clean_up() { local EXIT_CODE=$? if [ "$INFAKEROOT" = "1" ]; then # Don't clean up when leaving fakeroot, we're not done yet. return fi if [ $EXIT_CODE -eq 0 -a "$CLEANUP" = "1" ]; then # If it's a clean exit and -c/--clean has been passed... msg "$(gettext "Cleaning up...")" rm -rf "$pkgdir" "$srcdir" if [ "$pkgname" != "" ]; then # Can't do this unless the BUILDSCRIPT has been sourced. rm -f "${pkgname}-${pkgver}-${pkgrel}-${CARCH}.log*" fi fi remove_deps } ## # Signal Traps ## trap 'clean_up' 0 trap 'trap_exit "$(gettext "TERM signal caught. Exiting...")"' TERM HUP QUIT trap 'trap_exit "$(gettext "Aborted by user! Exiting...")"' INT trap 'trap_exit "$(gettext "An unknown error has occured. Exiting...")"' ERR strip_url() { echo "$1" | sed 's|^.*://.*/||g' } ## # Checks to see if options are present in makepkg.conf or PKGBUILD; # PKGBUILD options always take precedence. # # usage : check_option( $option ) # return : y - enabled # n - disabled # ? - not found ## check_option() { local ret=$(in_opt_array "$1" ${options[@]}) if [ "$ret" != '?' ]; then echo $ret return fi # fall back to makepkg.conf options ret=$(in_opt_array "$1" ${OPTIONS[@]}) if [ "$ret" != '?' ]; then echo $ret return fi echo '?' # Not Found } ## # Check if option is present in BUILDENV # # usage : check_buildenv( $option ) # return : y - enabled # n - disabled # ? - not found ## check_buildenv() { echo $(in_opt_array "$1" ${BUILDENV[@]}) } ## # usage : in_opt_array( $needle, $haystack ) # return : y - enabled # n - disabled # ? - not found ## in_opt_array() { local needle=$(echo $1 | tr '[:upper:]' '[:lower:]'); shift local opt for opt in "$@"; do opt=$(echo $opt | tr '[:upper:]' '[:lower:]') if [ "$opt" = "$needle" ]; then echo 'y' # Enabled return elif [ "$opt" = "!$needle" ]; then echo 'n' # Disabled return fi done echo '?' # Not Found } ## # usage : in_array( $needle, $haystack ) # return : 0 - found # 1 - not found ## in_array() { local needle=$1; shift [ -z "$1" ] && return 1 # Not Found local item for item in "$@"; do [ "$item" = "$needle" ] && return 0 # Found done return 1 # Not Found } get_downloadclient() { # $1 = url with valid protocol prefix local url=$1 local proto=$(echo "$url" | sed 's|://.*||') # loop through DOWNLOAD_AGENTS variable looking for protocol local i for i in "${DLAGENTS[@]}"; do local handler=$(echo $i | sed 's|::.*||') if [ "$proto" == "$handler" ]; then agent=$(echo $i | sed 's|^.*::||') break fi done # if we didn't find an agent, return an error if [ -z "$agent" ]; then error "$(gettext "There is no agent set up to handle %s URLs. Check %s.")" "$proto" "$confdir/makepkg.conf" plain "$(gettext "Aborting...")" exit 1 # $E_CONFIG_ERROR fi # ensure specified program is installed local program="$(echo $agent | awk '{print $1 }')" if [ ! -x "$program" ]; then local baseprog=$(basename $program) error "$(gettext "The download program %s is not installed.")" "$baseprog" plain "$(gettext "Aborting...")" exit 1 # $E_MISSING_PROGRAM fi echo "$agent" } download_file() { local dlcmd=$1 local netfile=$2 local file=$3 if echo "$dlcmd" | grep -q "%u" ; then dlcmd=${dlcmd//%o/$file.part} dlcmd=${dlcmd//%u/$netfile} else dlcmd="$dlcmd $netfile" fi $dlcmd } check_deps() { [ $# -gt 0 ] || return pmout=$(pacman $PACMAN_OPTS -T "$@") ret=$? if [ $ret -eq 127 ]; then #unresolved deps echo "$pmout" elif [ $ret -ne 0 ]; then error "$(gettext "Pacman returned a fatal error (%i): %s")" "$ret" "$pmout" exit 1 fi } handledeps() { local R_DEPS_SATISFIED=0 local R_DEPS_MISSING=1 [ $# -eq 0 ] && return $R_DEPS_SATISFIED local deplist="$*" if [ "$DEP_BIN" = "0" ]; then return $R_DEPS_MISSING fi if [ "$DEP_BIN" = "1" ]; then # install missing deps from binary packages (using pacman -S) msg "$(gettext "Installing missing dependencies...")" local ret=0 if [ "$ASROOT" = 0 ]; then sudo pacman $PACMAN_OPTS -S --asdeps $deplist || ret=$? else pacman $PACMAN_OPTS -S --asdeps $deplist || ret=$? fi if [ $ret -ne 0 ]; then error "$(gettext "Pacman failed to install missing dependencies.")" exit 1 # TODO: error code fi fi # we might need the new system environment # set -e can cause problems during sourcing profile scripts set +e source /etc/profile &>/dev/null set -e return $R_DEPS_SATISFIED } resolve_deps() { # $pkgdeps is a GLOBAL variable, used by remove_deps() local R_DEPS_SATISFIED=0 local R_DEPS_MISSING=1 local deplist="$(check_deps $*)" if [ "$deplist" = "" ]; then return $R_DEPS_SATISFIED fi if handledeps $deplist; then pkgdeps="$pkgdeps $deplist" # check deps again to make sure they were resolved deplist="$(check_deps $*)" [ "$deplist" = "" ] && return $R_DEPS_SATISFIED elif [ "$DEP_BIN" = "1" ]; then error "$(gettext "Failed to install all missing dependencies.")" fi msg "$(gettext "Missing Dependencies:")" local dep for dep in $deplist; do msg2 "$dep" done return $R_DEPS_MISSING } # fix flyspray bug #5923 remove_deps() { # $pkgdeps is a GLOBAL variable, set by resolve_deps() [ "$RMDEPS" = "0" ] && return [ "$pkgdeps" = "" ] && return local dep depstrip deplist deplist="" for dep in $pkgdeps; do depstrip=$(echo $dep | sed -e 's|=.*$||' -e 's|>.*$||' -e 's|<.*$||') deplist="$deplist $depstrip" done msg "Removing installed dependencies..." local ret=0 if [ "$ASROOT" = "0" ]; then sudo pacman $PACMAN_OPTS -Rns $deplist || ret=$? else pacman $PACMAN_OPTS -Rns $deplist || ret=$? fi # Fixes FS#10039 - exit cleanly as package has built successfully if [ $ret -ne 0 ]; then warning "$(gettext "Failed to remove installed dependencies.")" return 0 fi } download_sources() { msg "$(gettext "Retrieving Sources...")" if [ ! -w "$SRCDEST" ] ; then error "$(gettext "You do not have write permission to store downloads in %s.")" "$SRCDEST" plain "$(gettext "Aborting...")" exit 1 fi pushd "$SRCDEST" &>/dev/null local netfile for netfile in "${source[@]}"; do local file=$(strip_url "$netfile") if [ -f "$startdir/$file" ]; then msg2 "$(gettext "Found %s in build dir")" "$file" rm -f "$srcdir/$file" ln -s "$startdir/$file" "$srcdir/" continue elif [ -f "$SRCDEST/$file" ]; then msg2 "$(gettext "Using cached copy of %s")" "$file" rm -f "$srcdir/$file" ln -s "$SRCDEST/$file" "$srcdir/" continue fi # if we get here, check to make sure it was a URL, else fail if [ "$file" = "$netfile" ]; then error "$(gettext "%s was not found in the build directory and is not a URL.")" "$netfile" exit 1 # $E_MISSING_FILE fi # find the client we should use for this URL local dlclient=$(get_downloadclient "$netfile") || exit $? msg2 "$(gettext "Downloading %s...")" "$file" # fix flyspray bug #3289 local ret=0 download_file "$dlclient" "$netfile" "$file" || ret=$? if [ $ret -gt 0 ]; then error "$(gettext "Failure while downloading %s")" "$file" plain "$(gettext "Aborting...")" exit 1 fi if echo "$dlclient" | grep -q "%o" ; then mv -f "$SRCDEST/$file.part" "$SRCDEST/$file" fi rm -f "$srcdir/$file" ln -s "$SRCDEST/$file" "$srcdir/" done popd &>/dev/null } generate_checksums() { msg "$(gettext "Generating checksums for source files...")" plain "" local integ for integ in ${INTEGRITY_CHECK[@]}; do integ="$(echo $integ | tr '[:upper:]' '[:lower:]')" case "$integ" in md5|sha1|sha256|sha384|sha512) : ;; *) error "$(gettext "Invalid integrity algorithm '%s' specified.")" "$integ" exit 1;; # $E_CONFIG_ERROR esac if [ ! $(type -p openssl) ]; then error "$(gettext "Cannot find openssl.")" exit 1 # $E_MISSING_PROGRAM fi local ct=0 local numsrc=${#source[@]} echo -n "${integ}sums=(" local i=0; local indent='' while [ $i -lt $((${#integ}+6)) ]; do indent="$indent " i=$(($i+1)) done local netfile for netfile in "${source[@]}"; do local file="$(strip_url "$netfile")" if [ ! -f "$file" ] ; then if [ ! -f "$SRCDEST/$file" ] ; then error "$(gettext "Unable to find source file %s to generate checksum.")" "$file" plain "$(gettext "Aborting...")" exit 1 else file="$SRCDEST/$file" fi fi local sum="$(openssl dgst -${integ} "$file" | awk '{print $NF}')" [ $ct -gt 0 ] && echo -n "$indent" echo -n "'$sum'" ct=$(($ct+1)) [ $ct -lt $numsrc ] && echo done echo ")" done } check_checksums() { local integ for integ in ${INTEGRITY_CHECK[@]}; do integ="$(echo $integ | tr '[:upper:]' '[:lower:]')" case "$integ" in md5|sha1|sha256|sha384|sha512) : ;; *) error "$(gettext "Invalid integrity algorithm '%s' specified")" "$integ" exit 1;; # $E_CONFIG_ERROR esac if [ ! $(type -p openssl) ]; then error "$(gettext "Cannot find openssl.")" exit 1 # $E_MISSING_PROGRAM fi local integrity_sums=($(eval echo "\${${integ}sums[@]}")) if [ ${#integrity_sums[@]} -eq ${#source[@]} ]; then msg "$(gettext "Validating source files with %s...")" "${integ}sums" local errors=0 local idx=0 local file for file in "${source[@]}"; do file="$(strip_url "$file")" echo -n " $file ... " >&2 if [ ! -f "$file" ] ; then if [ ! -f "$file" ] ; then echo "$(gettext "NOT FOUND")" >&2 errors=1 continue else file="$SRCDEST/$file" fi fi local expectedsum="$(echo ${integrity_sums[$idx]} | tr '[A-F]' '[a-f]')" local realsum="$(openssl dgst -${integ} "$file" | awk '{print $NF}')" if [ "$expectedsum" = "$realsum" ]; then echo "$(gettext "Passed")" >&2 else echo "$(gettext "FAILED")" >&2 errors=1 fi idx=$((idx + 1)) done if [ $errors -gt 0 ]; then error "$(gettext "One or more files did not pass the validity check!")" exit 1 # TODO: error code fi else warning "$(gettext "Integrity checks (%s) are missing or incomplete.")" "$integ" fi done } extract_sources() { msg "$(gettext "Extracting Sources...")" local netfile for netfile in "${source[@]}"; do 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 if [ ! -f "$file" ] ; then if [ ! -f "$SRCDEST/$file" ] ; then error "$(gettext "Unable to find source file %s for extraction.")" "$file" plain "$(gettext "Aborting...")" exit 1 else file="$SRCDEST/$file" fi fi # fix flyspray #6246 local file_type=$(file -bizL "$file") local cmd='' case "$file_type" in *application/x-tar*|*application/zip*|*application/x-zip*|*application/x-cpio*) cmd="bsdtar -x -f $file" ;; *application/x-gzip*) cmd="gunzip -d -f $file" ;; *application/x-bzip*) cmd="bunzip2 -f $file" ;; *) # Don't know what to use to extract this file, # skip to the next file continue;; esac local ret=0 msg2 "$cmd" $cmd || ret=$? if [ $ret -ne 0 ]; then error "$(gettext "Failed to extract %s")" "$file" plain "$(gettext "Aborting...")" exit 1 fi done if [ $EUID -eq 0 ]; then # change perms of all source files to root user & root group chown -R 0:0 "$srcdir" fi } run_build() { # use distcc if it is requested (check buildenv and PKGBUILD opts) if [ "$(check_buildenv distcc)" = "y" -a "$(check_option distcc)" != "n" ]; then [ -d /usr/lib/distcc/bin ] && export PATH="/usr/lib/distcc/bin:$PATH" export DISTCC_HOSTS elif [ "$(check_option distcc)" = "n" ]; then # if it is not wanted, clear the makeflags too MAKEFLAGS="" fi # use ccache if it is requested (check buildenv and PKGBUILD opts) if [ "$(check_buildenv ccache)" = "y" -a "$(check_option ccache)" != "n" ]; then [ -d /usr/lib/ccache/bin ] && export PATH="/usr/lib/ccache/bin:$PATH" fi # clear user-specified makeflags if requested if [ "$(check_option makeflags)" = "n" ]; then MAKEFLAGS="" fi msg "$(gettext "Starting build()...")" cd "$srcdir" # ensure all necessary build variables are exported export CFLAGS CXXFLAGS MAKEFLAGS CHOST local ret=0 if [ "$LOGGING" = "1" ]; then BUILDLOG="${startdir}/${pkgname}-${pkgver}-${pkgrel}-${CARCH}.log" if [ -f "$BUILDLOG" ]; then local i=1 while true; do if [ -f "$BUILDLOG.$i" ]; then i=$(($i +1)) else break fi done mv "$BUILDLOG" "$BUILDLOG.$i" fi build 2>&1 | tee "$BUILDLOG"; ret=${PIPESTATUS[0]} else build 2>&1 || ret=$? fi if [ $ret -gt 0 ]; then error "$(gettext "Build Failed.")" plain "$(gettext "Aborting...")" remove_deps exit 2 # $E_BUILD_FAILED fi } tidy_install() { cd "$pkgdir" msg "$(gettext "Tidying install...")" if [ "$(check_option docs)" = "n" ]; then msg2 "$(gettext "Removing doc files...")" #fix flyspray bug #5021 rm -rf ${DOC_DIRS[@]} fi if [ "$(check_option zipman)" = "y" ]; then msg2 "$(gettext "Compressing man and info pages...")" local manpage mandirs ext file link hardlinks hl mandirs=({usr{,/local}{,/share},opt/*}/{man,info}) find ${mandirs[@]} -type f 2>/dev/null | while read manpage ; do # check file still exists (potentially compressed with hard link) if [ -f ${manpage} ]; then ext="${manpage##*.}" file="${manpage##*/}" if [ "$ext" != "gz" -a "$ext" != "bz2" ]; then # update symlinks to this manpage find ${mandirs[@]} -lname "$file" 2>/dev/null | while read link ; do rm -f "$link" ln -sf "${file}.gz" "${link}.gz" done # find hard links and remove them # the '|| true' part keeps the script from bailing if find returned an # error, such as when one of the man directories doesn't exist hardlinks="$(find ${mandirs[@]} \! -name "$file" -samefile "$manpage" 2>/dev/null)" || true for hl in ${hardlinks}; do rm -f "${hl}"; done # compress the original gzip -9 "$manpage" # recreate hard links removed earlier for hl in ${hardlinks}; do ln "${manpage}.gz" "${hl}.gz" chmod 644 ${hl}.gz done fi fi done fi if [ "$(check_option strip)" = "y" ]; then msg2 "$(gettext "Stripping debugging symbols from binaries and libraries...")" local binary if [ -z "${STRIP_DIRS[*]}" ]; then # fall back to default value STRIP_DIRS=(bin lib sbin usr/{bin,lib,sbin,local/{bin,lib,sbin}} opt/*/{bin,lib,sbin}) fi find ${STRIP_DIRS[@]} -type f 2>/dev/null | while read binary ; do case "$(file -biz "$binary")" in *application/x-sharedlib*) # Libraries (.so) /usr/bin/strip --strip-debug "$binary";; *application/x-archive*) # Libraries (.a) /usr/bin/strip --strip-debug "$binary";; *application/x-executable*) # Binaries /usr/bin/strip "$binary";; esac done fi if [ "$(check_option libtool)" = "n" ]; then msg2 "$(gettext "Removing libtool .la files...")" find . ! -type d -name "*.la" -exec rm -f -- '{}' \; fi if [ "$(check_option emptydirs)" = "n" ]; then msg2 "$(gettext "Removing empty directories...")" find . -depth -type d -empty -delete fi } create_package() { if [ ! -d "$pkgdir" ]; then error "$(gettext "Missing pkg/ directory.")" plain "$(gettext "Aborting...")" exit 1 # $E_MISSING_PKGDIR fi cd "$pkgdir" msg "$(gettext "Creating package...")" local builddate=$(date -u "+%s") if [ "$PACKAGER" != "" ]; then local packager="$PACKAGER" else local packager="Unknown Packager" fi local size=$(du -sk | awk '{print $1 * 1024}') # write the .PKGINFO file msg2 "$(gettext "Generating .PKGINFO file...")" echo "# Generated by makepkg $myver" >.PKGINFO if [ "$INFAKEROOT" = "1" ]; then echo "# using $(fakeroot -v)" >>.PKGINFO fi echo "# $(LC_ALL=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 if [ "$(check_option force)" = "y" ]; then echo "force = true" >> .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 "${optdepends[@]}"; do echo "optdepend = $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 "$(gettext "Please add a license line to your %s!")" "$BUILDSCRIPT" plain "$(gettext "Example for GPL'ed software: license=('GPL').")" fi local comp_files=".PKGINFO" # check for an install script if [ "$install" != "" ]; then msg2 "$(gettext "Adding install script...")" cp "$startdir/$install" .INSTALL comp_files="$comp_files .INSTALL" fi # do we have a changelog? if [ -f "$startdir/ChangeLog" ]; then msg2 "$(gettext "Adding package changelog...")" cp "$startdir/ChangeLog" .CHANGELOG comp_files="$comp_files .CHANGELOG" fi # tar it up msg2 "$(gettext "Compressing package...")" local pkg_file="$PKGDEST/${pkgname}-${pkgver}-${pkgrel}-${CARCH}${PKGEXT}" # when fileglobbing, we want * in an empty directory to expand to # the null string rather than itself shopt -s nullglob if ! bsdtar -czf "$pkg_file" $comp_files *; then error "$(gettext "Failed to create package file.")" exit 1 # TODO: error code fi shopt -u nullglob } create_xdelta() { if [ "$(check_buildenv xdelta)" != "y" ]; then return elif [ ! "$(type -p xdelta)" ]; then error "$(gettext "Cannot find the xdelta binary! Is xdelta installed?")" return fi local pkg_file=$1 local cache_dir="/var/cache/pacman/pkg" # TODO: autoconf me local pkginfo="$(mktemp "$startdir"/xdelta-pkginfo.XXXXXXXXX)" local old_file old_version for old_file in $(ls {"$cache_dir","$PKGDEST"}/${pkgname}-*-*{,-$CARCH}$PKGEXT 2>/dev/null); do bsdtar -xOf "$old_file" .PKGINFO > "$pkginfo" || continue if [ "$(cat "$pkginfo" | grep '^pkgname = ')" != "pkgname = $pkgname" ]; then continue # Package name does not match. elif [ "$(cat "$pkginfo" | grep '^arch = ')" != "arch = $CARCH" ] ; then continue # Not same arch. fi old_version="$(cat "$pkginfo" | grep '^pkgver = ' | sed 's/^pkgver = //')" # old_version may include the target package, only use the old versions local vercmp=$(vercmp "$old_version" "$latest_version") if [ "$old_version" != "$pkgver-$pkgrel" -a $vercmp -gt 0 ]; then local latest_version=$old_version local base_file=$old_file fi done rm -f "$pkginfo" if [ "$base_file" != "" ]; then msg "$(gettext "Making delta from version %s...")" "$latest_version" local delta_file="$PKGDEST/$pkgname-${latest_version}_to_$pkgver-$pkgrel-$CARCH.delta" local ret=0 # xdelta will decompress base_file & pkg_file into TMP_DIR (or /tmp if # TMP_DIR is unset) then perform the delta on the resulting tars xdelta delta "$base_file" "$pkg_file" "$delta_file" || ret=$? if [ $ret -eq 0 -o $ret -eq 1 ]; then # Generate the final gz using xdelta for compression. xdelta will be our # common denominator compression utility between the packager and the # users. makepkg and pacman must use the same compression algorithm or # the delta generated package may not match, producing md5 checksum # errors. msg2 "$(gettext "Recreating package tarball from delta to match md5 signatures")" msg2 "$(gettext "NOTE: the delta should ONLY be distributed with this tarball")" ret=0 xdelta patch "$delta_file" "$base_file" "$pkg_file" || ret=$? if [ $ret -ne 0 ]; then error "$(gettext "Could not generate the package from the delta.")" exit 1 fi else warning "$(gettext "Delta was not able to be created.")" fi else warning "$(gettext "No previous version found, skipping xdelta.")" fi } create_srcpackage() { cd "$startdir" if [ "$SOURCEONLY" = "2" ]; then # get back to our src directory so we can begin with sources mkdir -p "$srcdir" cd "$srcdir" download_sources # we can only check checksums if we have all files check_checksums cd "$startdir" fi msg "$(gettext "Creating source package...")" local srclinks="$(mktemp -d "$startdir"/srclinks.XXXXXXXXX)" mkdir "${srclinks}"/${pkgname} msg2 "$(gettext "Adding %s...")" "$BUILDSCRIPT" ln -s "${startdir}/${BUILDSCRIPT}" "${srclinks}/${pkgname}/" if [ "$install" != "" ]; then if [ -f $install ]; then msg2 "$(gettext "Adding install script...")" ln -s "${startdir}/$install" "${srclinks}/${pkgname}/" else error "$(gettext "Install script %s not found.")" "$install" fi fi if [ -f ChangeLog ]; then msg2 "$(gettext "Adding %s...")" "ChangeLog" ln -s "${startdir}/ChangeLog" "${srclinks}/${pkgname}" fi local netfile for netfile in "${source[@]}"; do local file=$(strip_url "$netfile") if [ -f "$netfile" ]; then msg2 "$(gettext "Adding %s...")" "$netfile" ln -s "${startdir}/$netfile" "${srclinks}/${pkgname}" elif [ "$SOURCEONLY" = "2" -a -f "$SRCDEST/$file" ]; then msg2 "$(gettext "Adding %s...")" "$file" ln -s "$SRCDEST/$file" "${srclinks}/${pkgname}/" fi done local pkg_file="$PKGDEST/${pkgname}-${pkgver}-${pkgrel}${SRCEXT}" # tar it up msg2 "$(gettext "Compressing source package...")" cd "${srclinks}" if ! bsdtar -czLf "$pkg_file" ${pkgname}; then error "$(gettext "Failed to create source package file.")" exit 1 # TODO: error code fi cd "${startdir}" rm -rf "${srclinks}" } install_package() { [ "$INSTALL" = "0" ] && return msg "$(gettext "Installing package ${pkgname} with pacman -U...")" if [ "$ASROOT" = "0" ]; then sudo pacman $PACMAN_OPTS -U $PKGDEST/${pkgname}-${pkgver}-${pkgrel}-${CARCH}${PKGEXT} || exit $? else pacman $PACMAN_OPTS -U $PKGDEST/${pkgname}-${pkgver}-${pkgrel}-${CARCH}${PKGEXT} || exit $? fi } devel_check() { newpkgver="" # Only update pkgver if --holdver is not set if [ "$HOLDVER" = "1" ]; then return fi if [ "$FORCE_VER" = "" ]; then # Check if this is a svn/cvs/etc PKGBUILD; set $newpkgver if so. # This will only be used on the first call to makepkg; subsequent # calls to makepkg via fakeroot will explicitly pass the version # number to avoid having to determine the version number twice. # Also do a brief check to make sure we have the VCS tool available. oldpkgver=$pkgver if [ ! -z ${_darcstrunk} ] && [ ! -z ${_darcsmod} ] ; then [ $(type -p darcs) ] || return 0 msg "$(gettext "Determining latest darcs revision...")" newpkgver=$(date +%Y%m%d) elif [ ! -z ${_cvsroot} ] && [ ! -z ${_cvsmod} ] ; then [ $(type -p cvs) ] || return 0 msg "$(gettext "Determining latest cvs revision...")" newpkgver=$(date +%Y%m%d) elif [ ! -z ${_gitroot} ] && [ ! -z ${_gitname} ] ; then [ $(type -p git) ] || return 0 msg "$(gettext "Determining latest git revision...")" newpkgver=$(date +%Y%m%d) elif [ ! -z ${_svntrunk} ] && [ ! -z ${_svnmod} ] ; then [ $(type -p svn) ] || return 0 msg "$(gettext "Determining latest svn revision...")" newpkgver=$(LC_ALL=C svn info $_svntrunk | sed -n 's/^Last Changed Rev: \([0-9]*\)$/\1/p') elif [ ! -z ${_bzrtrunk} ] && [ ! -z ${_bzrmod} ] ; then [ $(type -p bzr) ] || return 0 msg "$(gettext "Determining latest bzr revision...")" newpkgver=$(bzr revno ${_bzrtrunk}) elif [ ! -z ${_hgroot} ] && [ ! -z ${_hgrepo} ] ; then [ $(type -p hg) ] || return 0 msg "$(gettext "Determining latest hg revision...")" if [ -d ./src/$_hgrepo ] ; then cd ./src/$_hgrepo hg pull hg update else [[ ! -d ./src/ ]] && mkdir ./src/ hg clone $_hgroot/$_hgrepo ./src/$_hgrepo cd ./src/$_hgrepo fi newpkgver=$(hg tip | sed -n '1s/[^0-9]*\([^:]*\):.*$/\1/p') cd ../../ fi if [ "$newpkgver" != "" ]; then msg2 "$(gettext "Version found: %s")" "$newpkgver" pkgver=$newpkgver fi else # Version number retrieved from fakeroot->makepkg argument newpkgver=$FORCE_VER fi } devel_update() { # This is lame, but if we're wanting to use an updated pkgver for # retrieving svn/cvs/etc sources, we'll update the PKGBUILD with # the new pkgver and then re-source it. This is the most robust # method for dealing with PKGBUILDs that use, e.g.: # # pkgver=23 # ... # _foo=pkgver # if [ "$newpkgver" != "" ]; then if [ "newpkgver" != "$pkgver" ]; then sed -i "s/^pkgver=[^ ]*/pkgver=$newpkgver/" ./$BUILDSCRIPT source $BUILDSCRIPT fi fi } usage() { printf "makepkg (pacman) %s\n" "$myver" echo printf "$(gettext "Usage: %s [options]")\n" "$0" echo echo "$(gettext "Options:")" printf "$(gettext " -A, --ignorearch Ignore incomplete arch field in %s")\n" "$BUILDSCRIPT" echo "$(gettext " -c, --clean Clean up work files after build")" echo "$(gettext " -C, --cleancache Clean up source files from the cache")" echo "$(gettext " -d, --nodeps Skip all dependency checks")" echo "$(gettext " -e, --noextract Do not extract source files (use existing src/ dir)")" echo "$(gettext " -f, --force Overwrite existing package")" echo "$(gettext " -g, --geninteg Generate integrity checks for source files")" echo "$(gettext " -h, --help This help")" echo "$(gettext " -i, --install Install package after successful build")" echo "$(gettext " -L, --log Log package build process")" echo "$(gettext " -m, --nocolor Disable colorized output messages")" echo "$(gettext " -o, --nobuild Download and extract files only")" printf "$(gettext " -p <buildscript> Use an alternate build script (instead of '%s')")\n" "$BUILDSCRIPT" echo "$(gettext " -r, --rmdeps Remove installed dependencies after a successful build")" # fix flyspray feature request #2978 echo "$(gettext " -R, --repackage Repackage contents of pkg/ without building")" echo "$(gettext " -s, --syncdeps Install missing dependencies with pacman")" echo "$(gettext " --allsource Generate a source-only tarball including downloaded sources")" echo "$(gettext " --asroot Allow makepkg to run as root user")" echo "$(gettext " --holdver Prevent automatic version bumping for development PKGBUILDs")" echo "$(gettext " --source Generate a source-only tarball without downloaded sources")" echo echo "$(gettext "These options can be passed to pacman:")" echo echo "$(gettext " --noconfirm Do not ask for confirmation when resolving dependencies")" echo "$(gettext " --noprogressbar Do not show a progress bar when downloading files")" echo printf "$(gettext "If -p is not specified, makepkg will look for '%s'")\n" "$BUILDSCRIPT" echo } version() { printf "makepkg (pacman) %s\n" "$myver" printf "$(gettext "\ Copyright (C) 2002-2007 Judd Vinet <jvinet@zeroflux.org>.\n\n\ This is free software; see the source for copying conditions.\n\ There is NO WARRANTY, to the extent permitted by law.\n")" } # PROGRAM START # determine whether we have gettext; make it a no-op if we do not if [ ! $(type -t gettext) ]; then gettext() { echo "$@" } fi ARGLIST=$@ #preserve environment variables _PKGDEST=${PKGDEST} _SRCDEST=${SRCDEST} # Source makepkg.conf; fail if it is not found if [ -r "$confdir/makepkg.conf" ]; then source "$confdir/makepkg.conf" else error "$(gettext "%s not found.")" "$confdir/makepkg.conf" plain "$(gettext "Aborting...")" exit 1 # $E_CONFIG_ERROR fi # Source user-specific makepkg.conf overrides if [ -r ~/.makepkg.conf ]; then source ~/.makepkg.conf fi # override settings with an environment variable for batch processing PKGDEST=${_PKGDEST:-$PKGDEST} PKGDEST=${PKGDEST:-$startdir} #default to $startdir if undefined SRCDEST=${_SRCDEST:-$SRCDEST} SRCDEST=${SRCDEST:-$startdir} #default to $startdir if undefined # Parse Command Line Options. OPT_SHORT="AbcCdefFghiLmop:rRsV" OPT_LONG="allsource,asroot,ignorearch,builddeps,clean,cleancache,nodeps" OPT_LONG="$OPT_LONG,noextract,force,forcever:,geninteg,help,holdver" OPT_LONG="$OPT_LONG,install,log,nocolor,nobuild,rmdeps,repackage,source" OPT_LONG="$OPT_LONG,syncdeps,version" # Pacman Options OPT_LONG="$OPT_LONG,noconfirm,noprogressbar" OPT_TEMP="$(getopt -o "$OPT_SHORT" -l "$OPT_LONG" -n "$(basename "$0")" -- "$@" || echo 'GETOPT GO BANG!')" if echo "$OPT_TEMP" | grep -q 'GETOPT GO BANG!'; then # This is a small hack to stop the script bailing with 'set -e' echo; usage; exit 1 # E_INVALID_OPTION; fi eval set -- "$OPT_TEMP" unset OPT_SHORT OPT_LONG OPT_TEMP while true; do case "$1" in # Pacman Options --noconfirm) PACMAN_OPTS="$PACMAN_OPTS --noconfirm" ;; --noprogressbar) PACMAN_OPTS="$PACMAN_OPTS --noprogressbar" ;; # Makepkg Options --allsource) SOURCEONLY=2 ;; --asroot) ASROOT=1 ;; -A|--ignorearch) IGNOREARCH=1 ;; -c|--clean) CLEANUP=1 ;; -C|--cleancache) CLEANCACHE=1 ;; -d|--nodeps) NODEPS=1 ;; -e|--noextract) NOEXTRACT=1 ;; -f|--force) FORCE=1 ;; #hidden opt used by fakeroot call for svn/cvs/etc PKGBUILDs to set pkgver --forcever) shift; FORCE_VER=$1;; -F) INFAKEROOT=1 ;; -g|--geninteg) GENINTEG=1 ;; --holdver) HOLDVER=1 ;; -i|--install) INSTALL=1 ;; -L|--log) LOGGING=1 ;; -m|--nocolor) USE_COLOR='n' ;; -o|--nobuild) NOBUILD=1 ;; -p) shift; BUILDSCRIPT=$1 ;; -r|--rmdeps) RMDEPS=1 ;; -R|--repackage) REPKG=1 ;; --source) SOURCEONLY=1 ;; -s|--syncdeps) DEP_BIN=1 ;; -h|--help) usage; exit 0 ;; # E_OK -V|--version) version; exit 0 ;; # E_OK --) OPT_IND=0; shift; break;; *) usage; exit 1 ;; # E_INVALID_OPTION esac shift done if [ "$HOLDVER" = "1" -a "$FORCE_VER" != "" ]; then # The '\\0' is here to prevent gettext from thinking --holdver is an option error "$(gettext "\\0--holdver and --forcever cannot both be specified" )" exit 1 fi if [ "$CLEANCACHE" = "1" ]; then #fix flyspray feature request #5223 if [ -n "$SRCDEST" -a "$SRCDEST" != "$startdir" ]; then msg "$(gettext "Cleaning up ALL files from %s.")" "$SRCDEST" echo -n "$(gettext " Are you sure you wish to do this? ")" echo -n "$(gettext "[Y/n]")" read answer answer=$(echo $answer | tr '[:lower:]' '[:upper:]') if [ "$answer" = "$(gettext "YES")" -o "$answer" = "$(gettext "Y")" ]; then rm "$SRCDEST"/* if [ $? -ne 0 ]; then error "$(gettext "Problem removing files; you may not have correct permissions in %s")" "$SRCDEST" exit 1 else # removal worked msg "$(gettext "Source cache cleaned.")" exit 0 fi else # answer = no msg "$(gettext "No files have been removed.")" exit 0 fi else # $SRCDEST is $startdir, two possibilities error "$(gettext "Source destination must be defined in makepkg.conf.")" plain "$(gettext "In addition, please run makepkg -C outside of your cache directory.")" exit 1 fi fi if [ -z $BUILDSCRIPT ]; then error "$(gettext "BUILDSCRIPT is undefined! Ensure you have updated %s.")" "$confdir/makepkg.conf" exit 1 fi if [ "$INFAKEROOT" = "0" ]; then if [ $EUID -eq 0 -a "$ASROOT" = "0" ]; then # Warn those who like to live dangerously. error "$(gettext "Running makepkg as root is a BAD idea and can cause")" plain "$(gettext "permanent, catastrophic damage to your system. If you")" plain "$(gettext "wish to run as root, please use the --asroot option.")" exit 1 # $E_USER_ABORT elif [ $EUID -gt 0 -a "$ASROOT" = "1" ]; then # Warn those who try to use the --asroot option when they are not root error "$(gettext "The --asroot option is meant for the root user only.")" plain "$(gettext "Please rerun makepkg without the --asroot flag.")" exit 1 # $E_USER_ABORT elif [ "$(check_buildenv fakeroot)" = "y" -a $EUID -gt 0 ]; then if [ ! $(type -p fakeroot) ]; then error "$(gettext "Fakeroot must be installed if using the 'fakeroot' option")" plain "$(gettext "in the BUILDENV array in %s.")" "$confdir/makepkg.conf" exit 1 fi elif [ $EUID -gt 0 ]; then warning "$(gettext "Running makepkg as an unprivileged user will result in non-root")" plain "$(gettext "ownership of the packaged files. Try using the fakeroot environment by")" plain "$(gettext "placing 'fakeroot' in the BUILDENV array in makepkg.conf.")" sleep 1 fi else if [ "$FAKEROOTKEY" = "" ]; then error "$(gettext "Do not use the '-F' option. This option is only for use by makepkg.")" exit 1 # TODO: error code fi fi # check for sudo if we will need it during makepkg execution if [ "$ASROOT" = "0" -a \( "$DEP_BIN" = "1" \ -o "$RMDEPS" = "1" -o "$INSTALL" = "1" \) ]; then if [ ! "$(type -p sudo)" ]; then error "$(gettext "Cannot find the sudo binary! Is sudo installed?")" plain "$(gettext "Missing dependencies cannot be installed or removed as a normal user")" plain "$(gettext "without sudo; install and configure sudo to auto-resolve dependencies.")" exit 1 fi fi unset pkgname pkgver pkgrel pkgdesc url license groups provides md5sums unset replaces depends conflicts backup source install build makedepends unset optdepends options noextract if [ ! -f "$BUILDSCRIPT" ]; then error "$(gettext "%s does not exist.")" "$BUILDSCRIPT" exit 1 fi source "$BUILDSCRIPT" if [ "$GENINTEG" = "1" ]; then mkdir -p "$srcdir" cd "$srcdir" download_sources generate_checksums exit 0 # $E_OK fi # check for no-no's in the build script if [ -z "$pkgname" ]; then error "$(gettext "%s is not allowed to be empty.")" "pkgname" exit 1 fi if [ -z "$pkgver" ]; then error "$(gettext "%s is not allowed to be empty.")" "pkgver" exit 1 fi if [ -z "$pkgrel" ]; then error "$(gettext "%s is not allowed to be empty.")" "pkgrel" exit 1 fi if [ $(echo "$pkgver" | grep '-') ]; then error "$(gettext "%s is not allowed to contain hyphens.")" "pkgver" exit 1 fi if [ $(echo "$pkgrel" | grep '-') ]; then error "$(gettext "%s is not allowed to contain hyphens.")" "pkgrel" exit 1 fi if [ "$arch" = 'any' ]; then CARCH='any' fi if ! in_array $CARCH ${arch[@]}; then if [ "$IGNOREARCH" = "0" ]; then error "$(gettext "%s is not available for the '%s' architecture.")" "$pkgname" "$CARCH" plain "$(gettext "Note that many packages may need a line added to their %s")" "$BUILDSCRIPT" plain "$(gettext "such as arch=('%s').")" "$CARCH" exit 1 else warning "$(gettext "%s is not available for the '%s' architecture.")" "$pkgname" "$CARCH" plain "$(gettext "Note that many packages may need a line added to their %s")" "$BUILDSCRIPT" plain "$(gettext "such as arch=('%s').")" "$CARCH" fi fi if [ "$install" -a ! -f "$install" ]; then error "$(gettext "Install scriptlet (%s) does not exist.")" "$install" exit 1 fi valid_options=1 for opt in ${options[@]}; do known=0 # check if option matches a known option or its inverse for kopt in ${known_options[@]}; do if [ "${opt}" = "${kopt}" -o "${opt}" = "!${kopt}" ]; then known=1 fi done if [ $known -eq 0 ]; then error "$(gettext "options array contains unknown option '%s'")" "$opt" valid_options=0 fi done if [ $valid_options -eq 0 ]; then exit 1 fi unset valid_options opt known kopt # We need to run devel_update regardless of whether we are in the fakeroot # build process so that if the user runs makepkg --forcever manually, we # 1) output the correct pkgver, and 2) use the correct filename when # checking if the package file already exists - fixes FS #9194 devel_check devel_update if [ -f "$PKGDEST/${pkgname}-${pkgver}-${pkgrel}-${CARCH}${PKGEXT}" \ -a "$FORCE" = "0" -a "$SOURCEONLY" = "0" -a "$NOBUILD" = "0" ]; then if [ "$INSTALL" = "1" ]; then warning "$(gettext "A package has already been built, installing existing package...")" install_package exit $? else error "$(gettext "A package has already been built. (use -f to overwrite)")" exit 1 fi fi # Run the bare minimum in fakeroot # fix flyspray bug 6208 -- using makepkg with fakeroot gives an error if [ "$INFAKEROOT" = "1" ]; then if [ "$REPKG" = "1" ]; then warning "$(gettext "Skipping build.")" else run_build tidy_install fi create_package msg "$(gettext "Leaving fakeroot environment.")" exit 0 # $E_OK fi msg "$(gettext "Making package: %s")" "$pkgname $pkgver-$pkgrel $CARCH ($(date))" if [ $EUID -eq 0 ]; then warning "$(gettext "Running makepkg as root...")" fi # if we are creating a source-only package, go no further if [ "$SOURCEONLY" != "0" ]; then if [ -f "$PKGDEST/${pkgname}-${pkgver}-${pkgrel}${SRCEXT}" \ -a "$FORCE" = "0" ]; then error "$(gettext "A package has already been built. (use -f to overwrite)")" exit 1 fi create_srcpackage msg "$(gettext "Source package created: %s")" "$pkgname ($(date))" exit 0 fi # fix flyspray bug #5973 if [ "$NODEPS" = "1" -o "$NOBUILD" = "1" -o "$REPKG" = "1" ]; then if [ "$NODEPS" = "1" ]; then warning "$(gettext "Skipping dependency checks.")" fi # skip printing a warning message for the others: nobuild, repkg elif [ $(type -p pacman) ]; then unset pkgdeps # Set by resolve_deps() and used by remove_deps() deperr=0 msg "$(gettext "Checking Runtime Dependencies...")" resolve_deps ${depends[@]} || deperr=1 msg "$(gettext "Checking Buildtime Dependencies...")" resolve_deps ${makedepends[@]} || deperr=1 if [ $deperr -eq 1 ]; then error "$(gettext "Could not resolve all dependencies.")" exit 1 fi else warning "$(gettext "pacman was not found in PATH; skipping dependency checks.")" fi # ensure we have a sane umask set umask 0022 # get back to our src directory so we can begin with sources mkdir -p "$srcdir" cd "$srcdir" if [ "$NOEXTRACT" = "1" -o "$REPKG" = "1" ]; then warning "$(gettext "Skipping source retrieval -- using existing src/ tree")" warning "$(gettext "Skipping source integrity checks -- using existing src/ tree")" warning "$(gettext "Skipping source extraction -- using existing src/ tree")" if [ "$NOEXTRACT" = "1" -a "$(ls "$srcdir" 2>/dev/null)" = "" ]; then error "$(gettext "The source directory is empty, there is nothing to build!")" plain "$(gettext "Aborting...")" exit 1 elif [ "$REPKG" = "1" -a \( ! -d "$pkgdir" -o "$(ls "$pkgdir" 2>/dev/null)" = "" \) ]; then error "$(gettext "The package directory is empty, there is nothing to repackage!")" plain "$(gettext "Aborting...")" exit 1 fi else download_sources check_checksums extract_sources fi if [ "$NOBUILD" = "1" ]; then msg "$(gettext "Sources are ready.")" exit 0 #E_OK else # check for existing pkg directory; don't remove if we are repackaging if [ -d "$pkgdir" -a "$REPKG" = "0" ]; then msg "$(gettext "Removing existing pkg/ directory...")" rm -rf "$pkgdir" fi mkdir -p "$pkgdir" cd "$startdir" if [ "$(check_buildenv fakeroot)" != "y" -o $EUID -eq 0 ]; then # if we are root or if fakeroot is not enabled, then we don't use it if [ "$REPKG" = "1" ]; then warning "$(gettext "Skipping build.")" else devel_update run_build tidy_install fi create_package else msg "$(gettext "Entering fakeroot environment...")" if [ "$newpkgver" != "" ]; then fakeroot -- $0 --forcever $newpkgver -F $ARGLIST || exit $? else fakeroot -- $0 -F $ARGLIST || exit $? fi fi create_xdelta "$PKGDEST/${pkgname}-${pkgver}-${pkgrel}-${CARCH}${PKGEXT}" fi msg "$(gettext "Finished making: %s")" "$pkgname $pkgver-$pkgrel $CARCH ($(date))" install_package exit 0 #E_OK # vim: set ts=2 sw=2 noet: