The folder structure is one of the first thing you need to think through when setting up a new project. At Angels' Ware the goal has been to make a simple but yet powerful and extendable structure that is intuitive and easy to navigate.
At one of my previous places of work we had a fairly good setup (which I've been inspired by when setting up the one at Angels' Ware). The problem there was that it was too complex to grasp and got very time consuming as soon as you had to add a new dependency or add a necessary define or so. It was just not intuitive enough.
One of the good things with that system was that it utilised CMake to generate project files for all platform. This is something I've chosen to also do at Angels' Ware.
The root folder (referred to as ./) consists of a few folders together with some files and looks like this.
./
apps/
doc/
cmake/
packages/
scripts/
contributors.txt
README.md
apps/ - This is where the code for the applications is. The folder contains folders named on the form com.angelsware.PROJECT_NAME. As an example, the sample project's folder structure looks like this.
./apps/com.angelsware.sample/
assets/
platform-resources/
| - android/
| - | - AndroidManifest.xml
| - | - build.gradle
| - | - ...
| - ios/
| - | - Info.plist
| - | - ...
src/
| - android/
| - | - main.cpp
| - | - ...
| - common/
| - | - sample.cpp
| – | - sample.h
| - default/
| - | - main.cpp
| - ios/
CMakeLists.txt
The platform-resources/ folder contains platform specific files that aren't code. Info.plist for iOS and AndroidManifest.xml for Android are good examples.
src/ - Folder for game specific code.
doc/ - Contains documentation. In this case a single DocBook xml file.
cmake/ - Contains CMake toolchain files for supported platforms.
packages/ - This folder contains the engine code. It consists of more than fifty different packages (libraries) at the moment which serves as optional dependencies for the applications. By having the engine split up you can easily depend on just those parts you need. Compiling the network stack for an offline game just doesn't make sense.
Every package has the same structure, so I've picked one to show as an example. The time package makes a good one as it uses all features.
./packages/
time/
| - include/
| - | - time/
| - | - | *.h
| - src/
| - | - common/
| - | - | *.h;*.cpp
| - | - default/
| - | - | *_default.h;*_default.cpp
| - | - win32/
| - | - | *_win32.h;*_win32.cpp
| - CMakeLists.txt
The include/ folder is where all public headers are and results in an include line like this #include <time/aw_second.h>.
The src/ folder contains the source files as well as the private headers. These are split up in three types of folders.
The cross-platform code is put in the common/ directory. So no matter what platform you build on this code will be linked.
Platform specific code has their own folder. So when building for Windows the code from the win32/ folder will be used. Every platform has its own predefined folder. Currently those are win32, linux, emscripten, ios and android.
In this example there is also a folder called default/. This one works as a fallback folder meaning that if there are no platform specific folder for the targeted platform the code from this folder will be linked if it exists. So in this example all platforms except Windows will link with code from the common/ and default/ directories and Windows will link from common/ and win32/.
scripts/ - Build scripts, model exporters and other tools can be found here.
./scripts/
app-resources/
blender/
binary-to-header.py
create-app.py
create-package.py
...
The app-resources/ folder contains template files used when setting up a new application.
The blender/ folder contains model export script for Blender.
The last folder that isn't represented in the example above is the libs/ folder for pre-built libraries. If you need to link with a jar file on Android for example you put the file in ./packages/MY_PACKAGE/libs/android/ and it will be linked. The same principle applies for static (*.a;*.lib) and dynamic libraries (*.so;*.dll).
So how is all this working?
Before we take a look inside the CMakeListst.txt files we will have a look on how all of this is used in practice.My favorite IDE is QtCreator which can as CLion open CMake files directly, so this doesn't really apply for that those two. But for other IDEs the process of generating a project looks like this.
$ cmake ./apps/com.angelsware.sample -Dtarget=android
An alternative to this line is to run one of the build scripts available in the scripts/ folder.
And now to the CMakeLists.txt files. There are those for the apps and those for the packages. A very simple terminal Hello World application would have a CMakeLists.txt file that looks like this.
cmake_minimum_required (VERSION 2.8)
project (com.angelsware.helloworld)
include ("../../prerequisites.cmake")
build_app()
If you want to make your application depend on say a math library and time library you change the call from build_app() to:
build_app(
"math"
"time"
)
The names of the added dependencies corresponds to the names of the folders in the package/ folder. So in this case CMake understands that you want to link with the ./packages/math and ./packages/time packages.
If you're building for iOS you will need to link with additional frameworks. Simply add this line:
if(TARGET_IOS)
add_framework(Foundation)
endif()
Let's have a look at the packages CMakeLists.txt files. I will use the one from the platform package as an example. It looks like this:
cmake_minimum_required (VERSION 2.8)
project (platform)
build_library (
"math"
"util"
)
if(TARGET_LINUX)
target_link_libraries("${PROJECT_NAME}" "-lX11 -lXxf86vm")
elseif(TARGET_ANDROID)
target_link_libraries("${PROJECT_NAME}" "-landroid -lGLESv2 -llog")
elseif(TARGET_WIN32)
target_link_libraries("${PROJECT_NAME}" "opengl32.lib")
elseif(TARGET_EMSCRIPTEN)
target_link_libraries("${PROJECT_NAME}" "-lm -lX11")
endif()
This package is dependent on system libraries thus calls the target_link_libraries function.
Conclusion
Spending a few days thinking through the folder structure, how to the generate the project files and writing a CMake script to support it all, really pays of in the long run and will keep you from headache and frustration.