123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320 |
- #!/usr/bin/env python3
- #
- # Copyright (c) Contributors to the Open 3D Engine Project.
- # For complete copyright and license terms please see the LICENSE at the root of this distribution.
- #
- # SPDX-License-Identifier: Apache-2.0 OR MIT
- #
- #
- import argparse
- import glob
- import json
- import os
- import pathlib
- import subprocess
- import shutil
- import sys
- import urllib.request
- import zipfile
- from typing import List
- # O3DE_PACKAGE_NAME is given in the format <PACKAGE_NAME>-<VERSION>-<PLATFORM>
- O3DE_PACKAGE_NAME = os.environ['O3DE_PACKAGE_NAME']
- O3DE_PACKAGE_NAME_PARTS = O3DE_PACKAGE_NAME.split('-')
- PACKAGE_NAME: str = "AWSGameLiftServerSDK"
- PACKAGE_PLATFORM: str = ""
- PACKAGE_URL: str = "https://aws.amazon.com/documentation/gamelift/"
- PACKAGE_LICENSE: str = "Apache-2.0"
- PACKAGE_LICENSE_FILE: str = "LICENSE_AMAZON_GAMELIFT_SDK.TXT"
- GAMELIFT_SERVER_SDK_RELEASE_VERSION: str = O3DE_PACKAGE_NAME_PARTS[1]
- GAMELIFT_SERVER_SDK_DOWNLOAD_URL: str = "https://gamelift-server-sdk-release.s3.us-west-2.amazonaws.com/cpp/GameLift-Cpp-ServerSDK-5.1.2.zip"
- PACKAGE_BASE_PATH: pathlib.Path = pathlib.Path(os.path.dirname(__file__))
- PACKAGE_ROOT_PATH: pathlib.Path = PACKAGE_BASE_PATH.parent
- REPO_ROOT_PATH: pathlib.Path = PACKAGE_ROOT_PATH.parent
- GENERAL_SCRIPTS_PATH = REPO_ROOT_PATH / 'Scripts' / 'extras'
- PACKAGE_BUILD_TYPES: List[str] = ["Debug", "Release"]
- PACKAGE_LIB_TYPES: List[str] = ["Shared", "Static"]
- PACKAGE_PLATFORM_OPTIONS: List[str] = ["windows", "linux", "linux-aarch64"]
- # Insert the 3p scripts path so we can use the package downloader to
- # download the openssl dependency
- sys.path.insert(1, str(GENERAL_SCRIPTS_PATH.resolve()))
- from package_downloader import PackageDownloader
- # Lookup table for the Find{PACKAGE_NAME}.cmake.{platform} source file to copy in case there are shared files across different platforms
- FIND_CMAKE_PLATFORM_SUFFIX_BY_PLATFORM = {
- 'Windows': 'Windows',
- 'Linux': 'Linux',
- 'Linux-Aarch64': 'Linux'
- }
- # utils
- class WorkingDirectoryInfo(object):
- def __init__(self, root_path: pathlib.Path,
- source_path: pathlib.Path,
- build_path: pathlib.Path,
- output_path: pathlib.Path,
- libs_output_path: pathlib.Path) -> None:
- self.root_path = root_path
- self.source_path = source_path
- self.build_path = build_path
- self.output_path = output_path
- self.libs_output_path = libs_output_path
- self.dependencies_folder_path = (self.root_path / 'dependencies').resolve()
- # OpenSSL dependency: (packagename, hash)
- OPENSSL_PACKAGE = ('OpenSSL-1.1.1o-rev1-windows', '52b9d2bc5f3e0c6e405a0f290d1904bf545acc0c73d6a52351247d917c4a06d2')
- def get_dependencies(working_directory: WorkingDirectoryInfo) -> None:
- # Only windows has an additional dependency on openssl that we need to download from our own packages
- # The linux builds also rely on openssl, but they use the version on the system
- if PACKAGE_PLATFORM != 'windows':
- return
- print("Downloading dependencies...")
- package_name = OPENSSL_PACKAGE[0]
- package_hash = OPENSSL_PACKAGE[1]
- if not (working_directory.dependencies_folder_path / package_name).exists():
- if not PackageDownloader.DownloadAndUnpackPackage(package_name, package_hash, str(working_directory.dependencies_folder_path)):
- raise Exception("Failed to download OpenSSL dependency!")
- else:
- print(f'OpenSSL already in dependencies folder, skipping. Use --clean to refresh')
- def subp_args(args) -> str:
- arg_string = " ".join([arg for arg in args])
- print(f"Command: {arg_string}")
- return arg_string
- def delete_folder(folder: pathlib.Path) -> None:
- shutil.rmtree(folder.resolve())
- def copy_file_to_destination(file: str, destination: str) -> None:
- print(f"Copying {file} to {destination}")
- shutil.copy2(file, destination)
- # collect package build required information, like platform
- def collect_package_info() -> None:
- parser = argparse.ArgumentParser()
- parser.add_argument(
- '--platform-name',
- required=True,
- choices=PACKAGE_PLATFORM_OPTIONS
- )
- args = parser.parse_args()
- global PACKAGE_PLATFORM
- PACKAGE_PLATFORM = args.platform_name
- # prepare working directories for package build
- def prepare_working_directory() -> WorkingDirectoryInfo:
- # root directory
- root_directory: pathlib.Path = PACKAGE_BASE_PATH.joinpath("temp")
- if root_directory.is_dir():
- delete_folder(root_directory)
- # source and build directory
- source_directory = root_directory
- build_directory: pathlib.Path = root_directory.joinpath("build")
- build_directory.mkdir(parents=True)
- # build output and libs output directory
- build_output_directory: pathlib.Path = PACKAGE_ROOT_PATH.joinpath(f"{PACKAGE_NAME}-{PACKAGE_PLATFORM}")
- if build_output_directory.is_dir():
- delete_folder(build_output_directory)
- build_libs_output_directory: pathlib.Path = build_output_directory.joinpath(f"{PACKAGE_NAME}")
- build_libs_output_directory.mkdir(parents=True)
- return WorkingDirectoryInfo(root_directory, source_directory, build_directory, build_output_directory, build_libs_output_directory)
- # download gamelift server server sdk source code for package build
- def download_gamelift_server_sdk(working_directory: WorkingDirectoryInfo) -> None:
- # download sdk from url
- gamelift_sdk_zip_file: str = str(working_directory.root_path.joinpath("gamelift_server_sdk.zip").resolve())
- with urllib.request.urlopen(GAMELIFT_SERVER_SDK_DOWNLOAD_URL) as response, open(gamelift_sdk_zip_file, 'wb') as out_file:
- shutil.copyfileobj(response, out_file)
- # unzip sdk contents
- with zipfile.ZipFile(gamelift_sdk_zip_file, "r") as f:
- unzip_path = working_directory.root_path.resolve()
- f.extractall(unzip_path)
- # get required custom environment for package build
- def get_custom_build_env():
- if PACKAGE_PLATFORM in (PACKAGE_PLATFORM_OPTIONS[1], PACKAGE_PLATFORM_OPTIONS[2]):
- custom_env = os.environ.copy()
- custom_env["CC"] = "gcc"
- custom_env["CXX"] = "g++"
- custom_env["CFLAGS"] = "-fPIC"
- custom_env["CXXFLAGS"] = "-fPIC"
- return custom_env
- return None
- # configure gamelift server sdk project
- def configure_sdk_project(working_directory: WorkingDirectoryInfo,
- build_folder: str,
- build_type: str,
- lib_type: str) -> None:
- source_folder: str = working_directory.source_path.resolve()
- build_shared: str = "ON" if lib_type == "Shared" else "OFF"
- if PACKAGE_PLATFORM == PACKAGE_PLATFORM_OPTIONS[0]:
- generator: str = '-G "Visual Studio 17 2022" -A x64'
- elif PACKAGE_PLATFORM in (PACKAGE_PLATFORM_OPTIONS[1], PACKAGE_PLATFORM_OPTIONS[2]):
- generator: str = "-G \"Unix Makefiles\""
- else:
- raise Exception(f"Error unsupported platform: {PACKAGE_PLATFORM}")
- configure_cmd: List[str] = [f"cmake {generator} -S .",
- f"-B {build_folder}",
- f"-DBUILD_SHARED_LIBS={build_shared}",
- f"-DCMAKE_BUILD_TYPE={build_type}"]
- # On windows add our OpenSSL dependency to the DCMAKE_MODULE_PATH so the
- # GameLift build can find it
- if PACKAGE_PLATFORM == 'windows':
- package_name = OPENSSL_PACKAGE[0]
- openssl_dependency_path = (working_directory.dependencies_folder_path / package_name).resolve().as_posix()
- configure_cmd.append(f"-DCMAKE_MODULE_PATH=\"{openssl_dependency_path}\"")
- configure_result = subprocess.run(subp_args(configure_cmd),
- shell=True,
- capture_output=True,
- cwd=source_folder,
- env=get_custom_build_env())
- if configure_result.returncode != 0:
- raise Exception(f"Error generating project: {configure_result.stderr.decode()}")
- # build gamelift server sdk project
- def build_sdk_project(source_folder: str,
- build_folder: str,
- build_type: str) -> None:
- target = "--target aws-cpp-sdk-gamelift-server"
- build_cmd: List[str] = ["cmake",
- f"--build {build_folder}",
- f"--config {build_type}",
- f"{target} -j"]
- build_result = subprocess.run(subp_args(build_cmd),
- shell=True,
- capture_output=True,
- cwd=source_folder,
- env=get_custom_build_env())
- if build_result.returncode != 0:
- raise Exception(f"Error building project: {build_result.stderr.decode()}")
- # copy all built gamelift server sdk libs into expected output folder
- def copy_sdk_libs(libs_output_path: pathlib.Path,
- build_path: pathlib.Path,
- build_type: str,
- lib_type: str) -> None:
- if lib_type == PACKAGE_LIB_TYPES[0]:
- destination: pathlib.Path = libs_output_path.joinpath(f"bin/{build_type}")
- else:
- destination: pathlib.Path = libs_output_path.joinpath(f"lib/{build_type}")
- destination.mkdir(parents=True)
- install_folder: pathlib.Path = build_path.joinpath("prefix")
- if PACKAGE_PLATFORM == PACKAGE_PLATFORM_OPTIONS[0]:
- shared_libs_pattern: str = str(install_folder.joinpath("bin/*.dll"))
- static_libs_pattern: str = str(install_folder.joinpath("lib/*.lib"))
- # for windows, it always requires .lib file, .dll file is only required for shared libs
- if lib_type == PACKAGE_LIB_TYPES[0]:
- for file in glob.glob(shared_libs_pattern):
- copy_file_to_destination(file, str(destination.resolve()))
- for file in glob.glob(static_libs_pattern):
- copy_file_to_destination(file, str(destination.resolve()))
- elif PACKAGE_PLATFORM in (PACKAGE_PLATFORM_OPTIONS[1], PACKAGE_PLATFORM_OPTIONS[2]):
- shared_libs_pattern: str = str(install_folder.joinpath("lib/*.so*"))
- static_libs_pattern: str = str(install_folder.joinpath("lib/*.a"))
- # for linux, it requires .a file for static libs and .so file for shared libs
- if lib_type == PACKAGE_LIB_TYPES[0]:
- for file in glob.glob(shared_libs_pattern):
- copy_file_to_destination(file, str(destination.resolve()))
- else:
- for file in glob.glob(static_libs_pattern):
- copy_file_to_destination(file, str(destination.resolve()))
- else:
- raise Exception(f"Error unsupported platform: {PACKAGE_PLATFORM}")
- def build_gamelift_server_sdk(working_directory: WorkingDirectoryInfo,
- build_type: str,
- lib_type: str) -> None:
- build_folder: pathlib.Path = working_directory.build_path.joinpath(f"{build_type}_{lib_type}").resolve()
- print(f"Generating GameLift Server SDK project from source ({working_directory.source_path}) with {build_type} {lib_type} configuration...")
- configure_sdk_project(working_directory, build_folder.resolve(), build_type, lib_type)
- print(f"Building GameLift Server SDK project with {build_type} {lib_type} configuration...")
- build_sdk_project(working_directory.source_path.resolve(), build_folder.resolve(), build_type)
- print(f"Copying {build_type} {lib_type} built sdk libs into output folder...")
- copy_sdk_libs(working_directory.libs_output_path, build_folder, build_type, lib_type)
- # generate required information for package, like name, url, and license
- def generate_packageInfo(working_directory: WorkingDirectoryInfo) -> None:
- settings={
- 'PackageName': f'{O3DE_PACKAGE_NAME}',
- "URL" : f'{PACKAGE_URL}',
- "License" : f'{PACKAGE_LICENSE}',
- 'LicenseFile': f'{PACKAGE_NAME}/{PACKAGE_LICENSE_FILE}'
- }
- package_file: str = str(working_directory.output_path.joinpath("PackageInfo.json").resolve())
- with open(package_file, 'w') as fh:
- json.dump(settings, fh, indent=4)
- # generate required cmake file which is used to find package libs
- def generate_cmake_file(working_directory: WorkingDirectoryInfo) -> None:
- cmake_file_source_suffix = FIND_CMAKE_PLATFORM_SUFFIX_BY_PLATFORM[PACKAGE_PLATFORM.title()]
- cmake_file_source: pathlib.Path = PACKAGE_BASE_PATH.joinpath(f"Find{PACKAGE_NAME}.cmake.{cmake_file_source_suffix}")
- if cmake_file_source.is_file():
- find_cmake_content = cmake_file_source.read_text("UTF-8", "ignore")
- target_cmake_file: pathlib.Path = working_directory.output_path.joinpath(f"Find{PACKAGE_NAME}.cmake")
- target_cmake_file.write_text(find_cmake_content)
- else:
- raise Exception(f"Error finding cmake source file: {str(cmake_file_source.resolve())}")
- if __name__ == '__main__':
- try:
- print("Collecting package build info for GameLift Server SDK...")
- collect_package_info()
- print("Prepare working directory...")
- working_directory: WorkingDirectoryInfo = prepare_working_directory()
- print(f"Downloading GameLift Server SDK from {GAMELIFT_SERVER_SDK_DOWNLOAD_URL}...")
- download_gamelift_server_sdk(working_directory)
- # Retrieve any dependencies (if needed)
- get_dependencies(working_directory)
- # build sdk with different configurations
- for build_type in PACKAGE_BUILD_TYPES:
- for lib_type in PACKAGE_LIB_TYPES:
- build_gamelift_server_sdk(working_directory, build_type, lib_type)
- print("Copying include and license files into output directory...")
- shutil.copytree(working_directory.source_path.joinpath("gamelift-server-sdk/include").resolve(),
- working_directory.libs_output_path.joinpath("include").resolve())
- copy_file_to_destination(str(working_directory.source_path.joinpath(PACKAGE_LICENSE_FILE).resolve()),
- str(working_directory.libs_output_path.resolve()))
- copy_file_to_destination(str(working_directory.source_path.joinpath("NOTICE_C++_AMAZON_GAMELIFT_SDK.TXT").resolve()),
- str(working_directory.libs_output_path.resolve()))
- print("Generating package info into output directory...")
- generate_packageInfo(working_directory)
- print("Generating cmake file into output directory...")
- generate_cmake_file(working_directory)
- exit(0)
- except Exception as e:
- print(e)
- exit(1)
|