Home | History | Annotate | Download | only in core
      1 # Copyright (C) 2009 The Android Open Source Project
      2 #
      3 # Licensed under the Apache License, Version 2.0 (the "License");
      4 # you may not use this file except in compliance with the License.
      5 # You may obtain a copy of the License at
      6 #
      7 #      http://www.apache.org/licenses/LICENSE-2.0
      8 #
      9 # Unless required by applicable law or agreed to in writing, software
     10 # distributed under the License is distributed on an "AS IS" BASIS,
     11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 # See the License for the specific language governing permissions and
     13 # limitations under the License.
     14 #
     15 
     16 # A collection of shell function definitions used by various build scripts
     17 # in the Android NDK (Native Development Kit)
     18 #
     19 
     20 # Get current script name into PROGNAME
     21 PROGNAME=`basename $0`
     22 
     23 # Put location of Android NDK into ANDROID_NDK_ROOT and
     24 # perform a tiny amount of sanity check
     25 #
     26 if [ -z "$ANDROID_NDK_ROOT" ] ; then
     27     # Try to auto-detect the NDK root by walking up the directory
     28     # path to the current script.
     29     PROGDIR=`dirname $0`
     30     while [ -n "1" ] ; do
     31         if [ -d $PROGDIR/build/core ] ; then
     32             break
     33         fi
     34         if [ -z $PROGDIR -o $PROGDIR = '.' ] ; then
     35             echo "Please define ANDROID_NDK_ROOT to point to the root of your"
     36             echo "Android NDK installation."
     37             exit 1
     38         fi
     39         PROGDIR=`dirname $PROGDIR`
     40     done
     41     ANDROID_NDK_ROOT=`cd $PROGDIR && pwd`
     42 fi
     43 
     44 echo "$ANDROID_NDK_ROOT" | grep -q -e " "
     45 if [ $? = 0 ] ; then
     46     echo "ERROR: The Android NDK installation path contains a space !"
     47     echo "Please install to a different location."
     48     exit 1
     49 fi
     50 
     51 if [ ! -d $ANDROID_NDK_ROOT ] ; then
     52     echo "ERROR: Your ANDROID_NDK_ROOT variable does not point to a directory."
     53     exit 1
     54 fi
     55 
     56 if [ ! -f $ANDROID_NDK_ROOT/build/core/ndk-common.sh ] ; then
     57     echo "ERROR: Your ANDROID_NDK_ROOT variable does not point to a valid directory."
     58     exit 1
     59 fi
     60 
     61 ## Logging support
     62 ##
     63 VERBOSE=${VERBOSE-yes}
     64 VERBOSE2=${VERBOSE2-no}
     65 
     66 
     67 # If NDK_LOGFILE is defined in the environment, use this as the log file
     68 TMPLOG=
     69 if [ -n "$NDK_LOGFILE" ] ; then
     70     mkdir -p `dirname "$NDK_LOGFILE"` && touch "$NDK_LOGFILE"
     71     TMPLOG="$NDK_LOGFILE"
     72 fi
     73 
     74 # Setup a log file where all log() and log2() output will be sent
     75 #
     76 # $1: log file path  (optional)
     77 #
     78 setup_default_log_file ()
     79 {
     80     if [ -n "$NDK_LOGFILE" ] ; then
     81         return
     82     fi
     83     if [ -n "$1" ] ; then
     84         NDK_LOGFILE="$1"
     85     else
     86         NDK_LOGFILE=/tmp/ndk-log-$$.txt
     87     fi
     88     export NDK_LOGFILE
     89     TMPLOG="$NDK_LOGFILE"
     90     rm -rf "$TMPLOG" && mkdir -p `dirname "$TMPLOG"` && touch "$TMPLOG"
     91     echo "To follow build in another terminal, please use: tail -F $TMPLOG"
     92 }
     93 
     94 dump ()
     95 {
     96     if [ -n "$TMPLOG" ] ; then
     97         echo "$@" >> $TMPLOG
     98     fi
     99     echo "$@"
    100 }
    101 
    102 log ()
    103 {
    104     if [ "$VERBOSE" = "yes" ] ; then
    105         echo "$@"
    106     else
    107         if [ -n "$TMPLOG" ] ; then
    108             echo "$@" >> $TMPLOG
    109         fi
    110     fi
    111 }
    112 
    113 log2 ()
    114 {
    115     if [ "$VERBOSE2" = "yes" ] ; then
    116         echo "$@"
    117     else
    118         if [ -n "$TMPLOG" ] ; then
    119             echo "$@" >> $TMPLOG
    120         fi
    121     fi
    122 }
    123 
    124 run ()
    125 {
    126     if [ "$VERBOSE" = "yes" ] ; then
    127         echo "## COMMAND: $@"
    128         $@ 2>&1
    129     else
    130         if [ -n "$TMPLOG" ] ; then
    131             echo "## COMMAND: $@" >> $TMPLOG
    132             $@ >>$TMPLOG 2>&1
    133         else
    134             $@ > /dev/null 2>&1
    135         fi
    136     fi
    137 }
    138 
    139 panic ()
    140 {
    141     dump "ERROR: $@"
    142     exit 1
    143 }
    144 
    145 fail_panic ()
    146 {
    147     if [ $? != 0 ] ; then
    148         dump "ERROR: $@"
    149         exit 1
    150     fi
    151 }
    152 
    153 
    154 ## Utilities
    155 ##
    156 
    157 # Return the value of a given named variable
    158 # $1: variable name
    159 #
    160 # example:
    161 #    FOO=BAR
    162 #    BAR=ZOO
    163 #    echo `var_value $FOO`
    164 #    will print 'ZOO'
    165 #
    166 var_value ()
    167 {
    168     # find a better way to do that ?
    169     eval echo "$`echo $1`"
    170 }
    171 
    172 # convert to uppercase
    173 # assumes tr is installed on the platform ?
    174 #
    175 to_uppercase ()
    176 {
    177     echo $1 | tr "[:lower:]" "[:upper:]"
    178 }
    179 
    180 ## Normalize OS and CPU
    181 ##
    182 HOST_ARCH=`uname -m`
    183 case "$HOST_ARCH" in
    184     i?86) HOST_ARCH=x86
    185     ;;
    186     amd64) HOST_ARCH=x86_64
    187     ;;
    188     powerpc) HOST_ARCH=ppc
    189     ;;
    190 esac
    191 
    192 log2 "HOST_ARCH=$HOST_ARCH"
    193 
    194 # at this point, the supported values for CPU are:
    195 #   x86
    196 #   x86_64
    197 #   ppc
    198 #
    199 # other values may be possible but haven't been tested
    200 #
    201 HOST_EXE=""
    202 HOST_OS=`uname -s`
    203 case "$HOST_OS" in
    204     Darwin)
    205         HOST_OS=darwin
    206         ;;
    207     Linux)
    208         # note that building  32-bit binaries on x86_64 is handled later
    209         HOST_OS=linux
    210         ;;
    211     FreeBsd)  # note: this is not tested
    212         HOST_OS=freebsd
    213         ;;
    214     CYGWIN*|*_NT-*)
    215         HOST_OS=windows
    216         HOST_EXE=.exe
    217         if [ "x$OSTYPE" = xcygwin ] ; then
    218             HOST_OS=cygwin
    219         fi
    220         ;;
    221 esac
    222 
    223 log2 "HOST_OS=$HOST_OS"
    224 log2 "HOST_EXE=$HOST_EXE"
    225 
    226 # at this point, the value of HOST_OS should be one of the following:
    227 #   linux
    228 #   darwin
    229 #    windows (MSys)
    230 #    cygwin
    231 #
    232 # Note that cygwin is treated as a special case because it behaves very differently
    233 # for a few things. Other values may be possible but have not been tested
    234 #
    235 
    236 # define HOST_TAG as a unique tag used to identify both the host OS and CPU
    237 # supported values are:
    238 #
    239 #   linux-x86
    240 #   linux-x86_64
    241 #   darwin-x86
    242 #   darwin-ppc
    243 #   windows
    244 #
    245 # other values are possible but were not tested.
    246 #
    247 compute_host_tag ()
    248 {
    249     case "$HOST_OS" in
    250         windows|cygwin)
    251             HOST_TAG="windows"
    252             ;;
    253         *)  HOST_TAG="${HOST_OS}-${HOST_ARCH}"
    254     esac
    255     log2 "HOST_TAG=$HOST_TAG"
    256 }
    257 
    258 compute_host_tag
    259 
    260 # Compute the number of host CPU cores an HOST_NUM_CPUS
    261 #
    262 case "$HOST_OS" in
    263     linux)
    264         HOST_NUM_CPUS=`cat /proc/cpuinfo | grep processor | wc -l`
    265         ;;
    266     darwin|freebsd)
    267         HOST_NUM_CPUS=`sysctl -n hw.ncpu`
    268         ;;
    269     windows|cygwin)
    270         HOST_NUM_CPUS=$NUMBER_OF_PROCESSORS
    271         ;;
    272     *)  # let's play safe here
    273         HOST_NUM_CPUS=1
    274 esac
    275 
    276 log2 "HOST_NUM_CPUS=$HOST_NUM_CPUS"
    277 
    278 # If BUILD_NUM_CPUS is not already defined in your environment,
    279 # define it as the double of HOST_NUM_CPUS. This is used to
    280 # run Make commands in parralles, as in 'make -j$BUILD_NUM_CPUS'
    281 #
    282 if [ -z "$BUILD_NUM_CPUS" ] ; then
    283     BUILD_NUM_CPUS=`expr $HOST_NUM_CPUS \* 2`
    284 fi
    285 
    286 log2 "BUILD_NUM_CPUS=$BUILD_NUM_CPUS"
    287 
    288 
    289 ##  HOST TOOLCHAIN SUPPORT
    290 ##
    291 
    292 # force the generation of 32-bit binaries on 64-bit systems
    293 #
    294 FORCE_32BIT=no
    295 force_32bit_binaries ()
    296 {
    297     if [ "$HOST_ARCH" = x86_64 ] ; then
    298         log2 "Forcing generation of 32-bit host binaries on $HOST_ARCH"
    299         FORCE_32BIT=yes
    300         HOST_ARCH=x86
    301         log2 "HOST_ARCH=$HOST_ARCH"
    302         compute_host_tag
    303     fi
    304 }
    305 
    306 # On Windows, cygwin binaries will be generated by default, but
    307 # you can force mingw ones that do not link to cygwin.dll if you
    308 # call this function.
    309 #
    310 disable_cygwin ()
    311 {
    312     if [ $OS = cygwin ] ; then
    313         log2 "Disabling cygwin binaries generation"
    314         CFLAGS="$CFLAGS -mno-cygwin"
    315         LDFLAGS="$LDFLAGS -mno-cygwin"
    316         OS=windows
    317         HOST_OS=windows
    318         compute_host_tag
    319     fi
    320 }
    321 
    322 # Various probes are going to need to run a small C program
    323 TMPC=/tmp/android-$$-test.c
    324 TMPO=/tmp/android-$$-test.o
    325 TMPE=/tmp/android-$$-test$EXE
    326 TMPL=/tmp/android-$$-test.log
    327 
    328 # cleanup temporary files
    329 clean_temp ()
    330 {
    331     rm -f $TMPC $TMPO $TMPL $TMPE
    332 }
    333 
    334 # cleanup temp files then exit with an error
    335 clean_exit ()
    336 {
    337     clean_temp
    338     exit 1
    339 }
    340 
    341 # this function will setup the compiler and linker and check that they work as advertised
    342 # note that you should call 'force_32bit_binaries' before this one if you want it to
    343 # generate 32-bit binaries on 64-bit systems (that support it).
    344 #
    345 setup_toolchain ()
    346 {
    347     if [ -z "$CC" ] ; then
    348         CC=gcc
    349     fi
    350     if [ -z "$CXX" ] ; then
    351         CXX=g++
    352     fi
    353     if [ -z "$CXXFLAGS" ] ; then
    354         CXXFLAGS="$CFLAGS"
    355     fi
    356     if [ -z "$LD" ] ; then
    357         LD="$CC"
    358     fi
    359 
    360     log2 "Using '$CC' as the C compiler"
    361 
    362     # check that we can compile a trivial C program with this compiler
    363     cat > $TMPC <<EOF
    364 int main(void) {}
    365 EOF
    366 
    367     if [ "$FORCE_32BIT" = yes ] ; then
    368         CC="$CC -m32"
    369         CXX="$CXX -m32"
    370         LD="$LD -m32"
    371         compile
    372         if [ $? != 0 ] ; then
    373             # sometimes, we need to also tell the assembler to generate 32-bit binaries
    374             # this is highly dependent on your GCC installation (and no, we can't set
    375             # this flag all the time)
    376             CFLAGS="$CFLAGS -Wa,--32"
    377             compile
    378         fi
    379     fi
    380 
    381     compile
    382     if [ $? != 0 ] ; then
    383         echo "your C compiler doesn't seem to work:"
    384         cat $TMPL
    385         clean_exit
    386     fi
    387     log "CC         : compiler check ok ($CC)"
    388 
    389     # check that we can link the trivial program into an executable
    390     link
    391     if [ $? != 0 ] ; then
    392         OLD_LD="$LD"
    393         LD="$CC"
    394         compile
    395         link
    396         if [ $? != 0 ] ; then
    397             LD="$OLD_LD"
    398             echo "your linker doesn't seem to work:"
    399             cat $TMPL
    400             clean_exit
    401         fi
    402     fi
    403     log2 "Using '$LD' as the linker"
    404     log "LD         : linker check ok ($LD)"
    405 
    406     # check the C++ compiler
    407     log2 "Using '$CXX' as the C++ compiler"
    408 
    409     cat > $TMPC <<EOF
    410 #include <iostream>
    411 using namespace std;
    412 int main()
    413 {
    414   cout << "Hello World!" << endl;
    415   return 0;
    416 }
    417 EOF
    418 
    419     compile_cpp
    420     if [ $? != 0 ] ; then
    421         echo "your C++ compiler doesn't seem to work"
    422         cat $TMPL
    423         clean_exit
    424     fi
    425 
    426     log "CXX        : C++ compiler check ok ($CXX)"
    427 
    428     # XXX: TODO perform AR checks
    429     AR=ar
    430     ARFLAGS=
    431 }
    432 
    433 # try to compile the current source file in $TMPC into an object
    434 # stores the error log into $TMPL
    435 #
    436 compile ()
    437 {
    438     log2 "Object     : $CC -o $TMPO -c $CFLAGS $TMPC"
    439     $CC -o $TMPO -c $CFLAGS $TMPC 2> $TMPL
    440 }
    441 
    442 compile_cpp ()
    443 {
    444     log2 "Object     : $CXX -o $TMPO -c $CXXFLAGS $TMPC"
    445     $CXX -o $TMPO -c $CXXFLAGS $TMPC 2> $TMPL
    446 }
    447 
    448 # try to link the recently built file into an executable. error log in $TMPL
    449 #
    450 link()
    451 {
    452     log2 "Link      : $LD -o $TMPE $TMPO $LDFLAGS"
    453     $LD -o $TMPE $TMPO $LDFLAGS 2> $TMPL
    454 }
    455 
    456 # run a command
    457 #
    458 execute()
    459 {
    460     log2 "Running: $*"
    461     $*
    462 }
    463 
    464 # perform a simple compile / link / run of the source file in $TMPC
    465 compile_exec_run()
    466 {
    467     log2 "RunExec    : $CC -o $TMPE $CFLAGS $TMPC"
    468     compile
    469     if [ $? != 0 ] ; then
    470         echo "Failure to compile test program"
    471         cat $TMPC
    472         cat $TMPL
    473         clean_exit
    474     fi
    475     link
    476     if [ $? != 0 ] ; then
    477         echo "Failure to link test program"
    478         cat $TMPC
    479         echo "------"
    480         cat $TMPL
    481         clean_exit
    482     fi
    483     $TMPE
    484 }
    485 
    486 pattern_match ()
    487 {
    488     echo "$2" | grep -q -E -e "$1"
    489 }
    490 
    491 # Let's check that we have a working md5sum here
    492 check_md5sum ()
    493 {
    494     A_MD5=`echo "A" | md5sum | cut -d' ' -f1`
    495     if [ "$A_MD5" != "bf072e9119077b4e76437a93986787ef" ] ; then
    496         echo "Please install md5sum on this machine"
    497         exit 2
    498     fi
    499 }
    500 
    501 # Find if a given shell program is available.
    502 # We need to take care of the fact that the 'which <foo>' command
    503 # may return either an empty string (Linux) or something like
    504 # "no <foo> in ..." (Darwin). Also, we need to redirect stderr
    505 # to /dev/null for Cygwin
    506 #
    507 # $1: variable name
    508 # $2: program name
    509 #
    510 # Result: set $1 to the full path of the corresponding command
    511 #         or to the empty/undefined string if not available
    512 #
    513 find_program ()
    514 {
    515     local PROG
    516     PROG=`which $2 2>/dev/null`
    517     if [ -n "$PROG" ] ; then
    518         if pattern_match '^no ' "$PROG"; then
    519             PROG=
    520         fi
    521     fi
    522     eval $1="$PROG"
    523 }
    524 
    525 prepare_download ()
    526 {
    527     find_program CMD_WGET wget
    528     find_program CMD_CURL curl
    529     find_program CMD_SCRP scp
    530 }
    531 
    532 # Download a file with either 'curl', 'wget' or 'scp'
    533 #
    534 # $1: source URL (e.g. http://foo.com, ssh://blah, /some/path)
    535 # $2: target file
    536 download_file ()
    537 {
    538     # Is this HTTP, HTTPS or FTP ?
    539     if pattern_match "^(http|https|ftp):.*" "$1"; then
    540         if [ -n "$CMD_WGET" ] ; then
    541             run $CMD_WGET -O $2 $1
    542         elif [ -n "$CMD_CURL" ] ; then
    543             run $CMD_CURL -o $2 $1
    544         else
    545             echo "Please install wget or curl on this machine"
    546             exit 1
    547         fi
    548         return
    549     fi
    550 
    551     # Is this SSH ?
    552     # Accept both ssh://<path> or <machine>:<path>
    553     #
    554     if pattern_match "^(ssh|[^:]+):.*" "$1"; then
    555         if [ -n "$CMD_SCP" ] ; then
    556             scp_src=`echo $1 | sed -e s%ssh://%%g`
    557             run $CMD_SCP $scp_src $2
    558         else
    559             echo "Please install scp on this machine"
    560             exit 1
    561         fi
    562         return
    563     fi
    564 
    565     # Is this a file copy ?
    566     # Accept both file://<path> or /<path>
    567     #
    568     if pattern_match "^(file://|/).*" "$1"; then
    569         cp_src=`echo $1 | sed -e s%^file://%%g`
    570         run cp -f $cp_src $2
    571         return
    572     fi
    573 }
    574 
    575 
    576 # Unpack a given archive
    577 #
    578 # $1: archive file path
    579 # $2: optional target directory (current one if omitted)
    580 #
    581 unpack_archive ()
    582 {
    583     local ARCHIVE="$1"
    584     local DIR=${2-.}
    585     local RESULT TARFLAGS ZIPFLAGS
    586     mkdir -p "$DIR"
    587     if [ "$VERBOSE2" = "yes" ] ; then
    588         TARFLAGS="vxpf"
    589         ZIPFLAGS=""
    590     else
    591         TARFLAGS="xpf"
    592         ZIPFLAGS="q"
    593     fi
    594     case "$ARCHIVE" in
    595         *.zip)
    596             (cd $DIR && run unzip $ZIPFLAGS "$ARCHIVE")
    597             ;;
    598         *.tar)
    599             run tar $TARFLAGS "$ARCHIVE" -C $DIR
    600             ;;
    601         *.tar.gz)
    602             run tar z$TARFLAGS "$ARCHIVE" -C $DIR
    603             ;;
    604         *.tar.bz2)
    605             run tar j$TARFLAGS "$ARCHIVE" -C $DIR
    606             ;;
    607         *)
    608             panic "Cannot unpack archive with unknown extension: $ARCHIVE"
    609             ;;
    610     esac
    611 }
    612 
    613 # Pack a given archive
    614 #
    615 # $1: archive file path (including extension)
    616 # $2: source directory for archive content
    617 # $3+: list of files (including patterns), all if empty
    618 pack_archive ()
    619 {
    620     local ARCHIVE="$1"
    621     local SRCDIR="$2"
    622     local SRCFILES
    623     local TARFLAGS ZIPFLAGS
    624     shift; shift;
    625     if [ -z "$1" ] ; then
    626         SRCFILES="*"
    627     else
    628         SRCFILES="$@"
    629     fi
    630     if [ "`basename $ARCHIVE`" = "$ARCHIVE" ] ; then
    631         ARCHIVE="`pwd`/$ARCHIVE"
    632     fi
    633     mkdir -p `dirname $ARCHIVE`
    634     if [ "$VERBOSE2" = "yes" ] ; then
    635         TARFLAGS="vcf"
    636         ZIPFLAGS="-9r"
    637     else
    638         TARFLAGS="cf"
    639         ZIPFLAGS="-9qr"
    640     fi
    641     case "$ARCHIVE" in
    642         *.zip)
    643             (cd $SRCDIR && run zip $ZIPFLAGS "$ARCHIVE" $SRCFILES)
    644             ;;
    645         *.tar)
    646             (cd $SRCDIR && run tar $TARFLAGS "$ARCHIVE" $SRCFILES)
    647             ;;
    648         *.tar.gz)
    649             (cd $SRCDIR && run tar z$TARFLAGS "$ARCHIVE" $SRCFILES)
    650             ;;
    651         *.tar.bz2)
    652             (cd $SRCDIR && run tar j$TARFLAGS "$ARCHIVE" $SRCFILES)
    653             ;;
    654         *)
    655             panic "Unsupported archive format: $ARCHIVE"
    656             ;;
    657     esac
    658 }
    659 
    660 # Copy a directory, create target location if needed
    661 #
    662 # $1: source directory
    663 # $2: target directory location
    664 #
    665 copy_directory ()
    666 {
    667     local SRCDIR="$1"
    668     local DSTDIR="$2"
    669     if [ ! -d "$SRCDIR" ] ; then
    670         panic "Can't copy from non-directory: $SRCDIR"
    671     fi
    672     log "Copying directory: "
    673     log "  from $SRCDIR"
    674     log "  to $DSTDIR"
    675     mkdir -p "$DSTDIR" && (cd "$SRCDIR" && tar cf - *) | (tar xf - -C "$DSTDIR")
    676     fail_panic "Cannot copy to directory: $DSTDIR"
    677 }
    678 
    679 # This is the same than copy_directory(), but symlinks will be replaced
    680 # by the file they actually point to instead.
    681 copy_directory_nolinks ()
    682 {
    683     local SRCDIR="$1"
    684     local DSTDIR="$2"
    685     if [ ! -d "$SRCDIR" ] ; then
    686         panic "Can't copy from non-directory: $SRCDIR"
    687     fi
    688     log "Copying directory (without symlinks): "
    689     log "  from $SRCDIR"
    690     log "  to $DSTDIR"
    691     mkdir -p "$DSTDIR" && (cd "$SRCDIR" && tar chf - *) | (tar xf - -C "$DSTDIR")
    692     fail_panic "Cannot copy to directory: $DSTDIR"
    693 }
    694 
    695 # Copy certain files from one directory to another one
    696 # $1: source directory
    697 # $2: target directory
    698 # $3+: file list (including patterns)
    699 copy_file_list ()
    700 {
    701     local SRCDIR="$1"
    702     local DSTDIR="$2"
    703     shift; shift;
    704     if [ ! -d "$SRCDIR" ] ; then
    705         panic "Cant' copy from non-directory: $SRCDIR"
    706     fi
    707     log "Copying file: $@"
    708     log "  from $SRCDIR"
    709     log "  to $DSTDIR"
    710     mkdir -p "$DSTDIR" && (cd "$SRCDIR" && tar cf - $@) | (tar xf - -C "$DSTDIR")
    711     fail_panic "Cannot copy files to directory: $DSTDIR"
    712 }
    713