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## Normalize OS and CPU 194## 195HOST_ARCH=`uname -m` 196case "$HOST_ARCH" in 197 i?86) HOST_ARCH=x86 198 ;; 199 amd64) HOST_ARCH=x86_64 200 ;; 201 powerpc) HOST_ARCH=ppc 202 ;; 203esac 204 205log2 "HOST_ARCH=$HOST_ARCH" 206 207# at this point, the supported values for CPU are: 208# x86 209# x86_64 210# ppc 211# 212# other values may be possible but haven't been tested 213# 214HOST_EXE="" 215HOST_OS=`uname -s` 216case "$HOST_OS" in 217 Darwin) 218 HOST_OS=darwin 219 ;; 220 Linux) 221 # note that building 32-bit binaries on x86_64 is handled later 222 HOST_OS=linux 223 ;; 224 FreeBsd) # note: this is not tested 225 HOST_OS=freebsd 226 ;; 227 CYGWIN*|*_NT-*) 228 HOST_OS=windows 229 HOST_EXE=.exe 230 if [ "x$OSTYPE" = xcygwin ] ; then 231 HOST_OS=cygwin 232 fi 233 ;; 234esac 235 236log2 "HOST_OS=$HOST_OS" 237log2 "HOST_EXE=$HOST_EXE" 238 239# at this point, the value of HOST_OS should be one of the following: 240# linux 241# darwin 242# windows (MSys) 243# cygwin 244# 245# Note that cygwin is treated as a special case because it behaves very differently 246# for a few things. Other values may be possible but have not been tested 247# 248 249# define HOST_TAG as a unique tag used to identify both the host OS and CPU 250# supported values are: 251# 252# linux-x86 253# linux-x86_64 254# darwin-x86 255# darwin-ppc 256# windows 257# 258# other values are possible but were not tested. 259# 260compute_host_tag () 261{ 262 case "$HOST_OS" in 263 windows|cygwin) 264 HOST_TAG="windows" 265 ;; 266 *) HOST_TAG="${HOST_OS}-${HOST_ARCH}" 267 esac 268 log2 "HOST_TAG=$HOST_TAG" 269} 270 271compute_host_tag 272 273# Compute the number of host CPU cores an HOST_NUM_CPUS 274# 275case "$HOST_OS" in 276 linux) 277 HOST_NUM_CPUS=`cat /proc/cpuinfo | grep processor | wc -l` 278 ;; 279 darwin|freebsd) 280 HOST_NUM_CPUS=`sysctl -n hw.ncpu` 281 ;; 282 windows|cygwin) 283 HOST_NUM_CPUS=$NUMBER_OF_PROCESSORS 284 ;; 285 *) # let's play safe here 286 HOST_NUM_CPUS=1 287esac 288 289log2 "HOST_NUM_CPUS=$HOST_NUM_CPUS" 290 291# If BUILD_NUM_CPUS is not already defined in your environment, 292# define it as the double of HOST_NUM_CPUS. This is used to 293# run Make commands in parralles, as in 'make -j$BUILD_NUM_CPUS' 294# 295if [ -z "$BUILD_NUM_CPUS" ] ; then 296 BUILD_NUM_CPUS=`expr $HOST_NUM_CPUS \* 2` 297fi 298 299log2 "BUILD_NUM_CPUS=$BUILD_NUM_CPUS" 300 301 302## HOST TOOLCHAIN SUPPORT 303## 304 305# force the generation of 32-bit binaries on 64-bit systems 306# 307FORCE_32BIT=no 308force_32bit_binaries () 309{ 310 if [ "$HOST_ARCH" = x86_64 ] ; then 311 log2 "Forcing generation of 32-bit host binaries on $HOST_ARCH" 312 FORCE_32BIT=yes 313 HOST_ARCH=x86 314 log2 "HOST_ARCH=$HOST_ARCH" 315 compute_host_tag 316 fi 317} 318 319# On Windows, cygwin binaries will be generated by default, but 320# you can force mingw ones that do not link to cygwin.dll if you 321# call this function. 322# 323disable_cygwin () 324{ 325 if [ $OS = cygwin ] ; then 326 log2 "Disabling cygwin binaries generation" 327 CFLAGS="$CFLAGS -mno-cygwin" 328 LDFLAGS="$LDFLAGS -mno-cygwin" 329 OS=windows 330 HOST_OS=windows 331 compute_host_tag 332 fi 333} 334 335# Various probes are going to need to run a small C program 336mkdir -p /tmp/ndk-$USER/tmp/tests 337 338TMPC=/tmp/ndk-$USER/tmp/tests/test-$$.c 339TMPO=/tmp/ndk-$USER/tmp/tests/test-$$.o 340TMPE=/tmp/ndk-$USER/tmp/tests/test-$$$EXE 341TMPL=/tmp/ndk-$USER/tmp/tests/test-$$.log 342 343# cleanup temporary files 344clean_temp () 345{ 346 rm -f $TMPC $TMPO $TMPL $TMPE 347} 348 349# cleanup temp files then exit with an error 350clean_exit () 351{ 352 clean_temp 353 exit 1 354} 355 356# this function will setup the compiler and linker and check that they work as advertised 357# note that you should call 'force_32bit_binaries' before this one if you want it to 358# generate 32-bit binaries on 64-bit systems (that support it). 359# 360setup_toolchain () 361{ 362 if [ -z "$CC" ] ; then 363 CC=gcc 364 fi 365 if [ -z "$CXX" ] ; then 366 CXX=g++ 367 fi 368 if [ -z "$CXXFLAGS" ] ; then 369 CXXFLAGS="$CFLAGS" 370 fi 371 if [ -z "$LD" ] ; then 372 LD="$CC" 373 fi 374 375 log2 "Using '$CC' as the C compiler" 376 377 # check that we can compile a trivial C program with this compiler 378 mkdir -p $(dirname "$TMPC") 379 cat > $TMPC <<EOF 380int main(void) {} 381EOF 382 383 if [ "$FORCE_32BIT" = yes ] ; then 384 CC="$CC -m32" 385 CXX="$CXX -m32" 386 LD="$LD -m32" 387 compile 388 if [ $? != 0 ] ; then 389 # sometimes, we need to also tell the assembler to generate 32-bit binaries 390 # this is highly dependent on your GCC installation (and no, we can't set 391 # this flag all the time) 392 CFLAGS="$CFLAGS -Wa,--32" 393 compile 394 fi 395 fi 396 397 compile 398 if [ $? != 0 ] ; then 399 echo "your C compiler doesn't seem to work:" 400 cat $TMPL 401 clean_exit 402 fi 403 log "CC : compiler check ok ($CC)" 404 405 # check that we can link the trivial program into an executable 406 link 407 if [ $? != 0 ] ; then 408 OLD_LD="$LD" 409 LD="$CC" 410 compile 411 link 412 if [ $? != 0 ] ; then 413 LD="$OLD_LD" 414 echo "your linker doesn't seem to work:" 415 cat $TMPL 416 clean_exit 417 fi 418 fi 419 log2 "Using '$LD' as the linker" 420 log "LD : linker check ok ($LD)" 421 422 # check the C++ compiler 423 log2 "Using '$CXX' as the C++ compiler" 424 425 cat > $TMPC <<EOF 426#include <iostream> 427using namespace std; 428int main() 429{ 430 cout << "Hello World!" << endl; 431 return 0; 432} 433EOF 434 435 compile_cpp 436 if [ $? != 0 ] ; then 437 echo "your C++ compiler doesn't seem to work" 438 cat $TMPL 439 clean_exit 440 fi 441 442 log "CXX : C++ compiler check ok ($CXX)" 443 444 # XXX: TODO perform AR checks 445 AR=ar 446 ARFLAGS= 447} 448 449# try to compile the current source file in $TMPC into an object 450# stores the error log into $TMPL 451# 452compile () 453{ 454 log2 "Object : $CC -o $TMPO -c $CFLAGS $TMPC" 455 $CC -o $TMPO -c $CFLAGS $TMPC 2> $TMPL 456} 457 458compile_cpp () 459{ 460 log2 "Object : $CXX -o $TMPO -c $CXXFLAGS $TMPC" 461 $CXX -o $TMPO -c $CXXFLAGS $TMPC 2> $TMPL 462} 463 464# try to link the recently built file into an executable. error log in $TMPL 465# 466link() 467{ 468 log2 "Link : $LD -o $TMPE $TMPO $LDFLAGS" 469 $LD -o $TMPE $TMPO $LDFLAGS 2> $TMPL 470} 471 472# run a command 473# 474execute() 475{ 476 log2 "Running: $*" 477 $* 478} 479 480# perform a simple compile / link / run of the source file in $TMPC 481compile_exec_run() 482{ 483 log2 "RunExec : $CC -o $TMPE $CFLAGS $TMPC" 484 compile 485 if [ $? != 0 ] ; then 486 echo "Failure to compile test program" 487 cat $TMPC 488 cat $TMPL 489 clean_exit 490 fi 491 link 492 if [ $? != 0 ] ; then 493 echo "Failure to link test program" 494 cat $TMPC 495 echo "------" 496 cat $TMPL 497 clean_exit 498 fi 499 $TMPE 500} 501 502pattern_match () 503{ 504 echo "$2" | grep -q -E -e "$1" 505} 506 507# Let's check that we have a working md5sum here 508check_md5sum () 509{ 510 A_MD5=`echo "A" | md5sum | cut -d' ' -f1` 511 if [ "$A_MD5" != "bf072e9119077b4e76437a93986787ef" ] ; then 512 echo "Please install md5sum on this machine" 513 exit 2 514 fi 515} 516 517# Find if a given shell program is available. 518# We need to take care of the fact that the 'which <foo>' command 519# may return either an empty string (Linux) or something like 520# "no <foo> in ..." (Darwin). Also, we need to redirect stderr 521# to /dev/null for Cygwin 522# 523# $1: variable name 524# $2: program name 525# 526# Result: set $1 to the full path of the corresponding command 527# or to the empty/undefined string if not available 528# 529find_program () 530{ 531 local PROG 532 PROG=`which $2 2>/dev/null` 533 if [ -n "$PROG" ] ; then 534 if pattern_match '^no ' "$PROG"; then 535 PROG= 536 fi 537 fi 538 eval $1="$PROG" 539} 540 541prepare_download () 542{ 543 find_program CMD_WGET wget 544 find_program CMD_CURL curl 545 find_program CMD_SCRP scp 546} 547 548# Download a file with either 'curl', 'wget' or 'scp' 549# 550# $1: source URL (e.g. http://foo.com, ssh://blah, /some/path) 551# $2: target file 552download_file () 553{ 554 # Is this HTTP, HTTPS or FTP ? 555 if pattern_match "^(http|https|ftp):.*" "$1"; then 556 if [ -n "$CMD_WGET" ] ; then 557 run $CMD_WGET -O $2 $1 558 elif [ -n "$CMD_CURL" ] ; then 559 run $CMD_CURL -o $2 $1 560 else 561 echo "Please install wget or curl on this machine" 562 exit 1 563 fi 564 return 565 fi 566 567 # Is this SSH ? 568 # Accept both ssh://<path> or <machine>:<path> 569 # 570 if pattern_match "^(ssh|[^:]+):.*" "$1"; then 571 if [ -n "$CMD_SCP" ] ; then 572 scp_src=`echo $1 | sed -e s%ssh://%%g` 573 run $CMD_SCP $scp_src $2 574 else 575 echo "Please install scp on this machine" 576 exit 1 577 fi 578 return 579 fi 580 581 # Is this a file copy ? 582 # Accept both file://<path> or /<path> 583 # 584 if pattern_match "^(file://|/).*" "$1"; then 585 cp_src=`echo $1 | sed -e s%^file://%%g` 586 run cp -f $cp_src $2 587 return 588 fi 589} 590 591 592# Unpack a given archive 593# 594# $1: archive file path 595# $2: optional target directory (current one if omitted) 596# 597unpack_archive () 598{ 599 local ARCHIVE="$1" 600 local DIR=${2-.} 601 local RESULT TARFLAGS ZIPFLAGS 602 mkdir -p "$DIR" 603 if [ "$VERBOSE2" = "yes" ] ; then 604 TARFLAGS="vxpf" 605 ZIPFLAGS="" 606 else 607 TARFLAGS="xpf" 608 ZIPFLAGS="q" 609 fi 610 case "$ARCHIVE" in 611 *.zip) 612 (cd $DIR && run unzip $ZIPFLAGS "$ARCHIVE") 613 ;; 614 *.tar) 615 run tar $TARFLAGS "$ARCHIVE" -C $DIR 616 ;; 617 *.tar.gz) 618 run tar z$TARFLAGS "$ARCHIVE" -C $DIR 619 ;; 620 *.tar.bz2) 621 run tar j$TARFLAGS "$ARCHIVE" -C $DIR 622 ;; 623 *) 624 panic "Cannot unpack archive with unknown extension: $ARCHIVE" 625 ;; 626 esac 627} 628 629# Pack a given archive 630# 631# $1: archive file path (including extension) 632# $2: source directory for archive content 633# $3+: list of files (including patterns), all if empty 634pack_archive () 635{ 636 local ARCHIVE="$1" 637 local SRCDIR="$2" 638 local SRCFILES 639 local TARFLAGS ZIPFLAGS 640 shift; shift; 641 if [ -z "$1" ] ; then 642 SRCFILES="*" 643 else 644 SRCFILES="$@" 645 fi 646 if [ "`basename $ARCHIVE`" = "$ARCHIVE" ] ; then 647 ARCHIVE="`pwd`/$ARCHIVE" 648 fi 649 mkdir -p `dirname $ARCHIVE` 650 if [ "$VERBOSE2" = "yes" ] ; then 651 TARFLAGS="vcf" 652 ZIPFLAGS="-9r" 653 else 654 TARFLAGS="cf" 655 ZIPFLAGS="-9qr" 656 fi 657 case "$ARCHIVE" in 658 *.zip) 659 (cd $SRCDIR && run zip $ZIPFLAGS "$ARCHIVE" $SRCFILES) 660 ;; 661 *.tar) 662 (cd $SRCDIR && run tar $TARFLAGS "$ARCHIVE" $SRCFILES) 663 ;; 664 *.tar.gz) 665 (cd $SRCDIR && run tar z$TARFLAGS "$ARCHIVE" $SRCFILES) 666 ;; 667 *.tar.bz2) 668 (cd $SRCDIR && run tar j$TARFLAGS "$ARCHIVE" $SRCFILES) 669 ;; 670 *) 671 panic "Unsupported archive format: $ARCHIVE" 672 ;; 673 esac 674} 675 676# Copy a directory, create target location if needed 677# 678# $1: source directory 679# $2: target directory location 680# 681copy_directory () 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: " 689 log " from $SRCDIR" 690 log " to $DSTDIR" 691 mkdir -p "$DSTDIR" && (cd "$SRCDIR" && 2>/dev/null tar cf - *) | (tar xf - -C "$DSTDIR") 692 fail_panic "Cannot copy to directory: $DSTDIR" 693} 694 695# This is the same than copy_directory(), but symlinks will be replaced 696# by the file they actually point to instead. 697copy_directory_nolinks () 698{ 699 local SRCDIR="$1" 700 local DSTDIR="$2" 701 if [ ! -d "$SRCDIR" ] ; then 702 panic "Can't copy from non-directory: $SRCDIR" 703 fi 704 log "Copying directory (without symlinks): " 705 log " from $SRCDIR" 706 log " to $DSTDIR" 707 mkdir -p "$DSTDIR" && (cd "$SRCDIR" && tar chf - *) | (tar xf - -C "$DSTDIR") 708 fail_panic "Cannot copy to directory: $DSTDIR" 709} 710 711# Copy certain files from one directory to another one 712# $1: source directory 713# $2: target directory 714# $3+: file list (including patterns) 715copy_file_list () 716{ 717 local SRCDIR="$1" 718 local DSTDIR="$2" 719 shift; shift; 720 if [ ! -d "$SRCDIR" ] ; then 721 panic "Cant' copy from non-directory: $SRCDIR" 722 fi 723 log "Copying file: $@" 724 log " from $SRCDIR" 725 log " to $DSTDIR" 726 mkdir -p "$DSTDIR" && (cd "$SRCDIR" && tar cf - $@) | (tar xf - -C "$DSTDIR") 727 fail_panic "Cannot copy files to directory: $DSTDIR" 728} 729 730# Rotate a log file 731# If the given log file exist, add a -1 to the end of the file. 732# If older log files exist, rename them to -<n+1> 733# $1: log file 734# $2: maximum version to retain [optional] 735rotate_log () 736{ 737 # Default Maximum versions to retain 738 local MAXVER="5" 739 local LOGFILE="$1" 740 shift; 741 if [ ! -z "$1" ] ; then 742 local tmpmax="$1" 743 shift; 744 tmpmax=`expr $tmpmax + 0` 745 if [ $tmpmax -lt 1 ] ; then 746 panic "Invalid maximum log file versions '$tmpmax' invalid; defaulting to $MAXVER" 747 else 748 MAXVER=$tmpmax; 749 fi 750 fi 751 752 # Do Nothing if the log file does not exist 753 if [ ! -f "${LOGFILE}" ] ; then 754 return 755 fi 756 757 # Rename existing older versions 758 ver=$MAXVER 759 while [ $ver -ge 1 ] 760 do 761 local prev=$(( $ver - 1 )) 762 local old="-$prev" 763 764 # Instead of old version 0; use the original filename 765 if [ $ver -eq 1 ] ; then 766 old="" 767 fi 768 769 if [ -f "${LOGFILE}${old}" ] ; then 770 mv -f "${LOGFILE}${old}" "${LOGFILE}-${ver}" 771 fi 772 773 ver=$prev 774 done 775} 776