Using Dry library

Table of Contents

Project scaffolding

This section assumes you want to reuse Dry build system for your own downstream project. If, however, your project already has a different build system and that you just simply want to use the Dry library as an external library then you may want to skip to the next section Using pkg-config instead of CMake instead.

First of all, structure your project similar to Dry project as below. Although this is not mandatory, it should increase the chance the CMake modules designed for Dry project also works out of the box for your project too. CMake and our CMake modules are case-sensitive. It is recommended that your project adheres to the same naming convention as Dry project uses, even when the filesystem in your host system is not case-sensitive.

├ bin/
│ ├ Data/
│ └ CoreData/
├ CMake/
│ ├ Modules/
│ └ Toolchains/
├ CMakeLists.txt
├ *.cpp and *.h
└ *.bat or *.sh

The physical project root directory is also the logical project source tree in CMake terminology where your project main CMakeLists.txt should reside. The 'bin' directory should contain the 'Data' and 'CoreData' resource subdirs for your own assets. You must copy (or symlink) the 'CMake' subdir from Dry project root directory (or from Dry SDK installation, which can be found in the 'share/Dry/CMake') to your project root directory. You may also want to copy (or symlink) the build scripts from Dry project root directory (or from Dry SDK installation, which can be found in the 'share/Dry/Scripts') to your project root directory, unless you just want to use cmake-gui for your own project configuration and generation. Alternatively, you can add the Dry project root directory into the PATH environment variable in your host system in order to make the build scripts available everywhere. The build scripts work together with the Dry CMake modules and toolchains to configure and generate your initial project build tree. Both out-of-source build tree (recommended) and non out-of-source build tree are supported. Note that when you configure your project (either via one of the build script or via cmake-gui), you can only pass the Build options that are applicable to downstream projects. Be mindful that conflicting build options would be ignored.

In your own project root directory, create a main CMakeLists.txt file and add the following lines: (replace MyProjectName and MyExecutableName with the actual names you want)

# Set CMake minimum version and CMake policy required by DryCommon module
cmake_minimum_required (VERSION 3.5.1)
if (COMMAND cmake_policy)
# Libraries linked via full path no longer produce linker search paths
cmake_policy (SET CMP0003 NEW)
# INTERFACE_LINK_LIBRARIES defines the link interface
cmake_policy (SET CMP0022 NEW)
# Disallow use of the LOCATION target property - so we set to OLD as we still need it
cmake_policy (SET CMP0026 OLD)
# MACOSX_RPATH is enabled by default
cmake_policy (SET CMP0042 NEW)
# Honor the visibility properties for SHARED target types only
cmake_policy (SET CMP0063 OLD)
endif ()
# Set project name
project (MyProjectName)
# Set CMake modules search path
# Include DryCommon.cmake module after setting project name
include (DryCommon)
# Define target name
set (TARGET_NAME MyExecutableName)
# Define source files
define_source_files ()
# Setup target with resource copying
setup_main_executable ()

The CMAKE_MODULE_PATH is setup so that CMake can find the Dry-specific CMake modules provided by Dry project inside your own project. The DryCommon.cmake is the module where all the reusable commands and macros are defined. It also gives your project cross-platform build capability similar to Dry project. It does this by automatically finding the required software library components specific for your target platform and configuring your project to use them together with the platform-specific compiler flags and definitions. It utilizes CMake-provided as well as Dry custom-made FindXXX modules to get the work done. So, it is important to get the CMAKE_MODULE_PATH setup correctly in your project early on.

Your own project naturally depends on Dry project, or to be more precise it depends on Dry library. The Dry library needs to be built first so that it can be found later by your own project. When using GCC/Clang or one of its derivatives, both Dry static and shared libraries could be potentially built/installed at a same location and coexist. In such cases the FindDry.cmake module, the module responsible to find Dry software library component and is invoked automatically by the DryCommon.cmake module, has precedence to first find the static library type over over shared library type. However, you can use DRY_LIB_TYPE build option to override this precedence. When using MSVC compiler, both static and shared libraries could not be built/installed at a same location because both the static library and import library have a same file extension. However, for MSVC, it is possible to have both Release and Debug versions of either static or shared library type built/installed at a same location. In such cases the FindDry.cmake module would automatically utilize both of Release and Debug versions as appropriate in your project for Release and Debug build configuration, respectively, without user intervention.

When you build Dry library, you have an option to install the library to a installation location in your file system as if Dry is an SDK or you can just leave the library where it is in its build tree. Thus, there are two approaches for your project to link against Dry library as external library. However, there is really no difference between the two approaches from your project's point of view. The FindDry.cmake module is designed not only able to find Dry library from the Dry SDK installation, but also from any Dry project build tree directly. When searching in Dry SDK installed in a system-wide default location then no additional variable need to be set. When searching in a non-default SDK installation or when searching in any Dry project build tree then the actual location need to be provided via DRY_HOME environment variable (set in the host system) or DRY_HOME build option (set using -D at command line or in cmake-gui). That is, use the DRY_HOME to hint the build system to locate the library.

The define_source_files() and setup_main_executable() are Dry-specific macros. The define_source_files() macro is a shorthand to glob all the source files in the current directory. It is equivalent to these commands:

file (GLOB CPP_FILES *.cpp)
file (GLOB H_FILES *.h)

The setup_main_executable() macro then uses SOURCE_FILES and TARGET_NAME variables to setup a new CMake target that would work on ALL platforms supported by Dry. Make sure both your project name and target name are not called 'Dry', as this name is reserved for Dry project only.

If you have prepared your new project as outlined above then you can use the build instructions of Dry project to build your own project.

Sometimes you may want to have different resource directories to contain your assets than the one used by Dry project by convention. For example, you may have them arranged by scenes as follows:

├ bin/
│ ├ Scene1/
│ ├ Scene2/
│ └ Core/

If that is the case then you have to tell both the build system and the Dry library (more specifically the Engine class) where the resource paths are. Using the above example, you have to tell the build system by calling the define_resource_dirs() macro explicitly and passing it the option to glob your resource directories. The define_resource_dirs() macro is actually being called internally with the default option which glob directory names based on Dry project convention, but your project can override that by making the call explicitly in your CMakeLists.txt.

# Define source files
define_source_files ()
define_resource_dirs (GLOB_PATTERNS ${CMAKE_SOURCE_DIR}/bin/Scene* ${CMAKE_SOURCE_DIR}/bin/Core)

You also need to inform the Dry library that you have non-conventional resource directory paths. You do that by setting the EP_RESOURCE_PATHS engine parameter, either programmatically during compile time or via '-p' command line option during runtime. In this case you would set it to "Scene1;Scene2;Core" instead of the default "Data;CoreData".

Your project may also want to store the resource directories in other location instead of the 'bin' directory where the executable binary resides. For example when creating macOS/iOS/tvOS bundle, all the assets are automatically bundled into another directory outside where the executable binary resides. In fact, you can have them in a few separate parent directories and not just one. That is where the EP_RESOURCE_PREFIX_PATHS engine parameter and '-pp' command line option come into picture. With this you can define an alternate resource prefix path where your resource path would be prepended before locating your assets.

Using pkg-config instead of CMake

If for some reason you could not use CMake in your project, you could configure your project to compile and link against Dry library from SDK installation using 'pkg-config' tool with the help of 'Dry.pc' configuration file (which is installed as part of the SDK installation). If the Dry SDK is being installed into a local location (such as /usr/local) or a non-default location, then most likely you would need to specify PKG_CONFIG_PATH environment variable to point to the location of the configuration file for this to work. The 'Dry.pc' file contains important information on how to configure your compiler and linker correctly. You can use that information as a reference to configure your own project even when you do not use 'pkg-config' tool.

Below are a few invocation examples on a 64-bit RedHat-based distro with local installation:

# To get installed Dry version
PKG_CONFIG_PATH=/usr/local/lib64/pkgconfig pkg-config --modversion Dry
# To compile and link natively in one liner
c++ -o DryPlayer DryPlayer.cpp `PKG_CONFIG_PATH=/usr/local/lib64/pkgconfig pkg-config --cflags --libs Dry`
# To compile and link natively but in separate steps
export PKG_CONFIG_PATH=/usr/local/lib64/pkgconfig
c++ -c DryPlayer.cpp `pkg-config --cflags Dry`
c++ -o DryPlayer DryPlayer.o `pkg-config --libs Dry`
# To cross-compile and link for Raspberry Pi platform
export CC=${RPI_PREFIX}-c++
export PKG_CONFIG_PATH=${RPI_SYSROOT}/usr/local/lib/pkgconfig
$CC -o DryPlayer DryPlayer.cpp `pkg-config --cflags --libs Dry`
# To cross-compile and link for Windows platform using MinGW cross-compiler (in Debug configuration)
export CC=${MINGW_PREFIX}-c++
export PKG_CONFIG_PATH=${MINGW_SYSROOT}/usr/local/lib/pkgconfig
$CC -o DryPlayer_d.exe DryPlayer.cpp `pkg-config --variable CFLAGS_DEBUG Dry` `pkg-config --cflags --libs Dry`

Using rake tasks for project scaffolding and building

With Rake ruby gem installed, you can quickly setup all the above by simply using the 'scaffolding' rake task.

rake scaffolding dir=/path/to/new/project/root [project=Scaffolding] [target=Main]

As its name implies, this task just creates a basic project structure for your new project. You may also pass optional "project" and "target" parameters besides the "dir" parameter. Otherwise, you will get the default project name (Scaffolding) and target name (Main) in the main CMakeLists.txt. This task copies the DryPlayer.cpp and DryPlayer.h as placeholders for the source files. Normally, you should replace these two files with your own project source files before invoking one of the build scripts.

On Windows host system, this task requires privilege to create symlinks by using mklink command. The task would fail when the Windows user account does not have that privilege. We strongly advise you not to use "Administrator" account just for this purpose. Instead, grant your "normal" user account to have the privilege correctly.

WARNING: As of this writing, this rake scaffolding task does not yet create a complete new project suitable for Android platform. In future a custom Gradle scaffolding task will be added to perform this job. You need to supply the missing bits manually yourself for now.

The Ruby and Rake are not prerequisite software components for building Dry and your projects. However, if you are reading this section this far and that your host system actually already has them installed then you can take advantage of them by utilising the 'rake cmake' and 'rake make' tasks. The former configures and generates the build tree (by invoking one of our build scripts under the hood) and the latter builds the project in the generated build tree (by invoking 'cmake –build' command which in turns calls the respective build tools, such as 'make' or 'xcodebuild', or 'MSBuild.exe') at the convenient of your finger tips in a command line interface.

To configure and generate:

rake cmake [<generator>] [<platform>] [<option>=<value> [<option>=<value>]] [[<platform>_]build_tree=/path/to/build-tree] [fix_scm]

To build:

rake make [<platform>] [<option>=<value> [<option>=<value>]] [[<platform>_]build_tree=/path/to/build-tree] [numjobs=n] [clean_first] [unfilter]

The default <generator> when not specified is 'generic', which let CMake to detect and choose what generator is available in the host to use. The possible values are: 'codeblocks', 'eclipse', 'ninja', 'vs2015', 'vs2017', 'vs2019', 'xcode'.

The default <platform> when not specified is 'native'. The possible values are: 'android', 'web', 'ios', 'tvos', 'mingw', 'rpi'. Naturally this influences the compiler toolchain being used in the generated build tree.

When using the 'rake cmake' task, the <option>=<value> pairs are optional build options supported by our build scripts as usual. However, the format here does not expect a leading '-D' for each pair. When using the 'rake make' task, the <option>=<value> pairs are optional build options supported by the respective build tools. For example on iOS/tvOS platform using 'xcodebuild' build tool, one could pass the '-sdk' option as follows: 'rake make ios sdk=iphonesimulator'. Note the absence of leading '-' character in the example. To build a specific built-in target in the project, use the 'target' option, e.g. 'rake make target=install'. In a multi-config project, such as Xcode project or VS solution, use the 'config' option to choose which build configuration to use, e.g. 'rake make config=Release'. For Xcode project building using 'rake make' task, you may optional install the 'xcpretty' filter to address the verbosity of the 'xcodebuild' tool from its standard output stream. On the other hand, pass the 'unfilter' option to get the output from 'xcodebuild' tool unfiltered regardless the 'xcpretty' is installed or not. You can pass the 'clean_first' option to perform a clean build. By default this task invokes the respective build tool to use all the logical CPU cores available, but you can use the 'numjobs' option to override this default.

Use the 'build_tree' option to set the path to the desired build tree location. When not specified then the build tree location would be defaulted to '../<platform>-Build', relative to the project root. To avoid repeating the customized build tree locations for each platform, you can set and export them as environment variables. The '<platform_>build_tree' option takes precedence over normal 'build_tree' option. For example with these commands below, the native and RPI build tree will be generated in the ~/custom-native-Build and ~/custom-rpi-Build, respectively, and then build from there.

export native_build_tree=~/custom-native-Build rpi_build_tree=~/custom-rpi-Build
rake cmake DRY_LUAJIT=1 && rake make
rake cmake rpi DRY_LUAJIT=1 && rake make rpi

You can in fact set and export any other key/value pair build options as environment variables to avoid repeating yourself when invoking any of our Rake tasks.