#!/bin/bash # # makepkg - make packages compatible for use with pacman # @configure_input@ # # Copyright (c) 2006-2013 Pacman Development Team <pacman-dev@archlinux.org> # 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, 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, file, find (findutils), # gettext, gpg, grep, gzip, openssl, sed, tput (ncurses), xz # gettext initialization export TEXTDOMAIN='pacman-scripts' export TEXTDOMAINDIR='@localedir@' # file -i does not work on Mac OSX unless legacy mode is set export COMMAND_MODE='legacy' # Ensure CDPATH doesn't screw with our cd calls unset CDPATH # Ensure GREP_OPTIONS doesn't screw with our grep calls unset GREP_OPTIONS declare -r makepkg_version='@PACKAGE_VERSION@' declare -r confdir='@sysconfdir@' declare -r BUILDSCRIPT='@BUILDSCRIPT@' declare -r startdir="$PWD" LIBRARY=${LIBRARY:-'@libmakepkgdir@'} packaging_options=('strip' 'docs' 'libtool' 'staticlibs' 'emptydirs' 'zipman' \ 'purge' 'upx' 'debug') other_options=('ccache' 'distcc' 'buildflags' 'makeflags') splitpkg_overrides=('pkgver' 'pkgrel' 'epoch' 'pkgdesc' 'arch' 'url' 'license' \ 'groups' 'depends' 'optdepends' 'provides' 'conflicts' \ 'replaces' 'backup' 'options' 'install' 'changelog') readonly -a packaging_options other_options splitpkg_overrides known_hash_algos=('md5' 'sha1' 'sha224' 'sha256' 'sha384' 'sha512') # Options ASDEPS=0 ASROOT=0 BUILDFUNC=0 CHECKFUNC=0 CLEANBUILD=0 CLEANUP=0 DEP_BIN=0 FORCE=0 GENINTEG=0 HOLDVER=0 IGNOREARCH=0 INFAKEROOT=0 INSTALL=0 LOGGING=0 NEEDED=0 NOBUILD=0 NODEPS=0 NOEXTRACT=0 PKGFUNC=0 PKGLIST=() PKGVERFUNC=0 PREPAREFUNC=0 REPKG=0 RMDEPS=0 SKIPCHECKSUMS=0 SKIPPGPCHECK=0 SIGNPKG='' SPLITPKG=0 SOURCEONLY=0 VERIFYSOURCE=0 # Forces the pkgver of the current PKGBUILD. Used by the fakeroot call # when dealing with svn/cvs/etc PKGBUILDs. FORCE_VER="" PACMAN_OPTS= shopt -s extglob ### SUBROUTINES ### plain() { local mesg=$1; shift printf "${BOLD} ${mesg}${ALL_OFF}\n" "$@" >&2 } msg() { local mesg=$1; shift printf "${GREEN}==>${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}\n" "$@" >&2 } msg2() { local mesg=$1; shift printf "${BLUE} ->${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}\n" "$@" >&2 } warning() { local mesg=$1; shift printf "${YELLOW}==> $(gettext "WARNING:")${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}\n" "$@" >&2 } error() { local mesg=$1; shift printf "${RED}==> $(gettext "ERROR:")${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}\n" "$@" >&2 } ## # 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() { local signal=$1; shift if (( ! INFAKEROOT )); then echo error "$@" fi [[ -n $srclinks ]] && rm -rf "$srclinks" # unset the trap for this signal, and then call the default handler trap -- "$signal" kill "-$signal" "$$" } ## # Clean up function. Called automatically when the script exits. ## clean_up() { local EXIT_CODE=$? if (( INFAKEROOT )); then # Don't clean up when leaving fakeroot, we're not done yet. return fi if (( ! EXIT_CODE && CLEANUP )); then local pkg file # If it's a clean exit and -c/--clean has been passed... msg "$(gettext "Cleaning up...")" rm -rf "$pkgdirbase" "$srcdir" if [[ -n $pkgbase ]]; then local fullver=$(get_full_version) # Can't do this unless the BUILDSCRIPT has been sourced. if (( BUILDFUNC )); then rm -f "${pkgbase}-${fullver}-${CARCH}-build.log"* fi if (( CHECKFUNC )); then rm -f "${pkgbase}-${fullver}-${CARCH}-check.log"* fi if (( PKGFUNC )); then rm -f "${pkgbase}-${fullver}-${CARCH}-package.log"* elif (( SPLITPKG )); then for pkg in ${pkgname[@]}; do rm -f "${pkgbase}-${fullver}-${CARCH}-package_${pkg}.log"* done fi # clean up dangling symlinks to packages for pkg in ${pkgname[@]}; do for file in ${pkg}-*-*-*{${PKGEXT},${SRCEXT}}; do if [[ -h $file && ! -e $file ]]; then rm -f "$file" fi done done fi fi remove_deps } enter_fakeroot() { msg "$(gettext "Entering %s environment...")" "fakeroot" fakeroot -- $0 -F "${ARGLIST[@]}" || exit $? } # a source entry can have two forms : # 1) "filename::http://path/to/file" # 2) "http://path/to/file" # Return the absolute filename of a source entry get_filepath() { local file="$(get_filename "$1")" local proto="$(get_protocol "$1")" case $proto in bzr*|git*|hg*|svn*) if [[ -d "$startdir/$file" ]]; then file="$startdir/$file" elif [[ -d "$SRCDEST/$file" ]]; then file="$SRCDEST/$file" else return 1 fi ;; *) if [[ -f "$startdir/$file" ]]; then file="$startdir/$file" elif [[ -f "$SRCDEST/$file" ]]; then file="$SRCDEST/$file" else return 1 fi ;; esac printf "%s\n" "$file" } # extract the filename from a source entry get_filename() { local netfile=$1 # if a filename is specified, use it if [[ $netfile = *::* ]]; then printf "%s\n" ${netfile%%::*} return fi local proto=$(get_protocol "$netfile") case $proto in bzr*|git*|hg*|svn*) filename=${netfile%%#*} filename=${filename%/} filename=${filename##*/} if [[ $proto = bzr* ]]; then filename=${filename#*lp:} fi if [[ $proto = git* ]]; then filename=${filename%%.git*} fi ;; *) # if it is just an URL, we only keep the last component filename="${netfile##*/}" ;; esac printf "%s\n" "${filename}" } # extract the URL from a source entry get_url() { # strip an eventual filename printf "%s\n" "${1#*::}" } # extract the protocol from a source entry - return "local" for local sources get_protocol() { if [[ $1 = *://* ]]; then # strip leading filename local proto="${1##*::}" printf "%s\n" "${proto%%://*}" elif [[ $1 = *lp:* ]]; then local proto="${1##*::}" printf "%s\n" "${proto%%lp:*}" else printf "%s\n" local fi } get_downloadclient() { local proto=$1 # loop through DOWNLOAD_AGENTS variable looking for protocol local i for i in "${DLAGENTS[@]}"; do local handler="${i%%::*}" if [[ $proto = "$handler" ]]; then local agent="${i##*::}" break fi done # if we didn't find an agent, return an error if [[ -z $agent ]]; then error "$(gettext "Unknown download protocol: %s")" "$proto" plain "$(gettext "Aborting...")" exit 1 # $E_CONFIG_ERROR fi # ensure specified program is installed local program="${agent%% *}" if [[ ! -x $program ]]; then local baseprog="${program##*/}" error "$(gettext "The download program %s is not installed.")" "$baseprog" plain "$(gettext "Aborting...")" exit 1 # $E_MISSING_PROGRAM fi printf "%s\n" "$agent" } download_local() { local netfile=$1 local filepath=$(get_filepath "$netfile") if [[ -n "$filepath" ]]; then msg2 "$(gettext "Found %s")" "${filepath##*/}" else local filename=$(get_filename "$netfile") error "$(gettext "%s was not found in the build directory and is not a URL.")" "$filename" exit 1 # $E_MISSING_FILE fi } download_file() { local netfile=$1 local filepath=$(get_filepath "$netfile") if [[ -n "$filepath" ]]; then msg2 "$(gettext "Found %s")" "${filepath##*/}" return fi local proto=$(get_protocol "$netfile") # find the client we should use for this URL local dlcmd dlcmd=$(get_downloadclient "$proto") || exit $? local filename=$(get_filename "$netfile") local url=$(get_url "$netfile") if [[ $proto = "scp" ]]; then # scp downloads should not pass the protocol in the url url="${url##*://}" fi msg2 "$(gettext "Downloading %s...")" "$filename" # temporary download file, default to last component of the URL local dlfile="${url##*/}" # replace %o by the temporary dlfile if it exists if [[ $dlcmd = *%o* ]]; then dlcmd=${dlcmd//\%o/\"$filename.part\"} dlfile="$filename.part" fi # add the URL, either in place of %u or at the end if [[ $dlcmd = *%u* ]]; then dlcmd=${dlcmd//\%u/\"$url\"} else dlcmd="$dlcmd \"$url\"" fi local ret=0 eval "$dlcmd >&2 || ret=\$?" if (( ret )); then [[ ! -s $dlfile ]] && rm -f -- "$dlfile" error "$(gettext "Failure while downloading %s")" "$filename" plain "$(gettext "Aborting...")" exit 1 fi # rename the temporary download file to the final destination if [[ $dlfile != "$filename" ]]; then mv -f "$SRCDEST/$dlfile" "$SRCDEST/$filename" fi } extract_file() { local file=$1 local filepath=$(get_filepath "$file") rm -f "$srcdir/${file}" ln -s "$filepath" "$srcdir/" # do not rely on extension for file type local file_type=$(file -bizL "$file") local ext=${file##*.} local cmd='' case "$file_type" in *application/x-tar*|*application/zip*|*application/x-zip*|*application/x-cpio*) cmd="bsdtar" ;; *application/x-gzip*) case "$ext" in gz|z|Z) cmd="gzip" ;; *) return;; esac ;; *application/x-bzip*) case "$ext" in bz2|bz) cmd="bzip2" ;; *) return;; esac ;; *application/x-xz*) case "$ext" in xz) cmd="xz" ;; *) return;; esac ;; *) # See if bsdtar can recognize the file if bsdtar -tf "$file" -q '*' &>/dev/null; then cmd="bsdtar" else return fi ;; esac local ret=0 msg2 "$(gettext "Extracting %s with %s")" "$file" "$cmd" if [[ $cmd = "bsdtar" ]]; then $cmd -xf "$file" || ret=$? else rm -f -- "${file%.*}" $cmd -dcf "$file" > "${file%.*}" || ret=$? fi if (( ret )); then error "$(gettext "Failed to extract %s")" "$file" plain "$(gettext "Aborting...")" exit 1 fi if (( EUID == 0 )); then # change perms of all source files to root user & root group chown -R 0:0 "$srcdir" fi } download_bzr() { local netfile=$1 local url=$(get_url "$netfile") url=${url##*bzr+} url=${url%%#*} local repo=$(get_filename "$netfile") local displaylocation="$url" local dir=$(get_filepath "$netfile") [[ -z "$dir" ]] && dir="$SRCDEST/$(get_filename "$netfile")" if [[ ! -d "$dir" ]] || dir_is_empty "$dir" ; then msg2 "$(gettext "Branching %s ...")" "${displaylocation}" if ! bzr branch "$url" "$dir" --no-tree --use-existing-dir; then error "$(gettext "Failure while branching %s")" "${displaylocation}" plain "$(gettext "Aborting...")" exit 1 fi elif (( ! HOLDVER )); then # Make sure we are fetching the right repo local distant_url="$(bzr info $url 2> /dev/null | sed -n '/branch root/{s/ branch root: //p;q;}')" local local_url="$(bzr config parent_location -d $dir)" if [[ -n $distant_url ]]; then if [[ $distant_url != "$local_url" ]]; then error "$(gettext "%s is not a branch of %s")" "$dir" "$url" plain "$(gettext "Aborting...")" exit 1 fi else if [[ $url != "$local_url" ]] ; then error "$(gettext "%s is not a branch of %s")" "$dir" "$url" error "$(gettext "The local URL is %s")" "$local_url" plain "$(gettext "Aborting...")" exit 1 fi fi msg2 "$(gettext "Pulling %s ...")" "${displaylocation}" cd_safe "$dir" if ! bzr pull "$url" --overwrite; then # only warn on failure to allow offline builds warning "$(gettext "Failure while pulling %s")" "${displaylocation}" fi fi } extract_bzr() { local netfile=$1 local repo=$(get_filename "$netfile") local fragment=${netfile#*#} if [[ $fragment = "$netfile" ]]; then unset fragment fi if [[ -n $fragment ]]; then case ${fragment%%=*} in revision) revision=("-r" "${fragment#*=}") displaylocation="$url -r ${fragment#*=}" ;; *) error "$(gettext "Unrecognized reference: %s")" "${fragment}" plain "$(gettext "Aborting...")" exit 1 esac fi local dir=$(get_filepath "$netfile") [[ -z "$dir" ]] && dir="$SRCDEST/$(get_filename "$netfile")" msg2 "$(gettext "Creating working copy of %s %s repo...")" "${repo}" "bzr" pushd "$srcdir" &>/dev/null rm -rf "${dir##*/}" if ! { bzr checkout "$dir" "${revision[@]}" --lightweight && ( cd "$repo" && bzr pull "$dir" -q --overwrite "${revision[@]}" ); }; then error "$(gettext "Failure while creating working copy of %s %s repo")" "${repo}" "bzr" plain "$(gettext "Aborting...")" exit 1 fi popd &>/dev/null } download_git() { local netfile=$1 local dir=$(get_filepath "$netfile") [[ -z "$dir" ]] && dir="$SRCDEST/$(get_filename "$netfile")" local repo=$(get_filename "$netfile") local url=$(get_url "$netfile") url=${url##*git+} url=${url%%#*} if [[ ! -d "$dir" ]] || dir_is_empty "$dir" ; then msg2 "$(gettext "Cloning %s %s repo...")" "${repo}" "git" if ! git clone --mirror "$url" "$dir"; then error "$(gettext "Failure while downloading %s %s repo")" "${repo}" "git" plain "$(gettext "Aborting...")" exit 1 fi elif (( ! HOLDVER )); then cd_safe "$dir" # Make sure we are fetching the right repo if [[ "$url" != "$(git config --get remote.origin.url)" ]] ; then error "$(gettext "%s is not a clone of %s")" "$dir" "$url" plain "$(gettext "Aborting...")" exit 1 fi msg2 "$(gettext "Updating %s %s repo...")" "${repo}" "git" if ! git fetch --all -p; then # only warn on failure to allow offline builds warning "$(gettext "Failure while updating %s %s repo")" "${repo}" "git" fi fi } extract_git() { local netfile=$1 local fragment=${netfile#*#} if [[ $fragment = "$netfile" ]]; then unset fragment fi local repo=${netfile##*/} repo=${repo%%#*} repo=${repo%%.git*} local dir=$(get_filepath "$netfile") [[ -z "$dir" ]] && dir="$SRCDEST/$(get_filename "$netfile")" msg2 "$(gettext "Creating working copy of %s %s repo...")" "${repo}" "git" pushd "$srcdir" &>/dev/null rm -rf "${dir##*/}" if ! git clone "$dir"; then error "$(gettext "Failure while creating working copy of %s %s repo")" "${repo}" "git" plain "$(gettext "Aborting...")" exit 1 fi cd_safe "${dir##*/}" local ref if [[ -n $fragment ]]; then case ${fragment%%=*} in commit|tag) ref=${fragment##*=} ;; branch) ref=origin/${fragment##*=} ;; *) error "$(gettext "Unrecognized reference: %s")" "${fragment}" plain "$(gettext "Aborting...")" exit 1 esac fi if [[ -n $ref ]]; then if ! git checkout -b makepkg $ref; then error "$(gettext "Failure while creating working copy of %s %s repo")" "${repo}" "git" plain "$(gettext "Aborting...")" exit 1 fi fi popd &>/dev/null } download_hg() { local netfile=$1 local dir=$(get_filepath "$netfile") [[ -z "$dir" ]] && dir="$SRCDEST/$(get_filename "$netfile")" local repo=$(get_filename "$netfile") local url=$(get_url "$netfile") url=${url##*hg+} url=${url%%#*} if [[ ! -d "$dir" ]] || dir_is_empty "$dir" ; then msg2 "$(gettext "Cloning %s %s repo...")" "${repo}" "hg" if ! hg clone -U "$url" "$dir"; then error "$(gettext "Failure while downloading %s %s repo")" "${repo}" "hg" plain "$(gettext "Aborting...")" exit 1 fi elif (( ! HOLDVER )); then msg2 "$(gettext "Updating %s %s repo...")" "${repo}" "hg" cd_safe "$dir" if ! hg pull; then # only warn on failure to allow offline builds warning "$(gettext "Failure while updating %s %s repo")" "${repo}" "hg" fi fi } extract_hg() { local netfile=$1 local fragment=${netfile#*#} if [[ $fragment = "$netfile" ]]; then unset fragment fi local dir=$(get_filepath "$netfile") [[ -z "$dir" ]] && dir="$SRCDEST/$(get_filename "$netfile")" local repo=${netfile##*/} repo=${repo%%#*} msg2 "$(gettext "Creating working copy of %s %s repo...")" "${repo}" "hg" pushd "$srcdir" &>/dev/null rm -rf "${dir##*/}" local ref if [[ -n $fragment ]]; then case ${fragment%%=*} in branch|revision|tag) ref=('-u' "${fragment##*=}") ;; *) error "$(gettext "Unrecognized reference: %s")" "${fragment}" plain "$(gettext "Aborting...")" exit 1 esac fi if ! hg clone "${ref[@]}" "$dir" "${dir##*/}"; then error "$(gettext "Failure while creating working copy of %s %s repo")" "${repo}" "hg" plain "$(gettext "Aborting...")" exit 1 fi popd &>/dev/null } download_svn() { local netfile=$1 local fragment=${netfile#*#} if [[ $fragment = "$netfile" ]]; then unset fragment fi local dir=$(get_filepath "$netfile") [[ -z "$dir" ]] && dir="$SRCDEST/$(get_filename "$netfile")" local repo=$(get_filename "$netfile") local url=$(get_url "$netfile") if [[ $url != svn+ssh* ]]; then url=${url##*svn+} fi url=${url%%#*} if [[ ! -d "$dir" ]] || dir_is_empty "$dir" ; then msg2 "$(gettext "Cloning %s %s repo...")" "${repo}" "svn" mkdir -p "$dir/.makepkg" if ! svn checkout --config-dir "$dir/.makepkg" "$url" "$dir"; then error "$(gettext "Failure while downloading %s %s repo")" "${repo}" "svn" plain "$(gettext "Aborting...")" exit 1 fi elif (( ! HOLDVER )); then msg2 "$(gettext "Updating %s %s repo...")" "${repo}" "svn" cd_safe "$dir" if ! svn update; then # only warn on failure to allow offline builds warning "$(gettext "Failure while updating %s %s repo")" "${repo}" "svn" fi fi } extract_svn() { local netfile=$1 local fragment=${netfile#*#} if [[ $fragment = "$netfile" ]]; then unset fragment fi local dir=$(get_filepath "$netfile") [[ -z "$dir" ]] && dir="$SRCDEST/$(get_filename "$netfile")" local repo=${netfile##*/} repo=${repo%%#*} msg2 "$(gettext "Creating working copy of %s %s repo...")" "${repo}" "svn" pushd "$srcdir" &>/dev/null rm -rf "${dir##*/}" local ref if [[ -n $fragment ]]; then case ${fragment%%=*} in revision) ref="${fragment##*=}" ;; *) error "$(gettext "Unrecognized reference: %s")" "${fragment}" plain "$(gettext "Aborting...")" exit 1 esac fi cp -a "$dir" . if [[ -n ${ref} ]]; then cd_safe "$(get_filename "$netfile")" if ! svn update -r ${ref}; then error "$(gettext "Failure while creating working copy of %s %s repo")" "${repo}" "svn" plain "$(gettext "Aborting...")" fi fi popd &>/dev/null } download_sources() { msg "$(gettext "Retrieving sources...")" local GET_VCS=1 if [[ $1 == "fast" ]]; then GET_VCS=0 fi local netfile for netfile in "${source[@]}"; do pushd "$SRCDEST" &>/dev/null local proto=$(get_protocol "$netfile") case "$proto" in local) download_local "$netfile" ;; bzr*) (( GET_VCS )) && download_bzr "$netfile" ;; git*) (( GET_VCS )) && download_git "$netfile" ;; hg*) (( GET_VCS )) && download_hg "$netfile" ;; svn*) (( GET_VCS )) && download_svn "$netfile" ;; *) download_file "$netfile" ;; esac popd &>/dev/null done } # Automatically update pkgver variable if a pkgver() function is provided # Re-sources the PKGBUILD afterwards to allow for other variables that use $pkgver update_pkgver() { newpkgver=$(run_function_safe pkgver) if ! validate_pkgver "$newpkgver"; then error "$(gettext "pkgver() generated an invalid version: %s")" "$newpkgver" exit 1 fi if [[ -n $newpkgver && $newpkgver != "$pkgver" ]]; then if [[ -f $BUILDFILE && -w $BUILDFILE ]]; then if ! @SEDINPLACE@ "s:^pkgver=[^ ]*:pkgver=$newpkgver:" "$BUILDFILE"; then error "$(gettext "Failed to update %s from %s to %s")" \ "pkgver" "$pkgver" "$newpkgver" exit 1 fi @SEDINPLACE@ "s:^pkgrel=[^ ]*:pkgrel=1:" "$BUILDFILE" source_safe "$BUILDFILE" local fullver=$(get_full_version) msg "$(gettext "Updated version: %s")" "$pkgbase $fullver" else warning "$(gettext "%s is not writeable -- pkgver will not be updated")" \ "$BUILDFILE" fi fi } # Print 'source not found' error message and exit makepkg missing_source_file() { error "$(gettext "Unable to find source file %s.")" "$(get_filename "$1")" plain "$(gettext "Aborting...")" exit 1 # $E_MISSING_FILE } ## # usage : get_full_version( [$pkgname] ) # return : full version spec, including epoch (if necessary), pkgver, pkgrel ## get_full_version() { if [[ -z $1 ]]; then if [[ $epoch ]] && (( ! $epoch )); then printf "%s\n" "$pkgver-$pkgrel" else printf "%s\n" "$epoch:$pkgver-$pkgrel" fi else for i in pkgver pkgrel epoch; do local indirect="${i}_override" eval $(declare -f package_$1 | sed -n "s/\(^[[:space:]]*$i=\)/${i}_override=/p") [[ -z ${!indirect} ]] && eval ${indirect}=\"${!i}\" done if (( ! $epoch_override )); then printf "%s\n" "$pkgver_override-$pkgrel_override" else printf "%s\n" "$epoch_override:$pkgver_override-$pkgrel_override" fi fi } ## # usage : get_pkg_arch( [$pkgname] ) # return : architecture of the package ## get_pkg_arch() { if [[ -z $1 ]]; then if [[ $arch = "any" ]]; then printf "%s\n" "any" else printf "%s\n" "$CARCH" fi else local arch_override eval $(declare -f package_$1 | sed -n 's/\(^[[:space:]]*arch=\)/arch_override=/p') (( ${#arch_override[@]} == 0 )) && arch_override=("${arch[@]}") if [[ $arch_override = "any" ]]; then printf "%s\n" "any" else printf "%s\n" "$CARCH" fi fi } ## # Checks to see if options are present in makepkg.conf or PKGBUILD; # PKGBUILD options always take precedence. # # usage : check_option( $option, $expected_val ) # return : 0 - matches expected # 1 - does not match expected # 127 - not found ## check_option() { in_opt_array "$1" ${options[@]} case $? in 0) # assert enabled [[ $2 = y ]] return ;; 1) # assert disabled [[ $2 = n ]] return esac # fall back to makepkg.conf options in_opt_array "$1" ${OPTIONS[@]} case $? in 0) # assert enabled [[ $2 = y ]] return ;; 1) # assert disabled [[ $2 = n ]] return esac # not found return 127 } ## # Check if option is present in BUILDENV # # usage : check_buildenv( $option, $expected_val ) # return : 0 - matches expected # 1 - does not match expected # 127 - not found ## check_buildenv() { in_opt_array "$1" ${BUILDENV[@]} case $? in 0) # assert enabled [[ $2 = "y" ]] return ;; 1) # assert disabled [[ $2 = "n" ]] return ;; esac # not found return 127 } ## # usage : in_opt_array( $needle, $haystack ) # return : 0 - enabled # 1 - disabled # 127 - not found ## in_opt_array() { local needle=$1; shift local i opt for (( i = $#; i > 0; i-- )); do opt=${!i} if [[ $opt = "$needle" ]]; then # enabled return 0 elif [[ $opt = "!$needle" ]]; then # disabled return 1 fi done # not found return 127 } ## # usage : in_array( $needle, $haystack ) # return : 0 - found # 1 - not found ## in_array() { local needle=$1; shift local item for item in "$@"; do [[ $item = "$needle" ]] && return 0 # Found done return 1 # Not Found } source_has_signatures() { local file for file in "${source[@]}"; do if [[ ${file%%::*} = *.@(sig?(n)|asc) ]]; then return 0 fi done return 1 } run_pacman() { local cmd if [[ ! $1 = -@(T|Qq) ]]; then cmd=("$PACMAN_PATH" $PACMAN_OPTS "$@") else cmd=("$PACMAN_PATH" "$@") fi if (( ! ASROOT )) && [[ ! $1 = -@(T|Qq) ]]; then if type -p sudo >/dev/null; then cmd=(sudo "${cmd[@]}") else cmd=(su root -c "$(printf '%q ' "${cmd[@]}")") fi fi "${cmd[@]}" } check_deps() { (( $# > 0 )) || return 0 local ret=0 local pmout pmout=$(run_pacman -T "$@") ret=$? if (( ret == 127 )); then #unresolved deps printf "%s\n" "$pmout" elif (( ret )); then error "$(gettext "'%s' returned a fatal error (%i): %s")" "$PACMAN" "$ret" "$pmout" return "$ret" fi } handle_deps() { local R_DEPS_SATISFIED=0 local R_DEPS_MISSING=1 (( $# == 0 )) && return $R_DEPS_SATISFIED local deplist="$*" if (( ! DEP_BIN )); then return $R_DEPS_MISSING fi if (( DEP_BIN )); then # install missing deps from binary packages (using pacman -S) msg "$(gettext "Installing missing dependencies...")" if ! run_pacman -S --asdeps $deplist; then error "$(gettext "'%s' failed to install missing dependencies.")" "$PACMAN" exit 1 # TODO: error code fi fi # we might need the new system environment # save our shell options and turn off extglob local shellopts=$(shopt -p) shopt -u extglob source /etc/profile &>/dev/null eval "$shellopts" return $R_DEPS_SATISFIED } resolve_deps() { local R_DEPS_SATISFIED=0 local R_DEPS_MISSING=1 # deplist cannot be declared like this: local deplist=$(foo) # Otherwise, the return value will depend on the assignment. local deplist deplist="$(set +E; check_deps $*)" || exit 1 [[ -z $deplist ]] && return $R_DEPS_SATISFIED if handle_deps $deplist; then # check deps again to make sure they were resolved deplist="$(set +E; check_deps $*)" || exit 1 [[ -z $deplist ]] && return $R_DEPS_SATISFIED fi msg "$(gettext "Missing dependencies:")" local dep for dep in $deplist; do msg2 "$dep" done return $R_DEPS_MISSING } remove_deps() { (( ! RMDEPS )) && return # check for packages removed during dependency install (e.g. due to conflicts) # removing all installed packages is risky in this case if [[ -n $(grep -xvFf <(printf '%s\n' "${current_packagelist[@]}") \ <(printf '%s\n' "${original_packagelist[@]}") || true) ]]; then warning "$(gettext "Failed to remove installed dependencies.")" return 0 fi local deplist deplist=($(grep -xvFf <(printf "%s\n" "${original_pkglist[@]}") \ <(printf "%s\n" "${current_pkglist[@]}") || true)) if [[ -z $deplist ]]; then return fi msg "Removing installed dependencies..." # exit cleanly on failure to remove deps as package has been built successfully if ! run_pacman -Rn ${deplist[@]}; then warning "$(gettext "Failed to remove installed dependencies.")" return 0 fi } get_integlist() { local integ local integlist=() for integ in "${known_hash_algos[@]}"; do local sumname="${integ}sums[@]" if [[ -n ${!sumname} ]]; then integlist+=("$integ") fi done if (( ${#integlist[@]} > 0 )); then printf "%s\n" "${integlist[@]}" else printf "%s\n" "${INTEGRITY_CHECK[@]}" fi } generate_checksums() { msg "$(gettext "Generating checksums for source files...")" if ! type -p openssl >/dev/null; then error "$(gettext "Cannot find the %s binary required for generating sourcefile checksums.")" "openssl" exit 1 # $E_MISSING_PROGRAM fi local integlist if (( $# == 0 )); then IFS=$'\n' read -rd '' -a integlist < <(get_integlist) else integlist=("$@") fi local integ for integ in "${integlist[@]}"; do if ! in_array "$integ" "${known_hash_algos[@]}"; then error "$(gettext "Invalid integrity algorithm '%s' specified.")" "$integ" exit 1 # $E_CONFIG_ERROR fi local indentsz idx numsrc=${#source[@]} printf "%s%n" "${integ}sums=(" indentsz for (( idx = 0; idx < numsrc; i++ )); do local netfile=${source[idx]} local proto sum proto="$(get_protocol "$netfile")" case $proto in bzr*|git*|hg*|svn*) sum="SKIP" ;; *) if [[ ! $netfile = *.@(sig?(n)|asc) ]]; then local file file="$(get_filepath "$netfile")" || missing_source_file "$netfile" sum="$(openssl dgst -${integ} "$file")" sum=${sum##* } else sum="SKIP" fi ;; esac # indent checksum on lines after the first printf "%*s%s" $(( idx ? indentsz : 0 )) '' "'$sum'" # print a newline on lines before the last (( ++idx < numsrc )) && echo done echo ")" done } check_checksums() { (( SKIPCHECKSUMS )) && return 0 (( ! ${#source[@]} )) && return 0 local correlation=0 local integ required for integ in "${known_hash_algos[@]}"; do local sumname="${integ}sums[@]" local integrity_sums=("${!sumname}") if (( ${#integrity_sums[@]} == ${#source[@]} )); then msg "$(gettext "Validating source files with %s...")" "${integ}sums" correlation=1 local idx errors=0 for (( idx = 0; idx < ${#source[*]}; idx++ )); do local file="$(get_filename "${source[idx]}")" printf ' %s ... ' "$file" >&2 if [[ ${integrity_sums[idx]} = 'SKIP' ]]; then printf '%s\n' "$(gettext "Skipped")" >&2 continue fi if ! file="$(get_filepath "$file")"; then printf '%s\n' "$(gettext "NOT FOUND")" >&2 errors=1 continue fi local expectedsum="${integrity_sums[idx],,}" local realsum="$(openssl dgst -${integ} "$file")" realsum="${realsum##* }" if [[ $expectedsum = "$realsum" ]]; then printf '%s\n' "$(gettext "Passed")" >&2 else printf '%s\n' "$(gettext "FAILED")" >&2 errors=1 fi done if (( errors )); then error "$(gettext "One or more files did not pass the validity check!")" exit 1 # TODO: error code fi elif (( ${#integrity_sums[@]} )); then error "$(gettext "Integrity checks (%s) differ in size from the source array.")" "$integ" exit 1 # TODO: error code fi done if (( ! correlation )); then error "$(gettext "Integrity checks are missing.")" exit 1 # TODO: error code fi } check_pgpsigs() { (( SKIPPGPCHECK )) && return 0 ! source_has_signatures && return 0 msg "$(gettext "Verifying source file signatures with %s...")" "gpg" local file pubkey local warning=0 local errors=0 local statusfile=$(mktemp) for file in "${source[@]}"; do file="$(get_filename "$file")" if [[ ! $file = *.@(sig?(n)|asc) ]]; then continue fi printf " %s ... " "${file%.*}" >&2 if ! file="$(get_filepath "$file")"; then printf '%s\n' "$(gettext "SIGNATURE NOT FOUND")" >&2 errors=1 continue fi if ! sourcefile="$(get_filepath "${file%.*}")"; then printf '%s\n' "$(gettext "SOURCE FILE NOT FOUND")" >&2 errors=1 continue fi if ! gpg --quiet --batch --status-file "$statusfile" --verify "$file" "$sourcefile" 2> /dev/null; then printf '%s' "$(gettext "FAILED")" >&2 if ! pubkey=$(awk '/NO_PUBKEY/ { print $3; exit 1; }' "$statusfile"); then printf ' (%s)' "$(gettext "unknown public key") $pubkey" >&2 warnings=1 else errors=1 fi printf '\n' >&2 else if grep -q "REVKEYSIG" "$statusfile"; then printf '%s (%s)' "$(gettext "FAILED")" "$(gettext "the key has been revoked.")" >&2 errors=1 else printf '%s' "$(gettext "Passed")" >&2 if grep -q "EXPSIG" "$statusfile"; then printf ' (%s)' "$(gettext "WARNING:") $(gettext "the signature has expired.")" >&2 warnings=1 elif grep -q "EXPKEYSIG" "$statusfile"; then printf ' (%s)' "$(gettext "WARNING:") $(gettext "the key has expired.")" >&2 warnings=1 fi fi printf '\n' >&2 fi done rm -f "$statusfile" if (( errors )); then error "$(gettext "One or more PGP signatures could not be verified!")" exit 1 fi if (( warnings )); then warning "$(gettext "Warnings have occurred while verifying the signatures.")" plain "$(gettext "Please make sure you really trust them.")" fi } check_source_integrity() { if (( SKIPCHECKSUMS && SKIPPGPCHECK )); then warning "$(gettext "Skipping all source file integrity checks.")" elif (( SKIPCHECKSUMS )); then warning "$(gettext "Skipping verification of source file checksums.")" check_pgpsigs elif (( SKIPPGPCHECK )); then warning "$(gettext "Skipping verification of source file PGP signatures.")" check_checksums else check_checksums check_pgpsigs fi } extract_sources() { msg "$(gettext "Extracting sources...")" local netfile for netfile in "${source[@]}"; do local file=$(get_filename "$netfile") if in_array "$file" "${noextract[@]}"; then #skip source files in the noextract=() array # these are marked explicitly to NOT be extracted continue fi local proto=$(get_protocol "$netfile") case "$proto" in bzr*) extract_bzr "$netfile" ;; git*) extract_git "$netfile" ;; hg*) extract_hg "$netfile" ;; svn*) extract_svn "$netfile" ;; *) extract_file "$file" ;; esac done if (( PKGVERFUNC )); then update_pkgver check_build_status fi } error_function() { if [[ -p $logpipe ]]; then rm "$logpipe" fi # first exit all subshells, then print the error if (( ! BASH_SUBSHELL )); then error "$(gettext "A failure occurred in %s().")" "$1" plain "$(gettext "Aborting...")" fi exit 2 # $E_BUILD_FAILED } cd_safe() { if ! cd "$1"; then error "$(gettext "Failed to change to directory %s")" "$1" plain "$(gettext "Aborting...")" exit 1 fi } source_safe() { shopt -u extglob if ! source "$@"; then error "$(gettext "Failed to source %s")" "$1" exit 1 fi shopt -s extglob } run_function_safe() { local restoretrap set -e set -E restoretrap=$(trap -p ERR) trap 'error_function $pkgfunc' ERR run_function "$1" eval $restoretrap set +E set +e } run_function() { if [[ -z $1 ]]; then return 1 fi local pkgfunc="$1" # clear user-specified buildflags if requested if check_option "buildflags" "n"; then unset CPPFLAGS CFLAGS CXXFLAGS LDFLAGS fi if check_option "debug" "y"; then CFLAGS+=" $DEBUG_CFLAGS" CXXFLAGS+=" $DEBUG_CXXFLAGS" fi # clear user-specified makeflags if requested if check_option "makeflags" "n"; then unset MAKEFLAGS fi msg "$(gettext "Starting %s()...")" "$pkgfunc" cd_safe "$srcdir" # ensure all necessary build variables are exported export CPPFLAGS CFLAGS CXXFLAGS LDFLAGS MAKEFLAGS CHOST # save our shell options so pkgfunc() can't override what we need local shellopts=$(shopt -p) local ret=0 if (( LOGGING )); then local fullver=$(get_full_version) local BUILDLOG="$LOGDEST/${pkgbase}-${fullver}-${CARCH}-$pkgfunc.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 # ensure overridden package variables survive tee with split packages logpipe=$(mktemp -u "$LOGDEST/logpipe.XXXXXXXX") mkfifo "$logpipe" tee "$BUILDLOG" < "$logpipe" & local teepid=$! $pkgfunc &>"$logpipe" wait $teepid rm "$logpipe" else "$pkgfunc" fi # reset our shell options eval "$shellopts" } run_prepare() { run_function_safe "prepare" } run_build() { # use distcc if it is requested (check buildenv and PKGBUILD opts) if check_buildenv "distcc" "y" && ! check_option "distcc" "n"; then [[ -d /usr/lib/distcc/bin ]] && export PATH="/usr/lib/distcc/bin:$PATH" export DISTCC_HOSTS fi # use ccache if it is requested (check buildenv and PKGBUILD opts) if check_buildenv "ccache" "y" && ! check_option "ccache" "n"; then [[ -d /usr/lib/ccache/bin ]] && export PATH="/usr/lib/ccache/bin:$PATH" fi run_function_safe "build" } run_check() { run_function_safe "check" } run_package() { local pkgfunc if [[ -z $1 ]]; then pkgfunc="package" else pkgfunc="package_$1" fi run_function_safe "$pkgfunc" } build_id() { LANG=C readelf -n $1 | sed -n '/Build ID/ { s/.*: //p; q; }' } strip_file() { local binary=$1; shift if check_option "debug" "y"; then local bid=$(build_id "$binary") # has this file already been stripped if [[ -n "$bid" ]]; then if [[ -f "$dbgdir/.build_id/${bid:0:2}/${bid:2}.debug" ]]; then return fi elif [[ -f "$dbgdir/$binary.debug" ]]; then return fi mkdir -p "$dbgdir/${binary%/*}" objcopy --only-keep-debug "$binary" "$dbgdir/$binary.debug" objcopy --add-gnu-debuglink="$dbgdir/${binary#/}.debug" "$binary" # create any needed hardlinks while read -rd '' file ; do if [[ "${binary}" -ef "${file}" && ! -f "$dbgdir/${file}.debug" ]]; then mkdir -p "$dbgdir/${file%/*}" ln "$dbgdir/${binary}.debug" "$dbgdir/${file}.debug" fi done < <(find . -type f -perm -u+w -print0 2>/dev/null) if [[ -n "$bid" ]]; then local target mkdir -p "$dbgdir/.build_id/${bid:0:2}" target="../../../../../${binary#./}" target="${target/..\/..\/usr\/lib\/}" target="${target/..\/usr\/}" ln -s "$target" "$dbgdir/.build_id/${bid:0:2}/${bid:2}" target="../../${binary#./}.debug" ln -s "$target" "$dbgdir/.build_id/${bid:0:2}/${bid:2}.debug" fi fi strip $@ "$binary" } tidy_install() { cd_safe "$pkgdir" msg "$(gettext "Tidying install...")" if check_option "docs" "n" && [[ -n ${DOC_DIRS[*]} ]]; then msg2 "$(gettext "Removing doc files...")" rm -rf -- ${DOC_DIRS[@]} fi if check_option "purge" "y" && [[ -n ${PURGE_TARGETS[*]} ]]; then msg2 "$(gettext "Purging unwanted files...")" local pt for pt in "${PURGE_TARGETS[@]}"; do if [[ ${pt} = "${pt//\/}" ]]; then find . ! -type d -name "${pt}" -exec rm -f -- '{}' + else rm -f ${pt} fi done fi if check_option "libtool" "n"; then msg2 "$(gettext "Removing "%s" files...")" "libtool" find . ! -type d -name "*.la" -exec rm -f -- '{}' + fi if check_option "staticlibs" "n"; then msg2 "$(gettext "Removing "%s" files...")" "static library" find . ! -type d -name "*.a" -exec rm -f -- '{}' + fi if check_option "emptydirs" "n"; then msg2 "$(gettext "Removing empty directories...")" find . -depth -type d -exec rmdir '{}' + 2>/dev/null fi # check existence of backup files local file for file in "${backup[@]}"; do if [[ ! -f $file ]]; then warning "$(gettext "%s entry file not in package : %s")" "backup" "$file" fi done # check for references to the build and package directory if find "${pkgdir}" -type f -print0 | xargs -0 grep -q -I "${srcdir}" ; then warning "$(gettext "Package contains reference to %s")" "\$srcdir" fi if find "${pkgdir}" -type f -print0 | xargs -0 grep -q -I "${pkgdirbase}" ; then warning "$(gettext "Package contains reference to %s")" "\$pkgdir" fi if check_option "zipman" "y" && [[ -n ${MAN_DIRS[*]} ]]; then msg2 "$(gettext "Compressing man and info pages...")" local file files inode link while read -rd ' ' inode; do read file find ${MAN_DIRS[@]} -type l 2>/dev/null | while read -r link ; do if [[ "${file}" -ef "${link}" ]] ; then rm -f "$link" "${link}.gz" if [[ ${file%/*} = ${link%/*} ]]; then ln -s -- "${file##*/}.gz" "${link}.gz" else ln -s -- "/${file}.gz" "${link}.gz" fi fi done if [[ -z ${files[$inode]} ]]; then files[$inode]=$file gzip -9 -n -f "$file" else rm -f "$file" ln "${files[$inode]}.gz" "${file}.gz" chmod 644 "${file}.gz" fi done < <(find ${MAN_DIRS[@]} -type f \! -name "*.gz" \! -name "*.bz2" \ -exec @INODECMD@ '{}' + 2>/dev/null) fi if check_option "strip" "y"; then msg2 "$(gettext "Stripping unneeded symbols from binaries and libraries...")" # make sure library stripping variables are defined to prevent excess stripping [[ -z ${STRIP_SHARED+x} ]] && STRIP_SHARED="-S" [[ -z ${STRIP_STATIC+x} ]] && STRIP_STATIC="-S" if check_option "debug" "y"; then dbgdir="$pkgdir-@DEBUGSUFFIX@/usr/lib/debug" mkdir -p "$dbgdir" fi local binary strip_flags find . -type f -perm -u+w -print0 2>/dev/null | while read -rd '' binary ; do case "$(file -bi "$binary")" in *application/x-sharedlib*) # Libraries (.so) strip_flags="$STRIP_SHARED";; *application/x-archive*) # Libraries (.a) strip_flags="$STRIP_STATIC";; *application/x-executable*) # Binaries strip_flags="$STRIP_BINARIES";; *) continue ;; esac strip_file "$binary" ${strip_flags} done fi if check_option "upx" "y"; then msg2 "$(gettext "Compressing binaries with %s...")" "UPX" local binary find . -type f -perm -u+w 2>/dev/null | while read -r binary ; do if [[ $(file -bi "$binary") = *'application/x-executable'* ]]; then upx $UPXFLAGS "$binary" &>/dev/null || warning "$(gettext "Could not compress binary : %s")" "${binary/$pkgdir\//}" fi done fi } find_libdepends() { local d sodepends; sodepends=0; for d in "${depends[@]}"; do if [[ $d = *.so ]]; then sodepends=1; break; fi done if (( sodepends == 0 )); then printf '%s\n' "${depends[@]}" return; fi local libdeps filename soarch sofile soname soversion; declare -A libdeps; while read -r filename; do # get architecture of the file; if soarch is empty it's not an ELF binary soarch=$(LC_ALL=C readelf -h "$filename" 2>/dev/null | sed -n 's/.*Class.*ELF\(32\|64\)/\1/p') [[ -n "$soarch" ]] || continue # process all libraries needed by the binary for sofile in $(LC_ALL=C readelf -d "$filename" 2>/dev/null | sed -nr 's/.*Shared library: \[(.*)\].*/\1/p') do # extract the library name: libfoo.so soname="${sofile%.so?(+(.+([0-9])))}".so # extract the major version: 1 soversion="${sofile##*\.so\.}" if [[ ${libdeps[$soname]} ]]; then if [[ ${libdeps[$soname]} != *${soversion}-${soarch}* ]]; then libdeps[$soname]+=" ${soversion}-${soarch}" fi else libdeps[$soname]="${soversion}-${soarch}" fi done done < <(find "$pkgdir" -type f -perm -u+x) local libdepends v for d in "${depends[@]}"; do case "$d" in *.so) if [[ ${libdeps[$d]} ]]; then for v in ${libdeps[$d]}; do libdepends+=("$d=$v") done else warning "$(gettext "Library listed in %s is not required by any files: %s")" "'depends'" "$d" libdepends+=("$d") fi ;; *) libdepends+=("$d") ;; esac done printf '%s\n' "${libdepends[@]}" } find_libprovides() { local p libprovides missing for p in "${provides[@]}"; do missing=0 case "$p" in *.so) mapfile -t filename < <(find "$pkgdir" -type f -name $p\*) if [[ $filename ]]; then # packages may provide multiple versions of the same library for fn in "${filename[@]}"; do # check if we really have a shared object if LC_ALL=C readelf -h "$fn" 2>/dev/null | grep -q '.*Type:.*DYN (Shared object file).*'; then # get the string binaries link to (e.g. libfoo.so.1.2 -> libfoo.so.1) local sofile=$(LC_ALL=C readelf -d "$fn" 2>/dev/null | sed -n 's/.*Library soname: \[\(.*\)\].*/\1/p') if [[ -z "$sofile" ]]; then warning "$(gettext "Library listed in %s is not versioned: %s")" "'provides'" "$p" libprovides+=("$p") continue fi # get the library architecture (32 or 64 bit) local soarch=$(LC_ALL=C readelf -h "$fn" | sed -n 's/.*Class.*ELF\(32\|64\)/\1/p') # extract the library major version local soversion="${sofile##*\.so\.}" libprovides+=("${p}=${soversion}-${soarch}") else warning "$(gettext "Library listed in %s is not a shared object: %s")" "'provides'" "$p" libprovides+=("$p") fi done else libprovides+=("$p") missing=1 fi ;; *) libprovides+=("$p") ;; esac if (( missing )); then warning "$(gettext "Cannot find library listed in %s: %s")" "'provides'" "$p" fi done printf '%s\n' "${libprovides[@]}" } write_pkginfo() { local builddate=$(date -u "+%s") if [[ -n $PACKAGER ]]; then local packager="$PACKAGER" else local packager="Unknown Packager" fi local size="$(@DUPATH@ @DUFLAGS@)" size="$(( ${size%%[^0-9]*} * 1024 ))" msg2 "$(gettext "Generating %s file...")" ".PKGINFO" printf "# Generated by makepkg %s\n" "$makepkg_version" (( INFAKEROOT )) && printf "# using %s\n" "$(fakeroot -v)" printf "# %s\n" "$(LC_ALL=C date -u)" printf "pkgname = %s\n" "$pkgname" (( SPLITPKG )) && printf "pkgbase = %s\n" "$pkgbase" printf "pkgver = %s\n" "$(get_full_version)" printf "pkgdesc = %s\n" "${pkgdesc//+([[:space:]])/ }" printf "url = %s\n" "$url" printf "builddate = %s\n" "$builddate" printf "packager = %s\n" "$packager" printf "size = %s\n" "$size" printf "arch = %s\n" "$pkgarch" mapfile -t provides < <(find_libprovides) mapfile -t depends < <(find_libdepends) [[ $license ]] && printf "license = %s\n" "${license[@]}" [[ $replaces ]] && printf "replaces = %s\n" "${replaces[@]}" [[ $groups ]] && printf "group = %s\n" "${groups[@]}" [[ $conflicts ]] && printf "conflict = %s\n" "${conflicts[@]}" [[ $provides ]] && printf "provides = %s\n" "${provides[@]}" [[ $backup ]] && printf "backup = %s\n" "${backup[@]}" [[ $depends ]] && printf "depend = %s\n" "${depends[@]}" [[ $optdepends ]] && printf "optdepend = %s\n" "${optdepends[@]//+([[:space:]])/ }" [[ $makedepends ]] && printf "makedepend = %s\n" "${makedepends[@]}" [[ $checkdepends ]] && printf "checkdepend = %s\n" "${checkdepends[@]}" local it for it in "${packaging_options[@]}"; do check_option "$it" "y" case $? in 0) printf "makepkgopt = %s\n" "$it" ;; 1) printf "makepkgopt = %s\n" "!$it" ;; esac done } create_package() { if [[ ! -d $pkgdir ]]; then error "$(gettext "Missing %s directory.")" "\$pkgdir/" plain "$(gettext "Aborting...")" exit 1 # $E_MISSING_PKGDIR fi cd_safe "$pkgdir" msg "$(gettext "Creating package \"%s\"...")" "$pkgname" pkgarch=$(get_pkg_arch) write_pkginfo > .PKGINFO local comp_files=('.PKGINFO') # check for changelog/install files for i in 'changelog/.CHANGELOG' 'install/.INSTALL'; do IFS='/' read -r orig dest < <(printf '%s\n' "$i") if [[ -n ${!orig} ]]; then msg2 "$(gettext "Adding %s file...")" "$orig" cp "$startdir/${!orig}" "$dest" chmod 644 "$dest" comp_files+=("$dest") fi done # tar it up local fullver=$(get_full_version) local pkg_file="$PKGDEST/${pkgname}-${fullver}-${pkgarch}${PKGEXT}" local ret=0 [[ -f $pkg_file ]] && rm -f "$pkg_file" [[ -f $pkg_file.sig ]] && rm -f "$pkg_file.sig" # when fileglobbing, we want * in an empty directory to expand to # the null string rather than itself shopt -s nullglob msg2 "$(gettext "Generating .MTREE file...")" bsdtar -czf .MTREE --format=mtree \ --options='!all,use-set,type,uid,gid,mode,time,size,md5,sha256,link' \ "${comp_files[@]}" * comp_files+=(".MTREE") msg2 "$(gettext "Compressing package...")" # TODO: Maybe this can be set globally for robustness shopt -s -o pipefail # bsdtar's gzip compression always saves the time stamp, making one # archive created using the same command line distinct from another. # Disable bsdtar compression and use gzip -n for now. bsdtar -cf - "${comp_files[@]}" * | case "$PKGEXT" in *tar.gz) ${COMPRESSGZ[@]:-gzip -c -f -n} ;; *tar.bz2) ${COMPRESSBZ2[@]:-bzip2 -c -f} ;; *tar.xz) ${COMPRESSXZ[@]:-xz -c -z -} ;; *tar.lrz) ${COMPRESSLRZ[@]:-lrzip -q} ;; *tar.lzo) ${COMPRESSLZO[@]:-lzop -q} ;; *tar.Z) ${COMPRESSZ[@]:-compress -c -f} ;; *tar) cat ;; *) warning "$(gettext "'%s' is not a valid archive extension.")" \ "$PKGEXT"; cat ;; esac > "${pkg_file}" || ret=$? shopt -u nullglob shopt -u -o pipefail if (( ret )); then error "$(gettext "Failed to create package file.")" exit 1 # TODO: error code fi create_signature "$pkg_file" if (( ! ret )) && [[ ! "$PKGDEST" -ef "${startdir}" ]]; then rm -f "${pkg_file/$PKGDEST/$startdir}" ln -s "${pkg_file}" "${pkg_file/$PKGDEST/$startdir}" ret=$? if [[ -f $pkg_file.sig ]]; then rm -f "${pkg_file/$PKGDEST/$startdir}.sig" ln -s "$pkg_file.sig" "${pkg_file/$PKGDEST/$startdir}.sig" fi fi if (( ret )); then warning "$(gettext "Failed to create symlink to package file.")" fi } create_debug_package() { # check if a debug package was requested if ! check_option "debug" "y" || ! check_option "strip" "y"; then return fi pkgdir="${pkgdir}-@DEBUGSUFFIX@" # check if we have any debug symbols to package if dir_is_empty "$pkgdir/usr/lib/debug"; then return fi depends=("$pkgname=$(get_full_version)") pkgdesc="Detached debugging symbols for $pkgname" pkgname=$pkgname-@DEBUGSUFFIX@ unset groups optdepends provides conflicts replaces backup install changelog create_package } create_signature() { if [[ $SIGNPKG != 'y' ]]; then return fi local ret=0 local filename="$1" msg "$(gettext "Signing package...")" local SIGNWITHKEY="" if [[ -n $GPGKEY ]]; then SIGNWITHKEY="-u ${GPGKEY}" fi # The signature will be generated directly in ascii-friendly format gpg --detach-sign --use-agent ${SIGNWITHKEY} "$filename" &>/dev/null || ret=$? if (( ! ret )); then msg2 "$(gettext "Created signature file %s.")" "$filename.sig" else warning "$(gettext "Failed to sign package file.")" fi } create_srcpackage() { local ret=0 msg "$(gettext "Creating source package...")" local srclinks="$(mktemp -d "$startdir"/srclinks.XXXXXXXXX)" mkdir "${srclinks}"/${pkgbase} msg2 "$(gettext "Adding %s...")" "$BUILDSCRIPT" ln -s "${BUILDFILE}" "${srclinks}/${pkgbase}/${BUILDSCRIPT}" local file for file in "${source[@]}"; do if [[ "$file" = "$(get_filename "$file")" ]] || (( SOURCEONLY == 2 )); then local absfile absfile=$(get_filepath "$file") || missing_source_file "$file" msg2 "$(gettext "Adding %s...")" "${absfile##*/}" ln -s "$absfile" "$srclinks/$pkgbase" fi done local i for i in 'changelog' 'install'; do local file while read -r file; do # evaluate any bash variables used eval file=\"$(sed 's/^\(['\''"]\)\(.*\)\1$/\2/' <<< "$file")\" if [[ $file && ! -f "${srclinks}/${pkgbase}/$file" ]]; then msg2 "$(gettext "Adding %s file (%s)...")" "$i" "${file}" ln -s "${startdir}/$file" "${srclinks}/${pkgbase}/" fi done < <(sed -n "s/^[[:space:]]*$i=//p" "$BUILDFILE") done local TAR_OPT case "$SRCEXT" in *tar.gz) TAR_OPT="-z" ;; *tar.bz2) TAR_OPT="-j" ;; *tar.xz) TAR_OPT="-J" ;; *tar.lrz) TAR_OPT="--lrzip" ;; *tar.lzo) TAR_OPT="--lzop" ;; *tar.Z) TAR_OPT="-Z" ;; *tar) TAR_OPT="" ;; *) warning "$(gettext "'%s' is not a valid archive extension.")" \ "$SRCEXT" ;; esac local fullver=$(get_full_version) local pkg_file="$SRCPKGDEST/${pkgbase}-${fullver}${SRCEXT}" # tar it up msg2 "$(gettext "Compressing source package...")" cd_safe "${srclinks}" if ! bsdtar -cL ${TAR_OPT} -f "$pkg_file" ${pkgbase}; then error "$(gettext "Failed to create source package file.")" exit 1 # TODO: error code fi if [[ ! "$SRCPKGDEST" -ef "${startdir}" ]]; then rm -f "${pkg_file/$SRCPKGDEST/$startdir}" ln -s "${pkg_file}" "${pkg_file/$SRCPKGDEST/$startdir}" ret=$? fi if (( ret )); then warning "$(gettext "Failed to create symlink to source package file.")" fi cd_safe "${startdir}" rm -rf "${srclinks}" } install_package() { (( ! INSTALL )) && return if (( ! SPLITPKG )); then msg "$(gettext "Installing package %s with %s...")" "$pkgname" "$PACMAN -U" else msg "$(gettext "Installing %s package group with %s...")" "$pkgbase" "$PACMAN -U" fi local fullver pkgarch pkg pkglist (( ASDEPS )) && pkglist+=('--asdeps') (( NEEDED )) && pkglist+=('--needed') for pkg in ${pkgname[@]}; do fullver=$(get_full_version $pkg) pkgarch=$(get_pkg_arch $pkg) pkglist+=("$PKGDEST/${pkg}-${fullver}-${pkgarch}${PKGEXT}") if [[ -f "$PKGDEST/${pkg}-debug-${fullver}-${pkgarch}${PKGEXT}" ]]; then pkglist+=("$PKGDEST/${pkg}-debug-${fullver}-${pkgarch}${PKGEXT}") fi done if ! run_pacman -U "${pkglist[@]}"; then warning "$(gettext "Failed to install built package(s).")" return 0 fi } have_function() { declare -f "$1" >/dev/null } check_sanity() { # check for no-no's in the build script local i local ret=0 for i in 'pkgname' 'pkgrel'; do if [[ -z ${!i} ]]; then error "$(gettext "%s is not allowed to be empty.")" "$i" ret=1 fi done for i in "${pkgname[@]}"; do if [[ ${i:0:1} = "-" ]]; then error "$(gettext "%s is not allowed to start with a hyphen.")" "pkgname" ret=1 fi if [[ ${i:0:1} = "." ]]; then error "$(gettext "%s is not allowed to start with a dot.")" "pkgname" ret=1 fi if [[ $i = *[^[:alnum:]+_.@-]* ]]; then error "$(gettext "%s contains invalid characters: '%s'")" \ 'pkgname' "${pkgname//[[:alnum:]+_.@-]}" ret=1 fi done if [[ ${pkgbase:0:1} = "-" ]]; then error "$(gettext "%s is not allowed to start with a hyphen.")" "pkgbase" ret=1 fi if (( ! PKGVERFUNC )) ; then check_pkgver || ret=1 fi awk -F'=' '$1 ~ /^[[:space:]]*pkgrel$/' "$BUILDFILE" | sed "s/[[:space:]]*#.*//" | while IFS='=' read -r _ i; do eval i=\"$(sed 's/^\(['\''"]\)\(.*\)\1$/\2/' <<< "${i%%+([[:space:]])}")\" if [[ $i != +([0-9])?(.+([0-9])) ]]; then error "$(gettext "%s must be a decimal.")" "pkgrel" return 1 fi done || ret=1 awk -F'=' '$1 ~ /^[[:space:]]*epoch$/' "$BUILDFILE" | while IFS='=' read -r _ i; do eval i=\"$(sed 's/^\(['\''"]\)\(.*\)\1$/\2/' <<< "${i%%+([[:space:]])}")\" if [[ $i != *([[:digit:]]) ]]; then error "$(gettext "%s must be an integer.")" "epoch" return 1 fi done || ret=1 if [[ $arch != 'any' ]]; then if ! in_array $CARCH ${arch[@]}; then if (( ! IGNOREARCH )); then error "$(gettext "%s is not available for the '%s' architecture.")" "$pkgbase" "$CARCH" plain "$(gettext "Note that many packages may need a line added to their %s")" "$BUILDSCRIPT" plain "$(gettext "such as %s.")" "arch=('$CARCH')" ret=1 fi fi fi if (( ${#pkgname[@]} > 1 )); then for i in ${pkgname[@]}; do local arch_list="" eval $(declare -f package_${i} | sed -n 's/\(^[[:space:]]*arch=\)/arch_list=/p') if [[ ${arch_list[@]} && ${arch_list} != 'any' ]]; then if ! in_array $CARCH ${arch_list[@]}; then if (( ! IGNOREARCH )); then error "$(gettext "%s is not available for the '%s' architecture.")" "$i" "$CARCH" ret=1 fi fi fi done fi local provides_list=() eval $(awk '/^[[:space:]]*provides=/,/\)/' "$BUILDFILE" | \ sed -e "s/provides=/provides_list+=/" -e "s/#.*//" -e 's/\\$//') for i in ${provides_list[@]}; do if [[ $i == *['<>']* ]]; then error "$(gettext "%s array cannot contain comparison (< or >) operators.")" "provides" ret=1 fi done local backup_list=() eval $(awk '/^[[:space:]]*backup=/,/\)/' "$BUILDFILE" | \ sed -e "s/backup=/backup_list+=/" -e "s/#.*//" -e 's/\\$//') for i in "${backup_list[@]}"; do if [[ ${i:0:1} = "/" ]]; then error "$(gettext "%s entry should not contain leading slash : %s")" "backup" "$i" ret=1 fi done local optdepends_list=() eval $(awk '/^[[:space:]]*optdepends=\(/,/\)[[:space:]]*(#.*)?$/' "$BUILDFILE" | \ sed -e "s/optdepends=/optdepends_list+=/" -e "s/#.*//" -e 's/\\$//') for i in "${optdepends_list[@]}"; do local pkg=${i%%:[[:space:]]*} # the '-' character _must_ be first or last in the character range if [[ $pkg != +([-[:alnum:]><=.+_:]) ]]; then error "$(gettext "Invalid syntax for %s : '%s'")" "optdepend" "$i" ret=1 fi done for i in 'changelog' 'install'; do local file while read -r file; do # evaluate any bash variables used eval file=\"$(sed 's/^\(['\''"]\)\(.*\)\1$/\2/' <<< "$file")\" if [[ $file && ! -f $file ]]; then error "$(gettext "%s file (%s) does not exist.")" "$i" "$file" ret=1 fi done < <(sed -n "s/^[[:space:]]*$i=//p" "$BUILDFILE") done local valid_options=1 local known kopt options_list eval $(awk '/^[[:space:]]*options=/,/\)/' "$BUILDFILE" | \ sed -e "s/options=/options_list+=/" -e "s/#.*//" -e 's/\\$//') for i in ${options_list[@]}; do known=0 # check if option matches a known option or its inverse for kopt in ${packaging_options[@]} ${other_options[@]}; do if [[ ${i} = "${kopt}" || ${i} = "!${kopt}" ]]; then known=1 fi done if (( ! known )); then error "$(gettext "%s array contains unknown option '%s'")" "options" "$i" valid_options=0 fi done if (( ! valid_options )); then ret=1 fi if (( ${#pkgname[@]} == 1 )); then if have_function build && ! ( have_function package || have_function package_${pkgname}); then error "$(gettext "Missing %s function in %s")" "package()" "$BUILDFILE" ret=1 fi else for i in ${pkgname[@]}; do if ! have_function package_${i}; then error "$(gettext "Missing %s function for split package '%s'")" "package_$i()" "$i" ret=1 fi done fi for i in ${PKGLIST[@]}; do if ! in_array $i ${pkgname[@]}; then error "$(gettext "Requested package %s is not provided in %s")" "$i" "$BUILDFILE" ret=1 fi done local idx=("${!source[@]}") if (( (idx[-1] + 1) != ${#source[*]} )); then error "$(gettext "Sparse arrays are not allowed for source")" ret=1 fi return $ret } validate_pkgver() { if [[ $1 = *[[:space:]:-]* ]]; then error "$(gettext "%s is not allowed to contain colons, hyphens or whitespace.")" "pkgver" return 1 fi } check_pkgver() { local ret=0 if [[ -z ${pkgver} ]]; then error "$(gettext "%s is not allowed to be empty.")" "pkgver" ret=1 fi awk -F'=' '$1 ~ /^[[:space:]]*pkgver$/' "$BUILDFILE" | sed "s/[[:space:]]*#.*//" | while IFS='=' read -r _ i; do eval i=\"$(sed 's/^\(['\''"]\)\(.*\)\1$/\2/' <<< "${i%%+([[:space:]])}")\" validate_pkgver "$i" || return 1 done || ret=1 return $ret } check_software() { # check for needed software local ret=0 # check for PACMAN if we need it if (( ! INFAKEROOT && ( ! NODEPS || DEP_BIN || RMDEPS || INSTALL ) )); then if [[ -z $PACMAN_PATH ]]; then error "$(gettext "Cannot find the %s binary required for dependency operations.")" "$PACMAN" ret=1 fi fi # check for sudo if we will need it during makepkg execution if (( ! ( ASROOT || INFAKEROOT ) && ( DEP_BIN || RMDEPS || INSTALL ) )); then if ! type -p sudo >/dev/null; then warning "$(gettext "Cannot find the %s binary. Will use %s to acquire root privileges.")" "sudo" "su" fi fi # fakeroot - building as non-root user if check_buildenv "fakeroot" "y" && (( EUID > 0 )); then if ! type -p fakeroot >/dev/null; then error "$(gettext "Cannot find the %s binary required for building as non-root user.")" "fakeroot" ret=1 fi fi # gpg - package signing if [[ $SIGNPKG == 'y' ]] || { [[ -z $SIGNPKG ]] && check_buildenv "sign" "y"; }; then if ! type -p gpg >/dev/null; then error "$(gettext "Cannot find the %s binary required for signing packages.")" "gpg" ret=1 fi fi # gpg - source verification if (( ! SKIPPGPCHECK )) && source_has_signatures; then if ! type -p gpg >/dev/null; then error "$(gettext "Cannot find the %s binary required for verifying source files.")" "gpg" ret=1 fi fi # openssl - checksum operations if (( ! SKIPCHECKSUMS )); then if ! type -p openssl >/dev/null; then error "$(gettext "Cannot find the %s binary required for validating sourcefile checksums.")" "openssl" ret=1 fi fi # upx - binary compression if check_option "upx" "y"; then if ! type -p upx >/dev/null; then error "$(gettext "Cannot find the %s binary required for compressing binaries.")" "upx" ret=1 fi fi # distcc - compilation with distcc if check_buildenv "distcc" "y" && ! check_option "distcc" "n" ]]; then if ! type -p distcc >/dev/null; then error "$(gettext "Cannot find the %s binary required for distributed compilation.")" "distcc" ret=1 fi fi # ccache - compilation with ccache if check_buildenv "ccache" "y" && ! check_option "ccache" "n"; then if ! type -p ccache >/dev/null; then error "$(gettext "Cannot find the %s binary required for compiler cache usage.")" "ccache" ret=1 fi fi # strip - strip symbols from binaries/libraries if check_option "strip" "y"; then if ! type -p strip >/dev/null; then error "$(gettext "Cannot find the %s binary required for object file stripping.")" "strip" ret=1 fi fi # gzip - compressig man and info pages if check_option "zipman" "y"; then if ! type -p gzip >/dev/null; then error "$(gettext "Cannot find the %s binary required for compressing man and info pages.")" "gzip" ret=1 fi fi return $ret } check_build_status() { if (( ! SPLITPKG )); then fullver=$(get_full_version) pkgarch=$(get_pkg_arch) if [[ -f $PKGDEST/${pkgname}-${fullver}-${pkgarch}${PKGEXT} ]] \ && ! (( FORCE || SOURCEONLY || NOBUILD )); then if (( INSTALL )); 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 %s to overwrite)")" "-f" exit 1 fi fi else allpkgbuilt=1 somepkgbuilt=0 for pkg in ${pkgname[@]}; do fullver=$(get_full_version $pkg) pkgarch=$(get_pkg_arch $pkg) if [[ -f $PKGDEST/${pkg}-${fullver}-${pkgarch}${PKGEXT} ]]; then somepkgbuilt=1 else allpkgbuilt=0 fi done if ! (( FORCE || SOURCEONLY || NOBUILD )); then if (( allpkgbuilt )); then if (( INSTALL )); then warning "$(gettext "The package group has already been built, installing existing packages...")" install_package exit $? else error "$(gettext "The package group has already been built. (use %s to overwrite)")" "-f" exit 1 fi fi if (( somepkgbuilt && ! PKGVERFUNC )); then error "$(gettext "Part of the package group has already been built. (use %s to overwrite)")" "-f" exit 1 fi fi unset allpkgbuilt somepkgbuilt fi } backup_package_variables() { local var for var in ${splitpkg_overrides[@]}; do local indirect="${var}_backup" eval "${indirect}=(\"\${$var[@]}\")" done } restore_package_variables() { local var for var in ${splitpkg_overrides[@]}; do local indirect="${var}_backup" if [[ -n ${!indirect} ]]; then eval "${var}=(\"\${$indirect[@]}\")" else unset ${var} fi done } run_split_packaging() { local pkgname_backup=${pkgname[@]} for pkgname in ${pkgname_backup[@]}; do pkgdir="$pkgdirbase/$pkgname" mkdir "$pkgdir" backup_package_variables run_package $pkgname tidy_install create_package create_debug_package restore_package_variables done pkgname=${pkgname_backup[@]} } # Canonicalize a directory path if it exists canonicalize_path() { local path="$1"; if [[ -d $path ]]; then ( cd_safe "$path" pwd -P ) else printf "%s\n" "$path" fi } dir_is_empty() { ( shopt -s dotglob nullglob files=("$1"/*) (( ${#files} == 0 )) ) } m4_include(library/parseopts.sh) usage() { printf "makepkg (pacman) %s\n" "$makepkg_version" echo printf -- "$(gettext "Usage: %s [options]")\n" "$0" echo printf -- "$(gettext "Options:")\n" printf -- "$(gettext " -A, --ignorearch Ignore incomplete %s field in %s")\n" "arch" "$BUILDSCRIPT" printf -- "$(gettext " -c, --clean Clean up work files after build")\n" printf -- "$(gettext " -C, --cleanbuild Remove %s dir before building the package")\n" "\$srcdir/" printf -- "$(gettext " -d, --nodeps Skip all dependency checks")\n" printf -- "$(gettext " -e, --noextract Do not extract source files (use existing %s dir)")\n" "\$srcdir/" printf -- "$(gettext " -f, --force Overwrite existing package")\n" printf -- "$(gettext " -g, --geninteg Generate integrity checks for source files")\n" printf -- "$(gettext " -h, --help Show this help message and exit")\n" printf -- "$(gettext " -i, --install Install package after successful build")\n" printf -- "$(gettext " -L, --log Log package build process")\n" printf -- "$(gettext " -m, --nocolor Disable colorized output messages")\n" printf -- "$(gettext " -o, --nobuild Download and extract files only")\n" printf -- "$(gettext " -p <file> Use an alternate build script (instead of '%s')")\n" "$BUILDSCRIPT" printf -- "$(gettext " -r, --rmdeps Remove installed dependencies after a successful build")\n" printf -- "$(gettext " -R, --repackage Repackage contents of the package without rebuilding")\n" printf -- "$(gettext " -s, --syncdeps Install missing dependencies with %s")\n" "pacman" printf -- "$(gettext " -S, --source Generate a source-only tarball without downloaded sources")\n" printf -- "$(gettext " -V, --version Show version information and exit")\n" printf -- "$(gettext " --allsource Generate a source-only tarball including downloaded sources")\n" printf -- "$(gettext " --verifysource Download source files (if needed) and perform integrity checks")\n" printf -- "$(gettext " --asroot Allow %s to run as root user")\n" "makepkg" printf -- "$(gettext " --check Run the %s function in the %s")\n" "check()" "$BUILDSCRIPT" printf -- "$(gettext " --config <file> Use an alternate config file (instead of '%s')")\n" "$confdir/makepkg.conf" printf -- "$(gettext " --holdver Do not update VCS sources")\n" printf -- "$(gettext " --key <key> Specify a key to use for %s signing instead of the default")\n" "gpg" printf -- "$(gettext " --nocheck Do not run the %s function in the %s")\n" "check()" "$BUILDSCRIPT" printf -- "$(gettext " --noprepare Do not run the %s function in the %s")\n" "prepare()" "$BUILDSCRIPT" printf -- "$(gettext " --nosign Do not create a signature for the package")\n" printf -- "$(gettext " --pkg <list> Only build listed packages from a split package")\n" printf -- "$(gettext " --sign Sign the resulting package with %s")\n" "gpg" printf -- "$(gettext " --skipchecksums Do not verify checksums of the source files")\n" printf -- "$(gettext " --skipinteg Do not perform any verification checks on source files")\n" printf -- "$(gettext " --skippgpcheck Do not verify source files with PGP signatures")\n" echo printf -- "$(gettext "These options can be passed to %s:")\n" "pacman" echo printf -- "$(gettext " --asdeps Install packages as non-explicitly installed")\n" printf -- "$(gettext " --noconfirm Do not ask for confirmation when resolving dependencies")\n" printf -- "$(gettext " --needed Do not reinstall the targets that are already up to date")\n" printf -- "$(gettext " --noprogressbar Do not show a progress bar when downloading files")\n" echo printf -- "$(gettext "If %s is not specified, %s will look for '%s'")\n" "-p" "makepkg" "$BUILDSCRIPT" echo } version() { printf "makepkg (pacman) %s\n" "$makepkg_version" printf -- "$(gettext "\ Copyright (c) 2006-2013 Pacman Development Team <pacman-dev@archlinux.org>.\n\ Copyright (C) 2002-2006 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 -p gettext >/dev/null; then gettext() { printf "%s\n" "$@" } fi ARGLIST=("$@") # Parse Command Line Options. OPT_SHORT="AcCdefFghiLmop:rRsSV" OPT_LONG=('allsource' 'asroot' 'check' 'clean' 'cleanbuild' 'config:' 'force' 'geninteg' 'help' 'holdver' 'ignorearch' 'install' 'key:' 'log' 'nobuild' 'nocolor' 'nocheck' 'nodeps' 'noextract' 'noprepare' 'nosign' 'pkg:' 'repackage' 'rmdeps' 'sign' 'skipchecksums' 'skipinteg' 'skippgpcheck' 'source' 'syncdeps' 'verifysource' 'version') # Pacman Options OPT_LONG+=('asdeps' 'noconfirm' 'needed' 'noprogressbar') if ! parseopts "$OPT_SHORT" "${OPT_LONG[@]}" -- "$@"; then exit 1 # E_INVALID_OPTION; fi set -- "${OPTRET[@]}" unset OPT_SHORT OPT_LONG OPTRET while true; do case "$1" in # Pacman Options --asdeps) ASDEPS=1;; --noconfirm) PACMAN_OPTS+=" --noconfirm" ;; --needed) NEEDED=1;; --noprogressbar) PACMAN_OPTS+=" --noprogressbar" ;; # Makepkg Options --allsource) SOURCEONLY=2 ;; --asroot) ASROOT=1 ;; -A|--ignorearch) IGNOREARCH=1 ;; -c|--clean) CLEANUP=1 ;; -C|--cleanbuild) CLEANBUILD=1 ;; --check) RUN_CHECK='y' ;; --config) shift; MAKEPKG_CONF=$1 ;; -d|--nodeps) NODEPS=1 ;; -e|--noextract) NOEXTRACT=1 ;; -f|--force) FORCE=1 ;; -F) INFAKEROOT=1 ;; -g|--geninteg) GENINTEG=1 ;; --holdver) HOLDVER=1 ;; -i|--install) INSTALL=1 ;; --key) shift; GPGKEY=$1 ;; -L|--log) LOGGING=1 ;; -m|--nocolor) USE_COLOR='n' ;; --nocheck) RUN_CHECK='n' ;; --noprepare) RUN_PREPARE='n' ;; --nosign) SIGNPKG='n' ;; -o|--nobuild) NOBUILD=1 ;; -p) shift; BUILDFILE=$1 ;; --pkg) shift; IFS=, read -ra p <<<"$1"; PKGLIST+=("${p[@]}"); unset p ;; -r|--rmdeps) RMDEPS=1 ;; -R|--repackage) REPKG=1 ;; --skipchecksums) SKIPCHECKSUMS=1 ;; --skipinteg) SKIPCHECKSUMS=1; SKIPPGPCHECK=1 ;; --skippgpcheck) SKIPPGPCHECK=1;; --sign) SIGNPKG='y' ;; -s|--syncdeps) DEP_BIN=1 ;; -S|--source) SOURCEONLY=1 ;; --verifysource) VERIFYSOURCE=1 ;; -h|--help) usage; exit 0 ;; # E_OK -V|--version) version; exit 0 ;; # E_OK --) OPT_IND=0; shift; break 2;; esac shift done # attempt to consume any extra argv as environment variables. this supports # overriding (e.g. CC=clang) as well as overriding (e.g. CFLAGS+=' -g'). extra_environment=() while [[ $1 ]]; do if [[ $1 = [_[:alpha:]]*([[:alnum:]_])?(+)=* ]]; then extra_environment+=("$1") fi shift done # setup signal traps trap 'clean_up' 0 for signal in TERM HUP QUIT; do trap "trap_exit $signal \"$(gettext "%s signal caught. Exiting...")\" \"$signal\"" "$signal" done trap 'trap_exit INT "$(gettext "Aborted by user! Exiting...")"' INT trap 'trap_exit USR1 "$(gettext "An unknown error has occurred. Exiting...")"' ERR # preserve environment variables and canonicalize path [[ -n ${PKGDEST} ]] && _PKGDEST=$(canonicalize_path ${PKGDEST}) [[ -n ${SRCDEST} ]] && _SRCDEST=$(canonicalize_path ${SRCDEST}) [[ -n ${SRCPKGDEST} ]] && _SRCPKGDEST=$(canonicalize_path ${SRCPKGDEST}) [[ -n ${LOGDEST} ]] && _LOGDEST=$(canonicalize_path ${LOGDEST}) [[ -n ${BUILDDIR} ]] && _BUILDDIR=$(canonicalize_path ${BUILDDIR}) [[ -n ${PKGEXT} ]] && _PKGEXT=${PKGEXT} [[ -n ${SRCEXT} ]] && _SRCEXT=${SRCEXT} [[ -n ${GPGKEY} ]] && _GPGKEY=${GPGKEY} [[ -n ${PACKAGER} ]] && _PACKAGER=${PACKAGER} [[ -n ${CARCH} ]] && _CARCH=${CARCH} # default config is makepkg.conf MAKEPKG_CONF=${MAKEPKG_CONF:-$confdir/makepkg.conf} # Source the config file; fail if it is not found if [[ -r $MAKEPKG_CONF ]]; then source_safe "$MAKEPKG_CONF" else error "$(gettext "%s not found.")" "$MAKEPKG_CONF" plain "$(gettext "Aborting...")" exit 1 # $E_CONFIG_ERROR fi # Source user-specific makepkg.conf overrides, but only if no override config # file was specified if [[ $MAKEPKG_CONF = "$confdir/makepkg.conf" && -r ~/.makepkg.conf ]]; then source_safe ~/.makepkg.conf fi # set pacman command if not already defined PACMAN=${PACMAN:-pacman} # save full path to command as PATH may change when sourcing /etc/profile PACMAN_PATH=$(type -P $PACMAN) || true # check if messages are to be printed using color unset ALL_OFF BOLD BLUE GREEN RED YELLOW if [[ -t 2 && ! $USE_COLOR = "n" ]] && check_buildenv "color" "y"; then # prefer terminal safe colored and bold text when tput is supported if tput setaf 0 &>/dev/null; then ALL_OFF="$(tput sgr0)" BOLD="$(tput bold)" BLUE="${BOLD}$(tput setaf 4)" GREEN="${BOLD}$(tput setaf 2)" RED="${BOLD}$(tput setaf 1)" YELLOW="${BOLD}$(tput setaf 3)" else ALL_OFF="\e[0m" BOLD="\e[1m" BLUE="${BOLD}\e[34m" GREEN="${BOLD}\e[32m" RED="${BOLD}\e[31m" YELLOW="${BOLD}\e[33m" fi fi readonly ALL_OFF BOLD BLUE GREEN RED YELLOW # override settings with an environment variable for batch processing BUILDDIR=${_BUILDDIR:-$BUILDDIR} BUILDDIR=${BUILDDIR:-$startdir} #default to $startdir if undefined if [[ ! -d $BUILDDIR ]]; then if ! mkdir -p "$BUILDDIR"; then error "$(gettext "You do not have write permission to create packages in %s.")" "$BUILDDIR" plain "$(gettext "Aborting...")" exit 1 fi chmod a-s "$BUILDDIR" fi if [[ ! -w $BUILDDIR ]]; then error "$(gettext "You do not have write permission to create packages in %s.")" "$BUILDDIR" plain "$(gettext "Aborting...")" exit 1 fi # override settings from extra variables on commandline, if any if (( ${#extra_environment[*]} )); then export "${extra_environment[@]}" fi PKGDEST=${_PKGDEST:-$PKGDEST} PKGDEST=${PKGDEST:-$startdir} #default to $startdir if undefined if (( ! (NOBUILD || GENINTEG) )) && [[ ! -w $PKGDEST ]]; then error "$(gettext "You do not have write permission to store packages in %s.")" "$PKGDEST" plain "$(gettext "Aborting...")" exit 1 fi SRCDEST=${_SRCDEST:-$SRCDEST} SRCDEST=${SRCDEST:-$startdir} #default to $startdir if undefined if [[ ! -w $SRCDEST ]] ; then error "$(gettext "You do not have write permission to store downloads in %s.")" "$SRCDEST" plain "$(gettext "Aborting...")" exit 1 fi SRCPKGDEST=${_SRCPKGDEST:-$SRCPKGDEST} SRCPKGDEST=${SRCPKGDEST:-$startdir} #default to $startdir if undefined if (( SOURCEONLY )) && [[ ! -w $SRCPKGDEST ]]; then error "$(gettext "You do not have write permission to store source tarballs in %s.")" "$SRCPKGDEST" plain "$(gettext "Aborting...")" exit 1 fi LOGDEST=${_LOGDEST:-$LOGDEST} LOGDEST=${LOGDEST:-$startdir} #default to $startdir if undefined if (( LOGGING )) && [[ ! -w $LOGDEST ]]; then error "$(gettext "You do not have write permission to store logs in %s.")" "$LOGDEST" plain "$(gettext "Aborting...")" exit 1 fi PKGEXT=${_PKGEXT:-$PKGEXT} SRCEXT=${_SRCEXT:-$SRCEXT} GPGKEY=${_GPGKEY:-$GPGKEY} PACKAGER=${_PACKAGER:-$PACKAGER} CARCH=${_CARCH:-$CARCH} if (( ! INFAKEROOT )); then if (( EUID == 0 && ! ASROOT )); then # Warn those who like to live dangerously. error "$(gettext "Running %s as root is a BAD idea and can cause permanent,\n\ catastrophic damage to your system. If you wish to run as root, please\n\ use the %s option.")" "makepkg" "--asroot" exit 1 # $E_USER_ABORT elif (( EUID > 0 && ASROOT )); then # Warn those who try to use the --asroot option when they are not root error "$(gettext "The %s option is meant for the root user only. Please\n\ rerun %s without the %s flag.")" "--asroot" "makepkg" "--asroot" exit 1 # $E_USER_ABORT elif (( EUID > 0 )) && ! check_buildenv "fakeroot" "y"; then warning "$(gettext "Running %s as an unprivileged user will result in non-root\n\ ownership of the packaged files. Try using the %s environment by\n\ placing %s in the %s array in %s.")" "makepkg" "fakeroot" "'fakeroot'" "BUILDENV" "$MAKEPKG_CONF" sleep 1 fi else if [[ -z $FAKEROOTKEY ]]; then error "$(gettext "Do not use the %s option. This option is only for use by %s.")" "'-F'" "makepkg" exit 1 # TODO: error code fi fi unset pkgname pkgbase pkgver pkgrel epoch pkgdesc url license groups provides unset md5sums replaces depends conflicts backup source install changelog build unset makedepends optdepends options noextract BUILDFILE=${BUILDFILE:-$BUILDSCRIPT} if [[ ! -f $BUILDFILE ]]; then error "$(gettext "%s does not exist.")" "$BUILDFILE" exit 1 else if [[ $(<"$BUILDFILE") = *$'\r'* ]]; then error "$(gettext "%s contains %s characters and cannot be sourced.")" "$BUILDFILE" "CRLF" exit 1 fi if [[ ${BUILDFILE:0:1} != "/" ]]; then BUILDFILE="$startdir/$BUILDFILE" fi source_safe "$BUILDFILE" fi # set defaults if they weren't specified in buildfile pkgbase=${pkgbase:-${pkgname[0]}} epoch=${epoch:-0} if [[ $BUILDDIR = "$startdir" ]]; then srcdir="$BUILDDIR/src" pkgdirbase="$BUILDDIR/pkg" else srcdir="$BUILDDIR/$pkgbase/src" pkgdirbase="$BUILDDIR/$pkgbase/pkg" fi # set pkgdir to something "sensible" for (not recommended) use during build() pkgdir="$pkgdirbase/$pkgbase" if (( GENINTEG )); then mkdir -p "$srcdir" chmod a-s "$srcdir" cd_safe "$srcdir" download_sources fast generate_checksums exit 0 # $E_OK fi if have_function pkgver; then PKGVERFUNC=1 fi # check the PKGBUILD for some basic requirements check_sanity || exit 1 # check we have the software required to process the PKGBUILD check_software || exit 1 if (( ${#pkgname[@]} > 1 )); then SPLITPKG=1 fi # test for available PKGBUILD functions if have_function prepare; then # "Hide" prepare() function if not going to be run if [[ $RUN_PREPARE != "n" ]]; then PREPAREFUNC=1 fi fi if have_function build; then BUILDFUNC=1 fi if have_function check; then # "Hide" check() function if not going to be run if [[ $RUN_CHECK = 'y' ]] || { ! check_buildenv "check" "n" && [[ $RUN_CHECK != "n" ]]; }; then CHECKFUNC=1 fi fi if have_function package; then PKGFUNC=1 elif [[ $SPLITPKG -eq 0 ]] && have_function package_${pkgname}; then SPLITPKG=1 fi if [[ -n "${PKGLIST[@]}" ]]; then unset pkgname pkgname=("${PKGLIST[@]}") fi # check if gpg signature is to be created and if signing key is valid if { [[ -z $SIGNPKG ]] && check_buildenv "sign" "y"; } || [[ $SIGNPKG == 'y' ]]; then SIGNPKG='y' if ! gpg --list-key ${GPGKEY} &>/dev/null; then if [[ ! -z $GPGKEY ]]; then error "$(gettext "The key %s does not exist in your keyring.")" "${GPGKEY}" else error "$(gettext "There is no key in your keyring.")" fi exit 1 fi fi if (( ! PKGVERFUNC )); then check_build_status fi # Run the bare minimum in fakeroot if (( INFAKEROOT )); then if (( SOURCEONLY )); then create_srcpackage msg "$(gettext "Leaving %s environment.")" "fakeroot" exit 0 # $E_OK fi chmod 755 "$pkgdirbase" if (( ! SPLITPKG )); then pkgdir="$pkgdirbase/$pkgname" mkdir "$pkgdir" if (( PKGFUNC )); then run_package fi tidy_install create_package create_debug_package else run_split_packaging fi msg "$(gettext "Leaving %s environment.")" "fakeroot" exit 0 # $E_OK fi fullver=$(get_full_version) msg "$(gettext "Making package: %s")" "$pkgbase $fullver ($(date))" # if we are creating a source-only package, go no further if (( SOURCEONLY )); then if [[ -f $SRCPKGDEST/${pkgbase}-${fullver}${SRCEXT} ]] \ && (( ! FORCE )); then error "$(gettext "A source package has already been built. (use %s to overwrite)")" "-f" exit 1 fi # Get back to our src directory so we can begin with sources. mkdir -p "$srcdir" chmod a-s "$srcdir" cd_safe "$srcdir" if (( SOURCEONLY == 2 )); then download_sources elif ( (( ! SKIPCHECKSUMS )) || \ ( (( ! SKIPPGPCHECK )) && source_has_signatures ) ); then download_sources fast fi check_source_integrity cd_safe "$startdir" # if we are root or if fakeroot is not enabled, then we don't use it if ! check_buildenv "fakeroot" "y" || (( EUID == 0 )); then create_srcpackage else enter_fakeroot fi msg "$(gettext "Source package created: %s")" "$pkgbase ($(date))" exit 0 fi if (( NODEPS || (NOBUILD && !DEP_BIN ) )); then # no warning message needed for nobuild if (( NODEPS )); then warning "$(gettext "Skipping dependency checks.")" fi else if (( RMDEPS && ! INSTALL )); then original_pkglist=($(run_pacman -Qq)) # required by remove_dep fi deperr=0 msg "$(gettext "Checking runtime dependencies...")" resolve_deps ${depends[@]} || deperr=1 if (( RMDEPS && INSTALL )); then original_pkglist=($(run_pacman -Qq)) # required by remove_dep fi msg "$(gettext "Checking buildtime dependencies...")" if (( CHECKFUNC )); then resolve_deps "${makedepends[@]}" "${checkdepends[@]}" || deperr=1 else resolve_deps "${makedepends[@]}" || deperr=1 fi if (( RMDEPS )); then current_pkglist=($(run_pacman -Qq)) # required by remove_deps fi if (( deperr )); then error "$(gettext "Could not resolve all dependencies.")" exit 1 fi 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" chmod a-s "$srcdir" cd_safe "$srcdir" if (( NOEXTRACT && ! VERIFYSOURCE )); then warning "$(gettext "Using existing %s tree")" "\$srcdir/" elif (( !REPKG )); then download_sources check_source_integrity (( VERIFYSOURCE )) && exit 0 # $E_OK if (( CLEANBUILD )); then msg "$(gettext "Removing existing %s directory...")" "\$srcdir/" rm -rf "$srcdir"/* fi extract_sources if (( PREPAREFUNC )); then run_prepare fi fi if (( NOBUILD )); then msg "$(gettext "Sources are ready.")" exit 0 #E_OK else # clean existing pkg directory if [[ -d $pkgdirbase ]]; then msg "$(gettext "Removing existing %s directory...")" "\$pkgdir/" rm -rf "$pkgdirbase" fi mkdir -p "$pkgdirbase" chmod a-srwx "$pkgdirbase" cd_safe "$startdir" # if we are root or if fakeroot is not enabled, then we don't use it if ! check_buildenv "fakeroot" "y" || (( EUID == 0 )); then if (( ! REPKG )); then if (( ! ( SPLITPKG || PKGFUNC ) )); then chmod 755 "$pkgdirbase" mkdir -p "$pkgdir" fi (( BUILDFUNC )) && run_build (( CHECKFUNC )) && run_check fi chmod 755 "$pkgdirbase" if (( ! SPLITPKG )); then pkgdir="$pkgdirbase/$pkgname" mkdir -p "$pkgdir" if (( PKGFUNC )); then run_package fi tidy_install create_package create_debug_package else run_split_packaging fi else if (( ! REPKG )); then (( BUILDFUNC )) && run_build (( CHECKFUNC )) && run_check cd_safe "$startdir" fi enter_fakeroot fi fi fullver=$(get_full_version) msg "$(gettext "Finished making: %s")" "$pkgbase $fullver ($(date))" install_package exit 0 #E_OK # vim: set ts=2 sw=2 noet: