• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/core/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## Logging support
75##
76VERBOSE=${VERBOSE-yes}
77VERBOSE2=${VERBOSE2-no}
78
79
80# If NDK_LOGFILE is defined in the environment, use this as the log file
81TMPLOG=
82if [ -n "$NDK_LOGFILE" ] ; then
83    mkdir -p `dirname "$NDK_LOGFILE"` && touch "$NDK_LOGFILE"
84    TMPLOG="$NDK_LOGFILE"
85fi
86
87# Setup a log file where all log() and log2() output will be sent
88#
89# $1: log file path  (optional)
90#
91setup_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
107dump ()
108{
109    if [ -n "$TMPLOG" ] ; then
110        echo "$@" >> $TMPLOG
111    fi
112    echo "$@"
113}
114
115log ()
116{
117    if [ "$VERBOSE" = "yes" ] ; then
118        echo "$@"
119    else
120        if [ -n "$TMPLOG" ] ; then
121            echo "$@" >> $TMPLOG
122        fi
123    fi
124}
125
126log2 ()
127{
128    if [ "$VERBOSE2" = "yes" ] ; then
129        echo "$@"
130    else
131        if [ -n "$TMPLOG" ] ; then
132            echo "$@" >> $TMPLOG
133        fi
134    fi
135}
136
137run ()
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
152panic ()
153{
154    dump "ERROR: $@"
155    exit 1
156}
157
158fail_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#
179var_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#
188to_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##
196HOST_EXE=""
197HOST_OS=`uname -s`
198case "$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        ;;
216esac
217
218log2 "HOST_OS=$HOST_OS"
219log2 "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##
226HOST_ARCH=`uname -m`
227case "$HOST_ARCH" in
228    i?86) HOST_ARCH=x86
229    ;;
230    amd64) HOST_ARCH=x86_64
231    ;;
232    powerpc) HOST_ARCH=ppc
233    ;;
234esac
235
236case "$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    ;;
253esac
254
255log2 "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#
287compute_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
299compute_host_tag
300
301# Compute the number of host CPU cores an HOST_NUM_CPUS
302#
303case "$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
315esac
316
317log2 "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#
323if [ -z "$BUILD_NUM_CPUS" ] ; then
324    BUILD_NUM_CPUS=`expr $HOST_NUM_CPUS \* 2`
325fi
326
327log2 "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#
335FORCE_32BIT=no
336force_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#
351disable_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
363mkdir -p /tmp/ndk-$USER/tmp/tests
364
365TMPC=/tmp/ndk-$USER/tmp/tests/test-$$.c
366TMPO=/tmp/ndk-$USER/tmp/tests/test-$$.o
367TMPE=/tmp/ndk-$USER/tmp/tests/test-$$$EXE
368TMPL=/tmp/ndk-$USER/tmp/tests/test-$$.log
369
370# cleanup temporary files
371clean_temp ()
372{
373    rm -f $TMPC $TMPO $TMPL $TMPE
374}
375
376# cleanup temp files then exit with an error
377clean_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#
387setup_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
407int main(void) {}
408EOF
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>
454using namespace std;
455int main()
456{
457  cout << "Hello World!" << endl;
458  return 0;
459}
460EOF
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#
479compile ()
480{
481    log2 "Object     : $CC -o $TMPO -c $CFLAGS $TMPC"
482    $CC -o $TMPO -c $CFLAGS $TMPC 2> $TMPL
483}
484
485compile_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#
493link()
494{
495    log2 "Link      : $LD -o $TMPE $TMPO $LDFLAGS"
496    $LD -o $TMPE $TMPO $LDFLAGS 2> $TMPL
497}
498
499# run a command
500#
501execute()
502{
503    log2 "Running: $*"
504    $*
505}
506
507# perform a simple compile / link / run of the source file in $TMPC
508compile_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
529pattern_match ()
530{
531    echo "$2" | grep -q -E -e "$1"
532}
533
534# Let's check that we have a working md5sum here
535check_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#
556find_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
568prepare_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
579download_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#
624unpack_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
661pack_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#
708copy_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.
724copy_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)
742copy_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]
762rotate_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