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