IN THIS ARTICLE
O3DE Packages
Open 3D Engine (O3DE) offers a packaging system to allow for shipping pre-compiled libraries or other external sources. With the packaging system, you can more easily add external binaries as dependencies for your Gem or project without having to maintain them in source control. If you plan to add a static dependency that can’t be shipped as source code, the packaging system offers consistency checks and target generation in addition to the fetching of resources.
Note:The CMake files that define the package system are located atcmake/3rdParty.cmakeandcmake/3rdPartyPackages.cmakein the O3DE source.
Overview
The O3DE package system is used to deliver source and binary dependencies from external sources. These dependencies are needed by Gems, projects, and even the O3DE engine itself. The dependency system correctly detects dependencies and version requirements, making sure that you have the packages needed on your system at compile-time.
The dependency system runs through the following steps when invoked:
It checks for a valid package location at the
LY_3RDPARTY_PATHvalue. Any directory can be used for storing packages, but it’s recommended that you avoid changing yourLY_3RDPARTY_PATHvalue once it’s set. After a directory has contents downloaded into it by the O3DE build system, a cache file is created there to make future dependency checks easily, so changing the package location will re-download all packages.The build system loads the available package sources. This is a semi-colon (
;) separated list of package sources that are stored in theLY_PACKAGE_SERVER_URLSCMake cache value. If theLY_PACKAGE_SERVER_URLSenvironment variable is set, it’s prepended to the cached value.For each package:
The next source in
LY_PACKAGE_SERVER_URLSis checked. If a matching package is found in the source, the package is downloaded toLY_PACKAGE_DOWNLOAD_CACHE_LOCATION.The package tarball is downloaded from the source, and checked against the checksum contained in the requesting CMake file. If there’s a checksum mismatch, the tarball is deleted and the next available package source is checked for the package.
The package is extracted into
LY_PACKAGE_UNPACK_LOCATION/<full-package-name>. By defaultLY_PACKAGE_UNPACK_LOCATIONisLY_3RDPARTY_PATH/packages.Each individual file in the package is checked against a checksum file contained in the package. If a checksum fails, the package tarball and unpacked contents are deleted and the next source is checked for the package.
You can also add Python module requirements so that pip is used to automatically to retrieve packages that are required by tools or other libraries.
Note:Package downloads and target creation run only as part of CMake configuration and generation. If a new dependency is added to a Gem or project, you need to reconfigure your CMake cache and regenerate.
See the CMake settings reference for the full list of available settings that affect the package system.
Create a Package
To create a package for consumption by O3DE, you need to provide the following:
- The package contents themselves - source, binaries, assets, and any other components.
- A manifest describing the package (
PackageInfo.json). - A
LICENSEfile contained the software license for the package. - Checksums for each file in the package (
SHA256SUMS). - A CMake file run when then package is used in another CMake file (
Find<Package>.cmake).
Packages must be distributed as tarfiles compressed with XZ or LZMA (.tar.xz).
O3DE package structure
O3DE packages are required to have the following structure:
.
├── PackageInfo.json # A manifest file describing the package.
├── Find<Package>.cmake # CMake file defining targets for this package.
├── <Package content dir> # Directory containing package contents.
└── SHA256SUMS # SHA256 checksums for each file in package. This includes the manifest and Find CMake files.
The PackageInfo.json file
PackageInfo.json is a minimalist manifest file that describes a package. Any key in the following block without
[optional] as part of its description doesn’t need to be included.
{
"PackageName" : "The full name of the package. This is the name of the generated target and the folder package that contents are extracted to.",
"URL" : "URL for the package project. This doesn't need to be the same as the URL the package is stored at. [optional]",
"License" : "The SPDX license for the package.",
"LicenseFile" : "Path to the LICENSE file. This path should start with the package content directory and use the `/` path separator."
}
See the
SPDX License List for allowed values of License.
The Find<Package>.cmake file
To create a target and allow it to be referenced from the O3DE build system, packages need to include a Find<Package>.cmake file, where
<Package> must be the name of the package content directory. This CMake file is responsible for identifying the target in the package
namespace, setting any cache or build values required, and registering the target with the O3DE build system.
For a package with the structure:
example-0.1
├── PackageInfo.json
├── FindExample.cmake
└── example
├── LICENSE.txt
├── lib
│ └── hello.dll
└── include
└── hello.h
A minimal FindExample.cmake file should look something like the following. Values related specifically to the example
structure are contained in angle brackets <...>.
set(MY_NAME "<example>")
set(TARGET_WITH_NAMESPACE "3rdParty::${MY_NAME}")
if (TARGET ${TARGET_WITH_NAMESPACE})
return()
endif()
add_library(${TARGET_WITH_NAMESPACE} INTERFACE IMPORTED GLOBAL)
# --- Package-specific starts ---
set(${MY_NAME}_LIB_DIR ${CMAKE_CURRENT_LIST_DIR}/${MY_NAME}/<lib>)
set(${MY_NAME}_INCLUDE_DIR ${CMAKE_CURRENT_LIST_DIR}/${MY_NAME}/<include>)
ly_add_target_files(TARGETS ${TARGET_WITH_NAMESPACE} FILES ${${MY_NAME}_LIB_DIR})
ly_target_include_system_directories(TARGET ${TARGET_WITH_NAMESPACE} INTERFACE ${${MY_NAME}_INCLUDE_DIR}
# --- Package-specific ends ---
set(${MY_NAME}_FOUND True)
In general, you need to:
- Short-circuit if the
Findfile was invoked while building the target that it defines. - Add a library as
IMPORTED INTERFACE GLOBAL. This library must be in the3rdParty::namespace, to avoid naming conflicts and assist in dependency detection. - Perform any package-specific configuration. Use
ly_add_target_filesto add libraries or binaries to the package target, andly_target_include_system_directoriesto add include directories. - Set the value of
<package name>_FOUNDto beTrue.
Note:CMake functions used in this example:
ly_add_target_files: Adds files to an existing target. See the definition incmake/LYWrappers.cmake.ly_target_include_system_directories: This is a thin wrapper around target_include_directories, providing support for this feature on platforms that don’t include it.For the full details of functions declared in O3DE CMake files, see CMake reference - functions.
Checksums file
This file is a plain text file containing one SHA256 checksum per line, for each file contained in the package. This list must
include checksums for the Find cmake file and the PackageInfo.json.
This file uses the output format produced by the Linux sha256sum tool, with one checksum and file per line:
<hash> *<full path to file>
As an example excerpt from the cityhash package:
f6adb0dfdbfd4b404d19e61fa86c4eda3e46aa06cfc98eefa0403fa397dd1db5 *PackageInfo.json
af173c7e6c28551679f6676cd923731b514f02c8a29980cb54987be8479a251e *cityhash/src/citycrc.h
Add a Package as a Dependency
When a package is uploaded to a source that’s available from LY_PACKAGE_SERVER_URLS, you need to register the package with the build system in the Gem or Project build file. Package registration is done with the ly_associate_package function.
For a Gem that would use the example-0.1 package using ly_associate_package, this would look like the following:
ly_associate_package(PACKAGE_NAME <example-0.1> TARGETS <example> PACKAGE_HASH <a0e852b21668bae2f5259a5e4a866db0488b0ef28e4559d4cc7b427f6c61a0b0>)
These are the minimal required arguments:
- PACKAGE_NAME: The name of the package. This must match the name of the
.tar.xzfile uploaded to the package repository. - TARGETS: The name of the target associated with the package.
- PACKAGE_HASH: The SHA256 checksum of the
.tar.xzfile.
Note:CMake functions used in this example:
ly_associate_package: Links a package name to a CMake target. See the definition incmake/3rdPartyPackages.cmake.
Pip Integration
For Gems, projects, or packages that rely on Python modules or tools from
PyPi or another pip repository, the O3DE build system offers the functions update_pip_requirements and ly_pip_install_local_package_editable.
update_pip_requirements: Processes arequirements.txtfile, and associates the requirements with a name in the O3DE build system.ly_pip_install_local_package_editable: Creates a symbolic link from a Python module in a non-standard location to a path inside of the user’s “local”pipSite-Packages directory, allowing the system Python to behave as if the module is installed. If the linked Python module has any requirements, you must callupdate_pip_requirementsbefore this function to ensure that Python dependencies are correctly tracked.
Important:You can’t declare another source forpipto download your Python modules from for those listed inrequirements.txt. If your Python dependencies are hosted on a service other than PyPi, manually configure the O3DE Python install’spipto use the appropriate CDN.
For example, say that your Gem contains a Python module called example-py, with a setup file at example-py/setup.py. To give O3DE access to this module, the CMake file for this Gem would require the following snippet:
update_pip_requirements(${CMAKE_CURRENT_LIST_DIR}/requirements.txt <ExamplePyGem>)
ly_pip_install_local_package_editable(${CMAKE_CURRENT_LIST_DIR}/<example-py> <ExamplePyGem>)
The <ExamplePyGem> can be any unique identifier for the Python module, but is conventionally the name of the Gem followed by Gem.
Changing the requirements.txt file or the identifier used for the module will re-run the registration.
Note:CMake functions used in this example:
update_pip_requirements: See the definition incmake/LYPython.cmake.ly_pip_install_local_package_editable: See the definition incmake/LYPython.cmake.