Files
o3de/cmake/Tools/Platform/Android/generate_android_project.py
T
Eric Phister 0c7605c9b6 Update minimum required CMake version to 3.20 (#1253)
* Update the minimum CMake version to 3.20

Sets the cmake_minimum_required calls to version 3.20 and updates the
README.md to point at the general CMake download page instead of a stale
link.

* Remove unnecessary cmake minimum version

It was using an old 3.0 version and can be removed.

* Additional updates to CMake 3.20, build scripts

Updates the version and remove logic to find a CMake in 3rdParty.

* Removing backup path to ninja path in the build_ninja_windows.cmd

The backup path for finding ninja was coming from the Perforce depot which isn't available for o3de builds.

* Removing reference to 3rdParty Android SDK 29 from the build and run unit test script

The Android SDK is not part of the new 3rdParty system and users are expected to install the Android SDK on their own in order to build the engine for Android.

* Update the get_python scripts and README

No longer try to append a CMake path to LY_3RDPARTY_PATH, but do still
support LY_CMAKE_PATH because there are still uses of it.  Remove
mention of an LY_3RDPARTY_PATH-relative CMake path from the README.md.

* Removing LY_NINJA_PATH from the build_ninja_windows.cmd

Co-authored-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>
2021-06-11 12:00:55 -05:00

411 lines
21 KiB
Python
Executable File

#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
import argparse
import logging
import os
import pathlib
import platform
import re
import sys
from distutils.version import LooseVersion
ROOT_DEV_PATH = os.path.realpath(os.path.join(os.path.dirname(__file__), '..', '..', '..', '..'))
if ROOT_DEV_PATH not in sys.path:
sys.path.append(ROOT_DEV_PATH)
from cmake.Tools import common
from cmake.Tools.Platform.Android import android_support
GRADLE_ARGUMENT_NAME = '--gradle-install-path'
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'
def verify_gradle(override_gradle_path=None):
"""
Verify the installed gradle requirement.
"""
return common.verify_tool(override_tool_path=override_gradle_path,
tool_name='gradle',
tool_filename=GRADLE_EXECUTABLE,
argument_name=GRADLE_ARGUMENT_NAME,
tool_version_argument='-v',
tool_version_regex=GRADLE_VERSION_REGEX,
min_version=GRADLE_MIN_VERSION,
max_version=GRADLE_MAX_VERSION)
CMAKE_ARGUMENT_NAME = '--cmake-install-path'
CMAKE_MIN_VERSION = LooseVersion('3.20.0')
CMAKE_VERSION_REGEX = re.compile(r'cmake version (\d+.\d+.?\d*)')
CMAKE_EXECUTABLE = 'cmake'
def verify_cmake(override_cmake_path=None):
"""
Verify the installed cmake requirement.
"""
return common.verify_tool(override_tool_path=override_cmake_path,
tool_name='cmake',
tool_filename=CMAKE_EXECUTABLE,
argument_name=CMAKE_ARGUMENT_NAME,
tool_version_argument='--version',
tool_version_regex=CMAKE_VERSION_REGEX,
min_version=CMAKE_MIN_VERSION,
max_version=None)
NINJA_ARGUMENT_NAME = '--ninja-install-path'
NINJA_VERSION_REGEX = re.compile(r'(\d+.\d+.?\d*)')
NINJA_EXECUTABLE = 'ninja'
def verify_ninja(override_ninja_path=None):
"""
Verify the installed ninja requirement.
"""
return common.verify_tool(override_tool_path=override_ninja_path,
tool_name='ninja',
tool_filename='ninja',
argument_name=NINJA_ARGUMENT_NAME,
tool_version_argument='--version',
tool_version_regex=NINJA_VERSION_REGEX,
min_version=None,
max_version=None)
SIGNING_PROFILE_STORE_FILE_ARGUMENT_NAME = "--signconfig-store-file"
SIGNING_PROFILE_STORE_PASSWORD_ARGUMENT_NAME = "--signconfig-store-password"
SIGNING_PROFILE_KEY_ALIAS_ARGUMENT_NAME = "--signconfig-key-alias"
SIGNING_PROFILE_KEY_PASSWORD_ARGUMENT_NAME = "--signconfig-key-password"
def build_optional_signing_profile(store_file, store_password, key_alias, key_password):
# If none of the arguments are set, then return None and skip the signing config generation
if not any([store_file, store_password, key_alias, key_password]):
return
return android_support.AndroidSigningConfig(store_file=store_file,
store_password=store_password,
key_alias=key_alias,
key_password=key_password)
ANDROID_SDK_ARGUMENT_NAME = '--android-sdk-path'
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_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"
ASSET_MODE_PAK = 'PAK'
ASSET_MODE_LOOSE = 'LOOSE'
ASSET_MODE_VFS = 'VFS'
ALL_ASSET_MODES = [ASSET_MODE_PAK, ASSET_MODE_LOOSE, ASSET_MODE_VFS]
ASSET_TYPE_ARGUMENT_NAME = '--asset-type'
DEFAULT_ASSET_TYPE = 'android'
def wrap_parsed_args(parsed_args):
"""
Function to add a method to the parsed argument object to transform a long-form argument name to and get the
parsed values based on the input long form.
This will allow us to read an argument like '--foo-bar=Orange' by using the built in method rather than looking for
the argparsed transformed attrobite 'foo_bar'
:param parsed_args: The parsed args object to wrap
"""
def parse_argument_attr(argument):
argument_attr = argument[2:].replace('-', '_')
return getattr(parsed_args, argument_attr)
parsed_args.get_argument = parse_argument_attr
def main(args):
"""
Perform the main argument processing and execution of the project generator
:param args: The arguments to process
"""
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())
parser.add_argument('--build-dir',
help='The build dir path. It will be concatenated to the project-path using the rules of os.path.join',
type=pathlib.Path,
required=True)
parser.add_argument('--third-party-path',
help='The path to the 3rd Party root directory',
required=True)
parser.add_argument(ANDROID_SDK_ARGUMENT_NAME,
help='The path to the android SDK',
required=True)
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 android SDK build tools version.',
required=False)
parser.add_argument(ANDROID_NDK_PLATFORM_ARGUMENT_NAME,
help='The android NDK version',
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,
required=False)
parser.add_argument(NINJA_ARGUMENT_NAME,
help='The path to the ninja build tool if not installed on the system path.',
default=None,
required=False)
# Asset Options
parser.add_argument(INCLUDE_APK_ASSETS_ARGUMENT_NAME,
action='store_true',
help='Option to include the game assets when building the APK. If this option is set, you must have the android assets built.')
parser.add_argument(ASSET_MODE_ARGUMENT_NAME,
choices=ALL_ASSET_MODES,
default=ASSET_MODE_LOOSE,
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=f'Asset Type to use when including assets into the APK. (Defaults to {DEFAULT_ASSET_TYPE})')
parser.add_argument('--debug',
action='store_true',
help='Enable debug logs.')
# Signing Config options
parser.add_argument(SIGNING_PROFILE_STORE_FILE_ARGUMENT_NAME,
default=None,
help='(Optional) If specified, create a signing profile based on this supplied android jks keystore file.')
parser.add_argument(SIGNING_PROFILE_STORE_PASSWORD_ARGUMENT_NAME,
default=None,
help='If an android jks keystore file is specified, this is the store password for the keystore.')
parser.add_argument(SIGNING_PROFILE_KEY_ALIAS_ARGUMENT_NAME,
default=None,
help='If an android jks keystore file is specified, this is the alias of the signing key in the keystore.')
parser.add_argument(SIGNING_PROFILE_KEY_PASSWORD_ARGUMENT_NAME,
default=None,
help='If an android jks keystore file is specified, this is the password of the signing key in the keystore.')
parser.add_argument('--unit-test',
action='store_true',
help='Generate a unit test APK instead of a game APK.')
parser.add_argument('--overwrite-existing',
action='store_true',
help='Option to overwrite existing scripts in the target build folder if they exist already.')
parsed_args = parser.parse_args(args)
wrap_parsed_args(parsed_args)
# Prepare the logging
logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.DEBUG if parsed_args.debug else logging.INFO)
# Verify the gradle requirements
gradle_version, override_gradle_path = verify_gradle(override_gradle_path=parsed_args.get_argument(GRADLE_ARGUMENT_NAME))
logging.info("Detected Gradle version %s", str(gradle_version))
# Verify the cmake requirements
cmake_version, override_cmake_path = verify_cmake(override_cmake_path=parsed_args.get_argument(CMAKE_ARGUMENT_NAME))
logging.info("Detected CMake version %s", str(cmake_version))
# Verify the ninja requirements
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))
# 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)
# 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,
engine_root=parsed_args.engine_root)
is_test_project = parsed_args.unit_test
# Verify the 3rd Party Root 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
build_dir = parsed_args.build_dir
signing_config = build_optional_signing_profile(store_file=parsed_args.get_argument(SIGNING_PROFILE_STORE_FILE_ARGUMENT_NAME),
store_password=parsed_args.get_argument(SIGNING_PROFILE_STORE_PASSWORD_ARGUMENT_NAME),
key_alias=parsed_args.get_argument(SIGNING_PROFILE_KEY_ALIAS_ARGUMENT_NAME),
key_password=parsed_args.get_argument(SIGNING_PROFILE_KEY_PASSWORD_ARGUMENT_NAME))
logging.debug("Engine Root : %s", str(verified_engine_root.resolve()))
logging.debug("Build Path : %s", str(build_dir.resolve()))
# Prepare the generator and execute
generator = android_support.AndroidProjectGenerator(engine_root=verified_engine_root,
build_dir=build_dir,
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,
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),
signing_config=signing_config,
is_test_project=is_test_project,
overwrite_existing=parsed_args.overwrite_existing)
generator.execute()
if __name__ == '__main__':
try:
main(sys.argv[1:])
exit(0)
except common.LmbrCmdError as err:
print(str(err), file=sys.stderr)
exit(err.code)