• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright (C) 2009 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#      http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14#
15
16# A collection of shell function definitions used by various build scripts
17# in the Android NDK (Native Development Kit)
18#
19
20# Get current script name into PROGNAME
21PROGNAME=`basename $0`
22
23# Find the Android NDK root, assuming we are invoked from a script
24# within its directory structure.
25#
26# $1: Variable name that will receive the path
27# $2: Path of invoking script
28find_ndk_root ()
29{
30    # Try to auto-detect the NDK root by walking up the directory
31    # path to the current script.
32    local PROGDIR="`dirname \"$2\"`"
33    while [ -n "1" ] ; do
34        if [ -d "$PROGDIR/build/core" ] ; then
35            break
36        fi
37        if [ -z "$PROGDIR" -o "$PROGDIR" = '/' ] ; then
38            return 1
39        fi
40        PROGDIR="`cd \"$PROGDIR/..\" && pwd`"
41    done
42    eval $1="$PROGDIR"
43}
44
45# Put location of Android NDK into ANDROID_NDK_ROOT and
46# perform a tiny amount of sanity check
47#
48if [ -z "$ANDROID_NDK_ROOT" ] ; then
49    find_ndk_root ANDROID_NDK_ROOT "$0"
50    if [ $? != 0 ]; then
51        echo "Please define ANDROID_NDK_ROOT to point to the root of your"
52        echo "Android NDK installation."
53        exit 1
54    fi
55fi
56
57echo "$ANDROID_NDK_ROOT" | grep -q -e " "
58if [ $? = 0 ] ; then
59    echo "ERROR: The Android NDK installation path contains a space !"
60    echo "Please install to a different location."
61    exit 1
62fi
63
64if [ ! -d $ANDROID_NDK_ROOT ] ; then
65    echo "ERROR: Your ANDROID_NDK_ROOT variable does not point to a directory."
66    exit 1
67fi
68
69if [ ! -f $ANDROID_NDK_ROOT/build/tools/ndk-common.sh ] ; then
70    echo "ERROR: Your ANDROID_NDK_ROOT variable does not point to a valid directory."
71    exit 1
72fi
73
74## Logging support
75##
76VERBOSE=${VERBOSE-yes}
77VERBOSE2=${VERBOSE2-no}
78
79
80# If NDK_LOGFILE is defined in the environment, use this as the log file
81TMPLOG=
82if [ -n "$NDK_LOGFILE" ] ; then
83    mkdir -p `dirname "$NDK_LOGFILE"` && touch "$NDK_LOGFILE"
84    TMPLOG="$NDK_LOGFILE"
85fi
86
87# Setup a log file where all log() and log2() output will be sent
88#
89# $1: log file path  (optional)
90#
91setup_default_log_file ()
92{
93    if [ -n "$NDK_LOGFILE" ] ; then
94        return
95    fi
96    if [ -n "$1" ] ; then
97        NDK_LOGFILE="$1"
98    else
99        NDK_LOGFILE=/tmp/ndk-log-$$.txt
100    fi
101    export NDK_LOGFILE
102    TMPLOG="$NDK_LOGFILE"
103    rm -rf "$TMPLOG" && mkdir -p `dirname "$TMPLOG"` && touch "$TMPLOG"
104    echo "To follow build in another terminal, please use: tail -F $TMPLOG"
105}
106
107dump ()
108{
109    if [ -n "$TMPLOG" ] ; then
110        echo "$@" >> $TMPLOG
111    fi
112    echo "$@"
113}
114
115dump_n ()
116{
117    if [ -n "$TMPLOG" ] ; then
118        printf %s "$@" >> $TMPLOG
119    fi
120    printf %s "$@"
121}
122
123log ()
124{
125    if [ "$VERBOSE" = "yes" ] ; then
126        echo "$@"
127    else
128        if [ -n "$TMPLOG" ] ; then
129            echo "$@" >> $TMPLOG
130        fi
131    fi
132}
133
134log_n ()
135{
136    if [ "$VERBOSE" = "yes" ] ; then
137        printf %s "$@"
138    else
139        if [ -n "$TMPLOG" ] ; then
140            printf %s "$@" >> $TMPLOG
141        fi
142    fi
143}
144
145log2 ()
146{
147    if [ "$VERBOSE2" = "yes" ] ; then
148        echo "$@"
149    else
150        if [ -n "$TMPLOG" ] ; then
151            echo "$@" >> $TMPLOG
152        fi
153    fi
154}
155
156run ()
157{
158    if [ "$VERBOSE" = "yes" ] ; then
159        echo "## COMMAND: $@"
160        "$@" 2>&1
161    else
162        if [ -n "$TMPLOG" ] ; then
163            echo "## COMMAND: $@" >> $TMPLOG
164            "$@" >>$TMPLOG 2>&1
165        else
166            "$@" > /dev/null 2>&1
167        fi
168    fi
169}
170
171run2 ()
172{
173    if [ "$VERBOSE2" = "yes" ] ; then
174        echo "## COMMAND: $@"
175        "$@" 2>&1
176    elif [ "$VERBOSE" = "yes" ]; then
177        echo "## COMMAND: $@"
178        if [ -n "$TMPLOG" ]; then
179            echo "## COMMAND: $@" >> $TMPLOG
180            "$@" >>$TMPLOG 2>&1
181        else
182            "$@" > /dev/null 2>&1
183        fi
184    else
185        if [ -n "$TMPLOG" ]; then
186            "$@" >>$TMPLOG 2>&1
187        else
188            "$@" > /dev/null 2>&1
189        fi
190    fi
191}
192
193panic ()
194{
195    dump "ERROR: $@"
196    exit 1
197}
198
199fail_panic ()
200{
201    if [ $? != 0 ] ; then
202        dump "ERROR: $@"
203        exit 1
204    fi
205}
206
207fail_warning ()
208{
209    if [ $? != 0 ] ; then
210        dump "WARNING: $@"
211    fi
212}
213
214
215## Utilities
216##
217
218# Return the value of a given named variable
219# $1: variable name
220#
221# example:
222#    FOO=BAR
223#    BAR=ZOO
224#    echo `var_value $FOO`
225#    will print 'ZOO'
226#
227var_value ()
228{
229    # find a better way to do that ?
230    eval echo "$`echo $1`"
231}
232
233# convert to uppercase
234# assumes tr is installed on the platform ?
235#
236to_uppercase ()
237{
238    echo $1 | tr "[:lower:]" "[:upper:]"
239}
240
241## First, we need to detect the HOST CPU, because proper HOST_ARCH detection
242## requires platform-specific tricks.
243##
244HOST_EXE=""
245HOST_OS=`uname -s`
246case "$HOST_OS" in
247    Darwin)
248        HOST_OS=darwin
249        ;;
250    Linux)
251        # note that building  32-bit binaries on x86_64 is handled later
252        HOST_OS=linux
253        ;;
254    FreeBsd)  # note: this is not tested
255        HOST_OS=freebsd
256        ;;
257    CYGWIN*|*_NT-*)
258        HOST_OS=windows
259        HOST_EXE=.exe
260        if [ "x$OSTYPE" = xcygwin ] ; then
261            HOST_OS=cygwin
262        fi
263        ;;
264esac
265
266log2 "HOST_OS=$HOST_OS"
267log2 "HOST_EXE=$HOST_EXE"
268
269## Now find the host architecture. This must correspond to the bitness of
270## the binaries we're going to run with this NDK. Certain platforms allow
271## you to use a 64-bit kernel with a 32-bit userland, and unfortunately
272## commands like 'uname -m' only report the kernel bitness.
273##
274HOST_ARCH=`uname -m`
275case "$HOST_ARCH" in
276    i?86) HOST_ARCH=x86
277    # "uname -m" reports i386 on Snow Leopard even though its architecture is
278    # 64-bit. In order to use it to build 64-bit toolchains we need to fix the
279    # reporting anomoly here.
280    if [ "$HOST_OS" = darwin ] ; then
281        if ! echo __LP64__ | (CCOPTS= gcc -E - 2>/dev/null) | grep -q __LP64__ ; then
282        # or if gcc -dM -E - < /dev/null | grep -q __LP64__; then
283            HOST_ARCH=x86_64
284        fi
285    fi
286    ;;
287    amd64) HOST_ARCH=x86_64
288    ;;
289    powerpc) HOST_ARCH=ppc
290    ;;
291esac
292
293HOST_FILE_PROGRAM="file"
294case "$HOST_OS-$HOST_ARCH" in
295  linux-x86_64|darwin-x86_64)
296    ## On Linux or Darwin, a 64-bit kernel doesn't mean that the user-land
297    ## is always 32-bit, so use "file" to determine the bitness of the shell
298    ## that invoked us. The -L option is used to de-reference symlinks.
299    ##
300    ## Note that on Darwin, a single executable can contain both x86 and
301    ## x86_64 machine code, so just look for x86_64 (darwin) or x86-64 (Linux)
302    ## in the output.
303    ##
304    ## Also note that some versions of 'file' in MacPort may report erroneous
305    ## result.  See http://b.android.com/53769.  Use /usr/bin/file if exists.
306    if [ "$HOST_OS" = "darwin" ]; then
307        SYSTEM_FILE_PROGRAM="/usr/bin/file"
308        test -x "$SYSTEM_FILE_PROGRAM" && HOST_FILE_PROGRAM="$SYSTEM_FILE_PROGRAM"
309    fi
310    "$HOST_FILE_PROGRAM" -L "$SHELL" | grep -q "x86[_-]64"
311    if [ $? != 0 ]; then
312      # $SHELL is not a 64-bit executable, so assume our userland is too.
313      log2 "Detected 32-bit userland on 64-bit kernel system!"
314      HOST_ARCH=x86
315    fi
316    ;;
317esac
318
319log2 "HOST_ARCH=$HOST_ARCH"
320
321# at this point, the supported values for HOST_ARCH are:
322#   x86
323#   x86_64
324#   ppc
325#
326# other values may be possible but haven't been tested
327#
328# at this point, the value of HOST_OS should be one of the following:
329#   linux
330#   darwin
331#    windows (MSys)
332#    cygwin
333#
334# Note that cygwin is treated as a special case because it behaves very differently
335# for a few things. Other values may be possible but have not been tested
336#
337
338# define HOST_TAG as a unique tag used to identify both the host OS and CPU
339# supported values are:
340#
341#   linux-x86
342#   linux-x86_64
343#   darwin-x86
344#   darwin-x86_64
345#   darwin-ppc
346#   windows
347#   windows-x86_64
348#
349# other values are possible but were not tested.
350#
351compute_host_tag ()
352{
353    HOST_TAG=${HOST_OS}-${HOST_ARCH}
354    # Special case for windows-x86 => windows
355    case $HOST_TAG in
356        windows-x86|cygwin-x86)
357            HOST_TAG="windows"
358            ;;
359    esac
360    log2 "HOST_TAG=$HOST_TAG"
361}
362
363compute_host_tag
364
365# Compute the number of host CPU cores an HOST_NUM_CPUS
366#
367case "$HOST_OS" in
368    linux)
369        HOST_NUM_CPUS=`cat /proc/cpuinfo | grep processor | wc -l`
370        ;;
371    darwin|freebsd)
372        HOST_NUM_CPUS=`sysctl -n hw.ncpu`
373        ;;
374    windows|cygwin)
375        HOST_NUM_CPUS=$NUMBER_OF_PROCESSORS
376        ;;
377    *)  # let's play safe here
378        HOST_NUM_CPUS=1
379esac
380
381log2 "HOST_NUM_CPUS=$HOST_NUM_CPUS"
382
383# If BUILD_NUM_CPUS is not already defined in your environment,
384# define it as the double of HOST_NUM_CPUS. This is used to
385# run Make commands in parralles, as in 'make -j$BUILD_NUM_CPUS'
386#
387if [ -z "$BUILD_NUM_CPUS" ] ; then
388    BUILD_NUM_CPUS=`expr $HOST_NUM_CPUS \* 2`
389fi
390
391log2 "BUILD_NUM_CPUS=$BUILD_NUM_CPUS"
392
393
394##  HOST TOOLCHAIN SUPPORT
395##
396
397# force the generation of 32-bit binaries on 64-bit systems
398#
399FORCE_32BIT=no
400force_32bit_binaries ()
401{
402    if [ "$HOST_ARCH" = x86_64 ] ; then
403        log2 "Forcing generation of 32-bit host binaries on $HOST_ARCH"
404        FORCE_32BIT=yes
405        HOST_ARCH=x86
406        log2 "HOST_ARCH=$HOST_ARCH"
407        compute_host_tag
408    fi
409}
410
411# On Windows, cygwin binaries will be generated by default, but
412# you can force mingw ones that do not link to cygwin.dll if you
413# call this function.
414#
415disable_cygwin ()
416{
417    if [ $HOST_OS = cygwin ] ; then
418        log2 "Disabling cygwin binaries generation"
419        CFLAGS="$CFLAGS -mno-cygwin"
420        LDFLAGS="$LDFLAGS -mno-cygwin"
421        HOST_OS=windows
422        compute_host_tag
423    fi
424}
425
426# Various probes are going to need to run a small C program
427mkdir -p /tmp/ndk-$USER/tmp/tests
428
429TMPC=/tmp/ndk-$USER/tmp/tests/test-$$.c
430TMPO=/tmp/ndk-$USER/tmp/tests/test-$$.o
431TMPE=/tmp/ndk-$USER/tmp/tests/test-$$$EXE
432TMPL=/tmp/ndk-$USER/tmp/tests/test-$$.log
433
434# cleanup temporary files
435clean_temp ()
436{
437    rm -f $TMPC $TMPO $TMPL $TMPE
438}
439
440# cleanup temp files then exit with an error
441clean_exit ()
442{
443    clean_temp
444    exit 1
445}
446
447# this function will setup the compiler and linker and check that they work as advertised
448# note that you should call 'force_32bit_binaries' before this one if you want it to
449# generate 32-bit binaries on 64-bit systems (that support it).
450#
451setup_toolchain ()
452{
453    if [ -z "$CC" ] ; then
454        CC=gcc
455    fi
456    if [ -z "$CXX" ] ; then
457        CXX=g++
458    fi
459    if [ -z "$CXXFLAGS" ] ; then
460        CXXFLAGS="$CFLAGS"
461    fi
462    if [ -z "$LD" ] ; then
463        LD="$CC"
464    fi
465
466    log2 "Using '$CC' as the C compiler"
467
468    # check that we can compile a trivial C program with this compiler
469    mkdir -p $(dirname "$TMPC")
470    cat > $TMPC <<EOF
471int main(void) {}
472EOF
473
474    if [ "$FORCE_32BIT" = yes ] ; then
475        CC="$CC -m32"
476        CXX="$CXX -m32"
477        LD="$LD -m32"
478        compile
479        if [ $? != 0 ] ; then
480            # sometimes, we need to also tell the assembler to generate 32-bit binaries
481            # this is highly dependent on your GCC installation (and no, we can't set
482            # this flag all the time)
483            CFLAGS="$CFLAGS -Wa,--32"
484            compile
485        fi
486    fi
487
488    compile
489    if [ $? != 0 ] ; then
490        echo "your C compiler doesn't seem to work:"
491        cat $TMPL
492        clean_exit
493    fi
494    log "CC         : compiler check ok ($CC)"
495
496    # check that we can link the trivial program into an executable
497    link
498    if [ $? != 0 ] ; then
499        OLD_LD="$LD"
500        LD="$CC"
501        compile
502        link
503        if [ $? != 0 ] ; then
504            LD="$OLD_LD"
505            echo "your linker doesn't seem to work:"
506            cat $TMPL
507            clean_exit
508        fi
509    fi
510    log2 "Using '$LD' as the linker"
511    log "LD         : linker check ok ($LD)"
512
513    # check the C++ compiler
514    log2 "Using '$CXX' as the C++ compiler"
515
516    cat > $TMPC <<EOF
517#include <iostream>
518using namespace std;
519int main()
520{
521  cout << "Hello World!" << endl;
522  return 0;
523}
524EOF
525
526    compile_cpp
527    if [ $? != 0 ] ; then
528        echo "your C++ compiler doesn't seem to work"
529        cat $TMPL
530        clean_exit
531    fi
532
533    log "CXX        : C++ compiler check ok ($CXX)"
534
535    # XXX: TODO perform AR checks
536    AR=ar
537    ARFLAGS=
538}
539
540# try to compile the current source file in $TMPC into an object
541# stores the error log into $TMPL
542#
543compile ()
544{
545    log2 "Object     : $CC -o $TMPO -c $CFLAGS $TMPC"
546    $CC -o $TMPO -c $CFLAGS $TMPC 2> $TMPL
547}
548
549compile_cpp ()
550{
551    log2 "Object     : $CXX -o $TMPO -c $CXXFLAGS $TMPC"
552    $CXX -o $TMPO -c $CXXFLAGS $TMPC 2> $TMPL
553}
554
555# try to link the recently built file into an executable. error log in $TMPL
556#
557link()
558{
559    log2 "Link      : $LD -o $TMPE $TMPO $LDFLAGS"
560    $LD -o $TMPE $TMPO $LDFLAGS 2> $TMPL
561}
562
563# run a command
564#
565execute()
566{
567    log2 "Running: $*"
568    $*
569}
570
571# perform a simple compile / link / run of the source file in $TMPC
572compile_exec_run()
573{
574    log2 "RunExec    : $CC -o $TMPE $CFLAGS $TMPC"
575    compile
576    if [ $? != 0 ] ; then
577        echo "Failure to compile test program"
578        cat $TMPC
579        cat $TMPL
580        clean_exit
581    fi
582    link
583    if [ $? != 0 ] ; then
584        echo "Failure to link test program"
585        cat $TMPC
586        echo "------"
587        cat $TMPL
588        clean_exit
589    fi
590    $TMPE
591}
592
593pattern_match ()
594{
595    echo "$2" | grep -q -E -e "$1"
596}
597
598# Let's check that we have a working md5sum here
599check_md5sum ()
600{
601    A_MD5=`echo "A" | md5sum | cut -d' ' -f1`
602    if [ "$A_MD5" != "bf072e9119077b4e76437a93986787ef" ] ; then
603        echo "Please install md5sum on this machine"
604        exit 2
605    fi
606}
607
608# Find if a given shell program is available.
609# We need to take care of the fact that the 'which <foo>' command
610# may return either an empty string (Linux) or something like
611# "no <foo> in ..." (Darwin). Also, we need to redirect stderr
612# to /dev/null for Cygwin
613#
614# $1: variable name
615# $2: program name
616#
617# Result: set $1 to the full path of the corresponding command
618#         or to the empty/undefined string if not available
619#
620find_program ()
621{
622    local PROG RET
623    PROG=`which $2 2>/dev/null`
624    RET=$?
625    if [ $RET != 0 ]; then
626        PROG=
627    fi
628    eval $1=\"$PROG\"
629    return $RET
630}
631
632prepare_download ()
633{
634    find_program CMD_WGET wget
635    find_program CMD_CURL curl
636    find_program CMD_SCRP scp
637}
638
639find_pbzip2 ()
640{
641    if [ -z "$_PBZIP2_initialized" ] ; then
642        find_program PBZIP2 pbzip2
643        _PBZIP2_initialized="yes"
644    fi
645}
646
647# Download a file with either 'curl', 'wget' or 'scp'
648#
649# $1: source URL (e.g. http://foo.com, ssh://blah, /some/path)
650# $2: target file
651download_file ()
652{
653    # Is this HTTP, HTTPS or FTP ?
654    if pattern_match "^(http|https|ftp):.*" "$1"; then
655        if [ -n "$CMD_WGET" ] ; then
656            run $CMD_WGET -O $2 $1
657        elif [ -n "$CMD_CURL" ] ; then
658            run $CMD_CURL -o $2 $1
659        else
660            echo "Please install wget or curl on this machine"
661            exit 1
662        fi
663        return
664    fi
665
666    # Is this SSH ?
667    # Accept both ssh://<path> or <machine>:<path>
668    #
669    if pattern_match "^(ssh|[^:]+):.*" "$1"; then
670        if [ -n "$CMD_SCP" ] ; then
671            scp_src=`echo $1 | sed -e s%ssh://%%g`
672            run $CMD_SCP $scp_src $2
673        else
674            echo "Please install scp on this machine"
675            exit 1
676        fi
677        return
678    fi
679
680    # Is this a file copy ?
681    # Accept both file://<path> or /<path>
682    #
683    if pattern_match "^(file://|/).*" "$1"; then
684        cp_src=`echo $1 | sed -e s%^file://%%g`
685        run cp -f $cp_src $2
686        return
687    fi
688}
689
690# Form the relative path between from one abs path to another
691#
692# $1 : start path
693# $2 : end path
694#
695# From:
696# http://stackoverflow.com/questions/2564634/bash-convert-absolute-path-into-relative-path-given-a-current-directory
697relpath ()
698{
699    [ $# -ge 1 ] && [ $# -le 2 ] || return 1
700    current="${2:+"$1"}"
701    target="${2:-"$1"}"
702    [ "$target" != . ] || target=/
703    target="/${target##/}"
704    [ "$current" != . ] || current=/
705    current="${current:="/"}"
706    current="/${current##/}"
707    appendix="${target##/}"
708    relative=''
709    while appendix="${target#"$current"/}"
710        [ "$current" != '/' ] && [ "$appendix" = "$target" ]; do
711        if [ "$current" = "$appendix" ]; then
712            relative="${relative:-.}"
713            echo "${relative#/}"
714            return 0
715        fi
716        current="${current%/*}"
717        relative="$relative${relative:+/}.."
718    done
719    relative="$relative${relative:+${appendix:+/}}${appendix#/}"
720    echo "$relative"
721}
722
723# Unpack a given archive
724#
725# $1: archive file path
726# $2: optional target directory (current one if omitted)
727#
728unpack_archive ()
729{
730    local ARCHIVE="$1"
731    local DIR=${2-.}
732    local RESULT TARFLAGS ZIPFLAGS
733    mkdir -p "$DIR"
734    if [ "$VERBOSE2" = "yes" ] ; then
735        TARFLAGS="vxpf"
736        ZIPFLAGS=""
737    else
738        TARFLAGS="xpf"
739        ZIPFLAGS="q"
740    fi
741    case "$ARCHIVE" in
742        *.zip)
743            (cd $DIR && run unzip $ZIPFLAGS "$ARCHIVE")
744            ;;
745        *.tar)
746            run tar $TARFLAGS "$ARCHIVE" -C $DIR
747            ;;
748        *.tar.gz)
749            run tar z$TARFLAGS "$ARCHIVE" -C $DIR
750            ;;
751        *.tar.bz2)
752            find_pbzip2
753            if [ -n "$PBZIP2" ] ; then
754                run tar --use-compress-prog=pbzip2 -$TARFLAGS "$ARCHIVE" -C $DIR
755            else
756                run tar j$TARFLAGS "$ARCHIVE" -C $DIR
757            fi
758            # remove ._* files by MacOSX to preserve resource forks we don't need
759            find $DIR -name "\._*" -exec rm {} \;
760            ;;
761        *)
762            panic "Cannot unpack archive with unknown extension: $ARCHIVE"
763            ;;
764    esac
765}
766
767# Pack a given archive
768#
769# $1: archive file path (including extension)
770# $2: source directory for archive content
771# $3+: list of files (including patterns), all if empty
772pack_archive ()
773{
774    local ARCHIVE="$1"
775    local SRCDIR="$2"
776    local SRCFILES
777    local TARFLAGS ZIPFLAGS
778    shift; shift;
779    if [ -z "$1" ] ; then
780        SRCFILES="*"
781    else
782        SRCFILES="$@"
783    fi
784    if [ "`basename $ARCHIVE`" = "$ARCHIVE" ] ; then
785        ARCHIVE="`pwd`/$ARCHIVE"
786    fi
787    mkdir -p `dirname $ARCHIVE`
788    if [ "$VERBOSE2" = "yes" ] ; then
789        TARFLAGS="vcf"
790        ZIPFLAGS="-9r"
791    else
792        TARFLAGS="cf"
793        ZIPFLAGS="-9qr"
794    fi
795    # Ensure symlinks are stored as is in zip files. for toolchains
796    # this can save up to 7 MB in the size of the final archive
797    #ZIPFLAGS="$ZIPFLAGS --symlinks"
798    case "$ARCHIVE" in
799        *.zip)
800            (cd $SRCDIR && run zip $ZIPFLAGS "$ARCHIVE" $SRCFILES)
801            ;;
802        *.tar)
803            (cd $SRCDIR && run tar $TARFLAGS "$ARCHIVE" $SRCFILES)
804            ;;
805        *.tar.gz)
806            (cd $SRCDIR && run tar z$TARFLAGS "$ARCHIVE" $SRCFILES)
807            ;;
808        *.tar.bz2)
809            find_pbzip2
810            if [ -n "$PBZIP2" ] ; then
811                (cd $SRCDIR && run tar --use-compress-prog=pbzip2 -$TARFLAGS "$ARCHIVE" $SRCFILES)
812            else
813                (cd $SRCDIR && run tar j$TARFLAGS "$ARCHIVE" $SRCFILES)
814            fi
815            ;;
816        *)
817            panic "Unsupported archive format: $ARCHIVE"
818            ;;
819    esac
820}
821
822# Copy a directory, create target location if needed
823#
824# $1: source directory
825# $2: target directory location
826#
827copy_directory ()
828{
829    local SRCDIR="$1"
830    local DSTDIR="$2"
831    if [ ! -d "$SRCDIR" ] ; then
832        panic "Can't copy from non-directory: $SRCDIR"
833    fi
834    log "Copying directory: "
835    log "  from $SRCDIR"
836    log "  to $DSTDIR"
837    mkdir -p "$DSTDIR" && (cd "$SRCDIR" && 2>/dev/null tar cf - *) | (tar xf - -C "$DSTDIR")
838    fail_panic "Cannot copy to directory: $DSTDIR"
839}
840
841# Move a directory, create target location if needed
842#
843# $1: source directory
844# $2: target directory location
845#
846move_directory ()
847{
848    local SRCDIR="$1"
849    local DSTDIR="$2"
850    if [ ! -d "$SRCDIR" ] ; then
851        panic "Can't move from non-directory: $SRCDIR"
852    fi
853    log "Move directory: "
854    log "  from $SRCDIR"
855    log "  to $DSTDIR"
856    mkdir -p "$DSTDIR" && (mv "$SRCDIR"/* "$DSTDIR")
857    fail_panic "Cannot move to directory: $DSTDIR"
858}
859
860# This is the same than copy_directory(), but symlinks will be replaced
861# by the file they actually point to instead.
862copy_directory_nolinks ()
863{
864    local SRCDIR="$1"
865    local DSTDIR="$2"
866    if [ ! -d "$SRCDIR" ] ; then
867        panic "Can't copy from non-directory: $SRCDIR"
868    fi
869    log "Copying directory (without symlinks): "
870    log "  from $SRCDIR"
871    log "  to $DSTDIR"
872    mkdir -p "$DSTDIR" && (cd "$SRCDIR" && tar chf - *) | (tar xf - -C "$DSTDIR")
873    fail_panic "Cannot copy to directory: $DSTDIR"
874}
875
876# Copy certain files from one directory to another one
877# $1: source directory
878# $2: target directory
879# $3+: file list (including patterns)
880copy_file_list ()
881{
882    local SRCDIR="$1"
883    local DSTDIR="$2"
884    shift; shift;
885    if [ ! -d "$SRCDIR" ] ; then
886        panic "Cant' copy from non-directory: $SRCDIR"
887    fi
888    log "Copying file: $@"
889    log "  from $SRCDIR"
890    log "  to $DSTDIR"
891    mkdir -p "$DSTDIR" && (cd "$SRCDIR" && (echo $@ | tr ' ' '\n' | tar cf - -T -)) | (tar xf - -C "$DSTDIR")
892    fail_panic "Cannot copy files to directory: $DSTDIR"
893}
894
895# Rotate a log file
896# If the given log file exist, add a -1 to the end of the file.
897# If older log files exist, rename them to -<n+1>
898# $1: log file
899# $2: maximum version to retain [optional]
900rotate_log ()
901{
902    # Default Maximum versions to retain
903    local MAXVER="5"
904    local LOGFILE="$1"
905    shift;
906    if [ ! -z "$1" ] ; then
907        local tmpmax="$1"
908        shift;
909        tmpmax=`expr $tmpmax + 0`
910        if [ $tmpmax -lt 1 ] ; then
911            panic "Invalid maximum log file versions '$tmpmax' invalid; defaulting to $MAXVER"
912        else
913            MAXVER=$tmpmax;
914        fi
915    fi
916
917    # Do Nothing if the log file does not exist
918    if [ ! -f "${LOGFILE}" ] ; then
919        return
920    fi
921
922    # Rename existing older versions
923    ver=$MAXVER
924    while [ $ver -ge 1 ]
925    do
926        local prev=$(( $ver - 1 ))
927        local old="-$prev"
928
929        # Instead of old version 0; use the original filename
930        if [ $ver -eq 1 ] ; then
931            old=""
932        fi
933
934        if [ -f "${LOGFILE}${old}" ] ; then
935            mv -f "${LOGFILE}${old}" "${LOGFILE}-${ver}"
936        fi
937
938        ver=$prev
939    done
940}
941