#!/bin/bash
#
#############################################################################
#
# tukbuild is meant to make it easier to write SlackBuild-like
# scripts which build software from source and package it.
#
# The latest version of tukbuild can be found from
# .
#
# Currently there is no other documentation than the comments in this script.
#
#############################################################################
#
# The GNU tools required to run tukbuild are included in Slackware 10.0 and
# later. Here's a list of required software and _minimum_ supported versions:
# - GNU bash 2.05b (3.x is recommended because it detects errors from pipes.)
# - GNU coreutils 5.2.1
# - GNU sed 4.xx
# - GNU grep 2.5
# - GNU findutils 4.1.7
# - getopt from util-linux 2.12a
# - GNU wget 1.9.x (only if something is downloaded)
# - GNU or HJL binutils (for the strip command)
# - file 4.xx (to detect which files to strip)
# - GCC 3.x.x
# - gzip 1.2.4
# - bzip2 1.0.x
# - Slackware 10.0 pkgtools, or Tukaani pkgtools tukaani_1.0.0
# - LZMA Utils 4.32.0beta1 (optional)
# - gconftool-2 (optional, used in _gconf())
#
#############################################################################
#
# Copyright (C) 2005, 2006, 2007 Lasse Collin
# Copyright (C) 2006 Zeqadious
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
#############################################################################
#
# Last modified: 2009-08-18 (only minor changes, previous was 2007-05-03)
_TUKBUILD_VERSION_MAJOR=2
_TUKBUILD_VERSION_MINOR=39
#
#############################################################################
###############
# shopt flags #
###############
# This must be before functions.
shopt -s dotglob extglob extquote
shopt -u sourcepath
# Fail if any part in a pipe fails. This is supported only by bash >=3.
[ -n "${BASH_VERSINFO[0]}" -a "${BASH_VERSINFO[0]}" -ge 3 ] && set -o pipefail
####################
# Helper functions #
####################
# These functions are intended to be used only internally by tukbuild.
# Of course, nothing prevents you from using these from your TukBuild
# file, but it is generally discouraged.
# Check if $1 is a non-negative integer.
_is_number() {
[[ $1 == +([0-9]) ]]
}
# Many commands interpret integer argument as a source/path/packagedir
# number, and non-integers as filename or pathname. _expand_arg makes
# this simple by detecting the argument type and settings RETURN_VALUE
# appropriately. Like all the arrays in bash, we start indexing from zero.
# $1 = integer or something else ;)
# $2... = expanded values if $1 is an integer.
_expand_arg() {
if _is_number "$1"; then
shift "$1"
shift 1
fi
if [ -z "$1" ]; then
echo '+ ERROR: Invalid parameter.'
exit 1
fi
RETURN_VALUE=$1
return 0
}
_upgradepkg() {
if type -P spkg &> /dev/null; then
spkg "$@"
else
upgradepkg "$@"
fi
}
_removepkg() {
if type -P spkg &> /dev/null; then
spkg -d "$@"
else
removepkg "$@"
fi
}
# Downloads a SOURCE or PATCH file to DISTFILES directory and verifies
# MD5 and SHA1 checksums.
_download() {
local URL FILE MD5 SHA1
URL=$1
FILE=$(basename "$1")
MD5=$2
SHA1=$3
RETURN_VALUE=
if [ -f "$CWD/$FILE" ]; then
echo "+ $FILE found in $CWD"
RETURN_VALUE="$CWD/$FILE"
elif [ -f "$DISTFILES/$FILE" ]; then
echo "+ $FILE found in $DISTFILES"
RETURN_VALUE="$DISTFILES/$FILE"
elif _is_url "$URL"; then
# Check that we have an URL where to download:
echo "+ $FILE not found, trying to download."
_wget "$URL" "$DISTFILES/$FILE"
RETURN_VALUE="$DISTFILES/$FILE"
elif _is_url "$CWD_URL"; then
# No URL was specified in TukBuild file but we have URL to
# the TukBuild file. Try to download from the same directory
# where the TukBuild file is:
echo "+ $FILE not found, trying to download."
_wget "$CWD_URL/$URL" "$DISTFILES/$FILE"
RETURN_VALUE="$DISTFILES/$FILE"
else
echo "+ File not found and no download URL" \
"specified: $FILE"
exit 1
fi
# Check the checksum(s):
if [ -n "$MD5" -a "$MD5" != "-" ]; then
echo "+ Verifying MD5 sum of $FILE"
if [ "$(md5sum < "$RETURN_VALUE" | cut -f 1 -d ' ')" \
!= "$MD5" ]; then
echo '+ ERROR: Verification of MD5 sum failed.'
exit 1
fi
fi
if [ -n "$SHA1" -a "$SHA1" != "-" ]; then
echo "+ Verifying SHA1 sum of $FILE"
if [ "$(sha1sum < "$RETURN_VALUE" | cut -f 1 -d ' ')" \
!= "$SHA1" ]; then
echo '+ ERROR: Verification of SHA1 sum failed.'
exit 1
fi
fi
}
# Helper for _doc_except().
_doc_except_helper_func() {
local I J
for I in ANNOUNCE AUTHORS BUGS CHANGELOG ChangeLog COPYING* \
INSTALL LICENSE HISTORY NEWS README* TODO USAGE
do (
for J; do
# If match, continue to next file:
[ "$I" = "$J" ] && exit 0
done
# Don't copy zero size files:
[ -s "$I" -o ! -f "$I" ] && echo "$I"
); done
}
# Create a package.
_makepkg() {
local PKGDIR PKGFILE PKGINFO I J K
_expand_arg "$1" "${PKG[@]}"
PKGDIR=$RETURN_VALUE
PKGFILE=$2
echo "+ _makepkg $PKGDIR -> $PKGFILE"
if [ ! -d "$PKGDIR" ]; then
echo "+ ERROR: Directory does not exists: $PKGDIR"
exit 1
fi
cd "$PKGDIR"
echo '+ Processing documentation files.'
# The info directory file, locale.alias and perllocal.pod should never
# exists in a normal package (info/dir can be in textinfo and
# locale.alias is in glibc and perllocal.pod in perl):
_docfix "$PKGDIR" # _docfix needs to be before the "rm" below.
rm -f ".$INFODIR"/dir{,.gz} usr/{,local/}share/locale/locale.alias \
usr/{,local/}lib*/perl5/5.*/*/perllocal.pod
rmdir -p --ignore-fail-on-non-empty usr/{,local/}lib*/perl5/5.*/* \
> /dev/null 2> /dev/null
# Man page fixes:
for K in usr{,/local,/X11{,R{6,7}}}{,/share}/man opt/*/{share/,}man; do
if [ -d "$K" -a ! -L "$K" ]; then
# Instead of having many small files that have only
# '.so' command we create symlinks. Note that this
# loop assumes that filenames don't have $IFS-chars.
for I in $(find "$K" -mindepth 2 \
-type f ! -size +128c); do
J=$(sed -n 's,^.*/\([^/ ]*\) *$,\1,p' "$I")
# Simple error check:
[ "$J" = "" ] && continue
rm -f "$I"
ln -sf "$J" "$I"
done
if [ "$COMPRESS_MAN" = "1" ]; then
# Fix symlinks:
for I in $(find "$K" -mindepth 2 \
-type l ! -name '*.gz'); do
J=$(readlink "$I")
rm -f "$I"
ln -sf "$J.gz" "$I.gz"
done
# Compress. ": | gzip" trick forces gzip to
# work non-interactively i.e. never ask if
# a file should be overwritten (never
# overwrite). Cannot use -f because it
# would destroy symlinks.
: | gzip -9rn "$K" 2> /dev/null
fi
fi
done
# Compress GNU info pages:
[ "$COMPRESS_MAN" = "1" -a -d ".$INFODIR" ] \
&& : | gzip -9rn ".$INFODIR" 2> /dev/null
# Fix possibly wrong permissions:
for I in ".$MANDIR" ".$INFODIR"; do
if [ -d "$I" ]; then
find "$I" -type f -print0 | xargs -0r chmod 0644 --
find "$I" -type d -print0 | xargs -0r chmod 0755 --
fi
done
# Strip binaries:
if [ "$STRIP" = "1" ]; then
# This breaks if filenames have spaces (or newlines):
echo '+ Stripping binaries and libraries.'
find . -type f -print0 | xargs -0r file -- | sed -n \
's/^\(.*\):.* ELF .* executable, .*, not stripped$/\1/p' \
| tr '\n' '\0' \
| xargs -0r strip --strip-all --
find . -type f -print0 | xargs -0r file -- | sed -n \
's/^\(.*\):.* ELF .* shared object, .*, not stripped$/\1/p' \
| tr '\n' '\0' \
| xargs -0r strip --strip-unneeded --
find . -type f -print0 | xargs -0r file -- | sed -n \
's/^\(.*\):.* ELF .* relocatable, .*, not stripped$/\1/p' \
| tr '\n' '\0' \
| xargs -0r strip --strip-debug --
find . -type f -name '*.a' -print0 | xargs -0r file -- \
| sed -n 's/^\(.*\): *current ar archive$/\1/p' \
| tr '\n' '\0' \
| xargs -0r strip --strip-debug --
fi
# Put the package description in place (slack-desc). We don't
# overwrite any slack-desc files, the build script might have
# generated it (e.g. because of version number dependent content).
PKGINFO=0
if [ ! -f install/slack-desc ] && _is_number "$1"; then
[ "$DISTRO" = 'vector' ] && PKGINFO=1
echo "+ Searching for file ${NAME[$1]}.desc."
for J in "$CWD/${NAME[$1]}.desc" \
"$CWD/${NAME[$1]}.desc"; do
if [ -f "$J" ]; then
echo "+ Creating" \
"${PKG[$1]}/install/slack-desc from $J."
cat "$J" | _desc "$1" quiet
break
fi
done
fi
# slack-required, slack-conflicts and slack-suggests:
for K in required conflicts suggests; do
if [ ! -f install/slack-$K ] && _is_number "$1"; then
echo "+ Searching for file ${NAME[$1]}.$K."
for J in "$CWD/${NAME[$1]}.$K" \
"$CWD/${NAME[$1]}.$K"; do
if [ -f "$J" ]; then
echo "+ Copying $J to " \
"${PKG[$1]}/install/slack-$K."
mkdir -p install # Should exist already.
cat "$J" > install/slack-$K
break
fi
done
fi
done
if [ ! -f install/slack-desc ]; then
echo "+ WARNING: $PKGDIR/install/slack-desc"
echo "+ doesn't exist and no .desc file" \
"for it was not found."
# Append Vector-specific stuff to slack-desc:
elif [ "$PKGINFO" = '1' ]; then
cat <<- EOF >> install/slack-desc
----------------------------------------
BUILDDATE : $(date)
PACKAGER : $([[ $PACKAGER_NAME ]] && echo "$PACKAGER_NAME" || echo "$USER")
HOST : $(uname -srm)
DISTRO : $({ cat /etc/vector-version \
|| cat /etc/slackware-version \
|| echo '(Unknown)'; } 2> /dev/null)
CFLAGS : $CFLAGS
CONFIG : ${_CONFIGURE_FLAGS}
EOF
fi
# Finally make the package:
echo "+ Creating the package file: $PKGFILE"
# Slackware has this only in /sbin:
/sbin/makepkg --prepend -c n -l y "$PKGFILE"
}
_cleanup() {
if [ "$CLEANUP" = "1" ]; then
if [ -f /var/log/packages/zzz_tukbuild_temp-$$-unknown-0tukbuild ]; then
echo '+ Removing temporary package.'
_removepkg zzz_tukbuild_temp-$$-unknown-0tukbuild
fi
echo '+ Removing temporary files.'
rm -rf "$TMP"
fi
}
_show_version() {
echo "${_TUKBUILD_VERSION_MAJOR}.${_TUKBUILD_VERSION_MINOR}"
}
_show_usage() {
cat << EOF
tukbuild version ${_TUKBUILD_VERSION_MAJOR}.${_TUKBUILD_VERSION_MINOR}
tukbuild is a tool used to build binary packages from TukBuild files.
Currently tukbuild supports only systems having Slackware(R) compatible
pkgtools and basic GNU tools installed.
Usage: tukbuild [options] [TukBuild files...]
-a, --arch Build package for architechture
-b, --build Build tag
-d, --distro Build for (and on) distribution (autodetected)
-j, --jobs Number of parallel jobs
-c, --command Run function/command(s) instead of build()
-S, --no-strip Do not strip binaries, same as STRIP=0
-C, --no-cleanup Do not remove temporary files, same as CLEANUP=0
--scripts-to-package Copy build scripts into pkg (default on Zenwalk)
--no-scripts-to-package Do not copy build scripts into the package.
--name-prefix Prefix NAME with PREFIXTYPE (default on Base).
--no-name-prefix Do not prefix package NAME with PREFIXTYPE.
-h, --help Show this help screen
-V, --version Show only version number
If no TukBuild files are given, all files in the current directory having
the suffix \`.TukBuild' will be built. You can also give HTTP or FTP URL
to pointing to a TukBuild file.
To learn how to write new TukBuild files, see the comments in the tukbuild
script and skim a few example .TukBuild files. Currently there is no better
documentation available
EOF
exit 0
}
_prefix_helper_prefixes() {
# $1 = PREFIXTYPE (optional)
PREFIXTYPE=${PREFIXTYPE:-normal}
[ -n "$1" ] && PREFIXTYPE=$1
echo "+ Setting PREFIXTYPE=$PREFIXTYPE"
# Slackware 11.1 moved X and KDE to /usr. Cope with that.
case $DISTRO in
slackware|slamd64)
if egrep -qs ' (10|11\.0)' /etc/slackware-version \
/etc/slamd64-version; then
case $PREFIXTYPE in
x11|kde) PREFIXTYPE=normal ;;
esac
fi
;;
esac
case "$ARCH-$DISTRO-$PREFIXTYPE" in
*-tukaani-normal|*-tukaani-x11) # Modular X.org goes to /usr
PREFIX=/usr
BINDIR=$PREFIX/bin
SBINDIR=$PREFIX/sbin
LIBDIR=$PREFIX/lib
LIBEXECDIR=$PREFIX/libexec
INCLUDEDIR=$PREFIX/include
DATADIR=$PREFIX/share
SYSCONFDIR=/etc
LOCALSTATEDIR=/var
# We decided to switch from Slackware-like locations
# to FHS. Symlinks are kept in the distro so should be
# no problem for those who are used to old locations.
INFODIR=$DATADIR/info
MANDIR=$DATADIR/man
DOCDIR=$DATADIR/doc
;;
*-tukaani-qt3|*-tukaani-qt4)
PREFIX=/opt/$PREFIXTYPE
BINDIR=$PREFIX/bin
SBINDIR=$PREFIX/sbin
LIBDIR=$PREFIX/lib
LIBEXECDIR=$PREFIX/libexec
INCLUDEDIR=$PREFIX/include
DATADIR=$PREFIX/share
SYSCONFDIR=$PREFIX/etc
LOCALSTATEDIR=$PREFIX/var
INFODIR=$DATADIR/info
MANDIR=$DATADIR/man
DOCDIR=$DATADIR/doc
;;
*-tukaani-gnome)
PREFIX=/opt/gnome
BINDIR=$PREFIX/bin
SBINDIR=$PREFIX/sbin
LIBDIR=$PREFIX/lib
LIBEXECDIR=$PREFIX/libexec
INCLUDEDIR=$PREFIX/include
DATADIR=$PREFIX/share
# FHS would say /etc/opt/gnome and /var/opt/gnome
# but we disagree.
SYSCONFDIR=$PREFIX/etc
LOCALSTATEDIR=$PREFIX/var
# These follow FHS 2.3. FHS 2.2 instructed to use
# $PREFIX/{info,man,doc} in /opt.
INFODIR=$DATADIR/info
MANDIR=$DATADIR/man
DOCDIR=$DATADIR/doc
;;
*-tukaani-kde)
# See the comments for *-tukaani-gnome above.
PREFIX=/opt/kde
BINDIR=$PREFIX/bin
SBINDIR=$PREFIX/sbin
LIBDIR=$PREFIX/lib
LIBEXECDIR=$PREFIX/libexec
INCLUDEDIR=$PREFIX/include
DATADIR=$PREFIX/share
SYSCONFDIR=$PREFIX/etc
LOCALSTATEDIR=$PREFIX/var
INFODIR=$DATADIR/info
MANDIR=$DATADIR/man
DOCDIR=$DATADIR/doc
;;
*-tukaani-games)
PREFIX=/usr
BINDIR=$PREFIX/games
SBINDIR=$PREFIX/sbin
LIBDIR=$PREFIX/lib
LIBEXECDIR=$PREFIX/libexec
INCLUDEDIR=$PREFIX/include
DATADIR=$PREFIX/share/games
SYSCONFDIR=/etc
LOCALSTATEDIR=/var/games
INFODIR=$DATADIR/info
MANDIR=$DATADIR/man
DOCDIR=$DATADIR/doc
;;
*-slackware-normal|*-vector-normal|*-vector-x11)
PREFIX=/usr
BINDIR=$PREFIX/bin
SBINDIR=$PREFIX/sbin
LIBDIR=$PREFIX/lib
LIBEXECDIR=$PREFIX/libexec
INCLUDEDIR=$PREFIX/include
DATADIR=$PREFIX/share
SYSCONFDIR=/etc
LOCALSTATEDIR=/var
# Doesn't follow recent FHS but I won't argue
# which one is better, FHS or Slack way:
INFODIR=$PREFIX/info
MANDIR=$PREFIX/man
DOCDIR=$PREFIX/doc
;;
*-slackware-x11)
PREFIX=/usr/X11R6
BINDIR=$PREFIX/bin
SBINDIR=/usr/sbin # Not used with x11?
LIBDIR=$PREFIX/lib
LIBEXECDIR=$PREFIX/libexec
INCLUDEDIR=$PREFIX/include
DATADIR=$PREFIX/share
SYSCONFDIR=$PREFIX/etc # ???
LOCALSTATEDIR=/var
INFODIR=/usr/info
MANDIR=$PREFIX/man
DOCDIR=/usr/doc
;;
*-slackware-kde|*-vector-kde)
PREFIX=/opt/kde
BINDIR=$PREFIX/bin
SBINDIR=$PREFIX/sbin
LIBDIR=$PREFIX/lib
LIBEXECDIR=$PREFIX/libexec
INCLUDEDIR=$PREFIX/include
DATADIR=$PREFIX/share
SYSCONFDIR=$PREFIX/etc
LOCALSTATEDIR=$PREFIX/var
INFODIR=$PREFIX/info
MANDIR=$PREFIX/man
DOCDIR=/usr/doc
;;
*-slackware-games|*-vector-games)
PREFIX=/usr
BINDIR=$PREFIX/games
SBINDIR=$PREFIX/sbin
LIBDIR=$PREFIX/lib
LIBEXECDIR=$PREFIX/libexec
INCLUDEDIR=$PREFIX/include
DATADIR=$PREFIX/share/games
SYSCONFDIR=/etc
LOCALSTATEDIR=/var/games
INFODIR=$PREFIX/info
MANDIR=$PREFIX/man
DOCDIR=$PREFIX/doc
;;
x86_64-slamd64-normal)
PREFIX=/usr
BINDIR=$PREFIX/bin
SBINDIR=$PREFIX/sbin
LIBDIR=$PREFIX/lib64
LIBEXECDIR=$PREFIX/libexec
INCLUDEDIR=$PREFIX/include
DATADIR=$PREFIX/share
SYSCONFDIR=/etc
LOCALSTATEDIR=/var
# Slackware-like locations:
INFODIR=$PREFIX/info
MANDIR=$PREFIX/man
DOCDIR=$PREFIX/doc
;;
x86_64-slamd64-kde)
PREFIX=/opt/kde
BINDIR=$PREFIX/bin
SBINDIR=$PREFIX/sbin
LIBDIR=$PREFIX/lib64
LIBEXECDIR=$PREFIX/libexec
INCLUDEDIR=$PREFIX/include
DATADIR=$PREFIX/share
SYSCONFDIR=$PREFIX/etc
LOCALSTATEDIR=$PREFIX/var
INFODIR=$PREFIX/info
MANDIR=$PREFIX/man
DOCDIR=/usr/doc
;;
x86_64-slamd64-games)
PREFIX=/usr
BINDIR=$PREFIX/games
SBINDIR=$PREFIX/sbin
LIBDIR=$PREFIX/lib64
LIBEXECDIR=$PREFIX/libexec
INCLUDEDIR=$PREFIX/include
DATADIR=$PREFIX/share/games
SYSCONFDIR=/etc
LOCALSTATEDIR=/var/games
INFODIR=$PREFIX/info
MANDIR=$PREFIX/man
DOCDIR=$PREFIX/doc
;;
*-zenwalk-normal|*-zenwalk-kde|*-zenwalk-gnome|*-zenwalk-x11)
PREFIX=/usr
BINDIR=$PREFIX/bin
SBINDIR=$PREFIX/sbin
LIBDIR=$PREFIX/lib
LIBEXECDIR=$PREFIX/libexec
INCLUDEDIR=$PREFIX/include
DATADIR=$PREFIX/share
SYSCONFDIR=/etc
LOCALSTATEDIR=/var
INFODIR=$PREFIX/info
MANDIR=$PREFIX/man
DOCDIR=$PREFIX/doc
;;
*-zenwalk-games)
PREFIX=/usr/games
BINDIR=$PREFIX/bin
SBINDIR=$PREFIX/sbin
LIBDIR=$PREFIX/lib
LIBEXECDIR=$PREFIX/libexec
INCLUDEDIR=$PREFIX/include
DATADIR=$PREFIX/share
SYSCONFDIR=/etc
LOCALSTATEDIR=/var/games
INFODIR=/usr/info
MANDIR=/usr/man
DOCDIR=/usr/doc
;;
*-base-normal|*-base-x11)
PREFIX=/usr
BINDIR=$PREFIX/bin
SBINDIR=$PREFIX/sbin
LIBDIR=$PREFIX/lib
LIBEXECDIR=$PREFIX/libexec
INCLUDEDIR=$PREFIX/include
DATADIR=$PREFIX/share
SYSCONFDIR=/etc
LOCALSTATEDIR=/var
INFODIR=$DATADIR/info
MANDIR=$DATADIR/man
DOCDIR=$DATADIR/doc
;;
*-base-*)
PREFIX=/opt/$PREFIXTYPE
BINDIR=$PREFIX/bin
SBINDIR=$PREFIX/sbin
LIBDIR=$PREFIX/lib
LIBEXECDIR=$PREFIX/libexec
INCLUDEDIR=$PREFIX/include
DATADIR=$PREFIX/share
SYSCONFDIR=$PREFIX/etc
# We will follow FHS in this (at least a little),
# because it makes it easier for multi-partitions
# systems where /var is usually a seperate partition.
LOCALSTATEDIR=/var/$PREFIXTYPE
INFODIR=$DATADIR/info
MANDIR=$DATADIR/man
DOCDIR=$DATADIR/doc
;;
custom)
;;
*)
echo "+ Unknown PREFIXTYPE: $PREFIXTYPE"
exit 1
;;
esac
}
# Set CONFIGURE to contain appropriate command and flags. It was idiotic
# to have a variable for this job, but this is kept here for backwards
# compatibility. Use _configure instead of $CONFIGURE.
_prefix_helper_configure() {
CONFIGURE="./configure \
--prefix=$PREFIX \
--bindir=$BINDIR \
--sbindir=$SBINDIR \
--libexecdir=$LIBEXECDIR \
--datadir=$DATADIR \
--sysconfdir=$SYSCONFDIR \
--localstatedir=$LOCALSTATEDIR \
--libdir=$LIBDIR \
--includedir=$INCLUDEDIR \
--infodir=$INFODIR \
--mandir=$MANDIR \
--program-prefix= \
--program-suffix= \
--build=$CONFIGURE_TRIPLET \
--cache-file=$TMP/configure.cache"
}
########################
# "Exported" functions #
########################
# These functions are intended to be used by TukBuild files.
# This is useful on Vector to store configure flags when finishing an aborted
# build process. Just replace call to _configure with _configure_flags.
_configure_flags() {
_CONFIGURE_FLAGS=$(echo \
--prefix=$PREFIX \
--bindir=$BINDIR \
--sbindir=$SBINDIR \
--libexecdir=$LIBEXECDIR \
--datadir=$DATADIR \
--sysconfdir=$SYSCONFDIR \
--localstatedir=$LOCALSTATEDIR \
--libdir=$LIBDIR \
--includedir=$INCLUDEDIR \
--infodir=$INFODIR \
--mandir=$MANDIR \
--program-prefix= \
--program-suffix= \
--build=$CONFIGURE_TRIPLET \
"$@")
}
# Runs the configure scripts created by GNU Autoconf.
_configure() {
# Grab the configure flags which can be stored to the slack-desc
# as a hidden part.
_configure_flags "$@"
# Run configure.
./configure \
--prefix=$PREFIX \
--bindir=$BINDIR \
--sbindir=$SBINDIR \
--libexecdir=$LIBEXECDIR \
--datadir=$DATADIR \
--sysconfdir=$SYSCONFDIR \
--localstatedir=$LOCALSTATEDIR \
--libdir=$LIBDIR \
--includedir=$INCLUDEDIR \
--infodir=$INFODIR \
--mandir=$MANDIR \
--program-prefix= \
--program-suffix= \
--build=$CONFIGURE_TRIPLET \
--cache-file=$TMP/configure.cache \
"$@"
}
# Explode an archive. If $1 is an integer, ${SOURCE[$1]} is exploded;
# otherwise $1 should be a filename. If you want to explode the archive
# to somewhere else than $TMP, you can specify the target directory as
# the second argument.
# $1 = Integer for SOURCE or a filename
# $2 = Target path, if omitted defaults to $TMP.
_explode() {
local FILE TARGET
# If we have been given at maximum of three characters as the first
# argument, assume that it is the index number of source file.
# The indexing starts from 0.
_expand_arg "$1" "${SOURCE[@]}"
FILE=$RETURN_VALUE
[ -z "$2" ] && TARGET=$TMP || TARGET=$2
echo "+ _explode $FILE -> $TARGET"
mkdir -p "$TARGET" || exit $?
case "$FILE" in
*.tar) tar xf "$FILE" -C "$TARGET" ;;
*.tar.gz|*.tgz) gzip -cd "$FILE" | tar xf - -C "$TARGET" ;;
*.tar.bz2|*.tbz) bzip2 -cd "$FILE" | tar xf - -C "$TARGET" ;;
*.tar.lzma|*.tlz) lzma -cd "$FILE" 2> /dev/null \
| tar xf - -C "$TARGET" ;;
*.zip) unzip -o "$FILE" -d "$TARGET" ;;
*.cpio) ( cd "$TARGET"; cpio -id ) < "$FILE" ;;
# TODO: rpm2cpio could be replaced.
*.rpm) ( cd "$TARGET"
rpm2cpio | cpio -id ) < "$FILE" ;;
*.gz) gzip -cd "$FILE" > \
"$TARGET/$(basename "$TARGET" .gz)" ;;
*.bz2) bzip2 -cd "$FILE" > \
"$TARGET/$(basename "$TARGET" .bz2)" ;;
*.lzma) lzma -cd "$FILE" > \
"$TARGET/$(basename "$TARGET" .lzma)" ;;
*) echo "+ WARNING: Unknown file type," \
"skipping: $FILE";;
esac
}
# Shortcut to explode all the SOURCE archives to $TMP.
_explode_all() {
local I
for (( I=0; I < ${#SOURCE[@]}; I++ )); do
_explode $I
done
}
# The preferred way to set CFLAGS and CXXFLAGS is to use OPTIMIZE
# variable. However, sometimes it is nice to change flags in the middle
# of the TukBuild. configure.cache has be removed every time flags are
# changed or else the next configure script will complain.
_cflags() {
rm -f "$TMP/configure.cache"
export CFLAGS="${_CPUFLAGS} $*"
export CXXFLAGS=$CFLAGS
export GCJFLAGS=$CFLAGS
}
# Check if $1 is an URL (HTTP, HTTPS or FTP).
_is_url() {
# =~ is not supported by bash 2.05b, neither is
# [[ $1 == foo* -o $1 == bar* ]] but this works:
[[ $1 == http://*/* ]] \
|| [[ $1 == https://*/* ]] \
|| [[ $1 == ftp://*/* ]] \
|| [[ $1 == svn://*/* ]]
}
# This was originally a wrapper for wget, but later it got support for
# svn too. This removes the target file if wget exists unsuccessfully
# or it is aborted by the user.
# $1 = URL
# $2 = Target filename
_wget() {
(
set +e
trap "rm -f \"$2\"" INT TERM
echo "+ _wget: $1 -> $2"
if [[ $1 == svn://*/* ]]; then
# This is probably broken. Suggestions are welcome.
svn co "$1" "$2"
else
wget $WGET_FLAGS -O "$2" "$1"
fi
if [ $? = 0 ]; then
echo '+ Download successful.'
else
rm -f "$2"
echo "+ ERROR: Downloading $(basename "$2") failed."
exit 1
fi
)
}
# Reset permissions: chown everything to root:root (actually using UID
# and GID 0), chmod all the directories to 0755, all the files with any
# exectuble bit set to 0755 and the rest to 0644. _chfix always operates
# recursively. You can specify which directories _chfix will modify.
# If no arguments are given, the default is to fix all package
# directories (${PKG[@]}).
_chfix() {
if [ "x$1" = "x--quiet" ]; then
shift
else
echo "+ _chfix $*"
fi
if [ $# = 0 ]; then
set -- "${PKG[@]}"
else
_expand_arg "$1" "${PKG[@]}"
set -- "$RETURN_VALUE"
fi
[ "$UID" = "0" ] && chown -hR 0:0 -- "$@"
find "$@" ! -type l -type d -print0 | xargs -0r chmod 0755 --
find "$@" ! -type l ! -type d -perm +0111 -print0 \
| xargs -0r chmod 0755 --
find "$@" ! -type l ! -type d ! -perm +0111 -print0 \
| xargs -0r chmod 0644 --
}
# Slackware and Slamd64 follow an ancient standard that says that all
# binaries in bin and sbin directories and the directories themselves
# should be owned by group bin instead of root. Always make sure you
# handle this one way or another. With most packages it is simples to
# have _chgrp_bin right after _chfix. Be careful with _chgrp_bin because
# if you have any setuid binaries the setuid bits will be turned off at
# least on Linux. The arguments for _chgrp_bin are given similarly to _chfix.
_chgrp_bin() {
local I J
if [ "$CHGRP_BIN" = "1" -a "$UID" = "0" ]; then
if [ $# = 0 ]; then
for J in "${PKG[@]}"; do
echo "+ _chgrp_bin (default dirs in $J)"
for I in {,usr/,usr/local/,opt/*/}{bin,sbin}; do
[ -e "$J/$I" ] && chgrp -R bin "$J/$I"
done
done
else
echo "+ _chgrp_bin $*"
for I; do
_expand_arg "$I" "${PKG[@]}"
[ -e "$RETURN_VALUE" ] && \
chgrp -R bin "$RETURN_VALUE"
done
fi
else
echo '+ _chgrp_bin (skipping)'
fi
# return 0 is important here. Otherwise we might return with
# non-zero exit status and stop the build process.
return 0
}
# Apply a patch. If $1 is an integer, it is interpreted as ${PATCH[$1]},
# otherwise it should be a patch file (possibly compressed).
# $1 = Integer for PATCH array, or patch filename
# $2 ... = Options to be passed to the patch command
_patch() {
local FILE
_expand_arg "$1" "${PATCH[@]}"
FILE=$RETURN_VALUE
shift 1
echo "+ _patch $FILE $*"
case "$FILE" in
*.gz) gzip -cd "$FILE" | patch "$@" ;;
*.bz2) bzip2 -cd "$FILE" | patch "$@" ;;
*.lzma) lzma -cd "$FILE" | patch "$@" ;;
*) patch "$@" < "$FILE" ;;
esac
}
# Copy documentation files.
# $1 = Integer for PKG array, or path to package root directory
# $2 = Directory name in the documentation directory
# $3 ... = Files to be copied
# Example: To copy README and COPYING to $PKG$DOCDIR/$NAME-$VERSION use
# a command like this: _doc 0 $NAME-$VERSION README COPYING
# The target directory is automatically created if it doesn't exist.
_doc() {
local I J
if [ $# -lt 2 ]; then
echo '+ ERROR: _doc requires at least two arguments.'
exit 1
fi
_expand_arg "$1" "${PKG[@]}"
J="$RETURN_VALUE$DOCDIR/$2"
shift 2
echo "+ _doc $* -> $J"
for I; do
# Not copying empty files:
[ -f "$I" -a ! -s "$I" ] && continue
if [ -e "$I" -o -L "$I" ]; then
[ ! -d "$J" ] && mkdir -p "$J"
cp -av "$I" "$J"
_chfix --quiet "$J/${I##*/}"
fi
done
}
# Copy documentation files that have commonly used names except listed files.
# $1 = Integer for PKG array, or path to package root directory
# $2 = Directory name in the documentation directory
# $3 ... = Files to be *excluded*
# These files are copied from the current directory:
# ANNOUNCE AUTHORS BUGS CHANGELOG ChangeLog COPYING*
# INSTALL LICENSE HISTORY NEWS README* TODO USAGE
_doc_except() {
if [ $# -lt 2 ]; then
echo '+ ERROR: _doc_except requires at least two arguments.'
exit 1
fi
local PKGDIR DIRNAME
_expand_arg "$1" "${PKG[@]}"
PKGDIR=$RETURN_VALUE
DIRNAME=$2
shift 2
echo "+ _doc_except $* -> ${PKGDIR}${DOCDIR}/$DIRNAME"
_doc "$PKGDIR" "$DIRNAME" $(_doc_except_helper_func "$@")
}
# Remove empty directories. If no arguments are given, all the package
# directories (${PKG[@]}) are scanned and empty directories removed.
# Be careful with this command if you have used _tmp_pkg since there
# might be directories that contain only symlinks, but symlinks are
# moved to doinst.sh; _rm_empty_dirs doesn't detect those.
_rm_empty_dirs() {
local I
[ $# = 0 ] && set -- "${PKG[@]}"
echo "+ _rm_emptry_dirs $*"
for I; do
_expand_arg "$I" "${PKG[@]}"
if [ ! -d "$RETURN_VALUE" ]; then
echo "+ ERROR: Directory doesn't exist: $RETURN_VALUE"
exit 1
fi
find "$RETURN_VALUE" -type d \
| xargs rmdir -pv --ignore-fail-on-non-empty --
done
# Avoid non-zero exit status:
return 0
}
# Create a temporary package and install it to the system. You should have
# specified ROOT_REQUIRED=1 in the toplevel of the TukBuild file. Make sure
# you have reasonable permissions in the package directory before using
# _tmp_pkg e.g. use _chfix and _chgrp_bin first.
# $1 = Integer for PKG, or a directory to be _makepkg'ed and upgradepkg'ed.
# $2 = "keep-symlinks" or empty.
_tmp_pkg() {
(
_expand_arg "$1" "${PKG[@]}"
if [ ! -d "$RETURN_VALUE" ]; then
echo "+ ERROR: Directory doesn't exist: $RETURN_VALUE"
exit 1
fi
echo "+ Creating temporary package from $RETURN_VALUE"
# Check if we have Tukaani pkgtools:
if [ "$HAS_TUKAANI_MAKEPKG" = "1" ]; then
PKGTYPE=tar
else
PKGTYPE=tgz
fi
PKGFILE="$TMP/zzz_tukbuild_temp-$$-unknown-0tukbuild.$PKGTYPE"
PKGDIR=$RETURN_VALUE
if [ "$2" = "keep-symlinks" ]; then
PKGDIR="$TMP/zzz_tukbuild_temp-$$"
rm -rf "$PKGDIR"
cp -al "$RETURN_VALUE" "$PKGDIR"
fi
# Some things may return errors that are safe to ignore:
set +e
_makepkg "$PKGDIR" "$PKGFILE" || exit $?
_upgradepkg --reinstall --install-new "$PKGFILE" || exit $?
[ "$2" = "keep-symlinks" ] && rm -rf "$PKGDIR"
: # Return with successful exit status.
)
}
# Move documentation files to correct directories by using some force.
# Sometimes packages put info, man and/or doc directories e.g. to /usr/doc
# when you would like to have them in /usr/share/doc (or vice versa). This
# function will fix that. This is run automatically by _makepkg() but
# in some relatively rare situations you might need to run it separately too.
# $1 ... = Package directories to be fixed; if omitted, defaults to all the
# package directories (${PKG[@]}).
_docfix() {
local I J K
if [ $# = 0 ]; then
set -- "${PKG[@]}"
else
_expand_arg "$1" "${PKG[@]}"
set -- "$RETURN_VALUE"
fi
echo "+ _docfix $*"
# Fix documentation locations, if needed. E.g. either
# /usr/{doc,man,info} or /usr/share/{doc,man,info}.
for K; do
for J in "$DOCDIR" "$MANDIR" "$INFODIR"; do
for I in "$PREFIX"/{,local/}{,share/}"$(basename "$J")"; do
if [ "$I" != "$J" -a -d "$K$I" ]; then
# Using cp is better because mv refuses
# to move files if the destination
# already exists (or am I missing
# something here?).
mkdir -p "$K$J"
cp -af "$K$I"/* "$K$J"
rm -rf "$K$I"
# Remove now possibly empty directory:
rmdir -p --ignore-fail-on-non-empty \
"$(dirname "$K$I")"
fi
done
done
done
}
# It is not nice to overwrite configuration files. _new will append
# an extension .new to the filenames and put a config_new() function
# to doinst.sh which will conditionally rename the files to their
# real names.
# $1 = Integer for PKG, or package root directory
# $2 ... = List of files to be renamed.
_new() {
local PKGDIR I
_expand_arg "$1" "${PKG[@]}"
PKGDIR=$RETURN_VALUE
if [ ! -d "$PKGDIR" ]; then
echo "+ ERROR: Directory does not exist: $PKGDIR"
exit 1
fi
shift 1
echo "+ _new $PKGDIR: $*"
if ! grep -q '^config_new() {$' "$PKGDIR/install/doinst.sh" \
> /dev/null 2> /dev/null; then
echo '+ Adding the config_new() function to ./install/doinst.sh.'
mkdir -p "$PKGDIR/install"
[ -e "$PKGDIR/install/doinst.sh" ] && mv -f \
"$PKGDIR/install/doinst.sh" \
"$PKGDIR/install/doinst.sh.tmp"
cat << "EOF" > "$PKGDIR/install/doinst.sh"
config_new() {
# If there's no config file by that name, mv it over:
if [ ! -e "$1" ]; then
mv -f "$1.new" "$1"
# Using 'md5sum' instead of 'cmp' to compare files, because it
# is possible that the diffutils package is not installed:
elif [ "$(md5sum < "$1")" = "$(md5sum < "$1.new")" ]; then
# Remove the redundant copy:
rm -f "$1.new"
fi
# Otherwise, we leave the .new copy for the admin to consider...
}
EOF
if [ -e "$PKGDIR/install/doinst.sh.tmp" ]; then
cat "$PKGDIR/install/doinst.sh.tmp" \
>> "$PKGDIR/install/doinst.sh"
rm -f "$PKGDIR/install/doinst.sh.tmp"
fi
fi
# Then the actual config commands:
for I; do
# Remove a prefixed slash, if any. This makes
# _new 0 $SYSCONFDIR/foo.conf
# work, which previously needed to be:
# _new 0 ${SYSCONFDIR#/}/foo.conf
I=${I#/}
if [ ! -f "$PKGDIR/$I" ]; then
echo "+ File doesn't exist: $PKGDIR/$I"
exit 1
fi
mv -f "$PKGDIR/$I" "$PKGDIR/$I.new"
echo "config_new '$(echo "$I" | sed "s/'/'\\\\''/g")'" \
>> "$PKGDIR/install/doinst.sh"
done
}
# Same as _new but the file is deleted if it cannot be renamed to its real
# name when installing. Handy if you don't want to leave unneeded .new files
# to users systems.
_new_delete() {
local PKGDIR I J
_expand_arg "$1" "${PKG[@]}"
PKGDIR=$RETURN_VALUE
shift 1
echo "+ _new_delete $PKGDIR $*"
for I; do
I=${I#/}
_new "$PKGDIR" "$I"
J="${I//"'"/"'\\''"}.new"
echo "[ -e '$J' ] && rm -f '$J'" \
>> "$PKGDIR/install/doinst.sh"
done
}
# User can override _prefix() in tukbuildrc.
_prefix() {
_prefix_helper_prefixes "$@"
_prefix_helper_configure
}
# Because of weirdness in Slackware's way to specify description, you need
# to give the basename of the package as the second argument if and only
# if you use directory name instead of package number.
_desc() {
if ! _is_number "$1"; then
echo '+ ERROR: _desc can be used only with a package number.'
exit 1
fi
_expand_arg "$1" "${PKG[@]}"
[ "$2" != "quiet" ] && echo "+ _desc $RETURN_VALUE/install/slack-desc"
[ ! -d "$RETURN_VALUE/install" ] && mkdir -p "$RETURN_VALUE/install"
# Prefix every line with "$BASENAME: " and replace certain strings
# in the description. This should allow Zenwalkers to use .desc files
# instead of embedding everything in the .TukBuild file:
sed "
s|^|$NAMEPREFIX${NAME[$1]}: |
s|@NAME@|${NAME[$1]}|g
s|@VERSION@|${VERSION[$1]}|g
" > "$RETURN_VALUE/install/slack-desc"
# Add PACKAGER_TAG as the 13th line of the description,
# if there is enough space:
local LINE_COUNT=$(wc -l < "$RETURN_VALUE/install/slack-desc" | tr -d ' ')
if [ -n "$PACKAGER_TAG" -a $LINE_COUNT -lt 13 ]; then
while [ $LINE_COUNT -lt 12 ]; do
echo "$NAMEPREFIX${NAME[$1]}: " \
>> "$RETURN_VALUE/install/slack-desc"
LINE_COUNT=$((LINE_COUNT + 1))
done
echo "$NAMEPREFIX${NAME[$1]}: $PACKAGER_TAG" \
>> "$RETURN_VALUE/install/slack-desc"
fi
}
# _suggests()
# _conflicts()
# $1 = Integer for PKG array, or path to package root directory
# $2 = GCONF_CONFIG_SOURCE; this argument is optional. The default
# value: $SYSCONFDIR/gconf/gconf.xml.defaults
_gconf() {
if [ $# -lt 1 ]; then
echo '+ ERROR: _gconf requires at least one argument.'
exit 1
fi
_expand_arg "$1" "${PKG[@]}"
# If we haven't been given a custom path for GCONF_CONFIG_SOURCE,
# use the default:
if [ -z "$2" ]; then
export GCONF_CONFIG_SOURCE="xml::$RETURN_VALUE$SYSCONFDIR/gconf/gconf.xml.defaults"
else
export GCONF_CONFIG_SOURCE="xml::$RETURN_VALUE$2"
fi
echo "+ _gconf $GCONF_CONFIG_SOURCE"
# Remove redundant scrollkeeper.
if [ -d "$RETURN_VALUE$LOCALSTATEDIR/scrollkeeper" ]; then
rm -rf "$RETURN_VALUE$LOCALSTATEDIR/scrollkeeper"
fi
if [ -d "$RETURN_VALUE$LOCALSTATEDIR/lib/scrollkeeper" ]; then
rm -rf "$RETURN_VALUE$LOCALSTATEDIR/lib/scrollkeeper"
fi
# Setup default install rules.
if [ -d "$RETURN_VALUE$SYSCONFDIR/gconf/schemas" ]; then
mkdir -p "$RETURN_VALUE$SYSCONFDIR/gconf/gconf.xml.defaults"
local I
for I in "$RETURN_VALUE$SYSCONFDIR/gconf/schemas"/*.schemas; do
if [ -e "$I" ]; then
gconftool-2 --makefile-install-rule "$I"
fi
done
for I in "$RETURN_VALUE$SYSCONFDIR/gconf/schemas"/*.entries; do
if [ -e "$I" ]; then
gconftool-2 --direct \
--config-source="$GCONF_CONFIG_SOURCE" \
--load "$I"
fi
done
# Reset / Verify correct permissions
find "$RETURN_VALUE$SYSCONFDIR/gconf" -type d -print0 \
| xargs -0r chmod 0755 --
find "$RETURN_VALUE$SYSCONFDIR/gconf" -type f -print0 \
| xargs -0r chmod 0644 --
fi
unset GCONF_CONFIG_SOURCE
}
# Adds dynamically generated dependency information to the package
# (creates slack-required). Alternative (and usually more comfortable way)
# is to use file packagename.required.
# $1 = Integer for PKG, or package directory name
# The data written to dependency information file should be given via stdin
# in identical format that is used in slack-required files.
_required() {
_expand_arg "$1" "${PKG[@]}"
echo "+ _required $RETURN_VALUE/install/slack-required"
[ ! -d "$RETURN_VALUE/install" ] && mkdir -p "$RETURN_VALUE/install"
cat > "$RETURN_VALUE/install/slack-required"
}
###################
# Initializations #
###################
# This cannot be overriden via environment:
unset BUILD_COMMAND
# Parse command line arguments
_ARGS=$(getopt -n tukbuild -o a:b:c:d:j:CSDhV \
-l arch:,build:,command:,distro:,jobs:,optimize \
-l no-cleanup,no-strip,download-only,help,version \
-l scripts-to-package,no-scripts-to-package \
-l name-prefix,no-name-prefix -- "$@")
[ $? != 0 ] && exit 99
eval set -- "${_ARGS}"
unset _ARGS
while :; do
case "$1" in
-a|--arch) ARCH=$2; shift ;;
-b|--build) BUILD=$2; shift ;;
-d|--distro) DISTRO=$2; shift ;;
-c|--command) BUILD_COMMAND=$2; shift ;;
-j|--jobs) MAKEJOBS=$2; shift ;;
-o|--optimize) OPTIM=$2; shift ;;
-C|--no-cleanup) CLEANUP=0 ;;
-S|--no-strip) STRIP=0 ;;
--scripts-to-package) SCRIPTS_TO_PACKAGE=1 ;;
--no-scripts-to-package) SCRIPTS_TO_PACKAGE=0 ;;
--name-prefix) NAME_WITH_PREFIXTYPE=1 ;;
--no-name-prefix) NAME_WITH_PREFIXTYPE=0 ;;
-D|--download-only) DOWNLOAD_ONLY=1 ;;
-h|--help) _show_usage; exit 0 ;;
-V|--version) _show_version; exit 0 ;;
--) shift; break ;;
*) echo '+ Bug in command line' \
'parsing code.'
exit 99
;;
esac
shift
done
# We didn't show help or version, so let's say hello:
echo "+ tukbuild version" \
"${_TUKBUILD_VERSION_MAJOR}.${_TUKBUILD_VERSION_MINOR}"
###################
# DISTRO and ARCH #
###################
# - DISTRO determines
# + the default package format (tgz or tlz)
# + if the directories bin and sbin should be owned by the bin group
# + if info and man pages should be gzipped.
# - ARCH determines the -march and -mtune/-mcpu values in CFLAGS/CXXFLAGS.
# - DISTRO-ARCH combination determines the value of CONFIGURE_TRIPLET.
# Detect DISTRO:
if [ -z "$DISTRO" ]; then
if [ -f /etc/tukaani-version ]; then
DISTRO=Tukaani
elif [ -f /etc/zenwalk-version ]; then
DISTRO=Zenwalk
elif [ -f /etc/base-version ]; then
DISTRO=Base
elif [ -f /etc/vector-version ]; then
DISTRO=Vector
elif [ -f /etc/slamd64-version ]; then
DISTRO=Slamd64
elif [ -f /etc/slackware-version ]; then
if [ "$(uname -m)" = "x86_64" ]; then
DISTRO=Slamd64
else
DISTRO=Slackware # Or similar enough
fi
else
echo '+ Autodetecting the distribution failed. Please set the'
echo '+ DISTRO environment variable manually. Supported values'
echo '+ for DISTRO (case-insensitive):'
echo '+ Slackware Slamd64 Tukaani Zenwalk Base Vector'
fi
echo "+ DISTRO=$DISTRO (autodetected)"
else
echo "+ DISTRO=$DISTRO (manually specified)"
fi
DISTRO=$(echo "$DISTRO" | tr A-Z a-z) # Force lower case
# Detect ARCH:
if [ -z "$ARCH" ]; then
case "$(uname -sm)" in
'Linux i386') ARCH=i386 ;; # No one should have this. ;-P
'Linux i486') ARCH=i486 ;; # Neither this for building.
'Linux i586')
case "$DISTRO" in
tukaani|base|vector) ARCH=i586 ;;
*) ARCH=i486 ;;
esac
;;
'Linux i686')
case "$DISTRO" in
tukaani|vector) ARCH=i586 ;;
base) ARCH=i686 ;;
*) ARCH=i486 ;;
esac
;;
'Linux x86_64') ARCH=x86_64 ;;
*) echo '+ Autodetecting ARCH failed. Please set ARCH' \
'+ environment variable manually.'
echo '+ Supported values for ARCH:'
echo '+ i386 i486 i586 i686 athlon athlonxp pentium2' \
'pentium3 pentium4 x86_64 noarch'
exit 1 ;;
esac
echo "+ ARCH=$ARCH (autodetected)"
else
echo "+ ARCH=$ARCH (manually specified)"
fi
ARCH=$(echo "$ARCH" | tr A-Z a-z) # Force lower case
# Set -march and -mtune:
case "$ARCH" in
i386) _CPUFLAGS="-march=i386 -mtune=i686" ;;
i486) _CPUFLAGS="-march=i486 -mtune=i686" ;;
i586) _CPUFLAGS="-march=i586 -mtune=i686" ;;
i686) _CPUFLAGS="-march=i686 -mtune=i686" ;;
athlon) _CPUFLAGS="-march=athlon -mtune=athlon" ;;
athlonxp) _CPUFLAGS="-march=athlon-xp -mtune=athlon-xp" ;;
pentium2) _CPUFLAGS="-march=pentium2 -mtune=pentium2" ;;
pentium3) _CPUFLAGS="-march=pentium3 -mtune=pentium3" ;;
pentium4) _CPUFLAGS="-march=pentium4 -mtune=pentium4" ;;
x86_64) _CPUFLAGS="-fPIC" ;;
noarch) _CPUFLAGS="" ;;
*) echo "+ Uknown ARCH: $ARCH"; exit 1 ;;
esac
# GCC 3.3 and older use -mcpu instead of -mtune:
gcc --version | head -n1 | grep -q ' \(3\.4\|4\.\)' \
|| _CPUFLAGS=${_CPUFLAGS//-mtune=/-mcpu=}
# General initializations and settings:
umask 0022
[ -z "$UID" ] && UID=$(id -u)
[ -z "$GID" ] && GID=$(id -g)
_TUKBUILD_TOOL="$(cd -- "$(dirname -- "$0")"; pwd)/${0##*/}"
TMP=${TMP:-"$TMPDIR"}
TMP=${TMP:-/tmp}
DISTFILES=${DISTFILES:-"$PWD"}
PACKAGES=${PACKAGES:-"$PWD"}
WGET_FLAGS=${WGET_FLAGS:-"--passive-ftp --tries=3 --timeout=15"}
CWD_URL=
STRIP=${STRIP:-1}
CLEANUP=${CLEANUP:-1}
DOWNLOAD_ONLY=${DOWNLOAD_ONLY:-0}
ROOT_REQUIRED=${ROOT_REQUIRED:-0}
MAKEJOBS=${MAKEJOBS:-1}
NUMJOBS=$MAKEJOBS # Backwards compatibility
NUMOBJS=$MAKEJOBS # and typocompatibility ;-)
BUILD_COMMAND=${BUILD_COMMAND:-build} # Cannot be overriden via environment.
BUILDPREFIX= # This gets prefixed to $BUILD.
# Mirrors, probably more in the future:
MIRROR_SF=${MIRROR_SF:-http://dl.sf.net}
MIRROR_KDE=${MIRROR_KDE:-ftp://ftp.kde.org/pub/kde}
MIRROR_GNOME=${MIRROR_GNOME:-http://ftp.gnome.org/pub/gnome/sources}
MIRROR_GNU=${MIRROR_GNU:-ftp://ftp.gnu.org/pub/gnu}
# Reset these before reading the config since user might have custom prefixes:
unset PREFIX BINDIR SBINDIR LIBDIR LIBEXECDIR INCLUDEDIR DATADIR \
SYSCONFDIR LOCALSTATEDIR INFODIR MANDIR DOCDIR
# Read config file:
if [ -f "$HOME/.tukbuildrc" ]; then
echo "+ Reading $HOME/.tukbuildrc."
source "$HOME/.tukbuildrc"
elif [ -f /etc/tukbuildrc ]; then
echo '+ Reading /etc/tukbuildrc.'
source /etc/tukbuildrc
fi
echo '+ Verifying basic variables and paths:'
for J in "TMP" "DISTFILES" "PACKAGES"; do
I=${!J}
if [ ! -d "$I" -o ! -r "$I" -o ! -w "$I" ]; then
echo "+ $J=$I: Not a directory or no read-write access allowed."
exit 1
fi
echo "+ $J=$I"
done
unset J
# Most packages can be compiled as non-root because all the
# files including binaries can be owned by root:root:
CHGRP_BIN=0
# Compressing man pages saves space when installed, but increases size of
# packages. Maybe in future the package manager can compress the man pages
# when packages are installed. ;-)
COMPRESS_MAN=1
CONFIGURE_TRIPLET=
case "$DISTRO" in
slackware)
PKGFORMAT=${PKGFORMAT:-tgz}
# String that can be passed to configure:
case "$ARCH" in
i386)
CONFIGURE_TRIPLET=i386-slackware-linux ;;
i?86|athlon|athlonxp|pentium[234])
CONFIGURE_TRIPLET=i486-slackware-linux ;;
esac
# Starting from Slack 11.0, the ancient habbit to chgrp
# all binaries to the bin group is no longer used. Since
# tukbuild doesn't support older Slackware versions than
# 10.0, this check should be enough to detect if CHGRP_BIN=1
# should be set.
fgrep -qs ' 10.' /etc/slackware-version && CHGRP_BIN=1
;;
slamd64)
PKGFORMAT=${PKGFORMAT:-tgz}
CONFIGURE_TRIPLET=x86_64-slackware-linux
fgrep -qs ' 10.' /etc/slackware-version /etc/slamd64-version \
&& CHGRP_BIN=1
;;
tukaani)
# Let's make smaller packages. :-)
PKGFORMAT=${PKGFORMAT:-tlz}
# String that can be passed to configure:
case "$ARCH" in
i386|i486)
echo '+ Tukaani for i386 and i486 is not supported.' \
'Use at least ARCH=i586.'
sleep 3
;;
i?86|athlon|athlonxp|pentium[234])
CONFIGURE_TRIPLET=i586-tukaani-linux-gnu ;;
esac
;;
zenwalk)
PKGFORMAT=${PKGFORMAT:-tgz}
CONFIGURE_TRIPLET=i486-slackware-linux
SCRIPTS_TO_PACKAGE=${SCRIPTS_TO_PACKAGE:-1}
# Starting from Zenwalk 4.0, the build tag contains the
# version number for which the package has been built.
if [ -f /etc/zenwalk-version ] && ! grep -qs \
'^Zenwalk [0-3]\.' /etc/zenwalk-version; then
BUILDPREFIX=$(sed -rn 's|^Zenwalk ([0-9]+)\.([0-9]+).*$|\1\2|p;q' \
/etc/zenwalk-version).
fi
;;
base)
PKGFORMAT=${PKGFORMAT:-tlz}
CONFIGURE_TRIPLET=i686-base-linux
NAME_WITH_PREFIXTYPE=${NAME_WITH_PREFIXTYPE:-1}
;;
vector)
PKGFORMAT=${PKGFORMAT:-tlz}
CONFIGURE_TRIPLET=i486-slackware-linux
if [[ -z $PACKAGER_NAME ]]; then
echo '+ WARNING: The variable PACKAGER_NAME is not set. You should set it'
echo ' in /etc/tukbuildrc or ~/.tukbuildrc to match the nick you'
echo ' use on the VectorLinux forum.'
sleep 3
fi
;;
*)
echo "+ ERROR: Unknown distribution: $DISTRO"
exit 1
;;
esac
# For non-Zenwalk:
SCRIPTS_TO_PACKAGE=${SCRIPTS_TO_PACKAGE:-0}
# For non-Base:
NAME_WITH_PREFIXTYPE=${NAME_WITH_PREFIXTYPE:-0}
# Detect type of makepkg.
if /sbin/makepkg --help | fgrep -qs Tukaani; then
HAS_TUKAANI_MAKEPKG=1
else
HAS_TUKAANI_MAKEPKG=0
fi
if [ "$UID" != "0" ]; then
# Show a small warning if we are running on Slackware or Slamd64
# which require chgrp'ing executables to the bin group:
if [ "$CHGRP_BIN" = "1" ]; then
echo "+ WARNING: Running as non-root." \
"This is not supported on $DISTRO."
sleep 3
fi
# Another warning if Slackware's makepkg is used as non-root.
# It doesn't support setting ownerships to root:root like
# Tukaani's makepkg.
if [ "$HAS_TUKAANI_MAKEPKG" = "0" ]; then
echo "+ WARNING: Running as non-root. This is not" \
"supported with Slackware's makepkg."
sleep 3
fi
fi
#############################
# Build and make package(s) #
#############################
# 1. Download the TukBuild file and *.desc, *.required, *.suggests and
# *.conflicts from the same directory.
# 2. Source (=read) the TukBuild file.
# 3. Set PREFIX, LIBDIR, ... and CONFIGURE variables. Their values depend
# on DISTRO, ARCH and PREFIXTYPE of which only PREFIXTYPE should be set
# by TukBuild (default is PREFIXTYPE=normal).
# 4. Download the the files specified in SOURCE and PATCH arrays unless
# they are found in DISTFILES. MD5 and SHA1 are always checked if they
# are specified in the TukBuild file.
# NEW FEATURE: If you need "holes" in NAME, SOURCE, PATCH or their
# MD5/SHA1 arrays, you can use a hyphen (`-'). The hyphen works also
# with BUILDSUFFIX, if you want plain numeric build tag.
# 5. The variable TMP is reset TMP="$TMP/build-$NAME". If TMP was originally
# not set, it will default to the value of TMPDIR and then fallback
# to /tmp. It is important to note that after this step TMP will point
# to e.g. /tmp/build-foo, not what it was set externally (e.g. /tmp).
# 6. The temporary directory pointer by TMP is created. Unless CLEANUP=0 is
# specified (equivalent to command line flag -C), the directory will be
# be erased first. If erasing is fails, tukbuild will return an error.
# 7. The current directory is changed to TMP. The command specified in
# BUILD_COMMAND (default is BUILD_COMMAND=build) is run and the actual
# build process should now start.
# 7. If an error occurs while building, tukbuild will exit and leave
# temporary files on the disk. When everything goes well, the contents
# of directories in the PKG array are used to create package files. This
# includes a few steps like stripping binaries unless STRIP=0 and
# compressing info and man pages unless COMPRESS_MAN=0. The packages will
# be put to the directory pointer by PACKAGES.
# 8. Unless CLEANUP=0 (command line flag -C) was specified, the temporary
# directory will be removed.
# 9. Repeat from the step 1. as long as no errors occur and there are
# TukBuild files left.
_STANDALONE=0
if [ "$(type -t build)" = "function" ]; then
# build() is already defined, so we are in standalone mode (the
# TukBuild file is embedded to the beginning of the tukbuild tool).
set -- "${_TUKBUILD_TOOL}"
_STANDALONE=1
elif [ $# = 0 ]; then
# We got no parameters. Search for TukBuild-files case insensitively:
shopt -s nocaseglob
set -- *.TukBuild
shopt -u nocaseglob
if [ "$1" = "*.TukBuild" ]; then
echo '+ ERROR: No *.TukBuild files in the' \
'current directory.'
exit 1
fi
fi
for _TUKBUILD; do ( # Open a subshell
# All errors stop the build process:
set -e
if _is_url "${_TUKBUILD}"; then
CWD_URL=$(dirname "${_TUKBUILD}")
if [ ! -f "${_TUKBUILD##*/}" ]; then
_wget "${_TUKBUILD}" "${_TUKBUILD##*/}"
echo '+ Checking the URL for .desc, .required,' \
'.suggests and .conflicts files.'
for I in $(wget $WGET_FLAGS -O - -q "$CWD_URL/" | sed -n \
's#^.*<\(a href\|A HREF\)="\([a-zA-Z0-9:/_.-]\+/\)*\([a-zA-Z0-9_.-]\+\.\(desc\|required\|conflicts\|suggests\)\)">\3[aA]>.*$#\3#p')
do
rm -f "$I"
_wget "$CWD_URL/$I" "$I"
done
fi
CWD=.
else
CWD=$(cd "$(dirname "${_TUKBUILD}")"; pwd)
fi
_TUKBUILD="${_TUKBUILD##*/}"
# In standalone mode we already have the TukBuild file,
# so it is not sourced.
if [ "${_STANDALONE}" = "0" ]; then
echo "+ Reading TukBuild file: $CWD/${_TUKBUILD}"
source "$CWD/${_TUKBUILD}"
fi
# Name and version:
NAME[0]=$NAME
VERSION[0]=$VERSION
# Set BUILDSUFFIX=- if you want no suffix.
BUILD=${BUILD:-1}
BUILDSUFFIX=${BUILDSUFFIX:-"$USER"}
if ! _is_number "$BUILD"; then
echo '+ WARNING: Non-numeric BUILD tags are deprecated.'
sleep 3
elif [ "$BUILDSUFFIX" != "-" ]; then
BUILD="$BUILD$BUILDSUFFIX"
fi
BUILD=$BUILDPREFIX$BUILD
# Force PREFIXTYPE to lowercase.
PREFIXTYPE=$(echo "$PREFIXTYPE" | tr A-Z a-z)
# Set NAMEPREFIX.
NAMEPREFIX=
if [ "$NAME_WITH_PREFIXTYPE" = "1" ]; then
if [ -z "$PREFIXTYPE" -o "$PREFIXTYPE" = "normal" \
-o "$PREFIXTYPE" = "x11" ]; then
NAMEPREFIX="base-"
else
NAMEPREFIX="${PREFIXTYPE}-"
fi
fi
# Check if TukBuild file has changed ROOT_REQUIRED:
if [ "$ROOT_REQUIRED" != "0" -a "$UID" != "0" \
-a "$DOWNLOAD_ONLY" != "1" ]; then
echo '+ ERROR: This package cannot be built without' \
'root priviledges.'
exit 1
fi
# Look for problematic pathnames and filenames.
if echo "$CWD$TMP$DISTFILES$PACKAGES" | fgrep -qs ' '; then
# "A little bit" generic message but whatever...
# feel free to improve.
echo '+ WARNING: Some paths contain spaces. Build may fail.'
sleep 3
fi
# Statistics:
if [ "$DOWNLOAD_ONLY" != "1" ]; then
if [ -z "$NAME" ]; then
echo '+ ERROR: No package names specified' \
'in sourced file.'
exit 1;
fi
J=0
for (( I=0; I < ${#NAME[@]}; I++ )); do
# Same TukBuild may build e.g. i686 (application)
# and noarch (docs) packages:
ARCH[I]=${ARCH[I]:-"$ARCH"}
[ "${NAME[I]}" = "-" -o "${VERSION[I]}" = "-" ] \
&& continue
if [ $J = 0 ]; then
echo '+ About to create the following' \
'package(s):'
J=1
fi
echo "+ $NAMEPREFIX${NAME[I]}-${VERSION[I]//-/_}-${ARCH[I]}-$BUILD.$PKGFORMAT"
done
if [ $J = 0 ]; then
echo '+ ERROR: The TukBuild file does not specify' \
'any packages to be built.'
exit 1
fi
unset J
# Verify PACKAGER_TAG:
if [ ${#PACKAGER_TAG} -gt 70 ]; then
echo '+ ERROR: PACKAGER_TAG is too long ' \
'(max 70 characters).'
fi
# Simple integrity check for *.desc files before downloading
# source and patch files (nicer to get the error message now):
# TODO: Verify also *.required, *.conflicts and *.suggests.
echo '+ Verifying *.desc files:'
for (( I=0; I < ${#SOURCE[@]}; I++ )); do
if [ -f "$CWD/${NAME[I]}.desc" ]; then
echo "+ $CWD/${NAME[I]}.desc"
# Descriptions are limited to 13 lines and
# 70 characters per line.
if [ ! -s "$CWD/${NAME[I]}.desc" ]; then
echo "+ ERROR: Empty description" \
"file: ${NAME[I]}.desc"
exit 1
fi
if [[ $(wc -l < "$CWD/${NAME[I]}.desc")\
-gt 13 ]]; then
echo "+ ERROR: Too many lines (over" \
"13) in ${NAME[I]}.desc."
exit 1
elif ! sed -n '/^.\{71\}/q1' \
"$CWD/${NAME[I]}.desc"
then
echo "+ ERROR: Too long lines (over" \
"70 chars) in ${NAME[I]}.desc."
exit 1
elif [ -n "$PACKAGER_TAG" ] && [[ $(wc -l < \
"$CWD/${NAME[I]}.desc") \
== 13 ]]; then
echo "+ WARNING: ${NAME[I]}.desc" \
"is 13 lines." \
"PACKAGER_TAG will" \
"not be added."
sleep 3
fi
fi
done
fi
echo '+ Locating source and patch files:'
# Zenwalk includes build scripts and related files in the packages.
# The indexes of non-URLs are stored to these variables:
NON_URL_SOURCES=
NON_URL_PATCHES=
# Download missing files and check MD5 and SHA1 sums:
for (( I=0; I < ${#SOURCE[@]}; I++ )); do
[ "${SOURCE[I]}" = "-" ] && continue
if ! _is_url "${SOURCE[I]}"; then
NON_URL_SOURCES="$NON_URL_SOURCES $I"
fi
_download "${SOURCE[I]}" "${SOURCE_MD5[I]}" \
"${SOURCE_SHA1[I]}"
SOURCE[I]=$RETURN_VALUE
done
for (( I=0; I < ${#PATCH[@]}; I++ )); do
[ "${PATCH[I]}" = "-" ] && continue
if ! _is_url "${PATCH[I]}"; then
NON_URL_PATCHES="$NON_URL_PATCHES $I"
fi
_download "${PATCH[I]}" "${PATCH_MD5[I]}" "${PATCH_SHA1[I]}"
PATCH[I]=$RETURN_VALUE
done
[ "$DOWNLOAD_ONLY" = "1" ] && exit 0
if [ "$(type -t build)" != "function" ]; then
echo '+ ERROR: build() function is not defined.'
exit 1
fi
# Initialize TMP and create temporary directory
TMP="$TMP/build-$NAME"
if [ "$CLEANUP" = 1 ]; then
echo "+ Creating an empty temporary directory: $TMP"
rm -rf "$TMP" || exit $?
elif [ -e "$TMP" -a -d "$TMP" ]; then
echo "+ Using existing temporary directory: $TMP"
else
echo "+ Creating a new temporary directory: $TMP"
fi
# "mkdir -m 0700" with GNU coreutils 5.2.1 isn't secure IIRC...
( umask 0077; mkdir -p "$TMP" ) || exit $?
# Set the prefixes:
_prefix
# Optimization flags:
_cflags "$OPTIMIZE"
# Create required PKG-directories:
for (( I=0; I < ${#NAME[@]}; I++ )); do
[ "${NAME[I]}" = "-" -o "${VERSION[I]}" = "-" ] && continue
# Having the version number in package-directory makes it
# possible to build packs like imagemagick-6.x.x_with_x11
# and imagemagick-6.x.x_no_x11 with the same script.
PKG[I]="$TMP/package-${NAME[I]}-${VERSION[I]}"
mkdir -p "${PKG[I]}/install"
done
unset I RETURN_VALUE
# Building starts always in the temporary directory:
echo '+ Starting the actual build process.'
cd "$TMP"
eval "$BUILD_COMMAND"
echo '+ The build process completed successfully.'
# Copy the non-URL files to the package if on Zenwalk:
if [ "$SCRIPTS_TO_PACKAGE" = "1" ]; then
K="$PKG/usr/src/$NAME-$VERSION"
echo "+ Copying build script and related files to $K:"
mkdir -p "$K"
# if [ "${_STANDALONE}" = "0" ]; then
# # Combine the .TukBuild and the tukbuild tool into
# # a standalone script.
# echo "+ $CWD/${_TUKBUILD} + ${_TUKBUILD_TOOL}"
# {
# printf '#!/bin/bash\n\n'
# cat "$CWD/${_TUKBUILD}"
# printf '\n\n### DO NOT EDIT BELOW THIS LINE! ###\n\n'
# cat "{_TUKBUILD_TOOL}"
# } > "$K/build-$NAME.sh"
# else
# # We are building using a standalone script, so
# # just copy it to the new package.
# echo "+ $CWD/${_TUKBUILD}"
# cat "$CWD/${_TUKBUILD}" > "$K/build-$NAME.sh"
# fi
# chmod 0755 "$K/build-$NAME.sh"
cat "${_TUKBUILD_TOOL}" > "$K/tukbuild"
# Yeah, .sh is very wrong suffix for 1) TukBuild files
# and 2) GNU bash scripts. But they want it this way.
cat "$CWD/${_TUKBUILD}" > "$K/build-$NAME.sh"
for (( I=0; I < ${#NAME[@]}; I++ )); do
for J in desc required conflicts suggests; do
if [ -f "$CWD/${NAME[I]}.$J" ]; then
echo "+ ${NAME[I]}.$J"
cp -a "$CWD/${NAME[I]}.$J" "$K"
fi
done
done
for I in $NON_URL_SOURCES; do
echo "+ ${SOURCE[I]##*/}"
cp -a "${SOURCE[I]}" "$K"
done
for I in $NON_URL_PATCHES; do
echo "+ ${PATCH[I]##*/}"
cp -a "${PATCH[I]}" "$K"
done
_chfix "$K"
fi
# No longer need to halt on error:
set +e
# Make the package(s):
for (( I=0; I < ${#NAME[@]}; I++ )); do
[ "${NAME[I]}" = "-" ] && continue
K="$NAMEPREFIX${NAME[I]}-${VERSION[I]//-/_}-${ARCH[I]}-$BUILD.$PKGFORMAT"
_makepkg "$I" "$PACKAGES/$K"
# Zenwalk uses separate .md5 file for every package.
# The MD5 sums are verified by netpkg.
if [ "$DISTRO" = "zenwalk" ]; then
( cd "$PACKAGES" && md5sum "$K" > "${K%.???}.md5" )
fi
done
_cleanup
echo "+ ${_TUKBUILD} built successfully."
) # Close the subshell
if [ $? != 0 ]; then
echo "+ ERROR: Build process returned an error. Build stopped."
exit 1;
fi
done
if [ "$DOWNLOAD_ONLY" = "1" ]; then
echo "+ All the requested packages successfully downloaded."
else
echo "+ All the requested packages successfully built."
fi
exit 0