Building SDL 2 with SCons on Linux

In this post I’m going to show you how to build SDL2 as a static library on Linux using a building tool called SCons. The static library will be used to create a basic program which opens a window, nothing fancy, but it’s a good proof of concept to check everything works as expected.

The technology behind

Simple DirectMedia Layer (SDL) is a cross-platform development library designed to provide low level access to audio, keyboard, mouse, joystick, and graphics hardware via OpenGL and Direct3D. It’s written in C, but works natively with C++, and there are bindings available for several other languages, including C# and Python.

SCons is an Open Source software construction tool—that is, a next-generation build tool. Think of SCons as an improved, cross-platform substitute for the classic Makeutility with integrated functionality similar to autoconf/automake and compiler caches such as ccache. It’s written in Python and the configuration files used to build the software are Python scripts.

The project Hierarchy

For this simple project I decided to have the following hierarchy:

my_scons_project/
|
+---- bin/
|
+---- build/
|
+---- SConstruct
|
+---- src/
      +---- game/
      |     +---- main.cpp
      |     |---- SConscript
      |
      +---- libs/
            +---- SDL2/
                  +---- SConscript

The bin/ directory will contain the executables generated by the build, whereas the build/ directory will contain all the object files generated by the compiler. Both of them will contain 3 subdirectories which separate the files according to the building mode (debug, profile, release) used to build the program.

The src/ directory contains all the source files and it’s divided in sub-directories to separate the different modules and libraries of the project.

In the SDL2/ directory I placed all the files extracted from the SDL2-2.0.2.tar.gz archive I downloaded from the SDL website. After extracting the files I’ve also run the autogen.sh and configure scripts contained in the package to be sure any configuration header was properly initialized.

The SConstruct file is the main script SCons is looking for to execute. Processing starts from it.

The two SConscript files are also SCons scripts, but they are used to organize hierarchical builds and are included from the main SConstruct file placed at the project root.

For your project you can decide to go for a different or more complex hierarchy, but the key concept is to have a SConstruct file in your project’s root and a SConscript file in every directory where there’s something to compile.

The SConstruct

This is the main SCons script in the project’s root:

# 3 build modes
vars = Variables()
vars.Add(EnumVariable('mode', 'Building mode', 'debug', allowed_values=('debug', 'profile', 'release')))

env = Environment(variables = vars)
Help(vars.GenerateHelpText(env))

# basic flags according to build mode
if env['mode'] == 'debug':
    env.Append(CCFLAGS = ['-Wall', '-g', '-O0', '-DDEBUG'])
elif env['mode'] == 'release':
    env.Append(CCFLAGS = ['-Wall', '-O3', '-DNDEBUG'])
    env.Append(LINKFLAGS = ['-s'])
elif env['mode'] == 'profile':
    env.Append(CCFLAGS = ['-Wall', '-pg', '-O0', '-DNDEBUG'])

env.Append(CCFLAGS = ['-DLINUX'])

# LIBS
SConscript('src/libs/SDL2/SConscript', exports = 'env', variant_dir = 'build/' + env['mode'], src_dir = 'src', duplicate = 0)

# GAME
SConscript('src/game/SConscript', exports = 'env', variant_dir = 'build/' + env['mode'], src_dir = 'src', duplicate = 0)

It allows to specify a “mode” parameter which controls the building mode (debug, profile, release).

The 2 SConscript

This is the SConscript in src/game/ :

Import('env')

print '[I] building game (' + env['mode'] + ')'

# create a local environment cloning the imported one
localEnv = env.Clone();

# sources
sources = Split(""" main.cpp """)

# flags
localEnv.Append(CCFLAGS = ['-D_REENTRANT'])
localEnv.Append(LINKFLAGS=['-Wl,--no-undefined'])

# libraries
localEnv.Append(LIBS = ['SDL2', 'pthread', 'm', 'dl', 'ts', 'pthread', 'rt'])

# paths
localEnv.Append(CPPPATH=['#/src/libs/SDL2/include'])
localEnv.Append(LIBPATH = ['../libs/SDL2'])

# build an executable
localEnv.Program('#/bin/%s/game/my_game' % localEnv['mode'], sources)

And this is the one in src/libs/SDL2/ :

Import('env')

print '[I] building SDL2 (' + env['mode'] + ')'

# create a local environment cloning the imported one
localEnv = env.Clone();

# sources
sources = Split(""" src/SDL.c
                    ......ALL THE SDL FILES HERE......
                    src/core/linux/SDL_evdev.c """)

# flags
localEnv.Append(CPPPATH=['include', '/usr/include/dbus-1.0', '/usr/lib/x86_64-linux-gnu/dbus-1.0/include'])

localEnv.Append(CCFLAGS = ['-DUSING_GENERATED_CONFIG_H', '-mmmx', '-m3dnow', '-msse', '-msse2', '-fvisibility=hidden', '-D_REENTRANT', '-DHAVE_LINUX_VERSION_H', '-fPIC', '-DPIC'])

# build a static library
localEnv.StaticLibrary(target = 'SDL2', source = sources)

 

Building the project

Building the project is pretty straightforward, go to the root directory and type:

scons -j8

This will start the build in debug mode (which is default) using 8 threads (you can change the value to any number you want).

If you wanted to build the project in a different mode you can type:

scons -j8 mode=release

As you may imagine, this will build the project in release mode.

Once the project is built you’ll find the executables under bin/debug/game/ and bin/release/game.

Project files

I created an archive containing all the files of the project you can use for reference: my_scons_project.tar.gz

It doesn’t contain the SDL2 files as they are pretty heavy, but you can download the current stable source code archive from the SDL website and add the content to the src/libs/SDL2/ directory.

Conclusion

SCons is a great tool which allows to build software in a simple and clean way, I strongly recommend to try it, especially if you’re still stuck with the ancient autotools.

Finally, thanks to Dirk Bächle which helped me fixing a bug and improving my SConscripts.

 

Leave a Comment

Your email address will not be published. Required fields are marked *