Home | History | Annotate | Download | only in tools
      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/tools/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 dump_n ()
    116 {
    117     if [ -n "$TMPLOG" ] ; then
    118         printf %s "$@" >> $TMPLOG
    119     fi
    120     printf %s "$@"
    121 }
    122 
    123 log ()
    124 {
    125     if [ "$VERBOSE" = "yes" ] ; then
    126         echo "$@"
    127     else
    128         if [ -n "$TMPLOG" ] ; then
    129             echo "$@" >> $TMPLOG
    130         fi
    131     fi
    132 }
    133 
    134 log_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 
    145 log2 ()
    146 {
    147     if [ "$VERBOSE2" = "yes" ] ; then
    148         echo "$@"
    149     else
    150         if [ -n "$TMPLOG" ] ; then
    151             echo "$@" >> $TMPLOG
    152         fi
    153     fi
    154 }
    155 
    156 run ()
    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 
    171 run2 ()
    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 
    193 panic ()
    194 {
    195     dump "ERROR: $@"
    196     exit 1
    197 }
    198 
    199 fail_panic ()
    200 {
    201     if [ $? != 0 ] ; then
    202         dump "ERROR: $@"
    203         exit 1
    204     fi
    205 }
    206 
    207 fail_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 #
    227 var_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 #
    236 to_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 ##
    244 HOST_EXE=""
    245 HOST_OS=`uname -s`
    246 case "$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         ;;
    264 esac
    265 
    266 log2 "HOST_OS=$HOST_OS"
    267 log2 "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 ##
    274 HOST_ARCH=`uname -m`
    275 case "$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     ;;
    291 esac
    292 
    293 HOST_FILE_PROGRAM="file"
    294 case "$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     ;;
    317 esac
    318 
    319 log2 "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 #
    351 compute_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 
    363 compute_host_tag
    364 
    365 # Compute the number of host CPU cores an HOST_NUM_CPUS
    366 #
    367 case "$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
    379 esac
    380 
    381 log2 "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 #
    387 if [ -z "$BUILD_NUM_CPUS" ] ; then
    388     BUILD_NUM_CPUS=`expr $HOST_NUM_CPUS \* 2`
    389 fi
    390 
    391 log2 "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 #
    399 FORCE_32BIT=no
    400 force_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 #
    415 disable_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
    427 mkdir -p /tmp/ndk-$USER/tmp/tests
    428 
    429 TMPC=/tmp/ndk-$USER/tmp/tests/test-$$.c
    430 TMPO=/tmp/ndk-$USER/tmp/tests/test-$$.o
    431 TMPE=/tmp/ndk-$USER/tmp/tests/test-$$$EXE
    432 TMPL=/tmp/ndk-$USER/tmp/tests/test-$$.log
    433 
    434 # cleanup temporary files
    435 clean_temp ()
    436 {
    437     rm -f $TMPC $TMPO $TMPL $TMPE
    438 }
    439 
    440 # cleanup temp files then exit with an error
    441 clean_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 #
    451 setup_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
    471 int main(void) {}
    472 EOF
    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>
    518 using namespace std;
    519 int main()
    520 {
    521   cout << "Hello World!" << endl;
    522   return 0;
    523 }
    524 EOF
    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 #
    543 compile ()
    544 {
    545     log2 "Object     : $CC -o $TMPO -c $CFLAGS $TMPC"
    546     $CC -o $TMPO -c $CFLAGS $TMPC 2> $TMPL
    547 }
    548 
    549 compile_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 #
    557 link()
    558 {
    559     log2 "Link      : $LD -o $TMPE $TMPO $LDFLAGS"
    560     $LD -o $TMPE $TMPO $LDFLAGS 2> $TMPL
    561 }
    562 
    563 # run a command
    564 #
    565 execute()
    566 {
    567     log2 "Running: $*"
    568     $*
    569 }
    570 
    571 # perform a simple compile / link / run of the source file in $TMPC
    572 compile_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 
    593 pattern_match ()
    594 {
    595     echo "$2" | grep -q -E -e "$1"
    596 }
    597 
    598 # Let's check that we have a working md5sum here
    599 check_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 #
    620 find_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 
    632 prepare_download ()
    633 {
    634     find_program CMD_WGET wget
    635     find_program CMD_CURL curl
    636     find_program CMD_SCRP scp
    637 }
    638 
    639 find_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
    651 download_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
    697 relpath ()
    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 #
    728 unpack_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
    772 pack_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 #
    827 copy_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 #
    846 move_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.
    862 copy_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)
    880 copy_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]
    900 rotate_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