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 23if [ -z "$TMPDIR" ]; then 24 export TMPDIR=/tmp/ndk-$USER 25fi 26 27OS=`uname -s` 28if [ "$OS" == "Darwin" -a -z "$MACOSX_DEPLOYMENT_TARGET" ]; then 29 export MACOSX_DEPLOYMENT_TARGET="10.8" 30fi 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 37find_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# 57if [ -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 64fi 65 66echo "$ANDROID_NDK_ROOT" | grep -q -e " " 67if [ $? = 0 ] ; then 68 echo "ERROR: The Android NDK installation path contains a space !" 69 echo "Please install to a different location." 70 exit 1 71fi 72 73if [ ! -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 77fi 78 79if [ ! -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 83fi 84 85## Use DRYRUN to find out top-level commands. 86DRYRUN=${DRYRUN-no} 87 88## Logging support 89## 90VERBOSE=${VERBOSE-yes} 91 92 93# If NDK_LOGFILE is defined in the environment, use this as the log file 94TMPLOG= 95if [ -n "$NDK_LOGFILE" ] ; then 96 mkdir -p `dirname "$NDK_LOGFILE"` && touch "$NDK_LOGFILE" 97 TMPLOG="$NDK_LOGFILE" 98fi 99 100# Setup a log file where all log() output will be sent 101# 102# $1: log file path (optional) 103# 104setup_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 120dump () 121{ 122 if [ -n "$TMPLOG" ] ; then 123 echo "$@" >> $TMPLOG 124 fi 125 echo "$@" 126} 127 128dump_n () 129{ 130 if [ -n "$TMPLOG" ] ; then 131 printf %s "$@" >> $TMPLOG 132 fi 133 printf %s "$@" 134} 135 136log () 137{ 138 if [ "$VERBOSE" = "yes" ] ; then 139 echo "$@" 140 else 141 if [ -n "$TMPLOG" ] ; then 142 echo "$@" >> $TMPLOG 143 fi 144 fi 145} 146 147log_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 158run () 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 175panic () 176{ 177 dump "ERROR: $@" 178 exit 1 179} 180 181fail_panic () 182{ 183 if [ $? != 0 ] ; then 184 dump "ERROR: $@" 185 exit 1 186 fi 187} 188 189fail_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# 209var_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# 218to_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## 226HOST_EXE="" 227HOST_OS=`uname -s` 228case "$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 ;; 246esac 247 248log "HOST_OS=$HOST_OS" 249log "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## 256HOST_ARCH=`uname -m` 257case "$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 ;; 273esac 274 275HOST_FILE_PROGRAM="file" 276case "$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 ;; 299esac 300 301log "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# 333compute_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 345compute_host_tag 346 347# Compute the number of host CPU cores an HOST_NUM_CPUS 348# 349case "$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 361esac 362 363log "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# 369if [ -z "$BUILD_NUM_CPUS" ] ; then 370 BUILD_NUM_CPUS=`expr $HOST_NUM_CPUS \* 2` 371fi 372 373log "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# 381FORCE_32BIT=no 382force_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# 397disable_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 409mkdir -p $TMPDIR/tmp/tests 410 411TMPC=$TMPDIR/tmp/tests/test-$$.c 412TMPO=$TMPDIR/tmp/tests/test-$$.o 413TMPE=$TMPDIR/tmp/tests/test-$$$EXE 414TMPL=$TMPDIR/tmp/tests/test-$$.log 415 416# cleanup temporary files 417clean_temp () 418{ 419 rm -f $TMPC $TMPO $TMPL $TMPE 420} 421 422# cleanup temp files then exit with an error 423clean_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# 433setup_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 453int main(void) {} 454EOF 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> 500using namespace std; 501int main() 502{ 503 cout << "Hello World!" << endl; 504 return 0; 505} 506EOF 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# 525compile () 526{ 527 log "Object : $CC -o $TMPO -c $CFLAGS $TMPC" 528 $CC -o $TMPO -c $CFLAGS $TMPC 2> $TMPL 529} 530 531compile_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# 539link() 540{ 541 log "Link : $LD -o $TMPE $TMPO $LDFLAGS" 542 $LD -o $TMPE $TMPO $LDFLAGS 2> $TMPL 543} 544 545# run a command 546# 547execute() 548{ 549 log "Running: $*" 550 $* 551} 552 553# perform a simple compile / link / run of the source file in $TMPC 554compile_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 575pattern_match () 576{ 577 echo "$2" | grep -q -E -e "$1" 578} 579 580# Let's check that we have a working md5sum here 581check_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# 602find_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 614prepare_download () 615{ 616 find_program CMD_WGET wget 617 find_program CMD_CURL curl 618 find_program CMD_SCRP scp 619} 620 621find_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 633download_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 679relpath () 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 710pack_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# 756copy_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# 775move_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. 791copy_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) 809copy_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] 829rotate_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 873dereference_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