Skip to content

Instantly share code, notes, and snippets.

@MartinMReed
Last active October 16, 2019 01:33
Show Gist options
  • Save MartinMReed/b5e8a56651fd25569ff3 to your computer and use it in GitHub Desktop.
Save MartinMReed/b5e8a56651fd25569ff3 to your computer and use it in GitHub Desktop.
Compile AOSP (Android Open Source Project) on macOS
#! /bin/sh
#
# Build directory:
BUILD_DIRECTORY=~/Desktop
#
#
#
# Each build of AOSP is tied to a specific source tag. You can find the match between the
# build number and source tag from the link below. If you choose a tag that does not properly
# match with the build number, you will receive a warning prompt when running this script.
# You will have the option to continue using the mismatch tag, however that is not recommended.
# https://source.android.com/source/build-numbers.html#source-code-tags-and-builds
# All possible AOSP tags and branches can be found at:
# https://android.googlesource.com/platform/manifest/+refs
ANDROID_BUILD=
ANDROID_VERSION=
ANDROID_DEVICE=
#
#
#
# The vendor drivers necessary for a build will be downloaded by this script, however the
# hash of each driver needs to be provided here. The drivers are provided by Google at:
# https://developers.google.com/android/nexus/drivers
# The URL will be in the form of:
# https://dl.google.com/dl/android/aosp/<vendor>-<device>-<build>-<hash>.tgz
# Example usage:
# declare driver_asus=<hash>
# declare driver_broadcom=<hash>
# declare driver_qcom=<hash>
#
# VENDORS=(google_devices nvidia broadcom lge qcom asus)
# for VENDOR in "${VENDORS[@]}"; do declare driver_${VENDOR}=; done
#
#
#
# Nexus 7 (old, mobile), OS 5.1.1
ANDROID_BUILD=lmy47v
ANDROID_VERSION=5.1.1_r1
ANDROID_DEVICE=tilapia
VENDORS=(asus broadcom elan invensense nvidia nxp widevine)
declare driver_asus=6272610e
declare driver_broadcom=e9d05927
declare driver_elan=5f509df5
declare driver_invensense=066fff5f
declare driver_nvidia=05744cf3
declare driver_nxp=7a361708
declare driver_widevine=2844d74a
if [ "$(uname)" != "Darwin" ]; then
echo "This script is intended to run on macOS"
exit 1
fi
_SCRIPT_PATH="$(cd $(dirname ${BASH_SOURCE[0]}) && pwd)/${BASH_SOURCE[0]}"
function err_trap {
echo "------------------------------------------------"
echo "Error occurred on line ${_SCRIPT_LINENO}:"
echo " ${_SCRIPT_COMMAND}"
echo "------------------------------------------------"
exit $1
}
function debug_trap {
# "fi" at the end of an if-block will result in BASH_COMMAND="return $ret"
if [ "$2" != "return \$ret" ]; then _SCRIPT_LINENO="$1"; _SCRIPT_COMMAND="$2"; fi
}
trap 'err_trap "$?"' ERR
trap 'debug_trap "${LINENO}" "${BASH_COMMAND}"' DEBUG
# Necessary for using a map in Bash 3
array_get() {
local i="$1_$2"
printf '%s' "${!i}"
}
OPT_ENABLE_SYNC=1
OPT_ENABLE_MAKE=1
OPT_ENABLE_REMAKE=
while test $# -gt 0; do
case "$1" in
--nosync) OPT_ENABLE_SYNC=;;
--nomake) OPT_ENABLE_MAKE=;;
--remake) OPT_ENABLE_REMAKE=1;;
--*) echo "Bad option $1"; exit 1;;
*) echo "Bad argument $1"; exit 1;;
esac
shift
done
if [ -z "$OPT_ENABLE_SYNC" ]; then
echo "SYNC has been disabled"
fi
if [ -z "$OPT_ENABLE_MAKE" ]; then
echo "MAKE has been disabled"
elif [ ! -z "$OPT_ENABLE_REMAKE" ]; then
echo "REMAKE has been enabled"
fi
# TOOLCHAINS
#
# Verify that the necessary tools are available.
# If some are missing, you can install through Homebrew.
#
function toolchain_require() { which $1 &> /dev/null; return $?; }
#
toolchain_require make
toolchain_require wget
toolchain_require git
toolchain_require java
toolchain_require gunzip
toolchain_require tar
toolchain_require python
toolchain_require curl
toolchain_require xcodebuild
#
#
ANDROID_VERSION_MAJOR=$(echo "${ANDROID_VERSION}" | sed -n 's/\([0-9]*\)\.\([0-9]*\)\(\.[0-9]*\)*_r[0-9]*/\1/p')
ANDROID_VERSION_MINOR=$(echo "${ANDROID_VERSION}" | sed -n 's/\([0-9]*\)\.\([0-9]*\)\(\.[0-9]*\)*_r[0-9]*/\2/p')
# CURL
#
if [[ ! -z "$(curl --version | grep SecureTransport)" ]]; then echo "Curl must be compiled with OpenSSL instead of SecureTransport. Please see details at $0:${LINENO}."; exit 1; fi
#
# While compiling, if you receive an error about cURL, you may need to install the version from brew.
#
# > Unsupported curl, please use a curl not based on SecureTransport
#
# $ brew install curl --with-openssl
#
#
if [[ ! -z "$(brew --prefix curl)" ]]; then
PATH=$(brew --prefix curl)/bin:$PATH
fi
#
#
# ERROR BUILDING: BOOT.IMG
#
if [[ ! -f "/usr/local/lib/libcrypto.1.0.0.dylib" ]]; then echo "libcrypto is expected under /usr/local/lib. Please see details at $0:${LINENO}."; exit 1; fi
if [[ ! -f "/usr/local/lib/libssl.1.0.0.dylib" ]]; then echo "libssl is expected under /usr/local/lib. Please see details at $0:${LINENO}."; exit 1; fi
#
# > dyld: Library not loaded: /opt/local/lib/libcrypto.1.0.0.dylib
#
# $ brew uninstall --ignore-dependencies --force openssl
# $ brew install openssl
# $ ln -s /usr/local/opt/openssl/lib/libcrypto.1.0.0.dylib /usr/local/lib/
# $ ln -s /usr/local/opt/openssl/lib/libssl.1.0.0.dylib /usr/local/lib/
#
#
# XCODE
#
# LATEST_XCODE_SDK=$(basename "$(xcrun --show-sdk-path)" | sed -n 's/MacOSX\(.*\)\.sdk/\1/p')
#
# You can check supported versions: cat workspace/build/core/combo/mac_version.mk | grep mac_sdk_versions_supported
if (( "${ANDROID_VERSION_MAJOR}" >= "7" )); then MACOS_SDK="10.11";
elif (( "${ANDROID_VERSION_MAJOR}" >= "6" )); then MACOS_SDK="10.9";
elif (( "${ANDROID_VERSION_MAJOR}" >= "5" )); then MACOS_SDK="10.9";
elif (( $(echo "${ANDROID_VERSION_MAJOR}.${ANDROID_VERSION_MINOR} >= 4.1" | bc -l) )); then MACOS_SDK="10.9";
else MACOS_SDK="10.9"; fi
#
#XCODE_SDK_PATH=$(dirname "$(xcrun --show-sdk-path 2>/dev/null)")
#if [[ ! -d "${XCODE_SDK_PATH}/MacOSX${MACOS_SDK}.sdk" ]]; then echo "macOS SDK ${MACOS_SDK} expected. Please see details at $0:${LINENO}."; exit 1; fi
#
if [[ -z "$(xcodebuild -showsdks | grep macosx${MACOS_SDK})" ]]; then echo "macOS SDK ${MACOS_SDK} expected. Please see details at $0:${LINENO}."; exit 1; fi
#
# XCode has a minimum SDK requirement and will ignore older SDK installs. Therefore you may also need an older version of XCode.
# Once downloaded, use xcode-select to switch to it:
#
# $ sudo xcode-select --switch /Applications/Xcode-5.0.2.app/Contents/Developer
#
#
# The required macOS SDK may be available through this repo (i.e. MacOSX10.11.sdk.tar.xz)
#
# $ sudo mv MacOSX10.11.sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/
# $ sudo chown root:wheel /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs
#
# Other MacOSX SDK are available through Github:
# https://github.com/phracker/MacOSX-SDKs/releases
#
#
# JAVA
#
if (( "${ANDROID_VERSION_MAJOR}" >= "7" )); then JAVA_SDK="1.8";
elif (( "${ANDROID_VERSION_MAJOR}" >= "6" )); then JAVA_SDK="1.7";
elif (( "${ANDROID_VERSION_MAJOR}" >= "5" )); then JAVA_SDK="1.7";
elif (( $(echo "${ANDROID_VERSION_MAJOR}.${ANDROID_VERSION_MINOR} >= 2.3" | bc -l) )); then JAVA_SDK="1.6";
else JAVA_SDK="1.5"; fi
#
export JAVA_HOME=$(/usr/libexec/java_home -v ${JAVA_SDK})
JAVA_VER=$(java -version 2>&1 | sed 's/java version "\(.*\)\.\(.*\)\..*"/\1.\2/; 1q')
if [ "${JAVA_VER}" != "${JAVA_SDK}" ]; then echo "java ${JAVA_SDK} expected. Please see details at $0:${LINENO}."; exit 1; fi
#
export PATH=$JAVA_HOME/bin:$PATH
#
# For some older Android OS you may need an lower JAVA version.
# Check requirements here: https://source.android.com/source/requirements.html
#
# To see current installed versions, look under /Library/Java/JavaVirtualMachines/
# If you do not have what you need, you can install using HomeBrew.
# See http://stackoverflow.com/a/29195815/2616171
#
# $ brew tap caskroom/versions
# $ brew cask search java
# $ brew cask install java6
#
#
# BUILD + TAG VALIDATION
#
#
EXPECTED_BRANCH_CMD="import re
build_number = '${ANDROID_BUILD}'.upper()
pattern = r'.*<tr>\s*<td>(' + build_number + r')</td>\s*<td>android-([^<]*)</td>\s*<td>([^<]*)</td>\s*<td>([^<]*)</td>\s*</tr>.*'
output = \"\"\"$(curl --silent https://source.android.com/source/build-numbers.html)\"\"\"
matcher = re.search(pattern, output)
print(matcher.group(2))"
EXPECTED_BRANCH=$(python -c "${EXPECTED_BRANCH_CMD}")
if [ "${EXPECTED_BRANCH}" != "${ANDROID_VERSION}" ]; then
echo "The expected tag for build ${ANDROID_BUILD} is ${EXPECTED_BRANCH}, but ${ANDROID_VERSION} was selected."
while true; do
read -p "Continue using ${ANDROID_VERSION} anyway? [yN] " yn
case $yn in
[Yy]* ) break;;
* ) echo "exiting..."; exit 1;;
esac
done
fi
#
#
# DRIVER DOWNLOAD VALIDATION
#
#
for VENDOR in "${VENDORS[@]}"; do
DRIVER_HASH=$(array_get driver ${VENDOR})
if [ ! -z "${DRIVER_HASH}" ]; then
DRIVER_FILENAME="${VENDOR}-${ANDROID_DEVICE}-${ANDROID_BUILD}-${DRIVER_HASH}"
DRIVER_URL="https://dl.google.com/dl/android/aosp/${DRIVER_FILENAME}.tgz"
curl --output /dev/null --silent --head --fail "${DRIVER_URL}"
if [ "$?" != "0" ]; then echo "url not found: ${DRIVER_URL}"; exit 1; fi
fi
done
#
#
# VOLUME CREATION AND AVAILABILITY
#
#
VOLUME_SPACE_REQUIRED=100
VOLUME_SPACE_AVAILABLE=$(( $(df -k ${BUILD_DIRECTORY} | tail -1 | awk '{print $4}') / 1024 / 1024 ))
if (( "${VOLUME_SPACE_REQUIRED}" > "${VOLUME_SPACE_AVAILABLE}" )); then echo "Requires ${VOLUME_SPACE_REQUIRED}GB, but only ${VOLUME_SPACE_AVAILABLE}GB is available. Please see details at $0:${LINENO}."; exit 1; fi
#
#
VOLUME=android-${ANDROID_DEVICE}_${ANDROID_BUILD}-${ANDROID_VERSION}
VOLUME_DMG=${BUILD_DIRECTORY}/${VOLUME}.dmg
#
# Create the volume DMG file that will provide the necessary filesystem type
if [ ! -f "${VOLUME_DMG}" ]; then
hdiutil create -volname "${VOLUME}" -fs "Case-sensitive Journaled HFS+" -size ${BUILD_SPACE_REQUIRED}GB ${VOLUME_DMG}
if [ ! -f "${VOLUME_DMG}" ]; then echo "Could not create ${VOLUME_DMG}"; exit 1; fi
fi
#
# Check which volume path should be used. If attached manually, it will be in a different location.
if [ -d "/Volumes/${VOLUME}" ]; then
BIN=/Volumes/${VOLUME}
else
BIN=${BUILD_DIRECTORY}/${VOLUME}
fi
#
# Attach the volume if it is not already available
if [ ! -d "${BIN}" ]; then
hdiutil attach ${VOLUME_DMG} -mountroot ${BUILD_DIRECTORY}
if [ ! -d "${BIN}" ]; then echo "Could not open ${BIN}"; exit 1; fi
fi
#
#
WORKING_DIRECTORY=${BIN}/workspace
PATH=${BIN}:$PATH
# Download the latest version of the REPO tool
if [ ! -f "${BIN}/repo" ]; then
curl https://storage.googleapis.com/git-repo-downloads/repo > ${BIN}/repo
if [ ! -f "${BIN}/repo" ]; then echo "Could not create ${BIN}/repo"; exit 1; fi
fi
# Ensure that REPO is executable
if [ ! -x "${BIN}/repo" ]; then
chmod a+x ${BIN}/repo
if [ ! -x "${BIN}/repo" ]; then echo "Could not make ${BIN}/repo executable"; exit 1; fi
fi
# Create an empty directory to hold your working files
if [ ! -d "${WORKING_DIRECTORY}" ]; then
mkdir ${WORKING_DIRECTORY}
if [ ! -d "${WORKING_DIRECTORY}" ]; then echo "Could not open ${WORKING_DIRECTORY}"; exit 1; fi
fi
cd ${WORKING_DIRECTORY}
# Initialize the local REPO files using the remote manifest
if [ ! -d ".repo" ]; then
repo init -u https://android.googlesource.com/platform/manifest -b android-${ANDROID_VERSION}
if [ "$?" != "0" ]; then echo "Could not initialize repo"; rm -rf .repo; exit $?; fi
if [ ! -d ".repo" ]; then echo "Could not open ${WORKING_DIRECTORY}/.repo"; exit 1; fi
fi
# Synchronize all projects listed in the REPO manifest
if [ ! -z "$OPT_ENABLE_SYNC" ]; then
repo sync -j$(expr 2 \* $(sysctl hw.ncpu | awk '{print $2}'))
if [ "$?" != "0" ]; then echo "Syncing the repo projects has failed"; exit $?; fi
fi
# VENDOR DRIVER DOWNLOAD AND EXTRACTION
#
if [ ! -d "${BIN}/drivers" ]; then
mkdir ${BIN}/drivers
if [ ! -d "${BIN}/drivers" ]; then echo "Could not open ${BIN}/drivers"; exit 1; fi
fi
#
#
for VENDOR in "${VENDORS[@]}"; do
DRIVER_HASH=$(array_get driver ${VENDOR})
if [ ! -z "${DRIVER_HASH}" ] && [ ! -d "./vendor/${VENDOR}/${ANDROID_DEVICE}/proprietary" ]; then
DRIVER_FILENAME="${VENDOR}-${ANDROID_DEVICE}-${ANDROID_BUILD}-${DRIVER_HASH}"
if [ ! -f "${BIN}/drivers/${DRIVER_FILENAME}.tgz" ]; then
DRIVER_URL="https://dl.google.com/dl/android/aosp/${DRIVER_FILENAME}.tgz"
wget -O ${BIN}/drivers/${DRIVER_FILENAME}.tgz ${DRIVER_URL}
if [ ! -f "${BIN}/drivers/${DRIVER_FILENAME}.tgz" ]; then echo "Could not download ${DRIVER_URL}"; exit 1; fi
fi
if [ ! -f "./extract-${VENDOR}-${ANDROID_DEVICE}.sh" ]; then
gunzip -c ${BIN}/drivers/${DRIVER_FILENAME}.tgz | tar xopf -
if [ ! -f "./extract-${VENDOR}-${ANDROID_DEVICE}.sh" ]; then echo "Could not create ${WORKING_DIRECTORY}/extract-${VENDOR}-${ANDROID_DEVICE}.sh"; exit 1; fi
fi
# User expect to auto-accept all license prompts that will come up from these driver scripts
expect -c '
spawn sh ./extract-'${VENDOR}'-'${ANDROID_DEVICE}'.sh;
expect {
"Press Enter to view the license" { exp_send "\rq" ; exp_continue }
"Type \"I ACCEPT\" if you agree to the terms of the license:" { exp_send "I ACCEPT\r" ; exp_continue }
eof
}'
fi
done
#
#
. build/envsetup.sh
# LUNCH (build target selection)
#
# For some reason calling 'lunch' from a script is failing, so call the necessary parts directly.
# See https://android.googlesource.com/platform/build/+/jb-mr1.1-dev-plus-aosp/envsetup.sh
#
# lunch aosp_${ANDROID_DEVICE}-userdebug
#
# choosetype debug
# chooseproduct aosp_${ANDROID_DEVICE}
# choosevariant userdebug
choosecombo debug aosp_${ANDROID_DEVICE} userdebug
#
if [ "$?" != "0" ]; then echo "Unable to set product and variant. Please see details at $0:${LINENO}." exit $?; fi
#
# export TARGET_PRODUCT=aosp_${ANDROID_DEVICE}
# export TARGET_BUILD_VARIANT=userdebug
# export TARGET_BUILD_TYPE=debug
# set_stuff_for_environment
# printconfig
# OUT OF MEMORY
#
# > GC overhead limit exceeded.
# > Try increasing heap size with java option '-Xmx<size>'.
export ANDROID_JACK_VM_ARGS="-Dfile.encoding=UTF-8 -XX:+TieredCompilation -Xmx4g"
./prebuilts/sdk/tools/jack-admin kill-server 2>/dev/null || true
# ./prebuilts/sdk/tools/jack-admin start-server
if [ ! -z "$OPT_ENABLE_MAKE" ]; then
if [ ! -z "$OPT_ENABLE_REMAKE" ]; then
make clobber
fi
make -j$(expr 2 \* $(sysctl hw.ncpu | awk '{print $2}'))
# Generate IDE files
if [ -f "out/host/darwin-x86/framework/idegen.jar" ]; then
if [ ! -f "android.ipr" ]; then
mmm development/tools/idegen/
development/tools/idegen/idegen.sh
fi
fi
fi
echo
echo "Complete: ${VOLUME}"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment