#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
# System Imports
import os
# waflib imports
from waflib import Logs
from waflib.Configure import conf
# lmbrwaflib imports
from lmbrwaflib import lumberyard
from lmbrwaflib.cry_utils import append_to_unique_list
@conf
def load_android_toolchains(ctx, search_paths, CC, CXX, AR, STRIP, **addition_toolchains):
"""
Loads the native toolchains from the Android NDK
"""
try:
ctx.find_program(CC, var = 'CC', path_list = search_paths, silent_output = True)
ctx.find_program(CXX, var = 'CXX', path_list = search_paths, silent_output = True)
ctx.find_program(AR, var = 'AR', path_list = search_paths, silent_output = True)
# for debug symbol stripping
ctx.find_program(STRIP, var = 'STRIP', path_list = search_paths, silent_output = True)
# optional linker override
if 'LINK_CC' in addition_toolchains and 'LINK_CXX' in addition_toolchains:
ctx.find_program(addition_toolchains['LINK_CC'], var = 'LINK_CC', path_list = search_paths, silent_output = True)
ctx.find_program(addition_toolchains['LINK_CXX'], var = 'LINK_CXX', path_list = search_paths, silent_output = True)
else:
ctx.env['LINK_CC'] = ctx.env['CC']
ctx.env['LINK_CXX'] = ctx.env['CXX']
ctx.env['LINK'] = ctx.env['LINK_CC']
# common cc settings
ctx.cc_load_tools()
ctx.cc_add_flags()
# common cxx settings
ctx.cxx_load_tools()
ctx.cxx_add_flags()
# common link settings
ctx.link_add_flags()
except:
Logs.error('[ERROR] Failed to find the Android NDK standalone toolchain(s) in search path %s' % search_paths)
return False
return True
@conf
def load_android_tools(ctx):
"""
Loads the necessary build tools from the Android SDK
"""
android_sdk_home = ctx.env['ANDROID_SDK_HOME']
build_tools_version = ctx.get_android_build_tools_version()
build_tools_dir = os.path.join(android_sdk_home, 'build-tools', build_tools_version)
try:
ctx.find_program('aapt', var = 'AAPT', path_list = [ build_tools_dir ], silent_output = True)
ctx.find_program('aidl', var = 'AIDL', path_list = [ build_tools_dir ], silent_output = True)
ctx.find_program('dx', var = 'DX', path_list = [ build_tools_dir ], silent_output = True)
ctx.find_program('zipalign', var = 'ZIPALIGN', path_list = [ build_tools_dir ], silent_output = True)
except:
Logs.error('[ERROR] The desired Android SDK build-tools version - {} - appears to be incomplete. Please use Android SDK Manager to validate the build-tools version installation '
'or change BUILD_TOOLS_VER in _WAF_/android/android_settings.json to either a version installed or "latest" and run the configure command again.'.format(build_tools_version))
return False
return True
@conf
def load_android_common_settings(conf):
"""
Setup all compiler and linker settings shared over all android configurations
"""
env = conf.env
ndk_root = env['ANDROID_NDK_HOME']
defines = []
if env['ANDROID_NDK_REV_MAJOR'] < 19:
defines += [
'__ANDROID_API__={}'.format(env['ANDROID_NDK_PLATFORM_NUMBER']),
]
append_to_unique_list(env['DEFINES'], defines)
# Pattern to transform outputs
env['cprogram_PATTERN'] = env['cxxprogram_PATTERN'] = '%s'
env['cshlib_PATTERN'] = env['cxxshlib_PATTERN'] = 'lib%s.so'
env['cstlib_PATTERN'] = env['cxxstlib_PATTERN'] = 'lib%s.a'
env['RPATH_ST'] = '-Wl,-rpath,%s'
env['SONAME_ST'] = '-Wl,-soname,%s' # sets the DT_SONAME field in the shared object, used for ELF object loading
# frameworks aren't supported on Android, disable it
env['FRAMEWORK'] = []
env['FRAMEWORK_ST'] = ''
env['FRAMEWORKPATH'] = []
env['FRAMEWORKPATH_ST'] = ''
# java settings
env['JAVA_VERSION'] = '1.7'
env['CLASSPATH'] = []
platform = os.path.join(env['ANDROID_SDK_HOME'], 'platforms', env['ANDROID_SDK_VERSION'])
android_jar = os.path.join(platform, 'android.jar')
env['JAVACFLAGS'] = [
'-encoding', 'UTF-8',
'-bootclasspath', android_jar,
'-target', env['JAVA_VERSION'],
]
# android interface processing
env['AIDL_PREPROC_ST'] = '-p%s'
env['AIDL_PREPROCESSES'] = [ os.path.join(platform, 'framework.aidl') ]
# aapt settings
env['AAPT_ASSETS_ST'] = [ '-A' ]
env['AAPT_ASSETS'] = []
env['AAPT_RESOURCE_ST'] = [ '-S' ]
env['AAPT_RESOURCES'] = []
env['AAPT_INLC_ST'] = [ '-I' ]
env['AAPT_INCLUDES'] = [ android_jar ]
env['AAPT_PACKAGE_FLAGS'] = [ '--auto-add-overlay' ]
# apk packaging settings
env['ANDROID_MANIFEST'] = ''
env['ANDROID_DEBUG_MODE'] = ''
# manifest merger settings
tools_path = os.path.join(env['ANDROID_SDK_HOME'], 'tools', 'lib')
tools_contents = os.listdir(tools_path)
tools_jars = [ entry for entry in tools_contents if entry.lower().endswith('.jar') ]
manifest_merger_lib_names = [
# entry point for the merger
'manifest-merger',
# dependent libs
'sdk-common',
'common'
]
manifest_merger_libs = []
for jar in tools_jars:
if any(lib_name for lib_name in manifest_merger_lib_names if jar.lower().startswith(lib_name)):
manifest_merger_libs.append(jar)
if len(manifest_merger_libs) < len(manifest_merger_lib_names):
conf.fatal('[ERROR] Failed to find the required file(s) for the Manifest Merger. Please use the Android SDK Manager to update to the latest SDK Tools version and run the configure command again.')
env['MANIFEST_MERGER_CLASSPATH'] = os.pathsep.join([ os.path.join(tools_path, jar_file) for jar_file in manifest_merger_libs ])
# zipalign settings
env['ZIPALIGN_SIZE'] = '4' # alignment in bytes, e.g. '4' provides 32-bit alignment (has to be a string)
# jarsigner settings
env['KEYSTORE_ALIAS'] = conf.get_android_env_keystore_alias()
env['KEYSTORE'] = conf.get_android_env_keystore_path()
@lumberyard.multi_conf
def generate_ib_profile_tool_elements(ctx):
android_tool_elements = [
'',
'',
# The Android deploy command uses 'adb shell' to check files on the device and will return non 0 exit codes if the files don't exit. XGConsole will flag those processes as
# failures and since we are gracefully handing those cases, WAF will continue to execute till it exits naturally (we don't use /stoponerror). In most cases, this is causing
# false build failures even though the deploy command completes sucessfully. Whitelist the known return codes we handle internally.
#
''
]
return android_tool_elements
def _load_android_settings(ctx):
""" Helper function for loading the global android settings """
if hasattr(ctx, 'android_settings'):
return
settings_files = list()
for root_node in ctx._get_settings_search_roots():
settings_node = root_node.make_node(['_WAF_', 'android', 'android_settings.json'])
if os.path.exists(settings_node.abspath()):
settings_files.append(settings_node)
ctx.android_settings = dict()
for settings_file in settings_files:
try:
ctx.android_settings.update(ctx.parse_json_file(settings_file))
except Exception as e:
ctx.cry_file_error(str(e), settings_file.abspath())
def _get_android_setting(ctx, setting, default_value = None):
"""" Helper function for getting android settings """
_load_android_settings(ctx)
return ctx.android_settings.get(setting, default_value)
@conf
def get_android_dev_keystore_alias(conf):
return _get_android_setting(conf, 'DEV_KEYSTORE_ALIAS')
@conf
def get_android_dev_keystore_path(conf):
local_path = _get_android_setting(conf, 'DEV_KEYSTORE')
debug_ks_node = conf.engine_node.make_node(local_path)
return debug_ks_node.abspath()
@conf
def get_android_distro_keystore_alias(conf):
return _get_android_setting(conf, 'DISTRO_KEYSTORE_ALIAS')
@conf
def get_android_distro_keystore_path(conf):
local_path = _get_android_setting(conf, 'DISTRO_KEYSTORE')
release_ks_node = conf.engine_node.make_node(local_path)
return release_ks_node.abspath()
@conf
def get_android_build_environment(conf):
env = _get_android_setting(conf, 'BUILD_ENVIRONMENT')
if env == 'Development' or env == 'Distribution':
return env
else:
Logs.fatal('[ERROR] Invalid android build environment, valid environments are: Development and Distribution')
@conf
def get_android_env_keystore_alias(conf):
env = conf.get_android_build_environment()
if env == 'Development':
return conf.get_android_dev_keystore_alias()
elif env == 'Distribution':
return conf.get_android_distro_keystore_alias()
@conf
def get_android_env_keystore_path(conf):
env = conf.get_android_build_environment()
if env == 'Development':
return conf.get_android_dev_keystore_path()
elif env == 'Distribution':
return conf.get_android_distro_keystore_path()
@conf
def get_android_build_tools_version(conf):
"""
Get the version of build-tools to use for Android APK packaging process. Also
sets the 'buildToolsVersion' when generating the Android Studio gradle project.
The following is require in order for validation and use of "latest" value.
def configure(conf):
conf.load('android')
"""
build_tools_version = conf.env['ANDROID_BUILD_TOOLS_VER']
if not build_tools_version:
build_tools_version = _get_android_setting(conf, 'BUILD_TOOLS_VER')
return build_tools_version
@conf
def get_android_sdk_version(conf):
"""
Gets the desired Android API version used when building the Java code,
must be equal to or greater than the ndk_platform. The following is
required for the validation to work and use of "latest" value.
def configure(conf):
conf.load('android')
"""
sdk_version = conf.env['ANDROID_SDK_VERSION']
if not sdk_version:
sdk_version = _get_android_setting(conf, 'SDK_VERSION')
return sdk_version
@conf
def get_android_ndk_platform(conf):
"""
Gets the desired Android API version used when building the native code,
must be equal to or less than the sdk_version. If not specified, the
specified sdk version, or closest match, will be used. The following is
required for the auto-detection and validation to work.
def configure(conf):
conf.load('android')
"""
ndk_platform = conf.env['ANDROID_NDK_PLATFORM']
if not ndk_platform:
ndk_platform = _get_android_setting(conf, 'NDK_PLATFORM')
return ndk_platform
@conf
def get_android_project_relative_path(self):
return '{}/{}'.format(self.options.android_studio_project_folder, self.options.android_studio_project_name)
@conf
def get_android_project_absolute_path(self):
return self.path.make_node([ self.options.android_studio_project_folder, self.options.android_studio_project_name ]).abspath()
@conf
def get_android_patched_libraries_relative_path(self):
return '{}/{}'.format(self.get_android_project_relative_path(), 'AndroidLibraries')