diff --git a/Code/Tools/Android/ProjectBuilder/build.gradle.in b/Code/Tools/Android/ProjectBuilder/build.gradle.in index 66f58294ab..5980984516 100644 --- a/Code/Tools/Android/ProjectBuilder/build.gradle.in +++ b/Code/Tools/Android/ProjectBuilder/build.gradle.in @@ -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} } diff --git a/Code/Tools/Android/ProjectBuilder/local.properties.in b/Code/Tools/Android/ProjectBuilder/local.properties.in index 559ea67bcb..4e82cb2940 100644 --- a/Code/Tools/Android/ProjectBuilder/local.properties.in +++ b/Code/Tools/Android/ProjectBuilder/local.properties.in @@ -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} diff --git a/Code/Tools/Android/ProjectBuilder/root.build.gradle.in b/Code/Tools/Android/ProjectBuilder/root.build.gradle.in index 782a1f26b5..dfce99c3c7 100644 --- a/Code/Tools/Android/ProjectBuilder/root.build.gradle.in +++ b/Code/Tools/Android/ProjectBuilder/root.build.gradle.in @@ -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}' } diff --git a/cmake/Tools/Platform/Android/android_support.py b/cmake/Tools/Platform/Android/android_support.py index 6443077457..75e0cd2970 100755 --- a/cmake/Tools/Platform/Android/android_support.py +++ b/cmake/Tools/Platform/Android/android_support.py @@ -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}") + diff --git a/cmake/Tools/Platform/Android/generate_android_project.py b/cmake/Tools/Platform/Android/generate_android_project.py index 9a0e2760f5..d25b62dde8 100755 --- a/cmake/Tools/Platform/Android/generate_android_project.py +++ b/cmake/Tools/Platform/Android/generate_android_project.py @@ -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), diff --git a/cmake/Tools/Platform/Android/unit_test_generate_android_project.py b/cmake/Tools/Platform/Android/unit_test_generate_android_project.py index 5598942046..0cd0f16eaf 100755 --- a/cmake/Tools/Platform/Android/unit_test_generate_android_project.py +++ b/cmake/Tools/Platform/Android/unit_test_generate_android_project.py @@ -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 - diff --git a/cmake/Tools/common.py b/cmake/Tools/common.py index c6a3e89e67..9c0d31cd53 100755 --- a/cmake/Tools/common.py +++ b/cmake/Tools/common.py @@ -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 diff --git a/scripts/build/Platform/Android/build_config.json b/scripts/build/Platform/Android/build_config.json index adaa417380..b871670cd0 100644 --- a/scripts/build/Platform/Android/build_config.json +++ b/scripts/build/Platform/Android/build_config.json @@ -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" diff --git a/scripts/build/Platform/Android/gradle_windows.cmd b/scripts/build/Platform/Android/gradle_windows.cmd index 56423af95a..dd5285bdbf 100644 --- a/scripts/build/Platform/Android/gradle_windows.cmd +++ b/scripts/build/Platform/Android/gradle_windows.cmd @@ -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 diff --git a/scripts/build/Platform/Android/pipeline.json b/scripts/build/Platform/Android/pipeline.json index ed10e7022d..551374a027 100644 --- a/scripts/build/Platform/Android/pipeline.json +++ b/scripts/build/Platform/Android/pipeline.json @@ -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",