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 if [ -z "$TMPDIR" ]; then
     24     export TMPDIR=/tmp/ndk-$USER
     25 fi
     26 
     27 OS=`uname -s`
     28 if [ "$OS" == "Darwin" -a -z "$MACOSX_DEPLOYMENT_TARGET" ]; then
     29     export MACOSX_DEPLOYMENT_TARGET="10.8"
     30 fi
     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
     37 find_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 #
     57 if [ -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
     64 fi
     65 
     66 echo "$ANDROID_NDK_ROOT" | grep -q -e " "
     67 if [ $? = 0 ] ; then
     68     echo "ERROR: The Android NDK installation path contains a space !"
     69     echo "Please install to a different location."
     70     exit 1
     71 fi
     72 
     73 if [ ! -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
     77 fi
     78 
     79 if [ ! -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
     83 fi
     84 
     85 ## Use DRYRUN to find out top-level commands.
     86 DRYRUN=${DRYRUN-no}
     87 
     88 ## Logging support
     89 ##
     90 VERBOSE=${VERBOSE-yes}
     91 
     92 
     93 # If NDK_LOGFILE is defined in the environment, use this as the log file
     94 TMPLOG=
     95 if [ -n "$NDK_LOGFILE" ] ; then
     96     mkdir -p `dirname "$NDK_LOGFILE"` && touch "$NDK_LOGFILE"
     97     TMPLOG="$NDK_LOGFILE"
     98 fi
     99 
    100 # Setup a log file where all log() output will be sent
    101 #
    102 # $1: log file path  (optional)
    103 #
    104 setup_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 
    120 dump ()
    121 {
    122     if [ -n "$TMPLOG" ] ; then
    123         echo "$@" >> $TMPLOG
    124     fi
    125     echo "$@"
    126 }
    127 
    128 dump_n ()
    129 {
    130     if [ -n "$TMPLOG" ] ; then
    131         printf %s "$@" >> $TMPLOG
    132     fi
    133     printf %s "$@"
    134 }
    135 
    136 log ()
    137 {
    138     if [ "$VERBOSE" = "yes" ] ; then
    139         echo "$@"
    140     else
    141         if [ -n "$TMPLOG" ] ; then
    142             echo "$@" >> $TMPLOG
    143         fi
    144     fi
    145 }
    146 
    147 log_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 
    158 run ()
    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 
    175 panic ()
    176 {
    177     dump "ERROR: $@"
    178     exit 1
    179 }
    180 
    181 fail_panic ()
    182 {
    183     if [ $? != 0 ] ; then
    184         dump "ERROR: $@"
    185         exit 1
    186     fi
    187 }
    188 
    189 fail_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 #
    209 var_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 #
    218 to_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 ##
    226 HOST_EXE=""
    227 HOST_OS=`uname -s`
    228 case "$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         ;;
    246 esac
    247 
    248 log "HOST_OS=$HOST_OS"
    249 log "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 ##
    256 HOST_ARCH=`uname -m`
    257 case "$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     ;;
    273 esac
    274 
    275 HOST_FILE_PROGRAM="file"
    276 case "$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     ;;
    299 esac
    300 
    301 log "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 #
    333 compute_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 
    345 compute_host_tag
    346 
    347 # Compute the number of host CPU cores an HOST_NUM_CPUS
    348 #
    349 case "$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
    361 esac
    362 
    363 log "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 #
    369 if [ -z "$BUILD_NUM_CPUS" ] ; then
    370     BUILD_NUM_CPUS=`expr $HOST_NUM_CPUS \* 2`
    371 fi
    372 
    373 log "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 #
    381 FORCE_32BIT=no
    382 force_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 #
    397 disable_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
    409 mkdir -p $TMPDIR/tmp/tests
    410 
    411 TMPC=$TMPDIR/tmp/tests/test-$$.c
    412 TMPO=$TMPDIR/tmp/tests/test-$$.o
    413 TMPE=$TMPDIR/tmp/tests/test-$$$EXE
    414 TMPL=$TMPDIR/tmp/tests/test-$$.log
    415 
    416 # cleanup temporary files
    417 clean_temp ()
    418 {
    419     rm -f $TMPC $TMPO $TMPL $TMPE
    420 }
    421 
    422 # cleanup temp files then exit with an error
    423 clean_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 #
    433 setup_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
    453 int main(void) {}
    454 EOF
    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>
    500 using namespace std;
    501 int main()
    502 {
    503   cout << "Hello World!" << endl;
    504   return 0;
    505 }
    506 EOF
    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 #
    525 compile ()
    526 {
    527     log "Object     : $CC -o $TMPO -c $CFLAGS $TMPC"
    528     $CC -o $TMPO -c $CFLAGS $TMPC 2> $TMPL
    529 }
    530 
    531 compile_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 #
    539 link()
    540 {
    541     log "Link      : $LD -o $TMPE $TMPO $LDFLAGS"
    542     $LD -o $TMPE $TMPO $LDFLAGS 2> $TMPL
    543 }
    544 
    545 # run a command
    546 #
    547 execute()
    548 {
    549     log "Running: $*"
    550     $*
    551 }
    552 
    553 # perform a simple compile / link / run of the source file in $TMPC
    554 compile_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 
    575 pattern_match ()
    576 {
    577     echo "$2" | grep -q -E -e "$1"
    578 }
    579 
    580 # Let's check that we have a working md5sum here
    581 check_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 #
    602 find_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 
    614 prepare_download ()
    615 {
    616     find_program CMD_WGET wget
    617     find_program CMD_CURL curl
    618     find_program CMD_SCRP scp
    619 }
    620 
    621 find_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
    633 download_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
    679 relpath ()
    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
    710 pack_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 #
    756 copy_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 #
    775 move_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.
    791 copy_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)
    809 copy_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]
    829 rotate_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
    873 dereference_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