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