scons

Good-bye SCons, Hello CMake

Last year, I wrote of my initial impressions of SCons for controlling software builds. My initial impressions were positive, but even then I was wary of performance issues.

A few months later, I wrote about a performance problem with SCons and MSVC. I was able to hack SCons to make things a little better.

Things weren't bad for me, because I've been doing all my development on Linux, where SCons is pretty well-behaved. But the Windows developers hated it. SCons will build MSVC project files so that developers can edit and browse code through the IDE, but the builds are still controlled by SCons, and SCons was painfully slow on Windows. As the codebase grew, SCons got slower and slower.

The boss put up with it for a while, but he finally decided that enough was enough. I was ordered to find something better than SCons for our cross-platform builds.

We weren't the only people dissatisfied with SCons. The KDE team had tried SCons, found it lacking, then started their own Python-based build system based on SCons, which eventually became Waf. I looked at Waf briefly, but the immaturity of the project and lack of documentation turned me off.

I read that the autotools system was starting to provide better support for Windows, but I didn't think that solution would go over well with the team members and leaders who passionately hate things that are too UNIX-ish.

So, after reading that the KDE team finally settled on CMake, I decided to give that a try.

I've spent the last couple of days translating build scripts from SCons into CMake. So far, I'm pretty pleased with the results.

Pros of CMake over SCons:

  • It generates real honest-to-goodness MSVC solution and project files that work as well as or better than those that Windows developers would create by hand. The CMake developers don't treat Windows developers as second-class citizens.
  • The default compiler settings in the generated MSVC files and Makefiles are remarkably sane.
  • It has lots of functionality built in. (In contrast, SCons often required lots of code to be written to do simple things.)
  • It provides a simple mechanism for handling unit tests.
  • Simpler support for hierarchical builds.
  • It has the feel of something that has been used for real-world work. (In contrast, SCons always felt like a grad student's summer project.)
  • I don't have to go take a coffee break every time I need to do a build.

Cons:

  • I don't like CMake's syntax. It's like they took the syntaxes of Make, Perl, Bourne shell, and BASIC, and mixed them all together. (Please, people, stop inventing your own application-specific scripting languages! Especially if you are going to invent one that sucks.)
  • Online documentation is poor. You have to buy a $50 book if you want to figure things out in a reasonable amount of time.
  • While it is cross-platform, you still have to write a lot of "IF( WINDOWS ) ... ELSE ..." code.
  • It has no built-in support for precompiled headers. (But then again, neither did SCons. As with SCons, you can use precompiled headers by writing some code.).

I'm happy with the switch to CMake, and I'm sure the boss will be too. But who knows; maybe next year I'll be writing yet another blog entry about the need to adopt a new build system.

Improving SCons Performance for MSVC8

The developers of SCons don't seem to be very interested in this, but I've found a way to dramatically speed up SCons builds for MSVC8 (Visual Studio 2005's C++ compiler).

We've got a fairly big codebase with a few levels. It was taking over a minute to read all the SConstruct/SConscript files, even when there was nothing to do.

I ran the profiler, and found that the bulk of the time was in minidom.py and expatbuilder.py. This was surprising, because I didn't think SCons used XML.

Searching further, it turns out that for MSVC8, to determine library and include paths SCons opens a registry key which contains XML, and parses it. For our codebase, it was doing this about 300 times per build.

So, I hacked up my personal copy of SCons/Tool/msvc.py, and now instead of over a minute, it only takes 20 seconds. I don't consider this a "patch", because I don't really know much about SCons internals, and so this could be totally wrong, but maybe someone can figure out the right way to do what I have done and get it into CVS.

The idea is to cache the results of _get_msvc8_path, so that the XML parsing doesn't happen every time. I added a global variable to msvc.py, containing an empty dictionary:

# START NEW CODE
# KDJ: cache results of _get_msvc8_path in a dictionary
cached_msvc8_path = {}
# END NEW CODE

Then, I changed a few lines in get_msvc_path as follows:

    if version_num >= 8.0:
        # ORIGINAL: return _get_msvc8_path(path, str(version_num), platform, suite)
        # START NEW CODE
        global cached_msvc8_path
        if not cached_msvc8_path.has_key(path):
            cached_msvc8_path[path] = _get_msvc8_path(path, str(version_num), platform, suite)
        return cached_msvc8_path[path]
        # END NEW CODE
    elif version_num >= 7.0:
        return _get_msvc7_path(path, str(version_num), platform)
Syndicate content