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