blob: fd10607e9f415ffeac43dffc7e8abca7222ce182 [file] [log] [blame]
# Copyright 2015 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.
# Common routines - do not execute directly.
# Sanitize environment
set -e
export LANG=C
export LC_ALL=C
if [ -z "$_SHU_PROGDIR" ]; then
_SHU_PROGDIR=$(dirname "$0")
elif [ ! -f "$_SHU_PROGDIR"/common.shi ]; then
echo "ERROR: Invalid _SHU_PROGDIR value (missing common.shi): $_SHU_PROGDIR"
fi
_SHU_PROGNAME=$(basename "$0")
# Print an error message to stderr then exit the current process.
panic () {
echo "ERROR: $@" >&2
exit 1
}
# Internal verbosity level. Note that this value is inherited from
# the VERBOSE environment variable.
_SHU_VERBOSE=${VERBOSE:-1}
# Increment internal verbosity level.
increment_verbosity () {
_SHU_VERBOSE=$(( $_SHU_VERBOSE + 1 ))
}
# Decrement internal verbosity level.
decrement_verbosity () {
_SHU_VERBOSE=$(( $_SHU_VERBOSE - 1 ))
}
# Set the verbosity to a given level.
set_verbosity () {
_SHU_VERBOSE=$1
}
# Return internal verbosity level, clamped to 0 as a minimum bound.
get_verbosity () {
local RET=$_SHU_VERBOSE
if [ "$RET" -lt 0 ]; then
RET=0
fi
echo "$RET"
}
# Used internally to conditionally print a message.
# $1: message's verbosity level. If greater or equal than $_SHU_VERBOSE then
# the message will be ignored.
# $2+: message to print to stdout.
dump_n () {
local LEVEL=$1
shift
if [ "$LEVEL" -lt "$_SHU_VERBOSE" ]; then
printf "%s\n" "$@"
fi
}
# Dump a message to standard output.
dump () {
dump_n 0 "$@"
}
# Dump a message to standard output if --verbose was used.
log () {
dump_n 1 "$@"
}
# Dump a message to standard output if --verbose --verbose is used.
log2 () {
dump_n 2 "$@"
}
# Run a command, but suppress stdout
# keep stderr open in case error
silent_run () {
"$@" >/dev/null
}
# Run a command, output depends on verbosity level
run () {
local VERBOSE=$_SHU_VERBOSE
if [ "$VERBOSE" -lt 0 ]; then
VERBOSE=0
fi
if [ "$VERBOSE" -gt 1 ]; then
echo "COMMAND: $@"
fi
case $VERBOSE in
0|1)
"$@" >/dev/null 2>&1
;;
2)
"$@" >/dev/null
;;
*)
"$@"
;;
esac
}
# Return the current script's directory.
program_directory () {
printf "%s" "$_SHU_PROGDIR"
}
# Return the current script's filename.
program_name () {
printf "%s" "$_SHU_PROGNAME"
}
# 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 () {
eval printf %s \"\$$1\"
}
# Return success if variable $1 is set and non-empty, failure otherwise.
# $1: Variable name.
# Usage example:
# if var_is_set FOO; then
# .. Do something the handle FOO condition.
# fi
var_is_set () {
test -n "$(var_value $1)"
}
_var_quote_value () {
printf %s "$1" | sed -e "s|'|\\'\"'\"\\'|g"
}
# Set the value of a given name variables.
# $1: Variable name.
# $2+: Variable value.
# example:
# FOO=BAR
# var_assign $FOO bar
# is equivalent to
# BAR=bar
var_assign () {
local _var_assign_varname _var_assign_value
_var_assign_varname=$1
shift
_var_assign_value=$(_var_quote_value "$*")
eval $_var_assign_varname=\'$_var_assign_value\'
}
# Append a space-separated list of items to a given variable.
# $1: Variable name.
# $2+: Variable value.
# Example:
# FOO=
# var_append FOO foo (FOO is now 'foo')
# var_append FOO bar (FOO is now 'foo bar')
# var_append FOO zoo (FOO is now 'foo bar zoo')
var_append () {
local _var_append_varname
_var_append_varname=$1
shift
if test "$(var_value $_var_append_varname)"; then
eval $_var_append_varname=\$$_var_append_varname\'\ $(_var_quote_value "$*")\'
else
eval $_var_append_varname=\'$(_var_quote_value "$*")\'
fi
}
# Import shell script $1. Similar to sourcing the script except
# that each script will only be sourced once, even with multiple
# dependencies.
shell_import () {
local SCRIPT="$_SHU_PROGDIR/$1"
if [ ! -f "$SCRIPT" ]; then
panic "Missing script: $SCRIPT"
fi
local SCRIPT_TAG=_SHU_SHELL_SCRIPT_TAG__${1%%.shi}
SCRIPT_TAG=$(echo "$SCRIPT_TAG" | tr '-' '_' | tr '/' '__')
case $(var_value $SCRIPT_TAG) in
imported)
# Script is already imported.
return 0
;;
importing)
# Script is already being imported, this is
# a circular dependency.
panic "Circular dependency when trying to import $1"
;;
*)
# Import the script.
var_assign $SCRIPT_TAG importing
. "$SCRIPT"
var_assign $SCRIPT_TAG imported
;;
esac
}
# Unpack a given archive into a given destination directory.
# $1: Archive path
# $2: Destination directory
unpack_archive () {
local PKG_PATH="$1"
local DEST_DIR="$2"
case $PKG_PATH in
*.tar.gz|*.tar.bz2|*.tar.xz)
silent_run tar xf "$PKG_PATH" -C "$DEST_DIR"
;;
*.zip)
silent_run unzip -q -o -d "$DEST_DIR" "$PKG_PATH"
;;
*)
panic "Unsupported package format: $PKG_PATH"
;;
esac
}
# Return the build machine's operating system tag.
# Valid return values are:
# linux
# darwin
# freebsd
# windows (really MSys)
# cygwin
get_build_os () {
if [ -z "$_SHU_BUILD_OS" ]; then
_SHU_BUILD_OS=$(uname -s)
case $_SHU_BUILD_OS in
Darwin)
_SHU_BUILD_OS=darwin
;;
FreeBSD) # note: this is not tested
_SHU_BUILD_OS=freebsd
;;
Linux)
# note that building 32-bit binaries on x86_64 is handled later
_SHU_BUILD_OS=linux
;;
CYGWIN*|*_NT-*)
_SHU_BUILD_OS=windows
if [ "x$OSTYPE" = xcygwin ] ; then
_SHU_BUILD_OS=cygwin
fi
;;
esac
fi
echo "$_SHU_BUILD_OS"
}
# Return the build machine's CPU architecture.
# Valid return values are:
# x86
# x86_64
get_build_arch () {
local TEST
if [ -z "$_SHU_BUILD_ARCH" ]; then
case $(get_build_os) in
linux|darwin)
_SHU_BUILD_ARCH=$(uname -m)
# Kernel bitness might not match user space, so test
# the bitness of our shell to know what the user is
# really running.
TEST=$(/usr/bin/file -L $SHELL 2>/dev/null | grep 'x86[_-]64')
if [ "$TEST" ]; then
_SHU_BUILD_ARCH=x86_64
fi
;;
windows|cygwin)
case $PROCESSOR_ARCHITECTURE in
ADM64)
_SHU_BUILD_ARCH=x86_64
;;
*)
_SHU_BUILD_ARCH=x86
;;
esac
;;
*)
_SHU_BUILD_ARCH=$(uname -p)
;;
esac
fi
echo "$_SHU_BUILD_ARCH"
}
# Return the executable extension for a given operating system tag.
# $1: operating system tag.
_shu_get_exe_extension_for () {
case $1 in
windows|cygwin)
echo ".exe"
;;
*)
echo ""
esac
}
# Return the dynamic library extension for a given operating system tag.
# $1: operating system tag.
_shu_get_dll_extension_for () {
case $1 in
darwin)
echo ".dylib"
;;
windows|cygwin)
echo ".dll"
;;
*)
echo ".so"
;;
esac
}
# Return the extension of executables on the build machine.
get_build_exe_extension () {
if [ -z "$_SHU_BUILD_EXE_EXTENSION_DEFINED" ]; then
_SHU_BUILD_EXE_EXTENSION=$(_shu_get_exe_extension_for $(get_build_os))
_SHU_BUILD_EXE_EXTENSION_DEFINED=true
fi
echo "$_SHU_BUILD_EXE_EXTENSION"
}
# Return the extension of dynamic libraries on the build machine.
get_build_dll_extension () {
if [ -z "$_SHU_BUILD_DLL_EXTENSION_DEFINED" ]; then
_SHU_BUILD_DLL_EXTENSION=$(_shu_get_dll_extension_for $(get_build_os))
_SHU_BUILD_DLL_EXTENSION_DEFINED=true
fi
echo "$_SHU_BUILD_DLL_EXTENSION"
}
# Return the number of CPU cores on the build machine.
get_build_num_cores () {
case $(get_build_os) in
linux)
grep -c -e processor /proc/cpuinfo 2>/dev/null || echo 1
;;
darwin|freebsd)
sysctl -n hw.ncpu 2>/dev/null || echo 1
;;
windows|cygwin)
echo "${NUMBER_OF_PROCESSORS:-1}"
;;
*)
echo "1"
;;
esac
}
# Convert commas into spaces.
# $1: input string
# Out: input string, with each comma replaced by a space.
commas_to_spaces () {
printf "%s" "$@" | tr ',' ' '
}
# Convert spaces into commas
# $1+: input string
# Out: input string, with contiguous spaces replaced by a comma.
# NOTE: This also strips leading/trailing space.
spaces_to_commas () {
local ITEM RET
for ITEM in $*; do
if [ -z "$RET" ]; then
RET=$ITEM
else
RET="$RET,$ITEM"
fi
done
printf "%s" "$RET"
}
# Return success iff item |$2| is in list |$1|.
# $1: input list
# $2: item to find in list.
list_contains () {
local ITEM
for ITEM in $(commas_to_spaces "$1"); do
if [ "$ITEM" = "$2" ]; then
return 0
fi
done
return 1
}
# 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: from [$SRCDIR] to [$DSTDIR]"
mkdir -p "$DSTDIR" && (cd "$SRCDIR" && 2>/dev/null tar cf - *) | (tar xf - -C "$DSTDIR") ||
panic "Cannot copy to directory: $DSTDIR"
}
# $1: source directory
# $2: target directory location
# $3+: list of file names and filters.
copy_directory_files ()
{
local SRCDIR="$1"
local DSTDIR="$2"
shift
shift
if [ ! -d "$SRCDIR" ] ; then
panic "Can't copy from non-directory: $SRCDIR"
fi
log "Copying directory: from [$SRCDIR] to [$DSTDIR]"
mkdir -p "$DSTDIR" || panic "Cannot create target directory: $DSTDIR"
(cd "$SRCDIR" && 2>/dev/null tar cf - "$@" | \
tar xf - -C "$DSTDIR") ||
panic "Cannot copy to directory: $DSTDIR"
}
# Compute the SHA-1 sum of a given file. Implementation depends on the
# current host machine.
# $1: File path
# Out: SHA-1 sum as an hexadecimal string.
compute_file_sha1 () {
case $(get_build_os) in
linux)
(sha1sum "$1" | awk '{ print $1; }') 2>/dev/null
;;
darwin)
(openssl sha1 "$1" | awk '{ print $2; }') 2>/dev/null
;;
*)
panic "Don't know how to compute a SHA-1 sum on this platform: \
$(get_build_os)"
esac
}
# Compute the SHA-1 sum of a given zip or tarball archive. This is done by
# generated a sorted list of all files from the archive, and their SHA-1, then
# computing the SHA-1 of the result. This means the output is only dependent
# on the archive's content, not on any user/group/mtime field present in the
# actual archive file. Required because the Darwin 'tar' command doesn't seem
# to be able to generate reproducible archives (and even if it could, they
# would probably be slightly different from the ones generated on Linux).
# $1: Archive file path
# Out: SHA-1 sum as an hexadecimal string.
compute_archive_sha1 () {
local LIST_FILE LIST_DIR RET
LIST_DIR=$(mktemp -d /tmp/sha1_archive.XXXXXX)
LIST_FILE=$(mktemp /tmp/sha1_list.XXXXXX)
unpack_archive "$1" "$LIST_DIR"
RET=0
(
unpack_archive "$1" "$LIST_DIR" &&
cd "$LIST_DIR" &&
LANG=C LC_ALL=C find . -type f -o -type l | sort -u -s | xargs openssl sha1 > "$LIST_FILE"
compute_file_sha1 "$LIST_FILE"
) || RET=$?
rm -rf "$LIST_DIR"
rm -rf "$LIST_FILE"
return $RET
}
# Return the file path of a given timestamp
# $1: Installation directory
# $2: Timestamp group name
_shu_timestamp_file () {
printf %s "$1/timestamps/$2"
}
timestamp_check () {
test -f "$(_shu_timestamp_file "$1" "$2")"
}
timestamp_set () {
local TIMESTAMP="$(_shu_timestamp_file "$1" "$2")"
run mkdir -p "$(dirname $TIMESTAMP)" && run touch "$TIMESTAMP"
}
timestamp_clear () {
run rm -f "$(_shu_timestamp_file "$1" "$2")"
}
# Find a given program in the current path. This function never fails.
# $1: Program name
# Out: program path, if found, or empty string otherwise.
find_program () {
which "$1" 2>/dev/null || true
}
# Return the dump_syms tool path for a given host system.
# $1: System name (e.g. 'linux-x86' or 'linux-x86_64')
aosp_prebuilt_breakpad_dump_syms_for () {
local SYMTOOL="dump_syms"
local HOST="$1"
if [ "$(get_build_os)" = "linux" ]; then
case ${HOST} in
*windows*)
SYMTOOL=${SYMTOOL}_dwarf
HOST=$(get_build_os)-$(get_build_arch)
;;
esac
fi
local SYMTOOL_PATH="$AOSP_DIR/$(aosp_prebuilt_breakpad_subdir_for $HOST)/bin/${SYMTOOL}"
if [ ! -f "${SYMTOOL_PATH}" ]; then
panic "Breakpad prebuilt ${SYMTOOL} could not be found at ${SYMTOOL_PATH}"
fi
printf %s "${SYMTOOL_PATH}"
}
# Build debug_info from unstripped binaries
# Platform specific. On mac, generates the dSYMs alongside the libraries
#
# $1: Src root directory
# $2: Target root directory
# $3+: List of binary paths relative to Src root
build_debug_info () {
local SRC_ROOT="$1"
local DEST_ROOT="$2"
shift
shift
local BINARY_FILES="$@"
for BINARY in $BINARY_FILES; do
if [ "$(get_build_os)" = "darwin" ]; then
DSYM_DIR=$DEST_ROOT/$BINARY.dSYM
if [ -L "$SRC_ROOT"/$BINARY ]; then
DEST=`readlink $SRC_ROOT/$BINARY`
ln -fs $DEST.dSYM $DSYM_DIR
else
dsymutil --out=$DSYM_DIR $SRC_ROOT/$BINARY ||
panic "Failed to create dsym for $SRC_ROOT/$BINARY"
fi
fi
done
}
# Build breakpad symbols from unstripped binaries
# Builds a symbols directory at the destination root dir and populates it with
# *.sym extension symbol files mirroring the binary file paths
#
# Expects Breakpad prebuilt to be built, panics otherwise.
# $1: Src root directory
# $2: Target root directory
# $3: Host target
# $4+: List of binary paths relative to Src root
build_symbols () {
local SRC_ROOT="$1"
local DEST_ROOT="$2"
local HOST="$3"
shift
shift
shift
local BINARY_FILES="$@"
local SYMBOL_DIR="$DEST_ROOT"/symbols
local BINARY_FULLNAME=
local BINARY_DIR=
local SYMTOOL="$(aosp_prebuilt_breakpad_dump_syms_for $HOST)"
if [ ! -f "$SYMTOOL" ]; then
panic "Breakpad prebuilt symbol dumper could not be found at $SYMTOOL"
fi
run mkdir -p "$SYMBOL_DIR"
for BINARY in $BINARY_FILES; do
if [ -L "$SRC_ROOT"/$BINARY ]; then
continue
fi
BINARY_FULLNAME="$(basename "$BINARY")"
BINARY_DIR="$(dirname "$BINARY")"
run mkdir -p $SYMBOL_DIR/$BINARY_DIR
if [ "$(get_build_os)" = "darwin" ]; then
DSYM_DIR=$(mktemp -d /tmp/$BINARY_FULLNAME.dsym.XXXXXX)
dsymutil --out=$DSYM_DIR $SRC_ROOT/$BINARY ||
panic "Failed to create dsym for $SRC_ROOT/$BINARY"
$SYMTOOL -g $DSYM_DIR $SRC_ROOT/$BINARY > $SYMBOL_DIR/$BINARY.sym ||
panic "Failed to create symbol file for $SRC_ROOT/$BINARY"
rm -rf "$DSYM_DIR"
else
$SYMTOOL $SRC_ROOT/$BINARY > $SYMBOL_DIR/$BINARY.sym ||
panic "Failed to create symbol file for $SRC_ROOT/$BINARY"
fi
done
}
# Strips a library in place
# Expects TOOLCHAIN_WRAPPER_DIR to be valid (set by builder_prepare_for_host*)
# Panics if not found
# $1: Target root directory
# $2+: List of lib paths relative to $1
strip_libs () {
local DEST_ROOT="$1"
shift
local LIBS="$@"
if [ -z $TOOLCHAIN_WRAPPER_DIR ]; then
panic "TOOLCHAIN_WRAPPER_DIR not set, was builder_prepare_for_host called?"
fi
if [ ! -d $TOOLCHAIN_WRAPPER_DIR ]; then
panic "TOOLCHAIN_WRAPPER_DIR is not a directory"
fi
for LIB in $LIBS; do
if [ "$(get_build_os)" = "darwin" ]; then
run strip -S $DEST_ROOT/$LIB || panic "Could not strip $DEST_ROOT"
else
local OBJCOPY="$TOOLCHAIN_WRAPPER_DIR"/$(builder_gnu_config_host_prefix)objcopy
if [ ! -f "$OBJCOPY" ]; then
panic "Could not find objcopy at $OBJCOPY"
fi
run "$OBJCOPY" --strip-unneeded $DEST_ROOT/$LIB || \
panic "Could not strip $DEST_ROOT/$LIB"
fi
done
}