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