VexFlow has gone through many iterations of design, implementation, and deployment, and I couldn't have brought it this far so quickly without my tools. (Well, automated testing had a lot to do with it too, but that's for another post.)
Yesterday, I added a new tool to my toolbox - SCons. I migrated all my building, packaging, test driving, and deployment code to SCons. Compared to the ugly shell scripts I previously used, SCons is a lot cleaner, a lot faster, and significantly easier to manage.
I chose SCons for two reasons:
- Simplicity. It's Python-based and super-easy to work with.
- Familiarity. I've used it before, so already know my way around it.
Since I use the Google Closure Compiler to build and minimize my JavaScript code, I had to write a new builder for SCons. That turned out to be pretty straightforward to implement.
def js_builder(target, source, env): """ A JavaScript builder using Google Closure Compiler. """ cmd = env.subst( "$JAVA -jar $JS_COMPILER --compilation_level $JS_COMPILATION_LEVEL"); # Add defines to the command for define in env['JS_DEFINES'].keys(): cmd += " --define=\"%s=%s\"" % (define, env['JS_DEFINES'][define]) # Add the source files for file in source: cmd += " --js " + str(file) # Add the output file cmd += " --js_output_file " + str(target[0]) # Log the command and run print env.subst(cmd) os.system(env.subst(cmd))
I also needed a new builder to stamp my output with the relevant build information. So, I created a Stamper, which is just a builder that runs some string substitution on files with sed. The stamper looks like this:
def vexflow_stamper(target, source, env): """ A Build Stamper for VexFlow """ cmd = "sed " cmd += " -e s/__VEX_BUILD_PREFIX__/$VEX_BUILD_PREFIX/" cmd += " -e s/__VEX_VERSION__/$VEX_VERSION/" cmd += ' -e "s/__VEX_BUILD_DATE__/${VEX_BUILD_DATE}/"' cmd += " -e s/__VEX_GIT_SHA1__/`git rev-list --max-count=1 HEAD`/ " cmd += ("%s > %s" % (source[0], target[0])) print env.subst(cmd) os.system(env.subst(cmd))
Before you can use these builders, you need to add them to your environment:
env.Append(BUILDERS = {'JavaScript': Builder(action = js_builder), 'VexFlowStamp': Builder(action = vexflow_stamper)})
Once this is done, you can add build JavaScript targets with the JavaScript command.
env['JAVA'] = "/usr/bin/java" env['JS_COMPILER'] = "support/compiler.jar" env['JS_DEFINES' ] = { "Vex.Debug": "true", "Vex.LogLevel": "4" } sources = ["src1.js", "src2.js", "src3.js"] env.JavaScript("src.min.js", sources)
This really is just scratching the surface. There's a lot more you can do with SCons to automate and streamline your builds. To learn more, take a look at the user guide.
I added support for testing, packaging, and deployment (of the web pages and demos) to my SCons scripts in a matter of hours, and finally purged all my nasty shell scripts from the VexFlow codebase.
Give it a try. I guarantee you'll be happier.
Have you taken a look at waf?
ReplyDeleteI use Apache Ant. :)
ReplyDeleteI try to build vexflow with scons, on windows it's possible ?
ReplyDeleteI have som many warnings...
'No installed VCs' ...