Skip to content

Instantly share code, notes, and snippets.

@chibicitiberiu
Last active February 14, 2020 01:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save chibicitiberiu/7cc01a63109043d54e9da58d9a333010 to your computer and use it in GitHub Desktop.
Save chibicitiberiu/7cc01a63109043d54e9da58d9a333010 to your computer and use it in GitHub Desktop.
Python script which implements most of cryptopp setupenv-android.
#!/usr/bin/env python3
#
# This is free and unencumbered software released into the public domain.
#
# Anyone is free to copy, modify, publish, use, compile, sell, or
# distribute this software, either in source code form or as a compiled
# binary, for any purpose, commercial or non-commercial, and by any
# means.
#
# In jurisdictions that recognize copyright laws, the author or authors
# of this software dedicate any and all copyright interest in the
# software to the public domain. We make this dedication for the benefit
# of the public at large and to the detriment of our heirs and
# successors. We intend this dedication to be an overt act of
# relinquishment in perpetuity of all present and future rights to this
# software under copyright law.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
#
# For more information, please refer to <http://unlicense.org>
#
import subprocess
import argparse
import os
import sys
from shutil import copy
# Maps Android STL name between format used by setenv and format used by cmake
def process_args():
parser = argparse.ArgumentParser(description="Sets up the environment and calls make for CryptoPP.")
parser.add_argument("source_path",
help='Path to CryptoPP source files')
parser.add_argument('--ndk-path',
required=True,
help='Path to Android NDK')
parser.add_argument('--api',
default='21',
help='API version')
parser.add_argument('--arch',
required=True,
help='Target architecture')
parser.add_argument('--stl',
default='gnu-shared',
help='STL implementation to use')
parser.add_argument('targets',
nargs=argparse.REMAINDER)
return parser.parse_args()
def setupenv_android(args, env):
'''
Sets up the android environment. This script is based on setupenv-android.sh. The reason why it's ported here
is to allow building from a Windows host OS (the cryptopp provided scripts assumes it is building on Linux).
'''
# Set AOSP_TOOLCHAIN_SUFFIX to your preference of tools and STL library.
# Note: 4.9 is required for the latest architectures, like ARM64/AARCH64.
# AOSP_TOOLCHAIN_SUFFIX=4.8
# AOSP_TOOLCHAIN_SUFFIX=4.9
AOSP_TOOLCHAIN_SUFFIX = '4.9'
if 'AOSP_TOOLCHAIN_SUFFIX' in env:
AOSP_TOOLCHAIN_SUFFIX = env['AOSP_TOOLCHAIN_SUFFIX']
# Set AOSP_API_VERSION to the API you want to use. 'armeabi' and 'armeabi-v7a' need
# API 3 (or above), 'mips' and 'x86' need API 9 (or above), etc.
# AOSP_API_VERSION="3" # Android 1.5 and above
# AOSP_API_VERSION="4" # Android 1.6 and above
# AOSP_API_VERSION="5" # Android 2.0 and above
# AOSP_API_VERSION="8" # Android 2.2 and above
# AOSP_API_VERSION="9" # Android 2.3 and above
# AOSP_API_VERSION="14" # Android 4.0 and above
# AOSP_API_VERSION="18" # Android 4.3 and above
# AOSP_API_VERSION="19" # Android 4.4 and above
# AOSP_API_VERSION="21" # Android 5.0 and above
# AOSP_API_VERSION="23" # Android 6.0 and above
AOSP_API_VERSION = args.api
AOSP_API = f"android-{AOSP_API_VERSION}"
#####################################################################
# ANDROID_NDK_ROOT should always be set by the user (even when not running this script)
# http://groups.google.com/group/android-ndk/browse_thread/thread/a998e139aca71d77.
ANDROID_NDK_ROOT = args.ndk_path
if not os.path.isdir(os.path.join(ANDROID_NDK_ROOT, "toolchains")):
print(f"Error: ANDROID_NDK_ROOT ({args.ndk_path}) not a valid path!", file=sys.stderr)
sys.exit(1)
#####################################################################
# https://developer.android.com/ndk/guides/abis.html
THE_ARCH = args.arch.lower()
if THE_ARCH in ['arm', 'armv5', 'armv6', 'armv7', 'armeabi']:
TOOLCHAIN_ARCH="arm-linux-androideabi"
TOOLCHAIN_NAME="arm-linux-androideabi"
AOSP_ABI="armeabi"
AOSP_ARCH="arch-arm"
AOSP_FLAGS="-march=armv5te -mtune=xscale -mthumb -msoft-float -DCRYPTOPP_DISABLE_ASM -funwind-tables -fexceptions -frtti"
elif THE_ARCH in ['armv7a', 'armv7-a', 'armeabi-v7a']:
TOOLCHAIN_ARCH="arm-linux-androideabi"
TOOLCHAIN_NAME="arm-linux-androideabi"
AOSP_ABI="armeabi-v7a"
AOSP_ARCH="arch-arm"
AOSP_FLAGS="-march=armv7-a -mthumb -mfpu=vfpv3-d16 -mfloat-abi=softfp -DCRYPTOPP_DISABLE_ASM -Wl,--fix-cortex-a8 -funwind-tables -fexceptions -frtti"
elif THE_ARCH in ['hard', 'armv7a-hard', 'armeabi-v7a-hard']:
print("Error: hard, armv7a-hard and armeabi-v7a-hard are not supported, as android uses softfloats", file=sys.stderr)
sys.exit(1)
elif THE_ARCH in ['neon', 'armv7a-neon']:
TOOLCHAIN_ARCH="arm-linux-androideabi"
TOOLCHAIN_NAME="arm-linux-androideabi"
AOSP_ABI="armeabi-v7a"
AOSP_ARCH="arch-arm"
AOSP_FLAGS="-march=armv7-a -mfpu=neon -mfloat-abi=softfp -Wl,--fix-cortex-a8 -funwind-tables -fexceptions -frtti"
elif THE_ARCH in ['armv8', 'armv8a', 'aarch64', 'arm64', 'arm64-v8a']:
TOOLCHAIN_ARCH="aarch64-linux-android"
TOOLCHAIN_NAME="aarch64-linux-android"
AOSP_ABI="arm64-v8a"
AOSP_ARCH="arch-arm64"
AOSP_FLAGS="-funwind-tables -fexceptions -frtti"
elif THE_ARCH in ['mips', 'mipsel']:
TOOLCHAIN_ARCH="mipsel-linux-android"
TOOLCHAIN_NAME="mipsel-linux-android"
AOSP_ABI="mips"
AOSP_ARCH="arch-mips"
AOSP_FLAGS="-funwind-tables -fexceptions -frtti"
elif THE_ARCH in ['mips64', 'mipsel64', 'mips64el']:
TOOLCHAIN_ARCH="mips64el-linux-android"
TOOLCHAIN_NAME="mips64el-linux-android"
AOSP_ABI="mips64"
AOSP_ARCH="arch-mips64"
AOSP_FLAGS="-funwind-tables -fexceptions -frtti"
elif THE_ARCH in ['x86']:
TOOLCHAIN_ARCH="x86"
TOOLCHAIN_NAME="i686-linux-android"
AOSP_ABI="x86"
AOSP_ARCH="arch-x86"
AOSP_FLAGS="-mtune=intel -mssse3 -mfpmath=sse -DCRYPTOPP_DISABLE_SSE4 -funwind-tables -fexceptions -frtti"
elif THE_ARCH in ['x86_64', 'x64']:
TOOLCHAIN_ARCH="x86_64"
TOOLCHAIN_NAME="x86_64-linux-android"
AOSP_ABI="x86_64"
AOSP_ARCH="arch-x86_64"
AOSP_FLAGS="-march=x86-64 -msse4.2 -mpopcnt -mtune=intel -DCRYPTOPP_DISABLE_CLMUL -DCRYPTOPP_DISABLE_AES -DCRYPTOPP_DISABLE_SHA -funwind-tables -fexceptions -frtti"
else:
print(f"Error: Unknown architecture {THE_ARCH}", file=sys.stderr)
sys.exit(1)
#####################################################################
# add missing android API version flag as of https://android.googlesource.com/platform/ndk.git/+/HEAD/docs/UnifiedHeaders.md
AOSP_FLAGS=f"-D__ANDROID_API__={AOSP_API_VERSION} {AOSP_FLAGS}"
# GNUmakefile-cross expects these to be set. They are also used in the tests below.
env['IS_ANDROID'] = '1'
env['AOSP_FLAGS'] = AOSP_FLAGS
#env['CPP'] = f"{TOOLCHAIN_NAME}-cpp" - makefile doesn't even use CPP, why does it look for it?
env['CC'] = f"{TOOLCHAIN_NAME}-gcc"
env['CXX'] = f"{TOOLCHAIN_NAME}-g++"
env['LD'] = f"{TOOLCHAIN_NAME}-ld"
env['AS'] = f"{TOOLCHAIN_NAME}-as"
env['AR'] = f"{TOOLCHAIN_NAME}-ar"
env['RANLIB'] = f"{TOOLCHAIN_NAME}-ranlib"
env['STRIP'] = f"{TOOLCHAIN_NAME}-strip"
env['AOSP_SYS_ARCH_INC'] = os.path.join(ANDROID_NDK_ROOT, 'sysroot', 'usr', 'include', TOOLCHAIN_NAME)
#####################################################################
# Based on ANDROID_NDK_ROOT, try and pick up the path for the tools. We expect something
# like /opt/android-ndk-r10e/toolchains/arm-linux-androideabi-4.7/prebuilt/linux-x86_64/bin
# Once we locate the tools, we add it to the PATH.
AOSP_TOOLCHAIN_PATH = None
for host in ["linux-x86_64", "darwin-x86_64", "linux-x86", "darwin-x86", "windows-x86_64", "windows-x86"]:
d = os.path.join(ANDROID_NDK_ROOT, 'toolchains', f'{TOOLCHAIN_ARCH}-{AOSP_TOOLCHAIN_SUFFIX}', 'prebuilt', host, 'bin')
if os.path.isdir(d):
AOSP_TOOLCHAIN_PATH = d
if not AOSP_TOOLCHAIN_PATH:
print(f"Error: Cannot determine toolchain path!", file=sys.stderr)
sys.exit(1)
# Make sure tools exist
for tool in ['CC', 'CXX', 'LD', 'AS', 'AR', 'RANLIB']:
path = os.path.join(AOSP_TOOLCHAIN_PATH, env[tool])
if os.name == 'nt':
for suffix in ['.exe.', '.cmd', '.bat' ]:
if os.path.isfile(path + suffix):
env[tool] += suffix
path += suffix
if not os.path.isfile(path):
print(f"Error: Failed to find {tool} ({env[tool]}) tool in toolchain path ({path})!", file=sys.stderr)
sys.exit(1)
env['PATH'] = AOSP_TOOLCHAIN_PATH + os.pathsep + env['PATH']
#####################################################################
# Error checking
if not os.path.isdir(os.path.join(ANDROID_NDK_ROOT, 'platforms', AOSP_API)):
print("Error: AOSP_API is not valid. Does the NDK support the API?", file=sys.stderr)
sys.exit(1)
elif not os.path.isdir(os.path.join(ANDROID_NDK_ROOT, 'platforms', AOSP_API, AOSP_ARCH)):
print("Error: AOSP_ARCH is not valid. Does the NDK support the architecture?", file=sys.stderr)
sys.exit(1)
# Android SYSROOT. It will be used on the command line with --sysroot
# http://android.googlesource.com/platform/ndk/+/ics-mr0/docs/STANDALONE-TOOLCHAIN.html
env['AOSP_SYSROOT'] = os.path.join(ANDROID_NDK_ROOT, 'sysroot')
env['AOSP_LD_SYSROOT'] = os.path.join(ANDROID_NDK_ROOT, 'platforms', AOSP_API, AOSP_ARCH)
#####################################################################
# Android STL. We support GNU, LLVM and STLport out of the box.
# Note: CMAKE constants also added here
THE_STL = args.stl.lower()
if THE_STL in [ 'stlport-static', 'stlport_static' ]:
AOSP_STL_INC = os.path.join(ANDROID_NDK_ROOT, 'sources', 'cxx-stl', 'stlport', 'stlport')
AOSP_STL_LIB = os.path.join(ANDROID_NDK_ROOT, 'sources', 'cxx-stl', 'stlport', 'libs', AOSP_ABI, 'libstlport_static.a')
elif THE_STL in [ 'stlport', 'stlport-shared', 'stlport_shared' ]:
AOSP_STL_INC = os.path.join(ANDROID_NDK_ROOT, 'sources', 'cxx-stl', 'stlport', 'stlport')
AOSP_STL_LIB = os.path.join(ANDROID_NDK_ROOT, 'sources', 'cxx-stl', 'stlport', 'libs', AOSP_ABI, 'libstlport_shared.so')
elif THE_STL in [ 'gabi++-static', 'gnu-static', 'gabi++_static', 'gnustl_static' ]:
AOSP_STL_INC = os.path.join(ANDROID_NDK_ROOT, 'sources', 'cxx-stl', 'gnu-libstdc++', AOSP_TOOLCHAIN_SUFFIX, 'include')
AOSP_BITS_INC = os.path.join(ANDROID_NDK_ROOT, 'sources', 'cxx-stl', 'gnu-libstdc++', AOSP_TOOLCHAIN_SUFFIX, 'libs', AOSP_ABI, 'include')
AOSP_STL_LIB = os.path.join(ANDROID_NDK_ROOT, 'sources', 'cxx-stl', 'gnu-libstdc++', AOSP_TOOLCHAIN_SUFFIX, 'libs', AOSP_ABI, 'libgnustl_static.a')
elif THE_STL in [ 'gnu', 'gabi++', 'gnu-shared', 'gabi++-shared', 'gnustl_shared', 'gabi++_shared' ]:
AOSP_STL_INC = os.path.join(ANDROID_NDK_ROOT, 'sources', 'cxx-stl', 'gnu-libstdc++', AOSP_TOOLCHAIN_SUFFIX, 'include')
AOSP_BITS_INC = os.path.join(ANDROID_NDK_ROOT, 'sources', 'cxx-stl', 'gnu-libstdc++', AOSP_TOOLCHAIN_SUFFIX, 'libs', AOSP_ABI, 'include')
AOSP_STL_LIB = os.path.join(ANDROID_NDK_ROOT, 'sources', 'cxx-stl', 'gnu-libstdc++', AOSP_TOOLCHAIN_SUFFIX, 'libs', AOSP_ABI, 'libgnustl_shared.so')
elif THE_STL in [ 'llvm-static', 'llvm', 'llvm-shared', 'c++_static', 'c++_shared' ]:
print("Error: llvm STL is not supported", file=sys.stderr)
sys.exit(1)
elif THE_STL in [ 'none', 'system' ]:
print("Error: C++ STL is required", file=sys.stderr)
sys.exit(1)
else:
print("Error: Unknown STL library", THE_STL, file=sys.stderr)
sys.exit(1)
# Error checking
if not os.path.isdir(AOSP_STL_INC):
print(f"Error: AOSP_STL_INC ({AOSP_STL_INC}) is not valid. Please edit this script.", file=sys.stderr)
sys.exit(1)
if not os.path.exists(AOSP_STL_LIB):
print(f"Error: AOSP_STL_LIB ({AOSP_STL_LIB}) is not valid. Please edit this script.", file=sys.stderr)
sys.exit(1)
env['AOSP_STL_INC'] = AOSP_STL_INC
env['AOSP_STL_LIB'] = AOSP_STL_LIB
if AOSP_BITS_INC:
env['AOSP_BITS_INC'] = AOSP_BITS_INC
# Now that we are using cpu-features from Android rather than CPU probing, we
# need to copy cpu-features.h and cpu-features.c from the NDK into our source
# directory and then build it.
cpu_features_h = os.path.join(ANDROID_NDK_ROOT, 'sources', 'android', 'cpufeatures', 'cpu-features.h')
cpu_features_c = os.path.join(ANDROID_NDK_ROOT, 'sources', 'android', 'cpufeatures', 'cpu-features.c')
if not os.path.exists(cpu_features_h) or not os.path.exists(cpu_features_h):
print( f"Error: Cannot locate cpu-features.h, cpu-features.c.", file=sys.stderr)
sys.exit(1)
copy(cpu_features_h, args.source_path)
copy(cpu_features_c, args.source_path)
if __name__ == "__main__":
args = process_args()
# Setup env
env = dict(os.environ)
setupenv_android(args, env)
# Call make
make_args = ["make",
"-C", args.source_path,
"-f", "GNUmakefile-cross"]
if len(args.targets) > 0:
make_args.extend(args.targets)
else:
make_args.extend(["clean", "static"])
print("Invoking make with args: ", make_args)
proc = subprocess.run(make_args, env=env)
sys.exit(proc.returncode)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment