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