Files
scratchpkg/pkgbuild
2019-04-25 17:37:52 +08:00

674 lines
16 KiB
Bash
Executable File

#!/bin/bash
#
# scratchpkg
#
# Copyright (c) 2018 by Emmett1 (emmett1.2miligrams@gmail.com)
#
# 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 3 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 <https://www.gnu.org/licenses/>.
#
msg() {
echo -e "==> $1"
}
msg2() {
echo -e " -> $1"
}
msgerr() {
echo -e "==> ERROR: $1"
}
msgwarn() {
echo -e "==> WARNING: $1"
}
updatemdsum() {
if [ -z $source ]; then
msgwarn "source=() is empty, no need md5sum."
abort 0
fi
for um in $(seq 0 $((${#source[@]} - 1))); do
if [ $(echo ${source[$um]} | grep -E "(ftp|http|https)://") ]; then
if [ $(echo ${source[$um]} | grep -E "::(ftp|http|https)://") ]; then
sourcename="$SOURCE_DIR/$(echo ${source[$um]} | awk -F '::' '{print $1}')"
else
sourcename="$SOURCE_DIR/$(echo ${source[$um]} | rev | cut -d / -f 1 | rev)"
fi
else
sourcename="${source[$um]}"
fi
needupdatechecksum="$needupdatechecksum $sourcename"
done
for file in ${needupdatechecksum[@]}; do
if [ ! -f $file ]; then
missingsource+=($file)
fi
done
if [ "${#missingsource[@]}" -gt 0 ]; then
msg "Missing source:"
for ms in ${missingsource[@]}; do
msg2 "$ms"
done
abort 1
fi
echo -e "md5sum=($(md5sum $needupdatechecksum | awk '{ print $1 }'))"
}
checkmdsum() {
if [ ! -z "$source" -a -z "$md5sum" ]; then
msgerr "md5sum=() is empty, please provide it."
abort 1
fi
if [ "${#source[@]}" != "${#md5sum[@]}" ]; then
msgerr "Total source and md5sums different."
abort 1
fi
for s in $(seq 0 $((${#source[@]} - 1))); do
if [ $(echo ${source[$s]} | grep -E "(ftp|http|https)://") ]; then
if [ $(echo ${source[$s]} | grep -E "::(ftp|http|https)://") ]; then
sourcename=$SOURCE_DIR/$(echo ${source[$s]} | awk -F '::' '{print $1}')
else
sourcename=$SOURCE_DIR/$(echo ${source[$s]} | rev | cut -d / -f 1 | rev)
fi
else
sourcename="${source[$s]}"
fi
sum=$(md5sum "$sourcename" | awk '{ print $1 }')
if [ "$sum" != "${md5sum[$s]}" ] && [ "SKIP" != "${md5sum[$s]}" ]; then
errormdsum+=($sourcename)
fi
done
if [ "${#errormdsum[@]}" -gt 0 ]; then
msgerr "md5sum mismatch:"
for mismatch in ${errormdsum[@]}; do
msg2 "$mismatch"
done
abort 1
fi
}
download_src() {
local FILE FILENAME
for FILE in ${source[@]}; do
if [[ $FILE =~ ::(http|https|ftp|file):// ]]; then
FILENAME=$(echo $FILE | awk -F '::' '{print $1}')
SRCURL=$(echo $FILE | awk -F '::' '{print $2}')
else
FILENAME=$(basename $FILE)
SRCURL=$FILE
fi
case $DOWNLOAD_PROG in
curl) DLCMD="curl -C - --fail --ftp-pasv --retry 3 --retry-delay 3 -o $SOURCE_DIR/$FILENAME.partial $CURL_OPTS" ;;
wget) DLCMD="wget -c --passive-ftp --no-directories --tries=3 --waitretry=3 --output-document=$SOURCE_DIR/$FILENAME.partial $WGET_OPTS" ;;
esac
if [ "$FILENAME" != "$FILE" ]; then
if [ -f "$SOURCE_DIR/$FILENAME" ] && [ -z "$REDOWNLOAD_SOURCE" ]; then
msg "Source '$FILENAME' found."
else
[ "$REDOWNLOAD_SOURCE" ] && rm -f "$SOURCE_DIR/$FILENAME.partial"
if [ -f "$SOURCE_DIR/$FILENAME.partial" ]; then
msg "Resuming '$SRCURL'."
else
msg "Downloading '$SRCURL'."
fi
$DLCMD $SRCURL
if [ $? = 0 ]; then
[ "$REDOWNLOAD_SOURCE" ] && rm -f "$SOURCE_DIR/$FILENAME"
mv $SOURCE_DIR/$FILENAME.partial $SOURCE_DIR/$FILENAME
else
msgerr "Failed downloading '$FILENAME'."
abort 1
fi
fi
else
if [ ! -f "$FILENAME" ]; then
msgerr "Source '$FILENAME' not found."
abort 1
else
msg "Source '$FILENAME' found."
fi
fi
done
}
prepare_src() {
local FILE FILENAME SRC_DIR
[ "$IGNORE_MDSUM" ] || checkmdsum
SRC=$WORK_DIR/$name/src
PKG=$WORK_DIR/$name/pkg
rm -fr $WORK_DIR/$name
mkdir -p $SRC $PKG
if [ "${#source[@]}" -gt 0 ]; then
for FILE in ${source[@]}; do
if [[ $FILE =~ ::(http|https|ftp|file):// ]]; then
FILENAME=$(echo $FILE | awk -F '::' '{print $1}')
SRC_DIR="$SOURCE_DIR"
elif [[ $FILE =~ ^(http|https|ftp|file):// ]]; then
FILENAME=$(basename $FILE)
SRC_DIR="$SOURCE_DIR"
else
FILENAME=$(basename $FILE)
SRC_DIR="$PWD"
fi
for NOEXT in ${noextract[@]}; do
if [ "$NOEXT" = "$FILENAME" ]; then
nxt=1
break
fi
done
if [ "$FILENAME" != "$FILE" ] && [ "$nxt" != 1 ]; then
case $FILENAME in
*.tar|*.tar.gz|*.tar.Z|*.tgz|*.tar.bz2|*.tbz2|*.tar.xz|*.txz|*.tar.lzma|*.zip|*.rpm)
if [ $(type -p bsdtar) ]; then
COMMAND="bsdtar -p -o -C $SRC -xf $SOURCE_DIR/$FILENAME"
else
COMMAND="tar -p -o -C $SRC -xf $SOURCE_DIR/$FILENAME"
fi
MODE="Unpacking" ;;
*)
COMMAND="cp $SOURCE_DIR/$FILENAME $SRC"
MODE="Preparing" ;;
esac
msg2 "$MODE '$FILENAME'..."
$COMMAND
if [ $? != 0 ]; then
msgerr "$MODE '$FILENAME' failed."
abort 1
fi
else
msg2 "Preparing '$FILENAME'..."
cp "$SRC_DIR/$FILENAME" "$SRC"
fi
nxt=
done
fi
}
runprebuildscript() {
if [ "`type -t pre_build`" = "function" ]; then
pre_build
fi
}
run_build() {
if [ "$UID" != 0 ]; then
msgerr "You must build package as root, or use fakeroot."
abort 1
fi
msg "Start build '$name-$version-$release'."
[ "$MAKE_FLAGS" = 1 ] && export MAKEFLAGS || unset MAKEFLAGS
[ "$BUILD_FLAGS" = 1 ] && export CFLAGS CXXFLAGS || unset CFLAGS CXXFLAGS
[ -f $PWD/install ] && source $PWD/install
pushd $SRC >/dev/null
runprebuildscript
if [ "$LOGGING" = yes ]; then
(
set -e -x; build 2>&1 | tee $LOG_DIR/$name-$version-$release.log
exit $PIPESTATUS
)
else
(set -e -x; build)
fi
if [ $? != 0 ]; then
msgerr "Build '$name-$version-$release' failed."
abort 1
else
msg "Build '$name-$version-$release' success."
fi
popd >/dev/null
}
purgefiles() {
local OPTIONS
[ "${#PURGE_FILES[@]}" -gt 0 ] || return 0
for OPTIONS in ${PURGE_FILES[@]}; do
if [ -e $OPTIONS ]; then
rm -fr $OPTIONS
fi
done
}
removeemptydirs() {
find . -type d -empty -delete
}
removelibtool() {
find . ! -type d -name "*.la" -delete
}
strip_files() {
find . -type f 2>/dev/null | while read -r binary ; do
case "$(file -bi "$binary")" in
*application/x-sharedlib*) # Libraries (.so)
strip --strip-unneeded "$binary" ;;
*application/x-pie-executable*) # Libraries (.so)
strip --strip-unneeded "$binary" ;;
*application/x-archive*) # Libraries (.a)
strip --strip-debug "$binary" ;;
*application/x-object*)
case "$binary" in
*.ko) # Kernel module
strip --strip-unneeded "$binary" ;;
*)
continue;;
esac;;
*application/x-executable*) # Binaries
strip --strip-all "$binary" ;;
*)
continue ;;
esac
done
}
compressinfomanpages() {
find . -type f -path "*/man/man*/*" | while read file; do
if [ "$file" = "${file%%.gz}" ]; then
gzip -9 -f "$file"
fi
done
find . -type l -path "*/man/man*/*" | while read file; do
FILE="${file%%.gz}.gz"
TARGET="$(readlink $file)"
TARGET="${TARGET##*/}"
TARGET="${TARGET%%.gz}.gz"
DIR=$(dirname "$FILE")
rm -f $file
if [ -e "$DIR/$TARGET" ]; then
ln -sf $TARGET $FILE
fi
done
if [ -d usr/share/info ]; then
(cd usr/share/info
for file in $(find . -type f); do
if [ "$file" = "${file%%.gz}" ]; then
gzip -9 "$file"
fi
done
)
fi
}
backupconf() {
local FILE
for FILE in ${backup[@]}; do
if [ ! -f $FILE ]; then
msgerr "File '$FILE' not exist!"
abort 1
else
mv $FILE $FILE.spkgnew
fi
done
}
packaging() {
local FILE
[ -f install ] && install -m644 install $PKG/.pkginstall
[ -f readme ] && install -m644 readme $PKG/.pkgreadme
pushd $PKG >/dev/null
rm -f usr/{,share/}info/dir
[ "$KEEP_EMPTYDIR" = 0 ] && removeemptydirs
[ "$KEEP_LIBTOOL" = 0 ] && removelibtool
[ "$STRIP_BINARY" = 1 ] && strip_files
[ "$ZIP_MAN" = 1 ] && compressinfomanpages
if [ "${#backup[@]}" -gt 0 ]; then
backupconf
fi
for FILE in .pkginstall .pkgreadme; do
if [ -f $FILE ]; then
addtotar+=($FILE)
fi
done
[ "$FORCE_REBUILD" ] && rm -f "$PACKAGE_DIR/$PKGNAME"
case $COMPRESSION_MODE in
xz) COMPRESS="-J" ;;
gz) COMPRESS="-z" ;;
bz2) COMPRESS="-j" ;;
esac
tar -c $COMPRESS -f $PACKAGE_DIR/$PKGNAME * "${addtotar[@]}"
if [ $? != 0 ]; then
rm -f $PACKAGE_DIR/$PKGNAME
msgerr "Packaging '$PKGNAME' failed."
abort 1
fi
tar -tvf $PACKAGE_DIR/$PKGNAME
pkgsize="$(ls -lh $PACKAGE_DIR/$PKGNAME | awk '{print $5}')"
msg "Successfully created package '$PKGNAME'. (${pkgsize})"
popd >/dev/null
}
check_buildscript() {
# check the required field in buildscript
if [ -z "$name" ]; then
msgerr "'name' is empty!"
exit 1
elif [ "$(basename `pwd`)" != "$name" ]; then
msgerr "Port name and Directory name is different!"
exit 1
elif [ -z "$version" ]; then
msgerr "'version' is empty!"
exit 1
elif [ -z "$release" ]; then
msgerr "'release' is empty!"
exit 1
elif [ "`type -t build`" != "function" ]; then
msgerr "'build' function not exist!"
exit 1
elif $(echo "$version" | grep -q '-'); then
msgerr "'version' should not contain '-'."
exit 1
elif $(echo "$release" | grep -q '-'); then
msgerr "'release' should not contain '-'."
exit 1
elif [ -z "$description" ]; then
msgerr "'description' cant empty."
exit 1
fi
}
set_options() {
local OPT
for OPT in ${OPTIONS[@]} ${options[@]}; do
case $OPT in
libtool) KEEP_LIBTOOL=1 ;;
!libtool) KEEP_LIBTOOL=0 ;;
emptydirs) KEEP_EMPTYDIR=1;;
!emptydirs) KEEP_EMPTYDIR=0;;
strip) STRIP_BINARY=1 ;;
!strip) STRIP_BINARY=0 ;;
zipman) ZIP_MAN=1 ;;
!zipman) ZIP_MAN=0 ;;
buildflags) BUILD_FLAGS=1 ;;
!buildflags) BUILD_FLAGS=0 ;;
makeflags) MAKE_FLAGS=1 ;;
!makeflags) MAKE_FLAGS=0 ;;
esac
done
}
checkdir() {
local DIR
for DIR in "$@"; do
if [ ! -d $DIR ]; then
msgerr "Directory '$DIR' not exist."
abort 1
elif [ ! -w $dir ]; then
msgerr "Directory '$DIR' not writable."
abort 1
elif [ ! -x $dir ] || [ ! -r $1 ]; then
msgerr "Directory '$DIR' not readable."
abort 1
fi
done
}
help() {
cat << EOF
Usage:
$(basename $0) [ <options> <arguments> ]
Options:
-i, --install install package into system
-u, --upgrade upgrade package
-r, --reinstall reinstall package
-c, --ignore-conflict ignore conflict when installing package
-v, --verbose verbose install process
-f, --force-rebuild rebuild package
-m, --skip-mdsum skip md5sum checking
-g, --genmdsum generate md5sum
-o, --download download only source file
-x, --extract extract only source file
-w, --keep-work keep working directory
-s, --silent print install message in simple format
-h, --help show this help message
--srcdir=<path> override directory path for sources
--pkgdir=<path> override directory path for compiled package
--no-prebuild skip prebuild script before build package
--no-preinstall skip preinstall script before install package
--no-postinstall skip postinstall script after install package
--no-preupgrade skip preupgrade script before upgrade package
--no-postupgrade skip postupgrade script after upgrade package
--no-color disable color
--no-backup skip backup configuration file when upgrading package
--redownload re-download source file
EOF
}
clearworkdir() {
if [ ! "$KEEP_WORK" ]; then
rm -fr $WORK_DIR/$name
fi
}
interrupted() {
echo
abort 100
}
abort() {
rm -f $LOCK_FILE
clearworkdir
exit $1
}
extract_opt() {
for opt in $@; do
case $opt in
--*) BOPTS+=($opt) ;;
-*) for (( i=1; i<${#opt}; i++ )); do BOPTS+=(-${opt:$i:1}); done ;;
*) BOPTS+=($opt) ;;
esac
done
echo ${BOPTS[@]}
}
parse_opts() {
while [ "$1" ]; do
case $1 in
-i | --install) INSTALL_PKG=yes ;;
-u | --upgrade) UPGRADE_PKG=yes; OPTS+=($1) ;;
-r | --reinstall) REINSTALL_PKG=yes; OPTS+=($1) ;;
-c | --ignore-conflict) OPTS+=($1) ;;
-v | --verbose) VERBOSE=yes; OPTS+=($1) ;;
-f | --force-rebuild) FORCE_REBUILD=yes ;;
-m | --skip-mdsum) IGNORE_MDSUM=yes ;;
-g | --genmdsum) UPDATE_MDSUM=yes ;;
-o | --download) DOWNLOAD_ONLY=yes ;;
-x | --extract) EXTRACT_ONLY=yes ;;
-w | --keep-work) KEEP_WORK=yes ;;
-s | --silent) INST_OPT=$1 ;;
-l | --log) LOGGING=yes ;;
-h | --help) SHOWHELP=yes ;;
--no-preinstall) NO_PREINSTALL=yes; OPTS+=($1) ;;
--no-postinstall) OPTS+=($1) ;;
--no-preupgrade) OPTS+=($1) ;;
--no-postupgrade) OPTS+=($1) ;;
--no-hook) OPTS+=($1) ;;
--no-color) NOCOLOR=yes; OPTS+=($1) ;;
--no-backup) OPTS+=($1) ;;
--redownload) REDOWNLOAD_SOURCE=yes ;;
--check-source) SOURCE_CHECK=yes ;;
--srcdir=*) SOURCE_DIR="${1#*=}" ;;
--pkgdir=*) PACKAGE_DIR="${1#*=}" ;;
*) msg "Invalid $(basename $0) option! ($1)"; exit 1 ;;
esac
shift
done
}
main() {
if [ -f $PKGBUILD_CONF ]; then
source $PKGBUILD_CONF
else
msgerr "'$PKGBUILD_CONF' file not found."
exit 1
fi
parse_opts $(extract_opt $@)
# show usage
if [ "$SHOWHELP" ]; then
help
exit 0
fi
if [ -f $PKGBUILD_BSCRIPT ]; then
source $PKGBUILD_BSCRIPT
else
msgerr "'$PKGBUILD_BSCRIPT' file not found."
exit 1
fi
description=$(grep "^# description[[:blank:]]*:" $PKGBUILD_BSCRIPT | sed 's/^# description[[:blank:]]*:[[:blank:]]*//')
backup=$(grep "^# backup[[:blank:]]*:" $PKGBUILD_BSCRIPT | sed 's/^# backup[[:blank:]]*:[[:blank:]]*//')
depends=$(grep "^# depends[[:blank:]]*:" $PKGBUILD_BSCRIPT | sed 's/^# depends[[:blank:]]*:[[:blank:]]*//')
noextract=$(grep "^# noextract[[:blank:]]*:" $PKGBUILD_BSCRIPT | sed 's/^# noextract[[:blank:]]*:[[:blank:]]*//')
if [ -z "$SOURCE_DIR" ]; then
msgerr "Option '--srcdir=' need argument (path)"
exit 1
elif [ -z "$PACKAGE_DIR" ]; then
msgerr "Option '--pkgdir=' need argument (path)"
exit 1
fi
checkdir "$SOURCE_DIR" "$PACKAGE_DIR" "$WORK_DIR"
if [ -f $LOG_DIR/$name-$version-$release.log ] && [ ! -w $LOG_DIR/$name-$version-$release.log ]; then
msgerr "You dont have write permission for '$LOG_DIR/$name-$version-$release.log'!"
abort 1
fi
check_buildscript
set_options
case $COMPRESSION_MODE in
gz|bz2|xz) PKGNAME="$name-$version-$release.spkg.tar.$COMPRESSION_MODE" ;;
*) msgerr "Invalid compression mode. ($COMPRESSION_MODE)"
exit 1 ;;
esac
LOCK_FILE="/tmp/pkgbuild.$name.lock"
# check for lock file
if [ -f "$LOCK_FILE" ]; then
msgerr "Cant build same package simultaneously."
msgerr "remove '$LOCK_FILE' if no build process for '$name'."
exit 1
else
touch "$LOCK_FILE"
if [ "$?" != 0 ]; then
msgerr "Cant create lock file in '$LOCK_FILE'"
exit 1
fi
fi
# download source only
if [ "$DOWNLOAD_ONLY" ]; then
download_src
abort 0
fi
# extract source only
if [ "$EXTRACT_ONLY" ]; then
download_src
prepare_src
KEEP_WORK=yes
abort 0
fi
# calculate & print md5sum
if [ "$UPDATE_MDSUM" ]; then
updatemdsum
abort 0
fi
# build package
if [ -f "$PACKAGE_DIR/$PKGNAME" ] && [ ! "$FORCE_REBUILD" ]; then
if [ ! "$INSTALL_PKG" ] && [ ! "$REINSTALL_PKG" ] && [ ! "$UPGRADE_PKG" ]; then
echo "Package '$PKGNAME' is up-to-date."
abort 0
fi
else
download_src
prepare_src
run_build
packaging
clearworkdir
fi
# install package
if [ "$INSTALL_PKG" ] || [ "$REINSTALL_PKG" ] || [ "$UPGRADE_PKG" ]; then
pkgadd $PACKAGE_DIR/$PKGNAME $INST_OPT ${OPTS[@]} || abort 1
fi
abort 0
}
trap "interrupted" SIGHUP SIGINT SIGQUIT SIGTERM
export LC_ALL=C
PKGBUILD_CONF="/etc/scratchpkg.conf"
PKGBUILD_BSCRIPT="spkgbuild"
SOURCE_DIR="/var/cache/scratchpkg/sources"
PACKAGE_DIR="/var/cache/scratchpkg/packages"
LOG_DIR="/var/cache/scratchpkg/log"
WORK_DIR="/tmp"
DOWNLOAD_PROG="wget"
COMPRESSION_MODE="xz"
OPTIONS=(!libtool emptydirs strip zipman buildflags makeflags)
main "$@"