WAF Adding a Module

This topic explains how to add a new module to a WAF project.

A module can be thought of as a collection of files that make up a static or dynamic library. With the exception of a pure file collection which does not produce any output but acts as an common file interface container to be includes in other modules.

A module itself is made up of a WAF Module File (wscript) and a WAF File List (*.waf_files). You can do without the later but it is uncommon practice.

Creating a module

The CRYENGINE is a collection of many different types of modules. A complete list of the various module type can be found here: WAF Module File (wscript).


The most common module types being:

  • CryEngineModule : Defines a CRYENGINE module. Use to extend the CRYENGINE with your own module.

  • CryPlugin : Defines an Editor plugin. Use to extend the editor with your own plugin.

A module itself is declared in a wscript file which is written in Python 2.7.

Creating a basic CryEngineModule:
def build(bld):
    bld.CryEngineModule(
    target        = 'MyModule',
        file_list     = 'my_module_files.waf_files',
    vs_filter     = 'Projects/MyModule'
)

The codeblock above show the most basic setup for any CRYENGINE module type.

The 'target' parameter identifies your modules among others. This name has to be unique throughout the CRYENGINE. How to add a module to the CRYENGINE will be covered later in this tutorial.

The 'file_list' parameter points to your WAF File List (*.waf_files) which tells WAF which source files to include in your compilation and also how to layout the files in the IDE.

The 'vs_filter' parameter is optional. If set, the module will be added as a visual studio project to the later generated Visual Studio solution file.

It is good practice to create a new folder for each of your modules. The <SDK_ROOT>/Code folder is a good starting place. This is where all the CRYENGINE and the GameSDK code resides.

In order for WAF to recognise your module you have to explicitly tell it where to look for it. This is done using the wscript in the folder in which you added your module.

For example, if you were to create a new module folder in <SDK_ROOT>/Code/MyModule you would have to modify the <SDK_ROOT>/Code/wscript as shown below:

SUBFOLDERS =    [
    'CryEngine',
  ...,
    'MyModule'
    ]
    
def build(bld):
    # Recursive into all sub projects    
    bld.recurse(SUBFOLDERS)
    
Creating a more advance CryEngineModule:
def build(bld):
    bld.CryEngineModule(
    target        = 'MyModule',
        file_list     = 'my_module_files.waf_files',
    vs_filter     = 'Projects/MyModule',
    pch           = 'StdAfx.cpp',

    includes           = [ Path('Code/SDKs/SomeSDK/includes'), "../path_relative_to_this_wscript/" ],
    win_includes       = [ Path('Code/SDKs/SomeWinOnlySDK/includes')],

    win_lib            = [ 'some_lib' ],
    win_debug_lib      = [ 'some_other_libd' ],

    win_x64_libpath          = [ Path('Code/SDKs/SomeWinOnlySDK/x64/libs')],
    win_x64_debug_libpath    = [ Path('Code/SDKs/SomeOtherWinOnlySDK/libs/x64/debug') ],

    defines         = ['MODULE_SPECIFIC_DEFINE'],
    win_defines       = ['MODULE_SPECIFIC_WINDOWS_ONLY_DEFINE'],
    win_x64_defines     = ['MODULE_SPECIFIC_WINDOWS_X64_ONLY_DEFINE'],
    win_x64_debug_defines   = ['MODULE_SPECIFIC_WINDOWS_X64_DEBUG_ONLY_DEFINE'],

    win_cxxflags   = ['/W3', '/fp:strict', '/wd4355'],

    win_linkflags   = '/NODEFAULTLIB:libcmt.lib'    
  )

The 'pch' parameter specifies the path to your pre-compiled-header if you choose to compile with pre-compiled-headers.

The 'includes' parameter specifies additional include paths to be passed into the compiler for this module.

The 'lib' parameter specifies additional libraries which should be included in the compilation for this module. Note: Only use the name of the library without its extension e.g. 'Ole32' and not 'Ole32.lib'

The 'libpath' parameter specifies additional library paths for the compiler to search for specified libraries to be included.

The 'defines' parameter specifies additional code defines to be injected into your source code by the compiler for this module.

The 'cxxflags' parameter specifies additional options which should be added to the default compiler options. In the example we define '/W3' for windows builds of this module. Windows is usually compiled with MSVC.

The 'linkflags' parameter specifies additional code which should be added to the default linker options.

Adding the module to your project:

To add the module to your project there are two ways:

  1. Via your project's spec file.
  2. Via an other modules wscript file using the "use_module" parameter.

Via project spec:

Let's add our module to the GameSDK project spec which can be found in <SDK_ROOT>/_WAF_/specs/gamesdk.json.

{            
    "description"           : Spec description",    
    ...,
    "visual_studio_name"    : "GameSDK",
    "game_projects"         : "GameSDK",
        
    "modules" : 
    [
    "MyModule"
     ...,
        "CrySystem", 
    "CryGameSDK",       
        ...                
    ],
    
    "win_modules"           : ["MyOtherWindowsOnlyModule" ],

Via an other module:

Alternatively you can individually select per module which other module should be included.

This can be done using the 'use_module' parameter in the modules wscript.

def build(bld):
    bld.CryEngineModule(
        target     = 'SomeModule',
        vs_filter  = 'SomeModule',
        file_list  = 'some_module.waf_files',
        ...
        use_module = [ 'MyModule' ],
        ...
    )

Creating a new game module

To create a new game module browse to the <SDK_ROOT>/Code folder.

Create a new folder with the name of your game e.g. "MyFirstGame". This is your game folder root.

As a next step we will create a folder where we will place all our source files. e.g. "GameDll"

Furthermore you will need to create a folder called "Resources" (note: name is fixed) where all platform resources needed during the build process e.g. the game icon, XBoxOne AppManifest.xml or PS4 nptitle.dat will be placed.

Since we are only building Window for now, copy the WindowsIcon.ico and WindowsServerIcon.ico from <SDK_ROOT>/Code/GameSDK/Resources to <SDK_ROOT>/Code/MyFirstGame/Resources. You can replace them at a later point if you want. Note that the name is currently fixes though.

Last but not least we will create a "wscript" file which does nothing more than to direct WAF to the "GameDLL" folder where we store all our source files.

SUBFOLDERS =    [
    'GameDLL',
    ]    
    
def build(bld):    
    # Recursive into all sub projects
    bld.recurse(SUBFOLDERS)    

Note: Unlike other modules, the game module's folder does not have to be added to the <SDK_ROOT>/Code/wscript "SUBFOLDERS" list. This is automatically handled by the games WAF Spec File (*.json) that will be created in a later step.

You should end up with a folder layout like this:

Let's explore the "GameDll" folder. This is where we decided the source code of our module will live and thus the module itself, too.

It should consist of your source files as well as a wscript file and a xxx.waf_files file.

Source Files:

This is where you hook your game into the CRYENGINE. This tutorial will not go into further details about the content of these files as this should be part of an other tutorial.

xxx.waf_files:

This file containes a list of all your source files which will be used to gather information about which files to compile for the project as well as generate solution files for your favorite IDE. A list of supported IDE's can be found here.

It is also worth noticing that you can group files into uber file groups to speed up compilation. For more information see WAF File List (*.waf_files).

{
    "NoUberFile":
    {
        "Root":
        [
            "StdAfx.cpp"
        ],
        "MyFilter":
        [
            "SubFolder/file_a.cpp",
      "SubFolder/file_a.h",
      "SubFolder/file_b.cpp",
      "SubFolder/file_b.h"
        ]
    },

   "my_uber_file_0.cpp":
   {
      "Root":
        [
      "file_e.cpp",
       "file_e.h"
    ],
        "MyFilter":
        [
       "file_c.cpp",
       "file_c.h",
           "SubFolder/file_d.cpp",
       "SubFolder/file_d.h"
    ]
  }
}

The "NoUberFile" file category is predefined. Add all files here that should be compiled individually. All other groups are treated as uber files.
The "Filter" subcategories help to visually layout the files in the IDE better and have no contribution to the compilation process. The predefined "Root" subcategory places the files at the "root" of the GameProject IDE filter.

wscript:

The Game Module wscript is no different to any of the other modules. For more information about how to setup the module see the previous section: Creatingamodule.

def build(bld):
    bld.CryEngineModule(
        target        = 'MyModule',
        vs_filter     = 'Projects/MyModule',        
        file_list     = 'my_module.waf_files', 
        pch           = 'StdAfx.cpp',
        ...
 )

The last missing step is to register the game module with WAF.

Unlike a normal module which has to be included in the search directory list of the parent's wscript the game module should not be added to the parents wscript.

Instead it should be added to the WAF Project File (projects.json) located in <SDK_ROOT>/_WAF_/projects.json.

{
"MyGameModule" :
    {        
        "code_folder"              : "Code/MyGameModule",        
        "game_data_dir"            : "MyGameModule",            
        "executable_name"          : "MyGameModule",
        ...
  }
}

At the root there is the project name. This is the name/target you can use in the WAF Spec File (*.json) to add your project to the compilation pipeline. See WAF Adding a Spec for how to add a spec file.

The "code_folder" points to your game module root in our example "<GAME_SDK_ROOT>/Code/MyGameModule". This will tell WAF where to find the Code as well as the Resource files.

The"game_data_dir" point to the game specific assets. E.g. for GameSDK this would be <GAME_SDK_ROOT>/GameSDK.

The "executable_name" defines the name of the final executable file in the output bin folder.

More information can be found WAF Project File (projects.json) as well as in WAF Adding a Project.