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