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