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 if [ -z "$TMPDIR" ]; then 24 export TMPDIR=/tmp/ndk-$USER 25 fi 26 27 OS=`uname -s` 28 if [ "$OS" == "Darwin" -a -z "$MACOSX_DEPLOYMENT_TARGET" ]; then 29 export MACOSX_DEPLOYMENT_TARGET="10.8" 30 fi 31 32 # Find the Android NDK root, assuming we are invoked from a script 33 # within its directory structure. 34 # 35 # $1: Variable name that will receive the path 36 # $2: Path of invoking script 37 find_ndk_root () 38 { 39 # Try to auto-detect the NDK root by walking up the directory 40 # path to the current script. 41 local PROGDIR="`dirname \"$2\"`" 42 while [ -n "1" ] ; do 43 if [ -d "$PROGDIR/build/core" ] ; then 44 break 45 fi 46 if [ -z "$PROGDIR" -o "$PROGDIR" = '/' ] ; then 47 return 1 48 fi 49 PROGDIR="`cd \"$PROGDIR/..\" && pwd`" 50 done 51 eval $1="$PROGDIR" 52 } 53 54 # Put location of Android NDK into ANDROID_NDK_ROOT and 55 # perform a tiny amount of sanity check 56 # 57 if [ -z "$ANDROID_NDK_ROOT" ] ; then 58 find_ndk_root ANDROID_NDK_ROOT "$0" 59 if [ $? != 0 ]; then 60 echo "Please define ANDROID_NDK_ROOT to point to the root of your" 61 echo "Android NDK installation." 62 exit 1 63 fi 64 fi 65 66 echo "$ANDROID_NDK_ROOT" | grep -q -e " " 67 if [ $? = 0 ] ; then 68 echo "ERROR: The Android NDK installation path contains a space !" 69 echo "Please install to a different location." 70 exit 1 71 fi 72 73 if [ ! -d $ANDROID_NDK_ROOT ] ; then 74 echo "ERROR: Your ANDROID_NDK_ROOT variable does not point to a directory." 75 echo "ANDROID_NDK_ROOT=$ANDROID_NDK_ROOT" 76 exit 1 77 fi 78 79 if [ ! -f $ANDROID_NDK_ROOT/build/tools/ndk-common.sh ] ; then 80 echo "ERROR: Your ANDROID_NDK_ROOT does not contain a valid NDK build system." 81 echo "ANDROID_NDK_ROOT=$ANDROID_NDK_ROOT" 82 exit 1 83 fi 84 85 ## Use DRYRUN to find out top-level commands. 86 DRYRUN=${DRYRUN-no} 87 88 ## Logging support 89 ## 90 VERBOSE=${VERBOSE-yes} 91 92 93 # If NDK_LOGFILE is defined in the environment, use this as the log file 94 TMPLOG= 95 if [ -n "$NDK_LOGFILE" ] ; then 96 mkdir -p `dirname "$NDK_LOGFILE"` && touch "$NDK_LOGFILE" 97 TMPLOG="$NDK_LOGFILE" 98 fi 99 100 # Setup a log file where all log() output will be sent 101 # 102 # $1: log file path (optional) 103 # 104 setup_default_log_file () 105 { 106 if [ -n "$NDK_LOGFILE" ] ; then 107 return 108 fi 109 if [ -n "$1" ] ; then 110 NDK_LOGFILE="$1" 111 else 112 NDK_LOGFILE=$TMPDIR/ndk-log-$$.txt 113 fi 114 export NDK_LOGFILE 115 TMPLOG="$NDK_LOGFILE" 116 rm -rf "$TMPLOG" && mkdir -p `dirname "$TMPLOG"` && touch "$TMPLOG" 117 echo "To follow build in another terminal, please use: tail -F $TMPLOG" 118 } 119 120 dump () 121 { 122 if [ -n "$TMPLOG" ] ; then 123 echo "$@" >> $TMPLOG 124 fi 125 echo "$@" 126 } 127 128 dump_n () 129 { 130 if [ -n "$TMPLOG" ] ; then 131 printf %s "$@" >> $TMPLOG 132 fi 133 printf %s "$@" 134 } 135 136 log () 137 { 138 if [ "$VERBOSE" = "yes" ] ; then 139 echo "$@" 140 else 141 if [ -n "$TMPLOG" ] ; then 142 echo "$@" >> $TMPLOG 143 fi 144 fi 145 } 146 147 log_n () 148 { 149 if [ "$VERBOSE" = "yes" ] ; then 150 printf %s "$@" 151 else 152 if [ -n "$TMPLOG" ] ; then 153 printf %s "$@" >> $TMPLOG 154 fi 155 fi 156 } 157 158 run () 159 { 160 if [ "$DRYRUN" = "yes" ] ; then 161 echo "## SKIP COMMAND: $@" 162 elif [ "$VERBOSE" = "yes" ] ; then 163 echo "## COMMAND: $@" 164 "$@" 2>&1 165 else 166 if [ -n "$TMPLOG" ] ; then 167 echo "## COMMAND: $@" >> $TMPLOG 168 "$@" >>$TMPLOG 2>&1 169 else 170 "$@" > /dev/null 2>&1 171 fi 172 fi 173 } 174 175 panic () 176 { 177 dump "ERROR: $@" 178 exit 1 179 } 180 181 fail_panic () 182 { 183 if [ $? != 0 ] ; then 184 dump "ERROR: $@" 185 exit 1 186 fi 187 } 188 189 fail_warning () 190 { 191 if [ $? != 0 ] ; then 192 dump "WARNING: $@" 193 fi 194 } 195 196 197 ## Utilities 198 ## 199 200 # Return the value of a given named variable 201 # $1: variable name 202 # 203 # example: 204 # FOO=BAR 205 # BAR=ZOO 206 # echo `var_value $FOO` 207 # will print 'ZOO' 208 # 209 var_value () 210 { 211 # find a better way to do that ? 212 eval echo "$`echo $1`" 213 } 214 215 # convert to uppercase 216 # assumes tr is installed on the platform ? 217 # 218 to_uppercase () 219 { 220 echo $1 | tr "[:lower:]" "[:upper:]" 221 } 222 223 ## First, we need to detect the HOST CPU, because proper HOST_ARCH detection 224 ## requires platform-specific tricks. 225 ## 226 HOST_EXE="" 227 HOST_OS=`uname -s` 228 case "$HOST_OS" in 229 Darwin) 230 HOST_OS=darwin 231 ;; 232 Linux) 233 # note that building 32-bit binaries on x86_64 is handled later 234 HOST_OS=linux 235 ;; 236 FreeBsd) # note: this is not tested 237 HOST_OS=freebsd 238 ;; 239 CYGWIN*|*_NT-*) 240 HOST_OS=windows 241 HOST_EXE=.exe 242 if [ "x$OSTYPE" = xcygwin ] ; then 243 HOST_OS=cygwin 244 fi 245 ;; 246 esac 247 248 log "HOST_OS=$HOST_OS" 249 log "HOST_EXE=$HOST_EXE" 250 251 ## Now find the host architecture. This must correspond to the bitness of 252 ## the binaries we're going to run with this NDK. Certain platforms allow 253 ## you to use a 64-bit kernel with a 32-bit userland, and unfortunately 254 ## commands like 'uname -m' only report the kernel bitness. 255 ## 256 HOST_ARCH=`uname -m` 257 case "$HOST_ARCH" in 258 i?86) HOST_ARCH=x86 259 # "uname -m" reports i386 on Snow Leopard even though its architecture is 260 # 64-bit. In order to use it to build 64-bit toolchains we need to fix the 261 # reporting anomoly here. 262 if [ "$HOST_OS" = darwin ] ; then 263 if ! echo __LP64__ | (CCOPTS= gcc -E - 2>/dev/null) | grep -q __LP64__ ; then 264 # or if gcc -dM -E - < /dev/null | grep -q __LP64__; then 265 HOST_ARCH=x86_64 266 fi 267 fi 268 ;; 269 amd64) HOST_ARCH=x86_64 270 ;; 271 powerpc) HOST_ARCH=ppc 272 ;; 273 esac 274 275 HOST_FILE_PROGRAM="file" 276 case "$HOST_OS-$HOST_ARCH" in 277 linux-x86_64|darwin-x86_64) 278 ## On Linux or Darwin, a 64-bit kernel doesn't mean that the user-land 279 ## is always 32-bit, so use "file" to determine the bitness of the shell 280 ## that invoked us. The -L option is used to de-reference symlinks. 281 ## 282 ## Note that on Darwin, a single executable can contain both x86 and 283 ## x86_64 machine code, so just look for x86_64 (darwin) or x86-64 (Linux) 284 ## in the output. 285 ## 286 ## Also note that some versions of 'file' in MacPort may report erroneous 287 ## result. See http://b.android.com/53769. Use /usr/bin/file if exists. 288 if [ "$HOST_OS" = "darwin" ]; then 289 SYSTEM_FILE_PROGRAM="/usr/bin/file" 290 test -x "$SYSTEM_FILE_PROGRAM" && HOST_FILE_PROGRAM="$SYSTEM_FILE_PROGRAM" 291 fi 292 "$HOST_FILE_PROGRAM" -L "$SHELL" | grep -q "x86[_-]64" 293 if [ $? != 0 ]; then 294 # $SHELL is not a 64-bit executable, so assume our userland is too. 295 log "Detected 32-bit userland on 64-bit kernel system!" 296 HOST_ARCH=x86 297 fi 298 ;; 299 esac 300 301 log "HOST_ARCH=$HOST_ARCH" 302 303 # at this point, the supported values for HOST_ARCH are: 304 # x86 305 # x86_64 306 # ppc 307 # 308 # other values may be possible but haven't been tested 309 # 310 # at this point, the value of HOST_OS should be one of the following: 311 # linux 312 # darwin 313 # windows (MSys) 314 # cygwin 315 # 316 # Note that cygwin is treated as a special case because it behaves very differently 317 # for a few things. Other values may be possible but have not been tested 318 # 319 320 # define HOST_TAG as a unique tag used to identify both the host OS and CPU 321 # supported values are: 322 # 323 # linux-x86 324 # linux-x86_64 325 # darwin-x86 326 # darwin-x86_64 327 # darwin-ppc 328 # windows 329 # windows-x86_64 330 # 331 # other values are possible but were not tested. 332 # 333 compute_host_tag () 334 { 335 HOST_TAG=${HOST_OS}-${HOST_ARCH} 336 # Special case for windows-x86 => windows 337 case $HOST_TAG in 338 windows-x86|cygwin-x86) 339 HOST_TAG="windows" 340 ;; 341 esac 342 log "HOST_TAG=$HOST_TAG" 343 } 344 345 compute_host_tag 346 347 # Compute the number of host CPU cores an HOST_NUM_CPUS 348 # 349 case "$HOST_OS" in 350 linux) 351 HOST_NUM_CPUS=`cat /proc/cpuinfo | grep processor | wc -l` 352 ;; 353 darwin|freebsd) 354 HOST_NUM_CPUS=`sysctl -n hw.ncpu` 355 ;; 356 windows|cygwin) 357 HOST_NUM_CPUS=$NUMBER_OF_PROCESSORS 358 ;; 359 *) # let's play safe here 360 HOST_NUM_CPUS=1 361 esac 362 363 log "HOST_NUM_CPUS=$HOST_NUM_CPUS" 364 365 # If BUILD_NUM_CPUS is not already defined in your environment, 366 # define it as the double of HOST_NUM_CPUS. This is used to 367 # run Make commands in parralles, as in 'make -j$BUILD_NUM_CPUS' 368 # 369 if [ -z "$BUILD_NUM_CPUS" ] ; then 370 BUILD_NUM_CPUS=`expr $HOST_NUM_CPUS \* 2` 371 fi 372 373 log "BUILD_NUM_CPUS=$BUILD_NUM_CPUS" 374 375 376 ## HOST TOOLCHAIN SUPPORT 377 ## 378 379 # force the generation of 32-bit binaries on 64-bit systems 380 # 381 FORCE_32BIT=no 382 force_32bit_binaries () 383 { 384 if [ "$HOST_ARCH" = x86_64 ] ; then 385 log "Forcing generation of 32-bit host binaries on $HOST_ARCH" 386 FORCE_32BIT=yes 387 HOST_ARCH=x86 388 log "HOST_ARCH=$HOST_ARCH" 389 compute_host_tag 390 fi 391 } 392 393 # On Windows, cygwin binaries will be generated by default, but 394 # you can force mingw ones that do not link to cygwin.dll if you 395 # call this function. 396 # 397 disable_cygwin () 398 { 399 if [ $HOST_OS = cygwin ] ; then 400 log "Disabling cygwin binaries generation" 401 CFLAGS="$CFLAGS -mno-cygwin" 402 LDFLAGS="$LDFLAGS -mno-cygwin" 403 HOST_OS=windows 404 compute_host_tag 405 fi 406 } 407 408 # Various probes are going to need to run a small C program 409 mkdir -p $TMPDIR/tmp/tests 410 411 TMPC=$TMPDIR/tmp/tests/test-$$.c 412 TMPO=$TMPDIR/tmp/tests/test-$$.o 413 TMPE=$TMPDIR/tmp/tests/test-$$$EXE 414 TMPL=$TMPDIR/tmp/tests/test-$$.log 415 416 # cleanup temporary files 417 clean_temp () 418 { 419 rm -f $TMPC $TMPO $TMPL $TMPE 420 } 421 422 # cleanup temp files then exit with an error 423 clean_exit () 424 { 425 clean_temp 426 exit 1 427 } 428 429 # this function will setup the compiler and linker and check that they work as advertised 430 # note that you should call 'force_32bit_binaries' before this one if you want it to 431 # generate 32-bit binaries on 64-bit systems (that support it). 432 # 433 setup_toolchain () 434 { 435 if [ -z "$CC" ] ; then 436 CC=gcc 437 fi 438 if [ -z "$CXX" ] ; then 439 CXX=g++ 440 fi 441 if [ -z "$CXXFLAGS" ] ; then 442 CXXFLAGS="$CFLAGS" 443 fi 444 if [ -z "$LD" ] ; then 445 LD="$CC" 446 fi 447 448 log "Using '$CC' as the C compiler" 449 450 # check that we can compile a trivial C program with this compiler 451 mkdir -p $(dirname "$TMPC") 452 cat > $TMPC <<EOF 453 int main(void) {} 454 EOF 455 456 if [ "$FORCE_32BIT" = yes ] ; then 457 CC="$CC -m32" 458 CXX="$CXX -m32" 459 LD="$LD -m32" 460 compile 461 if [ $? != 0 ] ; then 462 # sometimes, we need to also tell the assembler to generate 32-bit binaries 463 # this is highly dependent on your GCC installation (and no, we can't set 464 # this flag all the time) 465 CFLAGS="$CFLAGS -Wa,--32" 466 compile 467 fi 468 fi 469 470 compile 471 if [ $? != 0 ] ; then 472 echo "your C compiler doesn't seem to work:" 473 cat $TMPL 474 clean_exit 475 fi 476 log "CC : compiler check ok ($CC)" 477 478 # check that we can link the trivial program into an executable 479 link 480 if [ $? != 0 ] ; then 481 OLD_LD="$LD" 482 LD="$CC" 483 compile 484 link 485 if [ $? != 0 ] ; then 486 LD="$OLD_LD" 487 echo "your linker doesn't seem to work:" 488 cat $TMPL 489 clean_exit 490 fi 491 fi 492 log "Using '$LD' as the linker" 493 log "LD : linker check ok ($LD)" 494 495 # check the C++ compiler 496 log "Using '$CXX' as the C++ compiler" 497 498 cat > $TMPC <<EOF 499 #include <iostream> 500 using namespace std; 501 int main() 502 { 503 cout << "Hello World!" << endl; 504 return 0; 505 } 506 EOF 507 508 compile_cpp 509 if [ $? != 0 ] ; then 510 echo "your C++ compiler doesn't seem to work" 511 cat $TMPL 512 clean_exit 513 fi 514 515 log "CXX : C++ compiler check ok ($CXX)" 516 517 # XXX: TODO perform AR checks 518 AR=ar 519 ARFLAGS= 520 } 521 522 # try to compile the current source file in $TMPC into an object 523 # stores the error log into $TMPL 524 # 525 compile () 526 { 527 log "Object : $CC -o $TMPO -c $CFLAGS $TMPC" 528 $CC -o $TMPO -c $CFLAGS $TMPC 2> $TMPL 529 } 530 531 compile_cpp () 532 { 533 log "Object : $CXX -o $TMPO -c $CXXFLAGS $TMPC" 534 $CXX -o $TMPO -c $CXXFLAGS $TMPC 2> $TMPL 535 } 536 537 # try to link the recently built file into an executable. error log in $TMPL 538 # 539 link() 540 { 541 log "Link : $LD -o $TMPE $TMPO $LDFLAGS" 542 $LD -o $TMPE $TMPO $LDFLAGS 2> $TMPL 543 } 544 545 # run a command 546 # 547 execute() 548 { 549 log "Running: $*" 550 $* 551 } 552 553 # perform a simple compile / link / run of the source file in $TMPC 554 compile_exec_run() 555 { 556 log "RunExec : $CC -o $TMPE $CFLAGS $TMPC" 557 compile 558 if [ $? != 0 ] ; then 559 echo "Failure to compile test program" 560 cat $TMPC 561 cat $TMPL 562 clean_exit 563 fi 564 link 565 if [ $? != 0 ] ; then 566 echo "Failure to link test program" 567 cat $TMPC 568 echo "------" 569 cat $TMPL 570 clean_exit 571 fi 572 $TMPE 573 } 574 575 pattern_match () 576 { 577 echo "$2" | grep -q -E -e "$1" 578 } 579 580 # Let's check that we have a working md5sum here 581 check_md5sum () 582 { 583 A_MD5=`echo "A" | md5sum | cut -d' ' -f1` 584 if [ "$A_MD5" != "bf072e9119077b4e76437a93986787ef" ] ; then 585 echo "Please install md5sum on this machine" 586 exit 2 587 fi 588 } 589 590 # Find if a given shell program is available. 591 # We need to take care of the fact that the 'which <foo>' command 592 # may return either an empty string (Linux) or something like 593 # "no <foo> in ..." (Darwin). Also, we need to redirect stderr 594 # to /dev/null for Cygwin 595 # 596 # $1: variable name 597 # $2: program name 598 # 599 # Result: set $1 to the full path of the corresponding command 600 # or to the empty/undefined string if not available 601 # 602 find_program () 603 { 604 local PROG RET 605 PROG=`which $2 2>/dev/null` 606 RET=$? 607 if [ $RET != 0 ]; then 608 PROG= 609 fi 610 eval $1=\"$PROG\" 611 return $RET 612 } 613 614 prepare_download () 615 { 616 find_program CMD_WGET wget 617 find_program CMD_CURL curl 618 find_program CMD_SCRP scp 619 } 620 621 find_pbzip2 () 622 { 623 if [ -z "$_PBZIP2_initialized" ] ; then 624 find_program PBZIP2 pbzip2 625 _PBZIP2_initialized="yes" 626 fi 627 } 628 629 # Download a file with either 'curl', 'wget' or 'scp' 630 # 631 # $1: source URL (e.g. http://foo.com, ssh://blah, /some/path) 632 # $2: target file 633 download_file () 634 { 635 # Is this HTTP, HTTPS or FTP ? 636 if pattern_match "^(http|https|ftp):.*" "$1"; then 637 if [ -n "$CMD_WGET" ] ; then 638 run $CMD_WGET -O $2 $1 639 elif [ -n "$CMD_CURL" ] ; then 640 run $CMD_CURL -o $2 $1 641 else 642 echo "Please install wget or curl on this machine" 643 exit 1 644 fi 645 return 646 fi 647 648 # Is this SSH ? 649 # Accept both ssh://<path> or <machine>:<path> 650 # 651 if pattern_match "^(ssh|[^:]+):.*" "$1"; then 652 if [ -n "$CMD_SCP" ] ; then 653 scp_src=`echo $1 | sed -e s%ssh://%%g` 654 run $CMD_SCP $scp_src $2 655 else 656 echo "Please install scp on this machine" 657 exit 1 658 fi 659 return 660 fi 661 662 # Is this a file copy ? 663 # Accept both file://<path> or /<path> 664 # 665 if pattern_match "^(file://|/).*" "$1"; then 666 cp_src=`echo $1 | sed -e s%^file://%%g` 667 run cp -f $cp_src $2 668 return 669 fi 670 } 671 672 # Form the relative path between from one abs path to another 673 # 674 # $1 : start path 675 # $2 : end path 676 # 677 # From: 678 # http://stackoverflow.com/questions/2564634/bash-convert-absolute-path-into-relative-path-given-a-current-directory 679 relpath () 680 { 681 [ $# -ge 1 ] && [ $# -le 2 ] || return 1 682 current="${2:+"$1"}" 683 target="${2:-"$1"}" 684 [ "$target" != . ] || target=/ 685 target="/${target##/}" 686 [ "$current" != . ] || current=/ 687 current="${current:="/"}" 688 current="/${current##/}" 689 appendix="${target##/}" 690 relative='' 691 while appendix="${target#"$current"/}" 692 [ "$current" != '/' ] && [ "$appendix" = "$target" ]; do 693 if [ "$current" = "$appendix" ]; then 694 relative="${relative:-.}" 695 echo "${relative#/}" 696 return 0 697 fi 698 current="${current%/*}" 699 relative="$relative${relative:+/}.." 700 done 701 relative="$relative${relative:+${appendix:+/}}${appendix#/}" 702 echo "$relative" 703 } 704 705 # Pack a given archive 706 # 707 # $1: archive file path (including extension) 708 # $2: source directory for archive content 709 # $3+: list of files (including patterns), all if empty 710 pack_archive () 711 { 712 local ARCHIVE="$1" 713 local SRCDIR="$2" 714 local SRCFILES 715 local TARFLAGS ZIPFLAGS 716 shift; shift; 717 if [ -z "$1" ] ; then 718 SRCFILES="*" 719 else 720 SRCFILES="$@" 721 fi 722 if [ "`basename $ARCHIVE`" = "$ARCHIVE" ] ; then 723 ARCHIVE="`pwd`/$ARCHIVE" 724 fi 725 mkdir -p `dirname $ARCHIVE` 726 727 TARFLAGS="--exclude='*.py[cod]' --exclude='*.swp' --exclude=.git --exclude=.gitignore -cf" 728 ZIPFLAGS="-x *.git* -x *.pyc -x *.pyo -9qr" 729 # Ensure symlinks are stored as is in zip files. for toolchains 730 # this can save up to 7 MB in the size of the final archive 731 #ZIPFLAGS="$ZIPFLAGS --symlinks" 732 case "$ARCHIVE" in 733 *.zip) 734 rm -f $ARCHIVE 735 (cd $SRCDIR && run zip $ZIPFLAGS "$ARCHIVE" $SRCFILES) 736 ;; 737 *.tar.bz2) 738 find_pbzip2 739 if [ -n "$PBZIP2" ] ; then 740 (cd $SRCDIR && run tar --use-compress-prog=pbzip2 $TARFLAGS "$ARCHIVE" $SRCFILES) 741 else 742 (cd $SRCDIR && run tar -j $TARFLAGS "$ARCHIVE" $SRCFILES) 743 fi 744 ;; 745 *) 746 panic "Unsupported archive format: $ARCHIVE" 747 ;; 748 esac 749 } 750 751 # Copy a directory, create target location if needed 752 # 753 # $1: source directory 754 # $2: target directory location 755 # 756 copy_directory () 757 { 758 local SRCDIR="$1" 759 local DSTDIR="$2" 760 if [ ! -d "$SRCDIR" ] ; then 761 panic "Can't copy from non-directory: $SRCDIR" 762 fi 763 log "Copying directory: " 764 log " from $SRCDIR" 765 log " to $DSTDIR" 766 mkdir -p "$DSTDIR" && (cd "$SRCDIR" && 2>/dev/null tar cf - *) | (tar xf - -C "$DSTDIR") 767 fail_panic "Cannot copy to directory: $DSTDIR" 768 } 769 770 # Move a directory, create target location if needed 771 # 772 # $1: source directory 773 # $2: target directory location 774 # 775 move_directory () 776 { 777 local SRCDIR="$1" 778 local DSTDIR="$2" 779 if [ ! -d "$SRCDIR" ] ; then 780 panic "Can't move from non-directory: $SRCDIR" 781 fi 782 log "Move directory: " 783 log " from $SRCDIR" 784 log " to $DSTDIR" 785 mkdir -p "$DSTDIR" && (mv "$SRCDIR"/* "$DSTDIR") 786 fail_panic "Cannot move to directory: $DSTDIR" 787 } 788 789 # This is the same than copy_directory(), but symlinks will be replaced 790 # by the file they actually point to instead. 791 copy_directory_nolinks () 792 { 793 local SRCDIR="$1" 794 local DSTDIR="$2" 795 if [ ! -d "$SRCDIR" ] ; then 796 panic "Can't copy from non-directory: $SRCDIR" 797 fi 798 log "Copying directory (without symlinks): " 799 log " from $SRCDIR" 800 log " to $DSTDIR" 801 mkdir -p "$DSTDIR" && (cd "$SRCDIR" && tar chf - *) | (tar xf - -C "$DSTDIR") 802 fail_panic "Cannot copy to directory: $DSTDIR" 803 } 804 805 # Copy certain files from one directory to another one 806 # $1: source directory 807 # $2: target directory 808 # $3+: file list (including patterns) 809 copy_file_list () 810 { 811 local SRCDIR="$1" 812 local DSTDIR="$2" 813 shift; shift; 814 if [ ! -d "$SRCDIR" ] ; then 815 panic "Cant' copy from non-directory: $SRCDIR" 816 fi 817 log "Copying file: $@" 818 log " from $SRCDIR" 819 log " to $DSTDIR" 820 mkdir -p "$DSTDIR" && (cd "$SRCDIR" && (echo $@ | tr ' ' '\n' | tar cf - -T -)) | (tar xf - -C "$DSTDIR") 821 fail_panic "Cannot copy files to directory: $DSTDIR" 822 } 823 824 # Rotate a log file 825 # If the given log file exist, add a -1 to the end of the file. 826 # If older log files exist, rename them to -<n+1> 827 # $1: log file 828 # $2: maximum version to retain [optional] 829 rotate_log () 830 { 831 # Default Maximum versions to retain 832 local MAXVER="5" 833 local LOGFILE="$1" 834 shift; 835 if [ ! -z "$1" ] ; then 836 local tmpmax="$1" 837 shift; 838 tmpmax=`expr $tmpmax + 0` 839 if [ $tmpmax -lt 1 ] ; then 840 panic "Invalid maximum log file versions '$tmpmax' invalid; defaulting to $MAXVER" 841 else 842 MAXVER=$tmpmax; 843 fi 844 fi 845 846 # Do Nothing if the log file does not exist 847 if [ ! -f "${LOGFILE}" ] ; then 848 return 849 fi 850 851 # Rename existing older versions 852 ver=$MAXVER 853 while [ $ver -ge 1 ] 854 do 855 local prev=$(( $ver - 1 )) 856 local old="-$prev" 857 858 # Instead of old version 0; use the original filename 859 if [ $ver -eq 1 ] ; then 860 old="" 861 fi 862 863 if [ -f "${LOGFILE}${old}" ] ; then 864 mv -f "${LOGFILE}${old}" "${LOGFILE}-${ver}" 865 fi 866 867 ver=$prev 868 done 869 } 870 871 # Dereference symlink 872 # $1+: directories 873 dereference_symlink () 874 { 875 local DIRECTORY SYMLINKS DIR FILE LINK 876 for DIRECTORY in "$@"; do 877 if [ -d "$DIRECTORY" ]; then 878 while true; do 879 # Find all symlinks in this directory. 880 SYMLINKS=`find $DIRECTORY -type l` 881 if [ -z "$SYMLINKS" ]; then 882 break; 883 fi 884 # Iterate symlinks 885 for SYMLINK in $SYMLINKS; do 886 if [ -L "$SYMLINK" ]; then 887 DIR=`dirname "$SYMLINK"` 888 FILE=`basename "$SYMLINK"` 889 # Note that if `readlink $FILE` is also a link, we want to deal 890 # with it in the next iteration. There is potential infinite-loop 891 # situation for cicular link doesn't exist in our case, though. 892 (cd "$DIR" && \ 893 LINK=`readlink "$FILE"` && \ 894 test ! -L "$LINK" && \ 895 rm -f "$FILE" && \ 896 cp -a "$LINK" "$FILE") 897 fi 898 done 899 done 900 fi 901 done 902 } 903