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 TMPLOG= 67 68 # Setup a log file where all log() and log2() output will be sent 69 # 70 # $1: log file path (optional) 71 # 72 setup_log_file () 73 { 74 if [ -n "$1" ] ; then 75 TMPLOG="$1" 76 else 77 TMPLOG=/tmp/ndk-log-$$.log 78 fi 79 rm -f $TMPLOG && touch $TMPLOG 80 echo "To follow build in another terminal, please use: tail -F $TMPLOG" 81 } 82 83 dump () 84 { 85 if [ -n "$TMPLOG" ] ; then 86 echo "$@" >> $TMPLOG 87 fi 88 echo "$@" 89 } 90 91 log () 92 { 93 if [ "$VERBOSE" = "yes" ] ; then 94 echo "$@" 95 else 96 if [ "$TMPLOG" ] ; then 97 echo "$@" >> $TMPLOG 98 fi 99 fi 100 } 101 102 log2 () 103 { 104 if [ "$VERBOSE2" = "yes" ] ; then 105 echo "$@" 106 else 107 if [ -n "$TMPLOG" ] ; then 108 echo "$@" >> $TMPLOG 109 fi 110 fi 111 } 112 113 run () 114 { 115 if [ "$VERBOSE" = "yes" ] ; then 116 echo "##### NEW COMMAND" 117 echo "$@" 118 $@ 2>&1 119 else 120 if [ -n "$TMPLOG" ] ; then 121 echo "##### NEW COMMAND" >> $TMPLOG 122 echo "$@" >> $TMPLOG 123 $@ >>$TMPLOG 2>&1 124 else 125 $@ > /dev/null 2>&1 126 fi 127 fi 128 } 129 130 ## Utilities 131 ## 132 133 # return the value of a given named variable 134 # $1: variable name 135 # 136 # example: 137 # FOO=BAR 138 # BAR=ZOO 139 # echo `var_value $FOO` 140 # will print 'ZOO' 141 # 142 var_value () 143 { 144 # find a better way to do that ? 145 eval echo "$`echo $1`" 146 } 147 148 # convert to uppercase 149 # assumes tr is installed on the platform ? 150 # 151 to_uppercase () 152 { 153 echo $1 | tr "[:lower:]" "[:upper:]" 154 } 155 156 ## Normalize OS and CPU 157 ## 158 HOST_ARCH=`uname -m` 159 case "$HOST_ARCH" in 160 i?86) HOST_ARCH=x86 161 ;; 162 amd64) HOST_ARCH=x86_64 163 ;; 164 powerpc) HOST_ARCH=ppc 165 ;; 166 esac 167 168 log2 "HOST_ARCH=$HOST_ARCH" 169 170 # at this point, the supported values for CPU are: 171 # x86 172 # x86_64 173 # ppc 174 # 175 # other values may be possible but haven't been tested 176 # 177 HOST_EXE="" 178 HOST_OS=`uname -s` 179 case "$HOST_OS" in 180 Darwin) 181 HOST_OS=darwin 182 ;; 183 Linux) 184 # note that building 32-bit binaries on x86_64 is handled later 185 HOST_OS=linux 186 ;; 187 FreeBsd) # note: this is not tested 188 HOST_OS=freebsd 189 ;; 190 CYGWIN*|*_NT-*) 191 HOST_OS=windows 192 HOST_EXE=.exe 193 if [ "x$OSTYPE" = xcygwin ] ; then 194 HOST_OS=cygwin 195 fi 196 ;; 197 esac 198 199 log2 "HOST_OS=$HOST_OS" 200 log2 "HOST_EXE=$HOST_EXE" 201 202 # at this point, the value of HOST_OS should be one of the following: 203 # linux 204 # darwin 205 # windows (MSys) 206 # cygwin 207 # 208 # Note that cygwin is treated as a special case because it behaves very differently 209 # for a few things. Other values may be possible but have not been tested 210 # 211 212 # define HOST_TAG as a unique tag used to identify both the host OS and CPU 213 # supported values are: 214 # 215 # linux-x86 216 # linux-x86_64 217 # darwin-x86 218 # darwin-ppc 219 # windows 220 # 221 # other values are possible but were not tested. 222 # 223 compute_host_tag () 224 { 225 case "$HOST_OS" in 226 windows|cygwin) 227 HOST_TAG="windows" 228 ;; 229 *) HOST_TAG="${HOST_OS}-${HOST_ARCH}" 230 esac 231 log2 "HOST_TAG=$HOST_TAG" 232 } 233 234 compute_host_tag 235 236 # Compute the number of host CPU cores an HOST_NUM_CPUS 237 # 238 case "$HOST_OS" in 239 linux) 240 HOST_NUM_CPUS=`cat /proc/cpuinfo | grep processor | wc -l` 241 ;; 242 darwin|freebsd) 243 HOST_NUM_CPUS=`sysctl -n hw.ncpu` 244 ;; 245 windows|cygwin) 246 HOST_NUM_CPUS=$NUMBER_OF_PROCESSORS 247 ;; 248 *) # let's play safe here 249 HOST_NUM_CPUS=1 250 esac 251 252 log2 "HOST_NUM_CPUS=$HOST_NUM_CPUS" 253 254 # If BUILD_NUM_CPUS is not already defined in your environment, 255 # define it as the double of HOST_NUM_CPUS. This is used to 256 # run Make commends in parralles, as in 'make -j$BUILD_NUM_CPUS' 257 # 258 if [ -z "$BUILD_NUM_CPUS" ] ; then 259 BUILD_NUM_CPUS=`expr $HOST_NUM_CPUS \* 2` 260 fi 261 262 log2 "BUILD_NUM_CPUS=$BUILD_NUM_CPUS" 263 264 265 ## HOST TOOLCHAIN SUPPORT 266 ## 267 268 # force the generation of 32-bit binaries on 64-bit systems 269 # 270 FORCE_32BIT=no 271 force_32bit_binaries () 272 { 273 if [ "$HOST_ARCH" = x86_64 ] ; then 274 log2 "Forcing generation of 32-bit host binaries on $HOST_ARCH" 275 FORCE_32BIT=yes 276 HOST_ARCH=x86 277 log2 "HOST_ARCH=$HOST_ARCH" 278 compute_host_tag 279 fi 280 } 281 282 # On Windows, cygwin binaries will be generated by default, but 283 # you can force mingw ones that do not link to cygwin.dll if you 284 # call this function. 285 # 286 disable_cygwin () 287 { 288 if [ $OS = cygwin ] ; then 289 log2 "Disabling cygwin binaries generation" 290 CFLAGS="$CFLAGS -mno-cygwin" 291 LDFLAGS="$LDFLAGS -mno-cygwin" 292 OS=windows 293 HOST_OS=windows 294 compute_host_tag 295 fi 296 } 297 298 # Various probes are going to need to run a small C program 299 TMPC=/tmp/android-$$-test.c 300 TMPO=/tmp/android-$$-test.o 301 TMPE=/tmp/android-$$-test$EXE 302 TMPL=/tmp/android-$$-test.log 303 304 # cleanup temporary files 305 clean_temp () 306 { 307 rm -f $TMPC $TMPO $TMPL $TMPE 308 } 309 310 # cleanup temp files then exit with an error 311 clean_exit () 312 { 313 clean_temp 314 exit 1 315 } 316 317 # this function will setup the compiler and linker and check that they work as advertised 318 # note that you should call 'force_32bit_binaries' before this one if you want it to 319 # generate 32-bit binaries on 64-bit systems (that support it). 320 # 321 setup_toolchain () 322 { 323 if [ -z "$CC" ] ; then 324 CC=gcc 325 fi 326 327 log2 "Using '$CC' as the C compiler" 328 329 # check that we can compile a trivial C program with this compiler 330 cat > $TMPC <<EOF 331 int main(void) {} 332 EOF 333 334 if [ "$FORCE_32BIT" = yes ] ; then 335 CFLAGS="$CFLAGS -m32" 336 LDFLAGS="$LDFLAGS -m32" 337 compile 338 if [ $? != 0 ] ; then 339 # sometimes, we need to also tell the assembler to generate 32-bit binaries 340 # this is highly dependent on your GCC installation (and no, we can't set 341 # this flag all the time) 342 CFLAGS="$CFLAGS -Wa,--32" 343 compile 344 fi 345 fi 346 347 compile 348 if [ $? != 0 ] ; then 349 echo "your C compiler doesn't seem to work:" 350 cat $TMPL 351 clean_exit 352 fi 353 log "CC : compiler check ok ($CC)" 354 355 # check that we can link the trivial program into an executable 356 if [ -z "$LD" ] ; then 357 LD=$CC 358 fi 359 link 360 if [ $? != 0 ] ; then 361 OLD_LD=$LD 362 LD=gcc 363 compile 364 link 365 if [ $? != 0 ] ; then 366 LD=$OLD_LD 367 echo "your linker doesn't seem to work:" 368 cat $TMPL 369 clean_exit 370 fi 371 fi 372 log2 "Using '$LD' as the linker" 373 log "LD : linker check ok ($LD)" 374 375 # check the C++ compiler 376 if [ -z "$CXX" ] ; then 377 CXX=g++ 378 fi 379 if [ -z "$CXXFLAGS" ] ; then 380 CXXFLAGS=$CFLAGS 381 fi 382 383 log2 "Using '$CXX' as the C++ compiler" 384 385 cat > $TMPC <<EOF 386 #include <iostream> 387 using namespace std; 388 int main() 389 { 390 cout << "Hello World!" << endl; 391 return 0; 392 } 393 EOF 394 395 compile_cpp 396 if [ $? != 0 ] ; then 397 echo "your C++ compiler doesn't seem to work" 398 cat $TMPL 399 clean_exit 400 fi 401 402 log "CXX : C++ compiler check ok ($CXX)" 403 404 # XXX: TODO perform AR checks 405 AR=ar 406 ARFLAGS= 407 } 408 409 # try to compile the current source file in $TMPC into an object 410 # stores the error log into $TMPL 411 # 412 compile () 413 { 414 log2 "Object : $CC -o $TMPO -c $CFLAGS $TMPC" 415 $CC -o $TMPO -c $CFLAGS $TMPC 2> $TMPL 416 } 417 418 compile_cpp () 419 { 420 log2 "Object : $CXX -o $TMPO -c $CXXFLAGS $TMPC" 421 $CXX -o $TMPO -c $CXXFLAGS $TMPC 2> $TMPL 422 } 423 424 # try to link the recently built file into an executable. error log in $TMPL 425 # 426 link() 427 { 428 log2 "Link : $LD -o $TMPE $TMPO $LDFLAGS" 429 $LD -o $TMPE $TMPO $LDFLAGS 2> $TMPL 430 } 431 432 # run a command 433 # 434 execute() 435 { 436 log2 "Running: $*" 437 $* 438 } 439 440 # perform a simple compile / link / run of the source file in $TMPC 441 compile_exec_run() 442 { 443 log2 "RunExec : $CC -o $TMPE $CFLAGS $TMPC" 444 compile 445 if [ $? != 0 ] ; then 446 echo "Failure to compile test program" 447 cat $TMPC 448 cat $TMPL 449 clean_exit 450 fi 451 link 452 if [ $? != 0 ] ; then 453 echo "Failure to link test program" 454 cat $TMPC 455 echo "------" 456 cat $TMPL 457 clean_exit 458 fi 459 $TMPE 460 } 461 462 pattern_match () 463 { 464 echo "$2" | grep -q -E -e "$1" 465 } 466 467 # Let's check that we have a working md5sum here 468 check_md5sum () 469 { 470 A_MD5=`echo "A" | md5sum | cut -d' ' -f1` 471 if [ "$A_MD5" != "bf072e9119077b4e76437a93986787ef" ] ; then 472 echo "Please install md5sum on this machine" 473 exit 2 474 fi 475 } 476 477 # Find if a given shell program is available. 478 # We need to take care of the fact that the 'which <foo>' command 479 # may return either an empty string (Linux) or something like 480 # "no <foo> in ..." (Darwin). Also, we need to redirect stderr 481 # to /dev/null for Cygwin 482 # 483 # $1: variable name 484 # $2: program name 485 # 486 # Result: set $1 to the full path of the corresponding command 487 # or to the empty/undefined string if not available 488 # 489 find_program () 490 { 491 local PROG 492 PROG=`which $2 2>/dev/null` 493 if [ -n "$PROG" ] ; then 494 if pattern_match '^no ' "$PROG"; then 495 PROG= 496 fi 497 fi 498 eval $1="$PROG" 499 } 500 501 prepare_download () 502 { 503 find_program CMD_WGET wget 504 find_program CMD_CURL curl 505 find_program CMD_SCRP scp 506 } 507 508 # Download a file with either 'curl', 'wget' or 'scp' 509 # 510 # $1: source URL (e.g. http://foo.com, ssh://blah, /some/path) 511 # $2: target file 512 download_file () 513 { 514 # Is this HTTP, HTTPS or FTP ? 515 if pattern_match "^(http|https|ftp):.*" "$1"; then 516 if [ -n "$CMD_WGET" ] ; then 517 run $CMD_WGET -O $2 $1 518 elif [ -n "$CMD_CURL" ] ; then 519 run $CMD_CURL -o $2 $1 520 else 521 echo "Please install wget or curl on this machine" 522 exit 1 523 fi 524 return 525 fi 526 527 # Is this SSH ? 528 # Accept both ssh://<path> or <machine>:<path> 529 # 530 if pattern_match "^(ssh|[^:]+):.*" "$1"; then 531 if [ -n "$CMD_SCP" ] ; then 532 scp_src=`echo $1 | sed -e s%ssh://%%g` 533 run $CMD_SCP $scp_src $2 534 else 535 echo "Please install scp on this machine" 536 exit 1 537 fi 538 return 539 fi 540 541 # Is this a file copy ? 542 # Accept both file://<path> or /<path> 543 # 544 if pattern_match "^(file://|/).*" "$1"; then 545 cp_src=`echo $1 | sed -e s%^file://%%g` 546 run cp -f $cp_src $2 547 return 548 fi 549 } 550