Fips CMake Guide

Fips projects need to adhere to a few rules in their CMakeLists.txt file hierarchy. Fips provides a number of cmake macros, variables and toolchain files to simplify working with cmake files and implement some under-the-hood magic.

Fips CMake Macros

Fips provides the following cmake macros to describe a project structure:

fips_setup(PROJECT proj_name)

Initializes the fips build system in a cmake file hierarchy. Must be called once in the root CMakeLists.txt before any other fips cmake macros.

The PROJECT argument is optional, and if provided, is the name of the main project. For backward compatibility, it is still possible to call fips_setup() without arguments, and provide the project name later with fips_project(). It is also possible to define multi-project builds by calling fips_project() multiple times.

If a project imports apps or shared-libs from other fips project, fips_setup() MUST be called with a PROJECT argument (otherwise the imported apps and shared-libs would be created under the default cmake project name 'Project', since the import happened inside fips_setup() before the first call to fips_project()).

fips_finish()

Must be called in the root CMakeLists.txt file after any other fips macros and does any work that must happen once after each cmake run. Currently this is macro does nothing.

fips_project(name)

Starts a new project with the given name. This must be called at least once in a hierarchy of CMakeLists.txt files, usually right after fips_setup().

Use the fips_project() macro instead of cmake's builtin project() macro

fips_ide_group(name)

Start a new project explorer folder in an IDE. This can be used to group related build targets for a clearer layout in the IDE's project explorer.

fips_add_subdirectory(dir)

Include a child CMakeLists.txt file from a subdirectory. Use this instead of cmake's built-in add_subdirectory() macro.

fips_include_directories(dir ...)

Define one or more header search paths. Use this instead of cmake's built-in include_directories() macro.

fips_begin_module(name)

Begin defining a fips module. Modules are special high-level static link-libraries with a few additional features over conventional libs:

  • can define dependencies to other modules, which are automatically resolved when linking apps
  • can contain code-generation python scripts which are added as custom build targets to the build process

After a fips_begin_module() the following fips macros are valid:

  • fips_dir()
  • fips_files()
  • fips_generate()
  • fips_deps()
  • fips_libs()
  • fips_end_module()

fips_end_module()

This finishes a fips_begin_module() block.

fips_begin_lib(name)

Begin defining a fips library. A fips library is a collection of source files that compile into a static link library. Fips libraries are normally used to wrap 3rd-party code that would normally be linked as a pre-compiled static link library, but is instead compiled from source code into a fips project.

NOTE: currently, a fips library is equivalent to a fips module, this may change in the future though

After a fips_begin_lib() the following fips macros are valid:

  • fips_dir()
  • fips_files()
  • fips_generate()
  • fips_deps()
  • fips_libs()
  • fips_end_lib()

fips_end_lib()

This finishes a fips_begin_lib() block.

fips_begin_app(name type)

Begin defining a fips application. The type argument can be either 'windowed' or 'cmdline', this only makes a difference on platform with separate command-line and UI application types, like Windows (WinMain vs main) or OSX (app bundle vs command line tool).

After a fips_begin_app() the following fips macros are valid:

  • fips_dir()
  • fips_files()
  • fips_generate()
  • fips_deps()
  • fips_libs()
  • fips_end_app()

fips_end_app()

This finishes a fips_begin_app() block.

fips_dir(dir [GROUP ide_group])

Defines a source code subdirectory for the following fips_files() statements. This is only necessary if source files are located in subdirectories of the directory where the current CMakeLists.txt file is located. You don't need to provide a fips_dir() statement for files in the same directory as their CMakeLists.txt file.

fips will automatically derive an IDE group folder name from the directory path, so that the directory structure is reflected in IDE file explorers. This behaviour can be overriden with the optional GROUP argument, and an explicit group name (or path) can be defined.

This is the default usage of fips_dir, without explicitely overriding the IDE group name:

fips_dir(android)

This would switch the 'current source directory' to a subdirectory named 'android', and in IDEs these files will be grouped under a folder 'android' in the file explorer.

To switch back to the root directory (where the CMakeLists.txt file is located), just pass a . as argument:

fips_dir(.)

The following example groups files in a very deep directory hierarchy under a short group name "include":

fips_dir(some/deep/dir/hierarchy/include GROUP "include")

A GROUP argument can also contain slashes to define a whole group path:

fips_dir(some/deep/dir/hierarchy/include GROUP "engine/include")

Finally, to not put the files into any IDE group folder, pass a "." as special GROUP argument:

fips_dir(some/deep/dir/hierarchy/include GROUP ".")

fips_dir() must be called inside a module, lib, or app definition block.

fips_files(file ...)

Add source files in the currently set directory to the current module, lib or app. This isn't restricted to C/C++ files, but any file that should show up in the IDE project explorer. The actual build process will ignore any files with file extensions that cmake doesn't know how to build.

The following file extensions are recognized by the build process:

  • .cc, .cpp: C++ source files (compiled with C++11 support)
  • .c: C source files
  • .m, .mm: Objective-C and Objective-C++ source files
  • .h, .hh: C/C++/Obj-C headers
  • .py: Python source code generator scripts

fips_files() must be called inside a module, lib, or app definition block.

fips_files_ex(dir glob... [EXCEPT glob...] [GROUP ide_group] [NO_RECURSE])

Like fips_dir(), but will also do a fips_files() with files found in a directory that match an expression from the glob expression list excluding any file that is in the EXCEPT glob expression list. You can use NO_RECURSE so it will not search files in subdirectories.

To add all files contained in a folder but excluding some types to the group "everything":

fips_files_ex(src/ *.* EXCEPT *.rc *.txt GROUP "everything")

Or, if you do want only Objective-C sources for example:

fips_files_ex(src/ *.m *.mm GROUP "ObjC")

Note: You should avoid using this as CMake will not be able to detect when files are added or removed to the filesystem and consequently will not be able to reconfigure the project. A good use of this is if it can help porting libraries to fips.

fips_files_ex() must be called inside a module, lib, or app definition block.

fips_src(dir glob... [EXCEPT glob...] [GROUP ide_group] [NO_RECURSE])

Same as fips_files_ex() but with a default list of expressions valid for C/C++ projects: *.c *.cc *.cpp *.h *.hh *.hpp

So you can easily add any C/C++ sources from the directory "src/" to the project as simply as:

fips_src(src)

fips_src() must be called inside a module, lib, or app definition block.

fips_deps(dep ...)

Add module or lib dependencies to the current fips app, module or lib. A dependency must be the name of another fips module or lib defined with fips_begin_module() or fips_begin_lib(). Dependencies added to fips modules will be resolved recursively when linking apps. Fips will also take care of the dreaded linking order problem of GCC where symbols can't be resolved if the order of link libraries is wrong or in case of cyclic dependencies.

fips_deps() must be called inside a module, lib, or app definition block.

NOTE: dependencies listed in fips_deps() must have been defined before in the CMakeLists.txt file hierarchy

fips_libs(libs ...)

Add a static link library dependency to the current fips app, module or libs. This is similar to the fips_deps() macro but is used to define a link dependency to an existing, precompiled static link library.

fips_libs() must be called inside a module, lib, or app definition block.

fips_libs_debug(libs ...), fips_libs_release(libs ...)

These are rarely needed special variants of fips_libs() which add separate static link libraries for debug and non-debug compilation modes. This is necessary on Visual Studio when trying to link libraries that contain STL code.

fips_generate(...)

Defines a code-generation job. Code generation can be used to generate C/C++ source files at build time from other input files like JSON, XML, YAML, GLSL, ... Code generation is described in detail here

fips_generate() must be called inside a module, lib, or app definition block.

The fips-include.cmake File

A fips project may contain an optional cmake file called fips-include.cmake at the root level of a project (same directory level as the root CMakeLists.txt file). The fips-include.cmake file should contain all cmake definitions that need to be visible when using this fips project as an external dependency in another project. Fips will include this file either when the project itself is compiled, or the project is imported as an external dependency in other projects.

Check out the fips-include.cmake file included in the Oryol 3D engine for a complex example.

Fips Predefined CMake Variables

Fips defines a number of useful cmake variables:

  • FIPS_POSIX: target platform is UNIX-ish (basically anything but Windows)
  • FIPS_WINDOWS: target platform is Windows
  • FIPS_OSX: target platform is OSX-ish (either OSX 10.x or iOS)
  • FIPS_CLANG: C++ compiler is clang
  • FIPS_GCC: C++ compiler is GCC
  • FIPS_MSVC: C++ compiler is Visual Studio compiler
  • FIPS_LINUX: target platform is Linux
  • FIPS_MACOS: target platform is OSX 10.x
  • FIPS_IOS: target platform is iOS
  • FIPS_WIN32: target platform is 32-bit Windows
  • FIPS_WIN64: target platform is 64-bit Windows
  • FIPS_EMSCRIPTEN: target platform is emscripten
  • FIPS_PNACL: target platform is PNaCl
  • FIPS_ANDROID: target platform is Android
  • FIPS_HOST_WINDOWS: host platform is Windows
  • FIPS_HOST_OSX: host platform is OSX
  • FIPS_HOST_LINUX: host platform is Linux
  • FIPS_ROOT_DIR: absolute path of the fips root directory
  • FIPS_PROJECT_DIR: absolute path of the current project
  • FIPS_DEPLOY_DIR: absolute path of the deployment directory
  • FIPS_CONFIG: name of the current build configuration (e.g. osx-xcode-debug)
  • FIPS_IMPORT: set inside import CMakeLists.txt files

Fips CMake Options

Fips provides a few build options which can be tweaked by running ./fips config (requires the ccmake or cmake-gui tools to be in the path).

Besides ./fips config, cmake options can also be provided in a build config YAML file, for instance the following config file sets the FIPS_UNITTESTS and FIPS_UNITTESTS_HEADLESS options to ON:

---
platform: emscripten 
generator: Ninja 
build_tool: ninja
build_type: Debug
defines:
    FIPS_UNITTESTS: ON
    FIPS_UNITTESTS_HEADLESS: ON

CMakeLists.txt Samples

Here's a very simple root CMakeLists.txt file from the fips-hello-world sample project:

cmake_minimum_required(VERSION 2.8)

# include the fips main cmake file
get_filename_component(FIPS_ROOT_DIR "../fips" ABSOLUTE)
include("${FIPS_ROOT_DIR}/cmake/fips.cmake")

fips_setup()
fips_project(fips-hello-world)
fips_add_subdirectory(src)
fips_finish()

The src subdirectory contains the CMakeLists.txt file which defines the actual appliction:

fips_begin_app(hello cmdline)
    fips_files(hello.cc)
    fips_deps(dep1)
fips_end_app()

This is a more complex root CMakeLists.txt file from the Oryol 3D engine:

#----------------------------------------------------------
#   oryol cmake root file
#
#   See BUILD.md for details how to build oryol.
#----------------------------------------------------------
cmake_minimum_required(VERSION 2.8)

get_filename_component(FIPS_ROOT_DIR "../fips" ABSOLUTE)
include("${FIPS_ROOT_DIR}/cmake/fips.cmake")

option(ORYOL_SAMPLES "Build Oryol samples" ON)

include_directories(code)
include_directories(code/Modules)
include_directories(code/Ext)

fips_setup()
fips_project(oryol)
fips_add_subdirectory(code/Hello)
fips_ide_group(Modules)
fips_add_subdirectory(code/Modules)
fips_ide_group(Ext)
fips_add_subdirectory(code/Ext)
if (ORYOL_SAMPLES)
   fips_ide_group(Samples)
   fips_add_subdirectory(code/Samples)
endif()
fips_finish()

Next a sample which defines a code module with platform-specific source code in subdirectories, and towards the end some dependencies to other fips modules:

#----------------------------------------------------------
#   oryol Input module
#----------------------------------------------------------
fips_begin_module(Input)
    fips_files(Input.cc Input.h)
    fips_dir(Core)
    fips_files(
        CursorMode.h
        Gamepad.cc Gamepad.h
        InputSetup.h
        Key.cc Key.h
        Keyboard.cc Keyboard.h
        Mouse.cc Mouse.h
        Sensors.h
        Touchpad.cc Touchpad.h
        inputMgr.h
    )
    fips_dir(base)
    fips_files(inputMgrBase.cc inputMgrBase.h)
    fips_dir(touch)
    fips_files(
        gestureState.h
        panDetector.cc
        panDetector.h
        pinchDetector.cc
        pinchDetector.h
        tapDetector.cc
        tapDetector.h
        touchEvent.cc
        touchEvent.h
    )
    if (FIPS_ANDROID)
        fips_dir(android)
        fips_files(androidInputMgr.cc androidInputMgr.h)
    endif()
    if (FIPS_EMSCRIPTEN)
        fips_dir(emsc)
        fips_files(emscInputMgr.cc emscInputMgr.h)
    endif()
    if (FIPS_IOS)
        fips_dir(ios)
        fips_files(iosInputMgr.cc iosInputMgr.h)
    endif()
    if (FIPS_PNACL)
        fips_dir(pnacl)
        fips_files(pnaclInputMgr.cc pnaclInputMgr.h)
    endif()
    if (FIPS_MACOS OR FIPS_WINDOWS OR FIPS_LINUX)
        fips_dir(glfw)
        fips_files(glfwInputMgr.cc glfwInputMgr.h)
        fips_deps(glfw3)
    endif()
    fips_deps(Core Gfx Time)
fips_end_module()

Finally an example how to wrap a simple C library with a few custom C preprocessor defines:

fips_begin_lib(zlib)
    fips_files(
        adler32.c
        compress.c
        crc32.c crc32.h
        deflate.c deflate.h
        infback.c 
        inffast.c inffast.h
        inffixed.h
        inflate.c inflate.h
        inftrees.c inftrees.h
        trees.c trees.h
        uncompr.c
        zconf.h
        zlib.h
        zutil.c zutil.h
    )
    add_definitions(-D_NO_FSEEKO)
    add_definitions(-D_CRT_SECURE_NO_DEPRECATE)
    add_definitions(-D_CRT_NONSTDC_NO_DEPRECATE)
fips_end_lib(zlib)