#!/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.*$#\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