The choice fell on FinalBuilder, a comprehensive, graphical build automation utility sporting loads of predefined actions that can be arranged using drag and drop.

After the initial excitement had faded and we had converted all our build scripts to FinalBuilder projects, we realized that we had ended up with something that had a nice GUI, but was far from perfect. For instance, FinalBuilder uses a proprietary XML based file format for its project files and tracking changes in large XML documents is… hard. Managing control flow using drag and drop is… weird. And as a rookie in the team, learning a new tool only used for one specific purpose seems… unnecessary.

So after living with FinalBuilder for a few years we finally took the plunge and converted everything once more, but this time to our new favorite scripting language – Python! Our builds aren’t really that complicated and are usually just a sequence of some of the following actions:

  • Compile projects in a Visual Studio solution (.sln)
  • Add some digital signatures to binaries and .msi packages
  • Compress, package and move files around
  • Update various files with e.g. new version numbers

For one of our product builds we went from an XML document having more than 18,000 lines into two Python scripts totaling around 450 lines of code out of which more than half can be re-used!

While at it, we added some nice build progress start/stop messages for TeamCity (our CI system) so that it says “Currently building component X” and makes messages end up in a neat, collapsible block in the build log (more about build script interaction using TeamCity here). Python function decorators makes this really easy to write:

@log('Building Lime CRM desktop client')
def build_desktop_client(dest_dir, sign, copy_symbols, build_help_files):
print('Building Lime.sln')
msbuild('Lime.sln', 'Release')[...]

Here’s the decorator:

def log(message):
def wrap(f):
def wrapped_f(*args, **kwargs):
print("##teamcity[blockOpened name='{}']".format(message))
ret = f(*args, **kwargs)
print("##teamcity[blockClosed name='{}']".format(message))
return ret
return wrapped_f
return wrap

So nowadays there is no excuse for anyone in the team not to help out with build automation! One less tool to learn, way much simpler scripting (IMHO) and definitely way more flexible and powerful!