Monday, February 18, 2013

Replacing Make with SCons

After a long hiatus, I decided to start once again playing around with building a game engine in C++ on my Mac. I've been reading the very enjoyable Game Engine Architecture by Jason Gregory, and it's really opened my eyes to the number of distinct modules that go into even a hobby-developer game. In particular, I hadn't appreciated how many executables a game might have that aren't the game itself, e.g., map builders, scene debuggers, asset viewers, and servers. Many of these are going to share a lot of code, and so it seems very important to have a modular source tree and build system. (This is, of course, true for essentially any large project, game or not.) I've been using Makefiles for my build management for a long time - being an Emacs/Terminal type of software developer. The only exceptions are that I tend to use Ant when I'm writing Java, and I use Visual Studio when I have to write C#. I even use Makefiles for LaTeX.

Highly modular projects tend to have multiple source directories, and these tend to require complex recursive Makefiles that are not usually very efficient. In browsing StackOverflow, I've noticed several references to the tool SCons. I installed it via MacPorts and thought I'd check it out. The SCons Wiki has a list of reasons why you might prefer SCons to Make. Here is what I did to try it out on a tiny little stub of a project with dependencies across directories:

project/
  util/

    Logger.cc
    Logger.hh
  lua/
    Lua.cc
    Lua.hh
  client/
    ClientMain.cc
  server/
    ServerMain.cc

Honestly, the introductory documentation on the wiki for SCons is pretty poor.  Despite all the statements about it being scalable for large projects, the tutorial examples are for situations where you'd hardly even need a Makefile: the build system is just running one or two commands.  The user guide gradually reveals the more important details.

The basic situation is this: some of these subdirectories are library modules (here, "util" and "lua") and some are executables ("client" and "server").  What I want to happen is for "util" and "lua" to compile to "libutil.a" and "liblua.a", respectively.  Now, "client" depends on both "util" and "lua", but "server" only depends on "util".  The question is, how do I tell the SCons hierarchy to look for a set of other modules, without hard-coding them?  In browsing through the many StackOverflow questions about SCons, I came across this one, which made it seem easy for some reason.  However, it isn't quite that easy, as it's still necessary to tell it about the dependencies via Python string slinging.

project/SConstruct

project/util/SConscript

project/client/SConscript

project/server/SConscript There is almost certainly a better way to do this. The main SConstruct file first makes sure that everyone has the correct include directories. Then it loops over all of the modules. Each module specifies what other modules it requires and constructs an appropriate LIBS and LIBPATH to get them.

No comments:

Post a Comment