Update Android Project Generation to support AGP 4.2.0, Cmake 3.20, and newer versions of NDK&SDK

- build.gradle format updates for newer Android Gradle Plugin
- Remove hard coded Android Gradle Plugin version 3.6.4 to be passed in from command args
- Set Android Gradle Plugin min version 4.2.0 in order to support Min CMake version 3.20
- Add ability to use the android sdk to install missing components if needed rather than doing it externally
- Removed argument to pass in the NDK folder to use the android, use the android-sdk instead. Can request specific NDK versions if possible
- Android Gradle Plugin has dependencies by version and is being managed
- More defaults based on tools on path, agp version made possible so they are no longer needed in the command args
This commit is contained in:
Steve Pham
2021-05-28 23:24:51 -07:00
committed by GitHub
parent 50a9e94eca
commit 74ec7a362b
10 changed files with 490 additions and 501 deletions
@@ -15,14 +15,14 @@ android {
${SIGNING_CONFIGS}
compileSdkVersion sdkVer
buildToolsVersion buildToolsVer
ndkVersion ndkPlatformVer
lintOptions {
abortOnError false
checkReleaseBuilds false
}
defaultConfig {
minSdkVersion ndkPlatformVer
minSdkVersion minSdkVer
targetSdkVersion sdkVer
${NATIVE_CMAKE_SECTION_DEFAULT_CONFIG}
}
@@ -16,6 +16,5 @@
# For customization when using a Version Control System, please read the
# header note.
# ${GENERATION_TIMESTAMP}
ndk.dir=${ANDROID_NDK_PATH}
sdk.dir=${ANDROID_SDK_PATH}
${CMAKE_DIR_LINE}
@@ -12,10 +12,9 @@ buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.6.4'
classpath 'com.android.tools.build:gradle:${ANDROID_GRADLE_PLUGIN_VERSION}'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
@@ -26,14 +25,14 @@ allprojects {
repositories {
google()
jcenter()
}
}
subprojects {
ext {
minSdkVer = ${MIN_SDK_VER}
sdkVer = ${SDK_VER}
ndkPlatformVer = ${NDK_PLATFORM_VER}
ndkPlatformVer = '${NDK_VERSION}'
buildToolsVer = '${SDK_BUILD_TOOL_VER}'
lyEngineRoot = '${LY_ENGINE_ROOT}'
}
+320 -295
View File
@@ -10,7 +10,9 @@
#
import imghdr
import configparser
import datetime
import fnmatch
import logging
import os
import json
@@ -33,6 +35,13 @@ if ROOT_DEV_PATH not in sys.path:
from cmake.Tools import common
ANDROID_GRADLE_PLUGIN_COMPATIBILITY_MAP = {
'4.2.0': {'min_gradle_version': '6.7.1',
'sdk_build': '30.0.2',
'default_ndk': '21.4.7075529',
'min_cmake_version': '3.20'}
}
APP_NAME = 'app'
ANDROID_MANIFEST_FILE = 'AndroidManifest.xml'
ANDROID_LIBRARIES_JSON_FILE = 'android_libraries.json'
@@ -86,83 +95,93 @@ PYTHON_SCRIPT = 'python.cmd' if platform.system() == 'Windows' else 'python.sh'
ANDROID_LAUNCHER_NAME_PATTERN = "{project_name}.GameLauncher"
class AndroidProjectManifestEnvironment(object):
"""
This class manages the environment for the AndroidManifiest.xml template file, based on project settings and environments
This class manages the environment for the AndroidManifest.xml template file, based on project settings and environments
that were passed in or calculated from the command line arguments.
"""
def __init__(self, engine_root, project_path, android_sdk_version_number, android_ndk_platform_number, is_test:bool):
def __init__(self, engine_root, project_path, android_sdk_version_number, is_test:bool):
"""
Initialize the object with the project specific parameters and values for the game project
:param engine_root: The path where the engine is located
:param project_path: The path were the project is located
:param android_sdk_version_number: The android SDK platform version
:param android_ndk_platform_number: The android NDK platform version
:param is_test: Indicates if theAzTestRunner application should be run
"""
if is_test:
# The AzTestRunner project.json is located under {engine_root}/Code/Tools/AzTestRunner/Platform/Android/android_project.json
project_properties_path = engine_root / 'Code' / 'Tools' / 'AzTestRunner' / 'Platform' / 'Android' / 'android_project.json'
else:
# The project.json file is located under the game name folder
project_properties_path = project_path / 'project.json'
# Read and parse the project.json file into a dictionary to process the specific attributes needed for the manifest template
project_properties_content = project_properties_path.resolve(strict=True)\
.read_text(encoding=common.DEFAULT_TEXT_READ_ENCODING,
errors=common.ENCODING_ERROR_HANDLINGS)
self.project_path = project_path
try:
if is_test:
# The AzTestRunner project.json is located under {engine_root}/Code/Tools/AzTestRunner/Platform/Android/android_project.json
project_properties_path = engine_root / 'Code' / 'Tools' / 'AzTestRunner' / 'Platform' / 'Android' / 'android_project.json'
assert project_properties_path.is_file(), f'Missing required android settings file {project_properties_path.resolve()}'
project_properties_content = project_properties_path.read_text(encoding=common.DEFAULT_TEXT_READ_ENCODING,
errors=common.ENCODING_ERROR_HANDLINGS)
project_json = json.loads(project_properties_content)
# Extract the key attributes we need to process and build up our environment table
project_json = json.loads(project_properties_content)
android_settings = project_json['android_settings']
project_name = project_json.get('project_name')
if not project_name:
raise common.LmbrCmdError(f"Missing required 'project_name' from project.json for project at '{str(project_path)}'")
product_name = project_json.get('product_name', project_name)
else:
# O3DE projects have both a project.json and an android_project.json files (unless its internal)
project_properties_path = project_path / 'project.json'
assert project_properties_path.is_file(), f'Missing required project settings file {project_properties_path.resolve()}'
project_properties_content = project_properties_path.read_text(encoding=common.DEFAULT_TEXT_READ_ENCODING,
errors=common.ENCODING_ERROR_HANDLINGS)
project_json = json.loads(project_properties_content)
game_project_android_settings = project_json['android_settings']
android_project_properties_path = project_path / 'Platform' / 'Android' / 'android_project.json'
if android_project_properties_path.is_file():
android_project_properties_content = android_project_properties_path.read_text(encoding=common.DEFAULT_TEXT_READ_ENCODING,
errors=common.ENCODING_ERROR_HANDLINGS)
android_project_json = json.loads(android_project_properties_content)
android_settings = android_project_json['android_settings']
else:
android_settings = project_json['android_settings']
package_name = game_project_android_settings["package_name"]
self.project_path = project_path
package_path = package_name.replace('.', '/')
project_name = project_json['project_name']
product_name = project_json.get('product_name', project_name)
package_name = android_settings["package_name"]
package_path = package_name.replace('.', '/')
project_activity = f'{TEST_RUNNER_PROJECT}Activity' if is_test else f'{project_name}Activity'
project_activity = f'{TEST_RUNNER_PROJECT}Activity' if is_test else f'{project_name}Activity'
# Multiview options require special processing
multi_window_options = AndroidProjectManifestEnvironment.process_android_multi_window_options(game_project_android_settings)
# Multiview options require special processing
multi_window_options = AndroidProjectManifestEnvironment.process_android_multi_window_options(android_settings)
self.internal_dict = {
'ANDROID_PACKAGE': package_name,
'ANDROID_PACKAGE_PATH': package_path,
'ANDROID_VERSION_NUMBER': game_project_android_settings["version_number"],
"ANDROID_VERSION_NAME": game_project_android_settings["version_name"],
"ANDROID_SCREEN_ORIENTATION": game_project_android_settings["orientation"],
'ANDROID_APP_NAME': TEST_RUNNER_PROJECT if is_test else product_name, # external facing name
'ANDROID_PROJECT_NAME': TEST_RUNNER_PROJECT if is_test else project_name, # internal facing name
'ANDROID_PROJECT_ACTIVITY': project_activity,
'ANDROID_LAUNCHER_NAME': TEST_RUNNER_PROJECT if is_test else ANDROID_LAUNCHER_NAME_PATTERN.format(project_name=project_name),
'ANDROID_CONFIG_CHANGES': multi_window_options['ANDROID_CONFIG_CHANGES'],
'ANDROID_APP_PUBLIC_KEY': game_project_android_settings.get('app_public_key', 'NoKey'),
'ANDROID_APP_OBFUSCATOR_SALT': game_project_android_settings.get('app_obfuscator_salt', ''),
'ANDROID_USE_MAIN_OBB': game_project_android_settings.get('use_main_obb', 'false'),
'ANDROID_USE_PATCH_OBB': game_project_android_settings.get('use_patch_obb', 'false'),
'ANDROID_ENABLE_KEEP_SCREEN_ON': game_project_android_settings.get('enable_keep_screen_on', 'false'),
'ANDROID_DISABLE_IMMERSIVE_MODE': game_project_android_settings.get('disable_immersive_mode', 'false'),
'ANDROID_MIN_SDK_VERSION': android_ndk_platform_number,
'ANDROID_TARGET_SDK_VERSION': android_sdk_version_number,
'ICONS': game_project_android_settings.get('icons', None),
'SPLASH_SCREEN': game_project_android_settings.get('splash_screen', None),
self.internal_dict = {
'ANDROID_PACKAGE': package_name,
'ANDROID_PACKAGE_PATH': package_path,
'ANDROID_VERSION_NUMBER': android_settings["version_number"],
"ANDROID_VERSION_NAME": android_settings["version_name"],
"ANDROID_SCREEN_ORIENTATION": android_settings["orientation"],
'ANDROID_APP_NAME': TEST_RUNNER_PROJECT if is_test else product_name, # external facing name
'ANDROID_PROJECT_NAME': TEST_RUNNER_PROJECT if is_test else project_name, # internal facing name
'ANDROID_PROJECT_ACTIVITY': project_activity,
'ANDROID_LAUNCHER_NAME': TEST_RUNNER_PROJECT if is_test else ANDROID_LAUNCHER_NAME_PATTERN.format(project_name=project_name),
'ANDROID_CONFIG_CHANGES': multi_window_options['ANDROID_CONFIG_CHANGES'],
'ANDROID_APP_PUBLIC_KEY': android_settings.get('app_public_key', 'NoKey'),
'ANDROID_APP_OBFUSCATOR_SALT': android_settings.get('app_obfuscator_salt', ''),
'ANDROID_USE_MAIN_OBB': android_settings.get('use_main_obb', 'false'),
'ANDROID_USE_PATCH_OBB': android_settings.get('use_patch_obb', 'false'),
'ANDROID_ENABLE_KEEP_SCREEN_ON': android_settings.get('enable_keep_screen_on', 'false'),
'ANDROID_DISABLE_IMMERSIVE_MODE': android_settings.get('disable_immersive_mode', 'false'),
'ANDROID_TARGET_SDK_VERSION': android_sdk_version_number,
'ICONS': android_settings.get('icons', None),
'SPLASH_SCREEN': android_settings.get('splash_screen', None),
'ANDROID_MULTI_WINDOW': multi_window_options['ANDROID_MULTI_WINDOW'],
'ANDROID_MULTI_WINDOW_PROPERTIES': multi_window_options['ANDROID_MULTI_WINDOW_PROPERTIES'],
'ANDROID_MULTI_WINDOW': multi_window_options['ANDROID_MULTI_WINDOW'],
'ANDROID_MULTI_WINDOW_PROPERTIES': multi_window_options['ANDROID_MULTI_WINDOW_PROPERTIES'],
'SAMSUNG_DEX_KEEP_ALIVE': multi_window_options['SAMSUNG_DEX_KEEP_ALIVE'],
'SAMSUNG_DEX_LAUNCH_WIDTH': multi_window_options['SAMSUNG_DEX_LAUNCH_WIDTH'],
'SAMSUNG_DEX_LAUNCH_HEIGHT': multi_window_options['SAMSUNG_DEX_LAUNCH_HEIGHT']
}
'SAMSUNG_DEX_KEEP_ALIVE': multi_window_options['SAMSUNG_DEX_KEEP_ALIVE'],
'SAMSUNG_DEX_LAUNCH_WIDTH': multi_window_options['SAMSUNG_DEX_LAUNCH_WIDTH'],
'SAMSUNG_DEX_LAUNCH_HEIGHT': multi_window_options['SAMSUNG_DEX_LAUNCH_HEIGHT']
}
except KeyError as e:
raise common.LmbrCmdError(f"Missing key from android project settings for project at {project_path}:'{e}' ")
def __getitem__(self, item):
return self.internal_dict.get(item)
@@ -306,6 +325,7 @@ asset_deploy_type={asset_type}
android_sdk_path={android_sdk_path}
embed_assets_in_apk={embed_assets_in_apk}
is_unit_test={is_unit_test}
android_gradle_plugin={android_gradle_plugin_version}
"""
NATIVE_CMAKE_SECTION_ANDROID_FORMAT = """
@@ -425,26 +445,28 @@ class AndroidProjectGenerator(object):
Class the manages the process to generate an android project folder in order to build with gradle/android studio
"""
def __init__(self, engine_root, build_dir, android_ndk_path, android_sdk_path, android_sdk_version, android_ndk_platform,
project_path, third_party_path, cmake_version, override_cmake_path, override_gradle_path, override_ninja_path,
android_sdk_build_tool_version, include_assets_in_apk, asset_mode, asset_type, signing_config, is_test_project=False,
def __init__(self, engine_root, build_dir, android_sdk_path, build_tool, android_sdk_platform, android_native_api_level, android_ndk,
project_path, third_party_path, cmake_version, override_cmake_path, override_gradle_path, gradle_version, gradle_plugin_version,
override_ninja_path, include_assets_in_apk, asset_mode, asset_type, signing_config, is_test_project=False,
overwrite_existing=True):
"""
Initialize the object with all the required parameters needed to create an Android Project. The parameters should be verified before initializing this object
:param engine_root: The engine root that contains the engine
:param build_dir: The target folder under the where the android project folder will be created
:param android_ndk_path: The path to the ANDROID_NDK used for building the native android code
:param android_sdk_path: The path to the ANDROID_SDK used for building the android java code
:param android_sdk_version: The android platform version number to use for the Android SDK related builds
:param android_ndk_platform: The android platform version number to use for the Android NDK related builds
:param build_tool: The android SDK build-tool version.
:param android_sdk_platform: The android sdk platform version number to use for the Android SDK related builds
:param android_native_api_level:The android native API level (ANDROID_NATIVE_API_LEVEL) to set
:param android_ndk: The android ndk version number to use for the native builds
:param project_path: The path to the project
:param third_party_path: The required path to the lumberyard 3rd party path
:param cmake_version: The version number of cmake that will be used by gradle
:param override_cmake_path: The override path to cmake if it does not exists in the system path
:param override_gradle_path: The override path to gradle if it does not exists in the system path
:param gradle_version: The detected version of gradle being used
:param gradle_plugin_version: The android gradle plugin version
:param override_ninja_path: The override path to ninja if it does not exists in the system path
:param android_sdk_build_tool_version: The preferred android SDK build-tool version. Will default to the first one detected in the android sdk path
:param include_assets_in_apk:
:param asset_mode:
:param asset_type:
@@ -458,17 +480,16 @@ class AndroidProjectGenerator(object):
self.build_dir = build_dir
self.android_ndk_path = android_ndk_path
self.android_sdk_path = android_sdk_path
self.android_project_builder_path = self.engine_root / 'Code/Tools/Android/ProjectBuilder'
self.android_sdk_version = android_sdk_version
self.android_sdk_platform = android_sdk_platform
self.android_sdk_build_tool_version = build_tool.version
self.android_sdk_build_tool_version = android_sdk_build_tool_version
self.android_ndk_platform = android_ndk_platform
self.android_ndk = android_ndk
self.android_ndk_version = android_ndk.version
self.android_native_api_level = android_native_api_level
self.project_path = project_path
@@ -480,6 +501,10 @@ class AndroidProjectGenerator(object):
self.override_gradle_path = override_gradle_path
self.gradle_version = gradle_version
self.gradle_plugin_version = gradle_plugin_version
self.override_ninja_path = override_ninja_path
self.include_assets_in_apk = include_assets_in_apk
@@ -511,8 +536,10 @@ class AndroidProjectGenerator(object):
project_names.extend(self.create_lumberyard_app(project_names))
root_gradle_env = {
'SDK_VER': self.android_sdk_version,
'NDK_PLATFORM_VER': self.android_ndk_platform,
'ANDROID_GRADLE_PLUGIN_VERSION': str(self.gradle_plugin_version),
'SDK_VER': self.android_sdk_platform,
'MIN_SDK_VER': self.android_sdk_platform,
'NDK_VERSION': self.android_ndk_version,
'SDK_BUILD_TOOL_VER': self.android_sdk_build_tool_version,
'LY_ENGINE_ROOT': common.normalize_path_for_settings(self.engine_root)
}
@@ -557,7 +584,7 @@ class AndroidProjectGenerator(object):
if self.override_gradle_path:
gradle_wrapper_cmd = [self.override_gradle_path]
else:
gradle_wrapper_cmd = ['gradle.bat' if platform.system() == 'Windows' else 'gradle']
gradle_wrapper_cmd = ['gradle']
gradle_wrapper_cmd.extend(['wrapper', '-p', str(self.build_dir.resolve())])
@@ -580,7 +607,8 @@ class AndroidProjectGenerator(object):
asset_type='',
android_sdk_path=str(self.android_sdk_path),
embed_assets_in_apk=True,
is_unit_test=True)
is_unit_test=True,
android_gradle_plugin_version=self.gradle_plugin_version)
else:
platform_settings_content = PLATFORM_SETTINGS_FORMAT.format(generation_timestamp=str(datetime.datetime.now().strftime("%c")),
platform='android',
@@ -589,16 +617,28 @@ class AndroidProjectGenerator(object):
asset_type=self.asset_type,
android_sdk_path=str(self.android_sdk_path),
embed_assets_in_apk=str(self.include_assets_in_apk),
is_unit_test=False)
is_unit_test=False,
android_gradle_plugin_version=self.gradle_plugin_version)
platform_settings_file = self.build_dir / 'platform.settings'
# Check if there already exists the build folder and a 'platform.settings' file. If there is an android gradle
# plugin version set and it is different than the one configured here, we will always overwrite it since
# there could be significant differences from one plug-in to the next
if platform_settings_file.is_file():
config = configparser.ConfigParser()
config.read([str(platform_settings_file.resolve(strict=True))])
if config.has_option('android', 'android_gradle_plugin'):
exist_agp_version = config.get('android', 'android_gradle_plugin')
if exist_agp_version != self.gradle_plugin_version:
self.overwrite_existing = True
platform_settings_file.open('w').write(platform_settings_content)
def create_default_local_properties(self):
"""
Create the default 'local.properties' file in the build folder
"""
template_android_ndk_path = common.normalize_path_for_settings(self.android_ndk_path, True)
template_android_sdk_path = common.normalize_path_for_settings(self.android_sdk_path, True)
if self.override_cmake_path:
# The cmake dir references the base cmake folder, not the executable path itself, so resolve to the base folder
@@ -608,7 +648,6 @@ class AndroidProjectGenerator(object):
local_properties_env = {
"GENERATION_TIMESTAMP": str(datetime.datetime.now().strftime("%c")),
"ANDROID_NDK_PATH": template_android_ndk_path,
"ANDROID_SDK_PATH": template_android_sdk_path,
"CMAKE_DIR_LINE": f'cmake.dir={template_cmake_path}' if template_cmake_path else ''
}
@@ -626,8 +665,7 @@ class AndroidProjectGenerator(object):
# before we can process it.
android_libraries_substitution_table = {
"ANDROID_SDK_HOME": common.normalize_path_for_settings(self.android_sdk_path, False),
"ANDROID_NDK_HOME": common.normalize_path_for_settings(self.android_ndk_path, False),
"ANDROID_SDK_VERSION": "android-".format(self.android_sdk_version)
"ANDROID_SDK_VERSION": f"android-{self.android_sdk_platform}"
}
android_libraries_template_json_path = self.android_project_builder_path / ANDROID_LIBRARIES_JSON_FILE
@@ -717,7 +755,7 @@ class AndroidProjectGenerator(object):
template_engine_root = common.normalize_path_for_settings(self.engine_root)
template_third_party_path = common.normalize_path_for_settings(self.third_party_path)
template_ndk_path = common.normalize_path_for_settings(self.android_ndk_path)
template_ndk_path = common.normalize_path_for_settings(os.path.join(self.android_sdk_path, self.android_ndk.location))
gradle_build_env = dict()
@@ -733,7 +771,6 @@ class AndroidProjectGenerator(object):
gradle_build_env['OVERRIDE_JAVA_SOURCESET'] = OVERRIDE_JAVA_SOURCESET_STR.format(absolute_azandroid_path=absolute_azandroid_path)
gradle_build_env['OPTIONAL_JNI_SRC_LIB_SET'] = ', "outputs/native-lib"'
for native_config in BUILD_CONFIGURATIONS:
@@ -755,7 +792,7 @@ class AndroidProjectGenerator(object):
cmake_argument_list.append('"-DLY_TEST_PROJECT=1"')
cmake_argument_list.extend([
f'"-DANDROID_NATIVE_API_LEVEL={self.android_ndk_platform}"',
f'"-DANDROID_NATIVE_API_LEVEL={self.android_native_api_level}"',
f'"-DLY_NDK_DIR={template_ndk_path}"',
'"-DANDROID_STL=c++_shared"',
'"-Wno-deprecated"',
@@ -835,8 +872,7 @@ class AndroidProjectGenerator(object):
dest_src_main_path.mkdir(parents=True)
az_android_package_env = AndroidProjectManifestEnvironment(engine_root=self.engine_root,
project_path=self.project_path,
android_sdk_version_number=self.android_sdk_version,
android_ndk_platform_number=self.android_ndk_platform,
android_sdk_version_number=self.android_sdk_platform,
is_test=self.is_test_project)
self.create_file_from_project_template(src_template_file=ANDROID_MANIFEST_FILE,
template_env=az_android_package_env,
@@ -1304,218 +1340,7 @@ class AndroidProjectGenerator(object):
self.new = new
ANDROID_PLATFORM_PATTERN = re.compile(r'([\w\d]*-)?(\d+\d*)') # Regex to handle android platform naming for both SDKs and NDKs
def validate_android_platform_input(input_android_platform, platform_variable_type, min_version, max_version):
"""
Helper tool to support android platform number inputs and perform min/max version validation
:param input_android_platform: The inpuit argument to evaluate
:param platform_variable_type: The type of platform version to validate (android sdk / android ndk)
:param min_version: The minimum version to validate against
:param max_version: The maximum version to validate against
:return: The int version of the extracted platform number from the input
"""
# Validate the platform number's format and against the supported versions
platform_number_match = ANDROID_PLATFORM_PATTERN.search(input_android_platform)
if not platform_number_match or not platform_number_match.group(2) or (platform_number_match.group(1) and platform_number_match.group(1) != 'android-'):
raise common.LmbrCmdError(f"Invalid {platform_variable_type} version value ({input_android_platform}). It must be "
f"either 'XX' or android-'XX' where 'XX' is a platform number.",
common.ERROR_CODE_INVALID_PARAMETER)
android_platform_number = int(platform_number_match.group(2))
if android_platform_number < min_version:
raise common.LmbrCmdError(f"Invalid {platform_variable_type} version value ({input_android_platform}) is less than the minimum "
f"supported version ({min_version}).",
common.ERROR_CODE_INVALID_PARAMETER)
if android_platform_number > max_version:
raise common.LmbrCmdError(f"Invalid {platform_variable_type} version value ({input_android_platform}) is greater than the maximum "
f"supported version ({max_version}).",
common.ERROR_CODE_INVALID_PARAMETER)
return android_platform_number
ANDROID_SDK_ENV_NAME = 'ANDROID_SDK'
ANDROID_SDK_MIN_PLATFORM = 28
ANDROID_SDK_MAX_PLATFORM = 29
def verify_android_sdk(android_sdk_platform, argument_name, override_android_sdk_path=None, preferred_sdk_build_tools_ver=None):
"""
Verify the android sdk and the requested platform platform against the android sdk path
:param android_sdk_platform: The android sdk platform to use (e.g. '28' or 'android-28')
:param argument_name: The name of the argument for descriptive errors to present
:param override_android_sdk_path: The location of the android SDK path if not set through the environment variable
:param preferred_sdk_build_tools_ver: Option prefered built tool version under the android SDK if available. Will fallback to the first one discovered
:returns tuple of the verified android sdk platform number, path to the Android SDK path and the build tool version
"""
android_sdk_platform_number = validate_android_platform_input(input_android_platform=android_sdk_platform,
platform_variable_type='android sdk',
min_version=ANDROID_SDK_MIN_PLATFORM,
max_version=ANDROID_SDK_MAX_PLATFORM)
# Get the candidate android sdk path from either the override argument or the system environment variable
if override_android_sdk_path:
check_android_sdk_path = override_android_sdk_path
else:
check_android_sdk_path = os.environ.get(ANDROID_SDK_ENV_NAME)
if not check_android_sdk_path:
raise common.LmbrCmdError(f"Android SDK path not set. Make sure that either the '{ANDROID_SDK_ENV_NAME}' environment is "
f"set or it is passed in through the {argument_name} argument")
# The android sdk folder structure is expected to have a 'platforms' sub folder based on the android sdk-platform number
check_android_sdk_path = pathlib.Path(check_android_sdk_path)
android_sdk_platforms_path = check_android_sdk_path / 'platforms'
if not android_sdk_platforms_path.is_dir():
raise common.LmbrCmdError(f"Invalid Android SDK path '{str(check_android_sdk_path)}': Missing 'platforms' directory.")
# Collect the available platform numbers from the platforms subdirectory
validated_android_platforms = []
for dir_item in android_sdk_platforms_path.iterdir():
if not dir_item.is_dir():
continue
check_file = dir_item / 'package.xml'
if check_file.is_file():
validated_android_platforms.append(dir_item.name)
if not validated_android_platforms:
raise common.LmbrCmdError(f"Invalid Android SDK path '{str(check_android_sdk_path)}': Unable to find any android platforms.")
# Normalize the android_sdk argument to fit the same folder name pattern
android_sdk_platform_name = f'android-{android_sdk_platform_number}'
if android_sdk_platform_name not in validated_android_platforms:
raise common.LmbrCmdError(f"Android SDK platform {android_sdk_platform_name} is not a valid for the android SDK located under '{str(check_android_sdk_path)}'")
# Enumerate through the build tools under android sdk
android_sdk_build_tools_dir = check_android_sdk_path / 'build-tools'
if not android_sdk_build_tools_dir.is_dir():
raise common.LmbrCmdError(f"Invalid Android SDK path '{str(check_android_sdk_path)}': Unable to find any built-tools folder.")
supported_build_tools = [str(build_tool.name) for build_tool in android_sdk_build_tools_dir.iterdir() if build_tool.is_dir()]
if not supported_build_tools:
raise common.LmbrCmdError(f"Invalid Android SDK path '{str(check_android_sdk_path)}': Unable to find any built-tools.")
if preferred_sdk_build_tools_ver:
if preferred_sdk_build_tools_ver in supported_build_tools:
validated_build_tool = preferred_sdk_build_tools_ver
else:
validated_build_tool = supported_build_tools[0]
logging.warning("Unable to locate android sdk build tool version {preferred_sdk_build_tools_ver}. Defaulting to version {validated_build_tool}")
else:
validated_build_tool = supported_build_tools[0]
return android_sdk_platform_number, check_android_sdk_path, validated_build_tool
ANDROID_NDK_ENV_NAME = 'ANDROID_NDK'
ANDROID_NDK_MIN_PLATFORM = 21
ANDROID_NDK_MAX_PLATFORM = 29
ANDROID_NDK_SOURCE_PROPERTIES_REVISION_PATTERN = re.compile(r'Pkg.Revision\s*=\s*(\d+.\d+.\d+)')
def verify_android_ndk(android_ndk_platform, argument_name, override_android_ndk_path=None):
"""
Verify the android ndk and requested platform against the android ndk path
:param android_ndk_platform: The android ndk platform to use (e.g. '21' or 'android-21')
:param argument_name: The name of the argument for descriptive errors to present
:param override_android_ndk_path: The location of the android NDK path if not set through the environment variable
:returns tuple of the verified android ndk platform number and the Path to the Android SDK path and the
"""
android_ndk_platform_number = validate_android_platform_input(input_android_platform=android_ndk_platform,
platform_variable_type='android ndk',
min_version=ANDROID_NDK_MIN_PLATFORM,
max_version=ANDROID_NDK_MAX_PLATFORM)
# Get the candidate android ndk path from either the override argument or the system environment variable
if override_android_ndk_path:
check_android_ndk_path = str(override_android_ndk_path)
else:
check_android_ndk_path = os.environ.get(ANDROID_NDK_ENV_NAME)
if not check_android_ndk_path:
raise common.LmbrCmdError(f"Android NDK path not set. Make sure that either the {ANDROID_NDK_ENV_NAME} environment "
f"is set or it is passed in through the {argument_name} argument")
check_android_ndk_path = pathlib.Path(check_android_ndk_path)
# Validate the android ndk path
# Determine the NDK revision by reading the source.properties file
ndk_source_properties_file = check_android_ndk_path / 'source.properties'
if not ndk_source_properties_file.is_file():
raise common.LmbrCmdError(f"Invalid Android NDK path '{str(check_android_ndk_path)}'. Missing 'source.properties' file.",
common.ERROR_CODE_INVALID_PARAMETER)
ndk_source_properties_file_content = ndk_source_properties_file.read_text(encoding=common.DEFAULT_TEXT_READ_ENCODING,
errors=common.ENCODING_ERROR_HANDLINGS)
ndk_revision_match = ANDROID_NDK_SOURCE_PROPERTIES_REVISION_PATTERN.search(ndk_source_properties_file_content)
if not ndk_revision_match:
raise common.LmbrCmdError(f"Invalid Android NDK path '{str(check_android_ndk_path)}'. Unable to extract version from 'source.properties' file.",
common.ERROR_CODE_INVALID_PARAMETER)
ndk_revision_number = LooseVersion(ndk_revision_match.group(1))
logging.info(f"Detected Android NDK Revision {str(ndk_revision_number)}")
# Collect the supported android platforms from the required 'platforms' folder under the ndk path
android_ndk_platforms_path = check_android_ndk_path / 'platforms'
if not android_ndk_platforms_path.is_dir():
raise common.LmbrCmdError(f"Invalid Android NDK path '{str(check_android_ndk_path)}'. Missing 'platforms' folder.",
common.ERROR_CODE_INVALID_PARAMETER)
validated_android_platforms = []
for dir_item in android_ndk_platforms_path.iterdir():
if not dir_item.is_dir():
continue
api_version_match = ANDROID_PLATFORM_PATTERN.search(dir_item.name)
if not api_version_match or api_version_match.group(1) != 'android-':
continue
check_lib_path = dir_item / 'arch-arm64/usr/lib'
if check_lib_path.is_dir():
validated_android_platforms.append(dir_item.name)
# For NDK revisions 19 and up, there is a mapping file for version numbers that map to other version.
platforms_map_aliases = {}
if ndk_revision_number >= LooseVersion('19.0.0'):
platforms_map_file = check_android_ndk_path / 'meta/platforms.json'
if platforms_map_file.exists():
with open(platforms_map_file, 'r') as platforms_map_file_handle:
platforms_map_file_json = json.load(platforms_map_file_handle)
platforms_map_aliases = platforms_map_file_json['aliases']
elif validated_android_platforms:
# Revisions before 19 does not have a mapping file for API versions, they fall back to the previous one
# So we need to make a mapping file that does the same
platforms_map_aliases = {}
validated_android_platforms.sort()
max_supported_api_number = int(ANDROID_PLATFORM_PATTERN.search(validated_android_platforms[-1]).group(2))
for validated_android_platform in validated_android_platforms:
current_api_version = int(ANDROID_PLATFORM_PATTERN.search(validated_android_platform).group(2))
next_api_version = current_api_version + 1
while f'android-{next_api_version}' not in validated_android_platforms and next_api_version <= max_supported_api_number:
platforms_map_aliases[str(next_api_version)] = current_api_version
next_api_version += 1
# Go through the aliases and add to the validated platforms if it is mapped to an existing platform
for alias_key, alias_value in platforms_map_aliases.items():
if not ANDROID_PLATFORM_PATTERN.search(f'android-{alias_key}'):
# Skip any non android-XX (XX = number) aliases
continue
aliased_platform_key = f'android-{alias_value}'
if aliased_platform_key in validated_android_platforms:
validated_android_platforms.append(f'android-{alias_key}')
if not validated_android_platforms:
raise common.LmbrCmdError(f"Invalid Android NDK path {str(check_android_ndk_path)}")
# Verify the ndk platform against the ndk path
android_ndk_platform_name = f'android-{android_ndk_platform_number}'
if android_ndk_platform_name not in validated_android_platforms:
raise common.LmbrCmdError(f"Android NDK platform {android_ndk_platform_name} is not a valid for the Android NDK located under '{str(check_android_ndk_path)}'")
return android_ndk_platform_number, check_android_ndk_path
ADB_TARGET = 'adb.exe' if platform.system() == 'Windows' else 'adb'
def resolve_adb_tool(android_sdk_path):
@@ -1528,9 +1353,16 @@ def resolve_adb_tool(android_sdk_path):
if isinstance(android_sdk_path, str):
android_sdk_path = pathlib.Path(android_sdk_path)
check_adb_target = android_sdk_path / 'platform-tools' / ADB_TARGET
if not check_adb_target.exists():
raise common.LmbrCmdError(f"Invalid Android SDK path '{str(android_sdk_path)}': Unable to locate '{ADB_TARGET}'.")
file_found = False
for executable_path_ext in common.PLATFORM_EXECUTABLE_EXTENSIONS:
check_adb_target = android_sdk_path / 'platform-tools' / f'adb{executable_path_ext}'
if check_adb_target.is_file():
file_found = True
break
if not file_found:
raise common.LmbrCmdError(f"Invalid Android SDK path '{str(android_sdk_path)}': Unable to locate 'adb'.")
return check_adb_target
@@ -1633,3 +1465,196 @@ class AdbTool(common.CommandLineExec):
else:
adb_params = arguments
return super().popen(adb_params, cwd)
class AndroidGradlePluginInfo(object):
def __init__(self, android_gradle_plugin_version):
if android_gradle_plugin_version not in ANDROID_GRADLE_PLUGIN_COMPATIBILITY_MAP.keys():
raise common.LmbrCmdError(f"Android Gradle Plugin version {android_gradle_plugin_version} is not supported. "
f"Only the following version(s) are supported: {','.join(ANDROID_GRADLE_PLUGIN_COMPATIBILITY_MAP.keys())}")
details = ANDROID_GRADLE_PLUGIN_COMPATIBILITY_MAP[android_gradle_plugin_version]
self.default_sdk_build_tools_version = LooseVersion(details.get('sdk_build'))
self.default_ndk_version = LooseVersion(details.get('default_ndk'))
self.min_gradle_version = LooseVersion(details.get('min_gradle_version'))
self.min_cmake_version = LooseVersion(details.get('min_cmake_version'))
max_cmake_version_number = details.get('max_cmake_version')
self.max_cmake_version = None if max_cmake_version_number is None else LooseVersion(max_cmake_version_number)
class AndroidSDKResolver(object):
"""
Class that manages the Android SDK tool to validate, install packages (e.g. built tools, sdk platforms, ndk, etc)
"""
class InstalledPackage(object):
def __init__(self, installed_package_components):
assert len(installed_package_components) == 4, '4 sections expected for installed package components (path, version, description, location)'
self.path = installed_package_components[0]
self.version = LooseVersion(installed_package_components[1])
self.description = installed_package_components[2]
self.location = installed_package_components[3]
class AvailablePackage(object):
def __init__(self, available_package_components):
assert len(available_package_components) == 3, '3 sections expected for installed package components (path, version, description)'
self.path = available_package_components[0]
self.version = LooseVersion(available_package_components[1])
self.description = available_package_components[2]
class AvailableUpdate(object):
def __init__(self, available_update_components):
assert len(available_update_components) == 3, '3 sections expected for installed package components (path, version, available)'
self.path = available_update_components[0]
self.version = LooseVersion(available_update_components[1])
self.available = available_update_components[2]
def __init__(self, android_sdk_path):
self.android_sdk_path = android_sdk_path or os.environ.get(ANDROID_SDK_ENV_NAME)
if not self.android_sdk_path:
raise common.LmbrCmdError(f"Android SDK path not set or it was not passed into the command to generate the android project")
if not os.path.isdir(self.android_sdk_path):
raise common.LmbrCmdError(f"Android SDK path {self.android_sdk_path} is not valid")
if platform.system() == 'Windows':
self.sdk_manager_path = pathlib.Path(self.android_sdk_path) / 'tools' / 'bin' / 'sdkmanager.bat'
else:
raise common.LmbrCmdError(f"This tool is not supported on the current platform {platform.system()}")
if not self.sdk_manager_path.is_file():
raise common.LmbrCmdError(f"Android SDK path {self.android_sdk_path} is not valid or complete. Missing {self.sdk_manager_path}")
self.sdk_manager = common.CommandLineExec(str(self.sdk_manager_path.resolve()))
self.installed_packages = {}
self.available_packages = {}
self.available_updates = {}
self.refresh_sdk_installation()
def refresh_sdk_installation(self):
"""
Utilize the sdk_manager command line tool from the Android SDK to collect / refresh the list of
installed, available, and updateable packages that are managed by the android SDK.
"""
self.installed_packages = {}
self.available_packages = {}
self.available_updates = {}
def _factory_installed_package(package_map, item_components):
package_map[item_components[0]] = AndroidSDKResolver.InstalledPackage(item_components)
def _factory_available_package(package_map, item_components):
package_map[item_components[0]] = AndroidSDKResolver.AvailablePackage(item_components)
def _factory_available_update(package_map, item_components):
package_map[item_components[0]] = AndroidSDKResolver.AvailableUpdate(item_components)
# Use the SDK manager to collect the available and installed packages
result_code, result_stdout, result_stderr = self.sdk_manager.exec(['--list'], capture_stdout=True, suppress_stderr=True)
current_append_map = None
current_item_factory = None
for package_item in result_stdout.split('\n'):
package_item_stripped = package_item.strip()
if not package_item_stripped:
continue
if '|' not in package_item_stripped:
if package_item_stripped.upper() == 'INSTALLED PACKAGES:':
current_append_map = self.installed_packages
current_item_factory = _factory_installed_package
elif package_item_stripped.upper() == 'AVAILABLE PACKAGES:':
current_append_map = self.available_packages
current_item_factory = _factory_available_package
elif package_item_stripped.upper() == 'AVAILABLE UPDATES:':
current_append_map = self.available_updates
current_item_factory = _factory_available_update
else:
current_append_map = None
current_item_factory = None
continue
item_parts = [split.strip() for split in package_item_stripped.split('|')]
if len(item_parts) < 3:
continue
elif item_parts[1].upper() in ('VERSION', 'INSTALLED', '-------'):
continue
elif current_append_map is None:
continue
if current_append_map is not None and current_item_factory is not None:
current_item_factory(current_append_map, item_parts)
def is_package_installed(self, search_package_path):
"""
Check if a package path to see if its a package that is installed. The path can use wildcard '*'s
The function will return a list of the results that match the package paths, ordered by the newest version first
"""
def _package_sort(package):
return package.version
package_detail_result_list = []
for installed_package_path, installed_package_details in self.installed_packages.items():
if fnmatch.fnmatch(installed_package_path, search_package_path):
package_detail_result_list.append(installed_package_details)
package_detail_result_list.sort(reverse=True, key=_package_sort)
return package_detail_result_list
def is_package_available(self, search_package_path):
"""
Check if a package path to see if its an available package to install. The path can use wildcard '*'s
The function will return a list of the results that match the package paths, ordered by the newest version first
"""
def _package_sort(package):
return package.version
package_detail_result_list = []
for available_package_path, available_package_details in self.available_packages.items():
if fnmatch.fnmatch(available_package_path, search_package_path):
package_detail_result_list.append(available_package_details)
package_detail_result_list.sort(reverse=True, key=_package_sort)
return package_detail_result_list
def install_package(self, package_install_path, package_description):
"""
Install a package based on the path of an available android sdk package
"""
# Skip installation if the package is already installed
package_result_list = self.is_package_installed(package_install_path)
if package_result_list:
installed_package_detail = package_result_list[0]
logging.info(f"{installed_package_detail.description} (version {installed_package_detail.version}) Detected")
return installed_package_detail
# Make sure the package name is available
package_result_list = self.is_package_available(package_install_path)
if not package_result_list:
raise common.LmbrCmdError(f"Invalid Android SDK Package {package_description}: Bad package path {package_install_path}")
# Reverse sort and pick the first item, which should be the latest (if the install path contains wildcards)
def _available_sort(item):
return item.path
package_result_list.sort(reverse=True, key=_available_sort)
available_package_to_install = package_result_list[0] # For multiple hits, resolve to the first item which will be the latest version
# Perform the package installation
logging.info(f"Installing {available_package_to_install.description} ...")
result_code, result_stdout, result_stderr = self.sdk_manager.exec(['--install', available_package_to_install.path], capture_stdout=True, suppress_stderr=True)
if result_code != 0:
raise common.LmbrCmdError(f"Error installing package {available_package_to_install.path}: \n{result_stderr}")
# Refresh the tracked SDK Contents
self.refresh_sdk_installation()
# Get the package details to verify
package_result_list = self.is_package_installed(package_install_path)
if package_result_list:
installed_package_detail = package_result_list[0]
logging.info(f"{installed_package_detail.description} (version {installed_package_detail.version}) Installed")
return installed_package_detail
else:
raise common.LmbrCmdError(f"Error installing package {available_package_to_install.path}: \n{result_stderr}")
@@ -27,7 +27,7 @@ from cmake.Tools import common
from cmake.Tools.Platform.Android import android_support
GRADLE_ARGUMENT_NAME = '--gradle-install-path'
GRADLE_MIN_VERSION = LooseVersion('4.10.1')
GRADLE_MIN_VERSION = LooseVersion('6.5')
GRADLE_MAX_VERSION = LooseVersion('7.0.0')
GRADLE_VERSION_REGEX = re.compile(r"Gradle\s(\d+.\d+.?\d*)")
GRADLE_EXECUTABLE = 'gradle.bat' if platform.system() == 'Windows' else 'gradle'
@@ -48,9 +48,9 @@ def verify_gradle(override_gradle_path=None):
CMAKE_ARGUMENT_NAME = '--cmake-install-path'
CMAKE_MIN_VERSION = LooseVersion('3.17.0')
CMAKE_MIN_VERSION = LooseVersion('3.19.0')
CMAKE_VERSION_REGEX = re.compile(r'cmake version (\d+.\d+.?\d*)')
CMAKE_EXECUTABLE = 'cmake.exe' if platform.system() == 'Windows' else 'cmake'
CMAKE_EXECUTABLE = 'cmake'
def verify_cmake(override_cmake_path=None):
@@ -69,7 +69,7 @@ def verify_cmake(override_cmake_path=None):
NINJA_ARGUMENT_NAME = '--ninja-install-path'
NINJA_VERSION_REGEX = re.compile(r'(\d+.\d+.?\d*)')
NINJA_EXECUTABLE = 'ninja.exe' if platform.system() == 'Windows' else 'ninja'
NINJA_EXECUTABLE = 'ninja'
def verify_ninja(override_ninja_path=None):
@@ -78,7 +78,7 @@ def verify_ninja(override_ninja_path=None):
"""
return common.verify_tool(override_tool_path=override_ninja_path,
tool_name='ninja',
tool_filename='ninja.exe' if platform.system() == 'Windows' else 'ninja',
tool_filename='ninja',
argument_name=NINJA_ARGUMENT_NAME,
tool_version_argument='--version',
tool_version_regex=NINJA_VERSION_REGEX,
@@ -103,13 +103,21 @@ def build_optional_signing_profile(store_file, store_password, key_alias, key_pa
ANDROID_SDK_ARGUMENT_NAME = '--android-sdk-path'
ANDROID_SDK_PLATFORM_ARGUMENT_NAME = '--android-sdk-version'
ANDROID_SDK_PLATFORM_ARGUMENT_NAME = '--android-sdk-platform'
ANDROID_SDK_PREFERRED_TOOL_VER = '--android-sdk-build-tool-version'
ANDROID_NATIVE_API_LEVEL = '--android-native-api-level'
MIN_ANDROID_SDK_PLATFORM = 28 # The minimum platform/api level that is supported for the SDK Platform
MIN_NATIVE_API_LEVEL = 24 # The minimum Native API level that is supported for the NDK
ANDROID_NDK_ARGUMENT_NAME = '--android-ndk-path'
ANDROID_NDK_PLATFORM_ARGUMENT_NAME = '--android-ndk-version'
ANDROID_GRADLE_PLUGIN_ARGUMENT_NAME = '--gradle-plugin-version'
ANDROID_GRADLE_MIN_PLUGIN_VERSION = LooseVersion("4.2.0")
# Constants for asset-related options for APK generation
INCLUDE_APK_ASSETS_ARGUMENT_NAME = "--include-apk-assets"
ASSET_MODE_ARGUMENT_NAME = "--asset-mode"
@@ -147,6 +155,7 @@ def main(args):
parser = argparse.ArgumentParser(description="Prepare the android studio subfolder")
# Required Arguments
parser.add_argument('--engine-root',
help='The path to the engine root. Defaults to the current working directory.',
default=os.getcwd())
@@ -160,32 +169,42 @@ def main(args):
help='The path to the 3rd Party root directory',
required=True)
parser.add_argument(ANDROID_NDK_ARGUMENT_NAME,
help='The path to the android NDK',
required=True)
parser.add_argument(ANDROID_SDK_ARGUMENT_NAME,
help='The path to the android SDK',
required=True)
parser.add_argument(ANDROID_SDK_PLATFORM_ARGUMENT_NAME,
help='The android SDK version',
parser.add_argument('-g', '--project-path',
help='The project path to generate an android project',
required=True)
parser.add_argument(ANDROID_SDK_PLATFORM_ARGUMENT_NAME,
help=f'The android SDK platform number version to use for the APK. (Minimum {MIN_ANDROID_SDK_PLATFORM})',
type=int,
default=-1)
parser.add_argument(ANDROID_NATIVE_API_LEVEL,
help=f'The android native API level to use for the APK. If not set, this will default to the android SDK platform. (Minimum {MIN_ANDROID_SDK_PLATFORM})',
type=int,
default=-1)
# Override arguments
parser.add_argument(ANDROID_SDK_PREFERRED_TOOL_VER,
help='The preferred android sdk build version (i.e. 28.0.3). Will default to the first one detected under the android sdk',
default=None,
help='The android SDK build tools version.',
required=False)
parser.add_argument(ANDROID_NDK_PLATFORM_ARGUMENT_NAME,
help='The android NDK version',
required=True)
required=False)
parser.add_argument(GRADLE_ARGUMENT_NAME,
help=f'The path to installed gradle. The version of gradle must fall in between {str(GRADLE_MIN_VERSION)} and {str(GRADLE_MAX_VERSION)}.',
default=None,
required=False)
parser.add_argument(ANDROID_GRADLE_PLUGIN_ARGUMENT_NAME,
help=f'The version of the android gradle plugin to use. Defaults to the minimum version ({ANDROID_GRADLE_MIN_PLUGIN_VERSION})',
default=str(ANDROID_GRADLE_MIN_PLUGIN_VERSION))
parser.add_argument(CMAKE_ARGUMENT_NAME,
help=f'The path to cmake build tool if not installed on the system path. The version of cmake must be at least version {str(CMAKE_MIN_VERSION)}.',
default=None,
@@ -196,9 +215,6 @@ def main(args):
default=None,
required=False)
parser.add_argument('-g', '--project-path',
help='The project path to generate an android project')
# Asset Options
parser.add_argument(INCLUDE_APK_ASSETS_ARGUMENT_NAME,
action='store_true',
@@ -207,11 +223,11 @@ def main(args):
parser.add_argument(ASSET_MODE_ARGUMENT_NAME,
choices=ALL_ASSET_MODES,
default=ASSET_MODE_LOOSE,
help='Asset Mode (vfs|pak|loose) to use when including assets into the APK')
help=f'Asset Mode (vfs|pak|loose) to use when including assets into the APK. (Defaults to {ASSET_MODE_LOOSE})')
parser.add_argument(ASSET_TYPE_ARGUMENT_NAME,
default=DEFAULT_ASSET_TYPE,
help='Asset Type to use when including assets into the APK')
help=f'Asset Type to use when including assets into the APK. (Defaults to {DEFAULT_ASSET_TYPE})')
parser.add_argument('--debug',
action='store_true',
@@ -260,16 +276,81 @@ def main(args):
ninja_version, override_ninja_path = verify_ninja(override_ninja_path=parsed_args.get_argument(NINJA_ARGUMENT_NAME))
logging.info("Detected Ninja version %s", str(ninja_version))
# Verify the android sdk path and sdk version
verified_android_sdk_platform, verified_android_sdk_path, android_sdk_build_tool_ver = android_support.verify_android_sdk(android_sdk_platform=parsed_args.get_argument(ANDROID_SDK_PLATFORM_ARGUMENT_NAME),
argument_name=ANDROID_SDK_ARGUMENT_NAME,
override_android_sdk_path=parsed_args.get_argument(ANDROID_SDK_ARGUMENT_NAME),
preferred_sdk_build_tools_ver=parsed_args.get_argument(ANDROID_SDK_PREFERRED_TOOL_VER))
# Get the android sdk platform version to use from the arguments, but also handle the deprecated argument name
android_sdk_platform_version = parsed_args.get_argument(ANDROID_SDK_PLATFORM_ARGUMENT_NAME)
# Verify the android ndk path and ndk version
verified_android_ndk_platform, verified_android_ndk_path = android_support.verify_android_ndk(android_ndk_platform=parsed_args.get_argument(ANDROID_NDK_PLATFORM_ARGUMENT_NAME),
argument_name=ANDROID_NDK_ARGUMENT_NAME,
override_android_ndk_path=parsed_args.get_argument(ANDROID_NDK_ARGUMENT_NAME))
# Get the gradle plugin details and validate against the current environment
android_gradle_plugin_version = parsed_args.get_argument(ANDROID_GRADLE_PLUGIN_ARGUMENT_NAME)
android_gradle_plugin = android_support.AndroidGradlePluginInfo(android_gradle_plugin_version)
logging.info(f"Generating Android Gradle Plugin version {android_gradle_plugin_version} based project")
if gradle_version < android_gradle_plugin.min_gradle_version:
raise common.LmbrCmdError(f"The current version of gradle ({gradle_version}) does not satisfy the minimum version "
f"({android_gradle_plugin.min_gradle_version}) needed for the android gradle plugin "
f"({android_gradle_plugin_version}). Please upgrade your gradle.")
if cmake_version < android_gradle_plugin.min_cmake_version:
raise common.LmbrCmdError(f"The current version of cmake ({cmake_version}) does not satisfy the minimum version "
f"({android_gradle_plugin.min_cmake_version}) needed for the android gradle plugin "
f"({android_gradle_plugin_version}). Please upgrade your cmake.")
if android_gradle_plugin.max_cmake_version and cmake_version > android_gradle_plugin.max_cmake_version:
raise common.LmbrCmdError(f"The current version of cmake ({cmake_version}) exceeds the maximum version "
f"({android_gradle_plugin.max_cmake_version}) of the android gradle plugin "
f"({android_gradle_plugin_version}).")
# Use the SDK Resolver to make sure the build tools and ndk
android_sdk = android_support.AndroidSDKResolver(android_sdk_path=parsed_args.get_argument(ANDROID_SDK_ARGUMENT_NAME))
# If no SDK platform is provided, check for any installed one
if android_sdk_platform_version < 0:
android_sdk_platform_version = MIN_ANDROID_SDK_PLATFORM
installed_android_sdk_platforms = android_sdk.is_package_installed('platforms;*')
if installed_android_sdk_platforms:
# If there are installed platforms, check the most recent one
latest_platform_version = -1
for installed_android_sdk_platform in installed_android_sdk_platforms:
platform_number_match = re.match(r'platforms;android-([0-9]*)', installed_android_sdk_platform.path)
if not platform_number_match:
continue
check_platform_version = int(platform_number_match.group(1))
if check_platform_version > latest_platform_version:
latest_platform_version = check_platform_version
if latest_platform_version >= MIN_ANDROID_SDK_PLATFORM:
android_sdk_platform_version = latest_platform_version
else:
if android_sdk_platform_version < MIN_ANDROID_SDK_PLATFORM:
raise common.LmbrCmdError(f"Invalid argument for {ANDROID_SDK_PLATFORM_ARGUMENT_NAME} ({android_sdk_platform_version}). Must be greater than the minimum value supported {MIN_ANDROID_SDK_PLATFORM}.")
# Get the android native api level from the arguments. Default to the sdk platform version if not provided
android_native_api_level = parsed_args.get_argument(ANDROID_NATIVE_API_LEVEL)
if android_native_api_level < 0:
android_native_api_level = android_sdk_platform_version
else:
if android_native_api_level < MIN_NATIVE_API_LEVEL:
raise common.LmbrCmdError(f"Invalid argument for {ANDROID_NATIVE_API_LEVEL} ({android_native_api_level}). Must be greater than the minimum value supported {MIN_NATIVE_API_LEVEL}.")
# Check and make sure that the requested sdk platform exists, download if necessary
platform_package_name = f"platforms;android-{android_sdk_platform_version}"
android_sdk.install_package(package_install_path=platform_package_name,
package_description=f'Android SDK Platform {android_sdk_platform_version}')
# Make sure we have the extra android packages "market_apk_expansion" and "market_licensing" which is needed by the APK
android_sdk.install_package(package_install_path='extras;google;market_apk_expansion',
package_description='Google APK Expansion Library')
android_sdk.install_package(package_install_path='extras;google;market_licensing',
package_description='Google Play Licensing Library')
# Install either the requested SDK build tools or the default one for the android gradle plugin version
build_tools_version = parsed_args.get_argument(ANDROID_SDK_PREFERRED_TOOL_VER) or android_gradle_plugin.default_sdk_build_tools_version
build_tools_package_name = f'build-tools;{build_tools_version}'
build_tools_package = android_sdk.install_package(package_install_path=build_tools_package_name,
package_description='Android SDK Build Tools')
# Install either the requested NDK version or the default one for the android gradle plugin version
android_ndk_version = parsed_args.get_argument(ANDROID_NDK_PLATFORM_ARGUMENT_NAME) or android_gradle_plugin.default_ndk_version
android_ndk_package_name = f'ndk;{android_ndk_version}'
android_ndk_package = android_sdk.install_package(package_install_path=android_ndk_package_name,
package_description='Android NDK')
# Verify the engine root path and project path
verified_project_path, verified_engine_root = common.verify_project_and_engine_root(project_root=parsed_args.project_path,
@@ -277,10 +358,9 @@ def main(args):
is_test_project = parsed_args.unit_test
# Verify the 3rd Party Root Path
third_party_path = pathlib.Path(parsed_args.third_party_path) / '3rdParty.txt'
if not third_party_path.is_file():
raise common.LmbrCmdError("Invalid --third-party-path '{}'. Make sure it exists and contains "
"3rdParty.txt".format(parsed_args.third_party_path),
third_party_path = pathlib.Path(parsed_args.third_party_path)
if not third_party_path.is_dir():
raise common.LmbrCmdError(f"Invalid --third-party-path '{parsed_args.third_party_path}'.",
common.ERROR_CODE_INVALID_PARAMETER)
third_party_path = third_party_path.parent
@@ -293,23 +373,23 @@ def main(args):
logging.debug("Engine Root : %s", str(verified_engine_root.resolve()))
logging.debug("Build Path : %s", str(build_dir.resolve()))
logging.debug("Android NDK Path : %s", str(verified_android_ndk_path.resolve()))
logging.debug("Android SDK Path : %s", str(verified_android_sdk_path.resolve()))
# Prepare the generator and execute
generator = android_support.AndroidProjectGenerator(engine_root=verified_engine_root,
project_path=verified_project_path,
build_dir=build_dir,
android_sdk_path=verified_android_sdk_path,
android_ndk_path=verified_android_ndk_path,
android_sdk_version=verified_android_sdk_platform,
android_ndk_platform=verified_android_ndk_platform,
android_sdk_path=android_sdk.android_sdk_path,
build_tool=build_tools_package,
android_sdk_platform=android_sdk_platform_version,
android_native_api_level=android_native_api_level,
android_ndk=android_ndk_package,
project_path=verified_project_path,
third_party_path=third_party_path,
cmake_version=cmake_version,
override_cmake_path=override_cmake_path,
override_gradle_path=override_gradle_path,
gradle_version=gradle_version,
gradle_plugin_version=android_gradle_plugin_version,
override_ninja_path=override_ninja_path,
android_sdk_build_tool_version=android_sdk_build_tool_ver,
include_assets_in_apk=parsed_args.get_argument(INCLUDE_APK_ASSETS_ARGUMENT_NAME),
asset_mode=parsed_args.get_argument(ASSET_MODE_ARGUMENT_NAME),
asset_type=parsed_args.get_argument(ASSET_TYPE_ARGUMENT_NAME),
@@ -170,117 +170,3 @@ def test_verify_ninja(tmpdir, from_override, version_str, expected_result):
finally:
subprocess.check_output = orig_check_output
TEST_VALIDATE_VERSION_MIN = 19
TEST_VALIDATE_VERSION_MAX = 21
@pytest.mark.parametrize(
"test_input, expected", [
pytest.param('20', 20),
pytest.param('android-20', 20),
pytest.param('bad-21', "android-'XX'"),
pytest.param('10', "minimum"),
pytest.param('30', "maximum")
]
)
def test_validate_android_platform_input(test_input, expected):
try:
result = android_support.validate_android_platform_input(input_android_platform=test_input,
platform_variable_type='test',
min_version=TEST_VALIDATE_VERSION_MIN,
max_version=TEST_VALIDATE_VERSION_MAX)
assert isinstance(expected, int)
assert result == expected
except Exception as e:
assert expected in str(e)
def test_verify_android_sdk_success(tmpdir):
test_android_path = 'android_sdk'
sdk_version_number = 28
sdk_version = f'android-{sdk_version_number}'
tmpdir.ensure(f'{test_android_path}/platforms/{sdk_version}/package.xml')
tmpdir.ensure(f'{test_android_path}/build-tools/28.0.3/package.xml')
tmpdir.ensure(f'{test_android_path}/build-tools/29.0.3/package.xml')
input_sdk_path = tmpdir.join(test_android_path).realpath()
argument_name = '--android-sdk'
requested_build_tool_version = '29.0.3'
result_sdk_version, result_sdk_path, result_build_tool_version = android_support.verify_android_sdk(android_sdk_platform=sdk_version,
argument_name=argument_name,
override_android_sdk_path=input_sdk_path,
preferred_sdk_build_tools_ver=requested_build_tool_version)
assert result_sdk_version == sdk_version_number
assert result_sdk_path == input_sdk_path
assert result_build_tool_version == requested_build_tool_version
sdk_version_number_only = str(sdk_version_number)
result_sdk_version, result_sdk_path, result_build_tool_version = android_support.verify_android_sdk(android_sdk_platform=sdk_version_number_only,
argument_name=argument_name,
override_android_sdk_path=input_sdk_path)
assert result_sdk_version == sdk_version_number
assert result_sdk_path == input_sdk_path
assert result_build_tool_version == '28.0.3'
requested_build_tool_version = '30.0.3'
result_sdk_version, result_sdk_path, result_build_tool_version = android_support.verify_android_sdk(android_sdk_platform=sdk_version,
argument_name=argument_name,
override_android_sdk_path=input_sdk_path,
preferred_sdk_build_tools_ver=requested_build_tool_version)
assert result_sdk_version == sdk_version_number
assert result_sdk_path == input_sdk_path
assert result_build_tool_version == '28.0.3'
@pytest.mark.parametrize(
"desired_ndk_version_number, available_ndk_revisions, pkg_revision, mappings, expect_error", [
pytest.param(21, [21, 22, 24], '15.2.4203891', None, False, id='preNdk19ExactMatch'),
pytest.param(23, [21, 22, 24], '15.2.4203891', None, False, id='preNdk19FallbackMatch'),
pytest.param(22, [21, 22, 24], '19.2.4203891', {'23': 21}, False, id='postNdk19ExactMatch'),
pytest.param(23, [21, 22, 24], '21.2.4203891', {'23': 21}, False, id='postNdk19MappingMatch'),
pytest.param(android_support.ANDROID_NDK_MIN_PLATFORM-1, [21, 22, 24], '15.2.4203891', None, True, id='preNdk19BelowMinVer'),
pytest.param(android_support.ANDROID_NDK_MAX_PLATFORM+1, [21, 22, 24], '15.2.4203891', None, True, id='preNdk19AboveMaxVer'),
pytest.param(25, [21, 22, 24], '19.2.4203891', {'23': 21}, True, id='postNdk19NoMatch')
]
)
def test_verify_android_ndk_success(tmpdir, desired_ndk_version_number, available_ndk_revisions, pkg_revision, mappings, expect_error):
test_android_path = 'android_ndk'
for ndk_number in available_ndk_revisions:
tmpdir.ensure(f'{test_android_path}/platforms/android-{ndk_number}/arch-arm64/usr/lib/libc.so')
tmpdir.ensure(f'{test_android_path}/source.properties')
test_ndk_source_properties_file = tmpdir / test_android_path / 'source.properties'
test_ndk_source_properties_file.write_text(f'Pkg.Desc = Android NDK\nPkg.Revision = {pkg_revision}\n', encoding='ASCII')
if mappings:
platform_mapping = {
# min and max are arbitrary for now since we dont use it during evaluation, but if we do, parameterize it here as well
"min": 16, #
"max": 29,
"aliases": {}
}
for key, value in mappings.items():
platform_mapping['aliases'][key] = value
tmpdir.ensure(f'{test_android_path}/meta/platforms.json')
platform_mapping_file = tmpdir / test_android_path / 'meta/platforms.json'
platform_mapping_file.write_text(json.dumps(platform_mapping), encoding='ASCII')
input_ndk_path = tmpdir.join(test_android_path).realpath()
try:
android_ndk_platform_number, android_ndk_path = android_support.verify_android_ndk(android_ndk_platform=str(desired_ndk_version_number),
argument_name="--android-ndk",
override_android_ndk_path=input_ndk_path)
assert not expect_error
assert android_ndk_platform_number == desired_ndk_version_number
assert android_ndk_path == input_ndk_path
except Exception:
assert expect_error
+30 -7
View File
@@ -55,6 +55,7 @@ ENGINE_ROOT_CHECK_FILE = 'engine.json'
HASH_CHUNK_SIZE = 200000
class LmbrCmdError(Exception):
"""
Wrapper class to the general exception class where will absorb and prevent the printing of stack.
@@ -244,6 +245,19 @@ def load_template_file(template_file_path, template_env):
raise FileNotFoundError(f"Invalid file path. Cannot find template file located at {str(template_file_path)}")
# Determine the possible file extensions for executable files based on the host platform
PLATFORM_EXECUTABLE_EXTENSIONS = [''] # Files without extensions are always considered
if platform.system() == 'Windows':
# Windows manages its executable extensions through the %PATHEXT% environment variable
path_extensions_str = os.environ.get('PATHEXT', default='.EXE;.COM;.BAT;.CMD')
PLATFORM_EXECUTABLE_EXTENSIONS.extend([pathext.lower() for pathext in path_extensions_str.split(';')])
elif platform.system() == 'Linux':
PLATFORM_EXECUTABLE_EXTENSIONS = ['', '.out']
else:
PLATFORM_EXECUTABLE_EXTENSIONS = ['']
def verify_tool(override_tool_path, tool_name, tool_filename, argument_name, tool_version_argument, tool_version_regex, min_version, max_version):
"""
Support method to validate a required system tool needed for the build either through an installed tool in the
@@ -270,12 +284,21 @@ def verify_tool(override_tool_path, tool_name, tool_filename, argument_name, too
elif not isinstance(override_tool_path, pathlib.Path):
raise LmbrCmdError(f"Invalid {tool_name} path argument. '{override_tool_path}' must be a string or Path",
ERROR_CODE_INVALID_PARAMETER)
check_tool_path = override_tool_path / tool_filename
if not check_tool_path.is_file():
check_tool_path = pathlib.Path(override_tool_path) / 'bin' / tool_filename
file_found = False
for executable_path_ext in PLATFORM_EXECUTABLE_EXTENSIONS:
check_tool_filename = f'{tool_filename}{executable_path_ext}'
if not check_tool_path.is_file():
check_tool_path = override_tool_path / check_tool_filename
if check_tool_path.is_file():
file_found = True
break
check_tool_path = override_tool_path / 'bin' / check_tool_filename
if check_tool_path.is_file():
file_found = True
break
if not file_found:
raise LmbrCmdError(f"Invalid {tool_name} path argument. '{override_tool_path}' is not a valid {tool_name} path",
ERROR_CODE_INVALID_PARAMETER)
resolved_override_tool_path = str(check_tool_path.resolve())
@@ -284,7 +307,7 @@ def verify_tool(override_tool_path, tool_name, tool_filename, argument_name, too
else:
resolved_override_tool_path = None
tool_source = tool_name
tool_desc = "installed gradle in the system path"
tool_desc = f"installed {tool_name} in the system path"
# Extract the version and verify
version_output = subprocess.check_output([tool_source, tool_version_argument],
@@ -296,10 +319,10 @@ def verify_tool(override_tool_path, tool_name, tool_filename, argument_name, too
result_version = LooseVersion(str(version_match.group(1)).strip())
if min_version and result_version < min_version:
raise LmbrCmdError(f"The {tool_desc} does not meet the minimum version of gradle required ({str(min_version)}).",
raise LmbrCmdError(f"The {tool_desc} does not meet the minimum version of {tool_name} required ({str(min_version)}).",
ERROR_CODE_ENVIRONMENT_ERROR)
elif max_version and result_version > max_version:
raise LmbrCmdError(f"The {tool_desc} exceeds maximum version of gradle supported ({str(max_version)}).",
raise LmbrCmdError(f"The {tool_desc} exceeds maximum version of {tool_name} supported ({str(max_version)}).",
ERROR_CODE_ENVIRONMENT_ERROR)
return result_version, resolved_override_tool_path
@@ -141,10 +141,8 @@
"COMMAND":"gradle_windows.cmd",
"PARAMETERS": {
"CONFIGURATION":"profile",
"OUTPUT_DIRECTORY":"build\\android_gradle",
"OUTPUT_DIRECTORY":"build\\ad_grd",
"GAME_PROJECT": "AutomatedTesting",
"ANDROID_NDK_PLATFORM": "21",
"ANDROID_SDK_PLATFORM": "29",
"SIGN_APK": "false",
"GRADLE_BUILD_CMD": "build",
"ADDITIONAL_GENERATE_ARGS": ""
@@ -158,8 +156,6 @@
"CONFIGURATION":"profile",
"OUTPUT_DIRECTORY":"build\\android_unittest",
"GAME_PROJECT": "AutomatedTesting",
"ANDROID_NDK_PLATFORM": "21",
"ANDROID_SDK_PLATFORM": "29",
"SIGN_APK": "true",
"GRADLE_BUILD_CMD": "assemble",
"ADDITIONAL_GENERATE_ARGS": "--unit-test"
@@ -17,20 +17,12 @@ IF NOT EXIST "%LY_3RDPARTY_PATH%" (
GOTO :error
)
IF NOT EXIST "%GRADLE_HOME%" (
IF NOT EXIST "%GRADLE_BUILD_HOME%" (
REM This is the default for developers
SET GRADLE_HOME=C:\Gradle\gradle-5.6.4
SET GRADLE_BUILD_HOME=C:\Gradle\gradle-7.0
)
IF NOT EXIST "%GRADLE_HOME%" (
ECHO [ci_build] FAIL: GRADLE_HOME=%GRADLE_HOME%
GOTO :error
)
IF NOT EXIST "%CMAKE_HOME%" (
SET CMAKE_HOME=%LY_3RDPARTY_PATH%/CMake/3.19.1/Windows/
)
IF NOT EXIST "%CMAKE_HOME%" (
ECHO [ci_build] FAIL: CMAKE_HOME=%CMAKE_HOME%
IF NOT EXIST "%GRADLE_BUILD_HOME%" (
ECHO [ci_build] FAIL: GRADLE_BUILD_HOME=%GRADLE_BUILD_HOME%
GOTO :error
)
@@ -50,20 +42,9 @@ ECHO Ninja wasnt in the call path, add the value set by LY_NINJA_PATH
SET PATH=%PATH%;%LY_NINJA_PATH%
:ninja_on_path
IF NOT EXIST "%LY_ANDROID_SDK%" (
SET LY_ANDROID_SDK=!LY_3RDPARTY_PATH!/android-sdk/platform-29
)
IF NOT EXIST "%LY_ANDROID_SDK%" (
ECHO [ci_build] FAIL: LY_ANDROID_SDK=!LY_ANDROID_SDK!
GOTO :error
)
IF NOT EXIST "%LY_ANDROID_NDK%" (
set LY_ANDROID_NDK=!LY_3RDPARTY_PATH!/android-ndk/r21d
)
IF NOT EXIST "%LY_ANDROID_NDK%" (
ECHO [ci_build] LY_ANDROID_NDK=!LY_ANDROID_NDK!
GOTO :error
IF NOT "%ANDROID_GRADLE_PLUGIN%" == "" (
set ANDROID_GRADLE_PLUGIN_OPTION=--gradle-plugin-version=%ANDROID_GRADLE_PLUGIN%
)
IF NOT EXIST %OUTPUT_DIRECTORY% (
@@ -154,11 +135,11 @@ IF "%GENERATE_SIGNED_APK%"=="true" (
ECHO Using keystore file at %CI_ANDROID_KEYSTORE_FILE_ABS%
)
ECHO [ci_build] %PYTHON% cmake\Tools\Platform\Android\generate_android_project.py --engine-root=. --build-dir=%OUTPUT_DIRECTORY% -g %GAME_PROJECT% --gradle-install-path=%GRADLE_HOME% --cmake-install-path=%CMAKE_HOME% --ninja-install-path=%LY_NINJA_PATH% --third-party-path=%LY_3RDPARTY_PATH% --android-ndk-path=%LY_ANDROID_NDK% --android-sdk-path=%LY_ANDROID_SDK% --android-ndk-version=%ANDROID_NDK_PLATFORM% --android-sdk-version=%ANDROID_SDK_PLATFORM% --signconfig-store-file %CI_ANDROID_KEYSTORE_FILE_ABS% --signconfig-store-password %CI_ANDROID_KEYSTORE_PASSWORD% --signconfig-key-alias %CI_ANDROID_KEYSTORE_ALIAS% --signconfig-key-password %CI_ANDROID_KEYSTORE_PASSWORD% %OPTIONAL_TEST_FLAG% %ADDITIONAL_GENERATE_ARGS% --overwrite-existing
CALL %PYTHON% cmake\Tools\Platform\Android\generate_android_project.py --engine-root=. --build-dir=%OUTPUT_DIRECTORY% -g %GAME_PROJECT% --gradle-install-path=%GRADLE_HOME% --cmake-install-path=%CMAKE_HOME% --ninja-install-path=%LY_NINJA_PATH% --third-party-path=%LY_3RDPARTY_PATH% --android-ndk-path=%LY_ANDROID_NDK% --android-sdk-path=%LY_ANDROID_SDK% --android-ndk-version=%ANDROID_NDK_PLATFORM% --android-sdk-version=%ANDROID_SDK_PLATFORM% --signconfig-store-file %CI_ANDROID_KEYSTORE_FILE_ABS% --signconfig-store-password %CI_ANDROID_KEYSTORE_PASSWORD% --signconfig-key-alias %CI_ANDROID_KEYSTORE_ALIAS% --signconfig-key-password %CI_ANDROID_KEYSTORE_PASSWORD% %ADDITIONAL_GENERATE_ARGS% --overwrite-existing
ECHO [ci_build] %PYTHON% cmake\Tools\Platform\Android\generate_android_project.py --engine-root=. --build-dir=%OUTPUT_DIRECTORY% -g %GAME_PROJECT% --gradle-install-path=%GRADLE_BUILD_HOME% --ninja-install-path=%LY_NINJA_PATH% --third-party-path=%LY_3RDPARTY_PATH% --android-sdk-path=%ANDROID_HOME% %ANDROID_GRADLE_PLUGIN_OPTION% --signconfig-store-file %CI_ANDROID_KEYSTORE_FILE_ABS% --signconfig-store-password %CI_ANDROID_KEYSTORE_PASSWORD% --signconfig-key-alias %CI_ANDROID_KEYSTORE_ALIAS% --signconfig-key-password %CI_ANDROID_KEYSTORE_PASSWORD% %ADDITIONAL_GENERATE_ARGS% --overwrite-existing
CALL %PYTHON% cmake\Tools\Platform\Android\generate_android_project.py --engine-root=. --build-dir=%OUTPUT_DIRECTORY% -g %GAME_PROJECT% --gradle-install-path=%GRADLE_BUILD_HOME% --ninja-install-path=%LY_NINJA_PATH% --third-party-path=%LY_3RDPARTY_PATH% --android-sdk-path=%ANDROID_HOME% %ANDROID_GRADLE_PLUGIN_OPTION% --signconfig-store-file %CI_ANDROID_KEYSTORE_FILE_ABS% --signconfig-store-password %CI_ANDROID_KEYSTORE_PASSWORD% --signconfig-key-alias %CI_ANDROID_KEYSTORE_ALIAS% --signconfig-key-password %CI_ANDROID_KEYSTORE_PASSWORD% %ADDITIONAL_GENERATE_ARGS% --overwrite-existing
) ELSE (
ECHO [ci_build] %PYTHON% cmake\Tools\Platform\Android\generate_android_project.py --engine-root=. --build-dir=%OUTPUT_DIRECTORY% -g %GAME_PROJECT% --gradle-install-path=%GRADLE_HOME% --cmake-install-path=%CMAKE_HOME% --ninja-install-path=%LY_NINJA_PATH% --third-party-path=%LY_3RDPARTY_PATH% --android-ndk-path=%LY_ANDROID_NDK% --android-sdk-path=%LY_ANDROID_SDK% --android-ndk-version=%ANDROID_NDK_PLATFORM% --android-sdk-version=%ANDROID_SDK_PLATFORM% %ADDITIONAL_GENERATE_ARGS% --overwrite-existing
CALL %PYTHON% cmake\Tools\Platform\Android\generate_android_project.py --engine-root=. --build-dir=%OUTPUT_DIRECTORY% -g %GAME_PROJECT% --gradle-install-path=%GRADLE_HOME% --cmake-install-path=%CMAKE_HOME% --ninja-install-path=%LY_NINJA_PATH% --third-party-path=%LY_3RDPARTY_PATH% --android-ndk-path=%LY_ANDROID_NDK% --android-sdk-path=%LY_ANDROID_SDK% --android-ndk-version=%ANDROID_NDK_PLATFORM% --android-sdk-version=%ANDROID_SDK_PLATFORM% %ADDITIONAL_GENERATE_ARGS% --overwrite-existing
ECHO [ci_build] %PYTHON% cmake\Tools\Platform\Android\generate_android_project.py --engine-root=. --build-dir=%OUTPUT_DIRECTORY% -g %GAME_PROJECT% %GRADLE_OVERRIDE_OPTION% --ninja-install-path=%LY_NINJA_PATH% --third-party-path=%LY_3RDPARTY_PATH% %ANDROID_GRADLE_PLUGIN_OPTION% --android-sdk-path=%ANDROID_HOME% %ADDITIONAL_GENERATE_ARGS% --overwrite-existing
CALL %PYTHON% cmake\Tools\Platform\Android\generate_android_project.py --engine-root=. --build-dir=%OUTPUT_DIRECTORY% -g %GAME_PROJECT% --gradle-install-path=%GRADLE_BUILD_HOME% --ninja-install-path=%LY_NINJA_PATH% --third-party-path=%LY_3RDPARTY_PATH% %ANDROID_GRADLE_PLUGIN_OPTION% --android-sdk-path=%ANDROID_HOME% %ADDITIONAL_GENERATE_ARGS% --overwrite-existing
)
REM Validate the android project generation
+2 -2
View File
@@ -1,7 +1,7 @@
{
"ENV": {
"GRADLE_HOME": "C:/Gradle/gradle-5.6.4",
"NODE_LABEL": "windows-047e5cdf",
"GRADLE_HOME": "C:/Gradle/gradle-7.0",
"NODE_LABEL": "windows-b3c8994f1",
"LY_3RDPARTY_PATH": "C:/ly/3rdParty",
"TIMEOUT": 30,
"WORKSPACE": "D:/workspace",