blob: 737e0e4501f8c09e788516d0fc3d03fe848881e9 [file] [log] [blame]
# Copyright (C) 2009 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# A collection of shell function definitions used by various build scripts
# in the Android NDK (Native Development Kit)
#
# Get current script name into PROGNAME
PROGNAME=`basename $0`
if [ -z "$TMPDIR" ]; then
export TMPDIR=/tmp/ndk-$USER
fi
OS=`uname -s`
if [ "$OS" == "Darwin" -a -z "$MACOSX_DEPLOYMENT_TARGET" ]; then
export MACOSX_DEPLOYMENT_TARGET="10.8"
fi
# Find the Android NDK root, assuming we are invoked from a script
# within its directory structure.
#
# $1: Variable name that will receive the path
# $2: Path of invoking script
find_ndk_root ()
{
# Try to auto-detect the NDK root by walking up the directory
# path to the current script.
local PROGDIR="`dirname \"$2\"`"
while [ -n "1" ] ; do
if [ -d "$PROGDIR/build/core" ] ; then
break
fi
if [ -z "$PROGDIR" -o "$PROGDIR" = '/' ] ; then
return 1
fi
PROGDIR="`cd \"$PROGDIR/..\" && pwd`"
done
eval $1="$PROGDIR"
}
# Put location of Android NDK into ANDROID_NDK_ROOT and
# perform a tiny amount of sanity check
#
if [ -z "$ANDROID_NDK_ROOT" ] ; then
find_ndk_root ANDROID_NDK_ROOT "$0"
if [ $? != 0 ]; then
echo "Please define ANDROID_NDK_ROOT to point to the root of your"
echo "Android NDK installation."
exit 1
fi
fi
echo "$ANDROID_NDK_ROOT" | grep -q -e " "
if [ $? = 0 ] ; then
echo "ERROR: The Android NDK installation path contains a space !"
echo "Please install to a different location."
exit 1
fi
if [ ! -d $ANDROID_NDK_ROOT ] ; then
echo "ERROR: Your ANDROID_NDK_ROOT variable does not point to a directory."
echo "ANDROID_NDK_ROOT=$ANDROID_NDK_ROOT"
exit 1
fi
if [ ! -f $ANDROID_NDK_ROOT/build/tools/ndk-common.sh ] ; then
echo "ERROR: Your ANDROID_NDK_ROOT does not contain a valid NDK build system."
echo "ANDROID_NDK_ROOT=$ANDROID_NDK_ROOT"
exit 1
fi
## Use DRYRUN to find out top-level commands.
DRYRUN=${DRYRUN-no}
## Logging support
##
VERBOSE=${VERBOSE-yes}
dump ()
{
echo "$@"
}
dump_n ()
{
printf %s "$@"
}
log ()
{
if [ "$VERBOSE" = "yes" ] ; then
echo "$@"
fi
}
log_n ()
{
if [ "$VERBOSE" = "yes" ] ; then
printf %s "$@"
fi
}
run ()
{
if [ "$DRYRUN" = "yes" ] ; then
echo "## SKIP COMMAND: $@"
elif [ "$VERBOSE" = "yes" ] ; then
echo "## COMMAND: $@"
"$@" 2>&1
else
"$@" > /dev/null 2>&1
fi
}
panic ()
{
dump "ERROR: $@"
exit 1
}
fail_panic ()
{
if [ $? != 0 ] ; then
dump "ERROR: $@"
exit 1
fi
}
fail_warning ()
{
if [ $? != 0 ] ; then
dump "WARNING: $@"
fi
}
## Utilities
##
# Return the value of a given named variable
# $1: variable name
#
# example:
# FOO=BAR
# BAR=ZOO
# echo `var_value $FOO`
# will print 'ZOO'
#
var_value ()
{
# find a better way to do that ?
eval echo "$`echo $1`"
}
# convert to uppercase
# assumes tr is installed on the platform ?
#
to_uppercase ()
{
echo $1 | tr "[:lower:]" "[:upper:]"
}
## First, we need to detect the HOST CPU, because proper HOST_ARCH detection
## requires platform-specific tricks.
##
HOST_EXE=""
HOST_OS=`uname -s`
HOST_ARCH=x86_64
case "$HOST_OS" in
Darwin)
HOST_OS=darwin
;;
Linux)
# note that building 32-bit binaries on x86_64 is handled later
HOST_OS=linux
;;
FreeBsd) # note: this is not tested
HOST_OS=freebsd
;;
CYGWIN*|*_NT-*)
HOST_OS=windows
HOST_EXE=.exe
HOST_ARCH=`uname -m`
if [ "x$OSTYPE" = xcygwin ] ; then
HOST_OS=cygwin
fi
;;
esac
log "HOST_OS=$HOST_OS"
log "HOST_EXE=$HOST_EXE"
log "HOST_ARCH=$HOST_ARCH"
# at this point, the supported values for HOST_ARCH are:
# x86
# x86_64
#
# other values may be possible but haven't been tested
#
# at this point, the value of HOST_OS should be one of the following:
# linux
# darwin
# windows (MSys)
# cygwin
#
# Note that cygwin is treated as a special case because it behaves very differently
# for a few things. Other values may be possible but have not been tested
#
# define HOST_TAG as a unique tag used to identify both the host OS and CPU
# supported values are:
#
# linux-x86_64
# darwin-x86_64
# windows
# windows-x86_64
#
# other values are possible but were not tested.
#
compute_host_tag ()
{
HOST_TAG=${HOST_OS}-${HOST_ARCH}
# Special case for windows-x86 => windows
case $HOST_TAG in
windows-x86|cygwin-x86)
HOST_TAG="windows"
;;
cygwin-x86_64)
HOST_TAG="windows-x86_64"
;;
esac
log "HOST_TAG=$HOST_TAG"
}
compute_host_tag
# Compute the number of host CPU cores an HOST_NUM_CPUS
#
case "$HOST_OS" in
linux)
HOST_NUM_CPUS=`cat /proc/cpuinfo | grep processor | wc -l`
;;
darwin|freebsd)
HOST_NUM_CPUS=`sysctl -n hw.ncpu`
;;
windows|cygwin)
HOST_NUM_CPUS=$NUMBER_OF_PROCESSORS
;;
*) # let's play safe here
HOST_NUM_CPUS=1
esac
log "HOST_NUM_CPUS=$HOST_NUM_CPUS"
# If BUILD_NUM_CPUS is not already defined in your environment,
# define it as the double of HOST_NUM_CPUS. This is used to
# run Make commands in parralles, as in 'make -j$BUILD_NUM_CPUS'
#
if [ -z "$BUILD_NUM_CPUS" ] ; then
BUILD_NUM_CPUS=`expr $HOST_NUM_CPUS \* 2`
fi
log "BUILD_NUM_CPUS=$BUILD_NUM_CPUS"
## HOST TOOLCHAIN SUPPORT
##
# force the generation of 32-bit binaries on 64-bit systems
#
FORCE_32BIT=no
force_32bit_binaries ()
{
if [ "$HOST_ARCH" = x86_64 ] ; then
log "Forcing generation of 32-bit host binaries on $HOST_ARCH"
FORCE_32BIT=yes
HOST_ARCH=x86
log "HOST_ARCH=$HOST_ARCH"
compute_host_tag
fi
}
# On Windows, cygwin binaries will be generated by default, but
# you can force mingw ones that do not link to cygwin.dll if you
# call this function.
#
disable_cygwin ()
{
if [ $HOST_OS = cygwin ] ; then
log "Disabling cygwin binaries generation"
CFLAGS="$CFLAGS -mno-cygwin"
LDFLAGS="$LDFLAGS -mno-cygwin"
HOST_OS=windows
compute_host_tag
fi
}
# Various probes are going to need to run a small C program
mkdir -p $TMPDIR/tmp/tests
TMPC=$TMPDIR/tmp/tests/test-$$.c
TMPO=$TMPDIR/tmp/tests/test-$$.o
TMPE=$TMPDIR/tmp/tests/test-$$$EXE
TMPL=$TMPDIR/tmp/tests/test-$$.log
# cleanup temporary files
clean_temp ()
{
rm -f $TMPC $TMPO $TMPL $TMPE
}
# cleanup temp files then exit with an error
clean_exit ()
{
clean_temp
exit 1
}
# this function will setup the compiler and linker and check that they work as advertised
# note that you should call 'force_32bit_binaries' before this one if you want it to
# generate 32-bit binaries on 64-bit systems (that support it).
#
setup_toolchain ()
{
if [ -z "$CC" ] ; then
CC=gcc
fi
if [ -z "$CXX" ] ; then
CXX=g++
fi
if [ -z "$CXXFLAGS" ] ; then
CXXFLAGS="$CFLAGS"
fi
if [ -z "$LD" ] ; then
LD="$CC"
fi
log "Using '$CC' as the C compiler"
# check that we can compile a trivial C program with this compiler
mkdir -p $(dirname "$TMPC")
cat > $TMPC <<EOF
int main(void) {}
EOF
if [ "$FORCE_32BIT" = yes ] ; then
CC="$CC -m32"
CXX="$CXX -m32"
LD="$LD -m32"
compile
if [ $? != 0 ] ; then
# sometimes, we need to also tell the assembler to generate 32-bit binaries
# this is highly dependent on your GCC installation (and no, we can't set
# this flag all the time)
CFLAGS="$CFLAGS -Wa,--32"
compile
fi
fi
compile
if [ $? != 0 ] ; then
echo "your C compiler doesn't seem to work:"
cat $TMPL
clean_exit
fi
log "CC : compiler check ok ($CC)"
# check that we can link the trivial program into an executable
link
if [ $? != 0 ] ; then
OLD_LD="$LD"
LD="$CC"
compile
link
if [ $? != 0 ] ; then
LD="$OLD_LD"
echo "your linker doesn't seem to work:"
cat $TMPL
clean_exit
fi
fi
log "Using '$LD' as the linker"
log "LD : linker check ok ($LD)"
# check the C++ compiler
log "Using '$CXX' as the C++ compiler"
cat > $TMPC <<EOF
#include <iostream>
using namespace std;
int main()
{
cout << "Hello World!" << endl;
return 0;
}
EOF
compile_cpp
if [ $? != 0 ] ; then
echo "your C++ compiler doesn't seem to work"
cat $TMPL
clean_exit
fi
log "CXX : C++ compiler check ok ($CXX)"
# XXX: TODO perform AR checks
AR=ar
ARFLAGS=
}
# try to compile the current source file in $TMPC into an object
# stores the error log into $TMPL
#
compile ()
{
log "Object : $CC -o $TMPO -c $CFLAGS $TMPC"
$CC -o $TMPO -c $CFLAGS $TMPC 2> $TMPL
}
compile_cpp ()
{
log "Object : $CXX -o $TMPO -c $CXXFLAGS $TMPC"
$CXX -o $TMPO -c $CXXFLAGS $TMPC 2> $TMPL
}
# try to link the recently built file into an executable. error log in $TMPL
#
link()
{
log "Link : $LD -o $TMPE $TMPO $LDFLAGS"
$LD -o $TMPE $TMPO $LDFLAGS 2> $TMPL
}
# run a command
#
execute()
{
log "Running: $*"
$*
}
# perform a simple compile / link / run of the source file in $TMPC
compile_exec_run()
{
log "RunExec : $CC -o $TMPE $CFLAGS $TMPC"
compile
if [ $? != 0 ] ; then
echo "Failure to compile test program"
cat $TMPC
cat $TMPL
clean_exit
fi
link
if [ $? != 0 ] ; then
echo "Failure to link test program"
cat $TMPC
echo "------"
cat $TMPL
clean_exit
fi
$TMPE
}
pattern_match ()
{
echo "$2" | grep -q -E -e "$1"
}
# Let's check that we have a working md5sum here
check_md5sum ()
{
A_MD5=`echo "A" | md5sum | cut -d' ' -f1`
if [ "$A_MD5" != "bf072e9119077b4e76437a93986787ef" ] ; then
echo "Please install md5sum on this machine"
exit 2
fi
}
# Find if a given shell program is available.
# We need to take care of the fact that the 'which <foo>' command
# may return either an empty string (Linux) or something like
# "no <foo> in ..." (Darwin). Also, we need to redirect stderr
# to /dev/null for Cygwin
#
# $1: variable name
# $2: program name
#
# Result: set $1 to the full path of the corresponding command
# or to the empty/undefined string if not available
#
find_program ()
{
local PROG RET
PROG=`which $2 2>/dev/null`
RET=$?
if [ $RET != 0 ]; then
PROG=
fi
eval $1=\"$PROG\"
return $RET
}
prepare_download ()
{
find_program CMD_WGET wget
find_program CMD_CURL curl
find_program CMD_SCRP scp
}
find_pbzip2 ()
{
if [ -z "$_PBZIP2_initialized" ] ; then
find_program PBZIP2 pbzip2
_PBZIP2_initialized="yes"
fi
}
# Download a file with either 'curl', 'wget' or 'scp'
#
# $1: source URL (e.g. http://foo.com, ssh://blah, /some/path)
# $2: target file
download_file ()
{
# Is this HTTP, HTTPS or FTP ?
if pattern_match "^(http|https|ftp):.*" "$1"; then
if [ -n "$CMD_WGET" ] ; then
run $CMD_WGET -O $2 $1
elif [ -n "$CMD_CURL" ] ; then
run $CMD_CURL -o $2 $1
else
echo "Please install wget or curl on this machine"
exit 1
fi
return
fi
# Is this SSH ?
# Accept both ssh://<path> or <machine>:<path>
#
if pattern_match "^(ssh|[^:]+):.*" "$1"; then
if [ -n "$CMD_SCP" ] ; then
scp_src=`echo $1 | sed -e s%ssh://%%g`
run $CMD_SCP $scp_src $2
else
echo "Please install scp on this machine"
exit 1
fi
return
fi
# Is this a file copy ?
# Accept both file://<path> or /<path>
#
if pattern_match "^(file://|/).*" "$1"; then
cp_src=`echo $1 | sed -e s%^file://%%g`
run cp -f $cp_src $2
return
fi
}
# Form the relative path between from one abs path to another
#
# $1 : start path
# $2 : end path
#
# From:
# http://stackoverflow.com/questions/2564634/bash-convert-absolute-path-into-relative-path-given-a-current-directory
relpath ()
{
[ $# -ge 1 ] && [ $# -le 2 ] || return 1
current="${2:+"$1"}"
target="${2:-"$1"}"
[ "$target" != . ] || target=/
target="/${target##/}"
[ "$current" != . ] || current=/
current="${current:="/"}"
current="/${current##/}"
appendix="${target##/}"
relative=''
while appendix="${target#"$current"/}"
[ "$current" != '/' ] && [ "$appendix" = "$target" ]; do
if [ "$current" = "$appendix" ]; then
relative="${relative:-.}"
echo "${relative#/}"
return 0
fi
current="${current%/*}"
relative="$relative${relative:+/}.."
done
relative="$relative${relative:+${appendix:+/}}${appendix#/}"
echo "$relative"
}
# Pack a given archive
#
# $1: archive file path (including extension)
# $2: source directory for archive content
# $3+: list of files (including patterns), all if empty
pack_archive ()
{
local ARCHIVE="$1"
local SRCDIR="$2"
local SRCFILES
local TARFLAGS ZIPFLAGS
shift; shift;
if [ -z "$1" ] ; then
SRCFILES="*"
else
SRCFILES="$@"
fi
if [ "`basename $ARCHIVE`" = "$ARCHIVE" ] ; then
ARCHIVE="`pwd`/$ARCHIVE"
fi
mkdir -p `dirname $ARCHIVE`
TARFLAGS="--exclude='*.py[cod]' --exclude='*.swp' --exclude=.git --exclude=.gitignore -cf"
ZIPFLAGS="-x *.git* -x *.pyc -x *.pyo -0qr"
# Ensure symlinks are stored as is in zip files. for toolchains
# this can save up to 7 MB in the size of the final archive
#ZIPFLAGS="$ZIPFLAGS --symlinks"
case "$ARCHIVE" in
*.zip)
rm -f $ARCHIVE
(cd $SRCDIR && run zip $ZIPFLAGS "$ARCHIVE" $SRCFILES)
;;
*.tar.bz2)
find_pbzip2
if [ -n "$PBZIP2" ] ; then
(cd $SRCDIR && run tar --use-compress-prog=pbzip2 $TARFLAGS "$ARCHIVE" $SRCFILES)
else
(cd $SRCDIR && run tar -j $TARFLAGS "$ARCHIVE" $SRCFILES)
fi
;;
*)
panic "Unsupported archive format: $ARCHIVE"
;;
esac
}
# Copy a directory, create target location if needed
#
# $1: source directory
# $2: target directory location
#
copy_directory ()
{
local SRCDIR="$1"
local DSTDIR="$2"
if [ ! -d "$SRCDIR" ] ; then
panic "Can't copy from non-directory: $SRCDIR"
fi
log "Copying directory: "
log " from $SRCDIR"
log " to $DSTDIR"
mkdir -p "$DSTDIR" && (cd "$SRCDIR" && 2>/dev/null tar cf - *) | (tar xf - -C "$DSTDIR")
fail_panic "Cannot copy to directory: $DSTDIR"
}
# Move a directory, create target location if needed
#
# $1: source directory
# $2: target directory location
#
move_directory ()
{
local SRCDIR="$1"
local DSTDIR="$2"
if [ ! -d "$SRCDIR" ] ; then
panic "Can't move from non-directory: $SRCDIR"
fi
log "Move directory: "
log " from $SRCDIR"
log " to $DSTDIR"
mkdir -p "$DSTDIR" && (mv "$SRCDIR"/* "$DSTDIR")
fail_panic "Cannot move to directory: $DSTDIR"
}
# This is the same than copy_directory(), but symlinks will be replaced
# by the file they actually point to instead.
copy_directory_nolinks ()
{
local SRCDIR="$1"
local DSTDIR="$2"
if [ ! -d "$SRCDIR" ] ; then
panic "Can't copy from non-directory: $SRCDIR"
fi
log "Copying directory (without symlinks): "
log " from $SRCDIR"
log " to $DSTDIR"
mkdir -p "$DSTDIR" && (cd "$SRCDIR" && tar chf - *) | (tar xf - -C "$DSTDIR")
fail_panic "Cannot copy to directory: $DSTDIR"
}
# Copy certain files from one directory to another one
# $1: source directory
# $2: target directory
# $3+: file list (including patterns)
copy_file_list ()
{
local SRCDIR="$1"
local DSTDIR="$2"
shift; shift;
if [ ! -d "$SRCDIR" ] ; then
panic "Cant' copy from non-directory: $SRCDIR"
fi
log "Copying file: $@"
log " from $SRCDIR"
log " to $DSTDIR"
mkdir -p "$DSTDIR" && (cd "$SRCDIR" && (echo $@ | tr ' ' '\n' | tar hcf - -T -)) | (tar xf - -C "$DSTDIR")
fail_panic "Cannot copy files to directory: $DSTDIR"
}
# Rotate a log file
# If the given log file exist, add a -1 to the end of the file.
# If older log files exist, rename them to -<n+1>
# $1: log file
# $2: maximum version to retain [optional]
rotate_log ()
{
# Default Maximum versions to retain
local MAXVER="5"
local LOGFILE="$1"
shift;
if [ ! -z "$1" ] ; then
local tmpmax="$1"
shift;
tmpmax=`expr $tmpmax + 0`
if [ $tmpmax -lt 1 ] ; then
panic "Invalid maximum log file versions '$tmpmax' invalid; defaulting to $MAXVER"
else
MAXVER=$tmpmax;
fi
fi
# Do Nothing if the log file does not exist
if [ ! -f "${LOGFILE}" ] ; then
return
fi
# Rename existing older versions
ver=$MAXVER
while [ $ver -ge 1 ]
do
local prev=$(( $ver - 1 ))
local old="-$prev"
# Instead of old version 0; use the original filename
if [ $ver -eq 1 ] ; then
old=""
fi
if [ -f "${LOGFILE}${old}" ] ; then
mv -f "${LOGFILE}${old}" "${LOGFILE}-${ver}"
fi
ver=$prev
done
}
# Dereference symlink
# $1+: directories
dereference_symlink ()
{
local DIRECTORY SYMLINKS DIR FILE LINK
for DIRECTORY in "$@"; do
if [ -d "$DIRECTORY" ]; then
while true; do
# Find all symlinks in this directory.
SYMLINKS=`find $DIRECTORY -type l`
if [ -z "$SYMLINKS" ]; then
break;
fi
# Iterate symlinks
for SYMLINK in $SYMLINKS; do
if [ -L "$SYMLINK" ]; then
DIR=`dirname "$SYMLINK"`
FILE=`basename "$SYMLINK"`
# Note that if `readlink $FILE` is also a link, we want to deal
# with it in the next iteration. There is potential infinite-loop
# situation for cicular link doesn't exist in our case, though.
(cd "$DIR" && \
LINK=`readlink "$FILE"` && \
test ! -L "$LINK" && \
rm -f "$FILE" && \
cp -a "$LINK" "$FILE")
fi
done
done
fi
done
}