Build automation / task runners

GNU Make

DSL for compiling files

-- Makefile
hello: hello.c
$ make hello
cc -o hello hello.c

$ make hello
make: 'hello' er tidssvarende

GNU Make

Use file dependencies and modification time

# Makefile
hello: hello.c
# Catch system calls from process
$ strace -e stat "make hello"
...
stat("hello", {st_mode=S_IFREG|0775, st_size=8559, ...}) = 0
stat("hello.c", {st_mode=S_IFREG|0664, st_size=61, ...}) = 0
...
make: 'hello' er tidssvarende.

GNU Make

Rules

# Makefile
fisk.js: fisk.coffee

%.js: %.coffee
  coffee < $< > $@
  

GNU Make

JS_FINAL = js/project-name-all.js
JS_TARGETS = $(shell find js -name "*.js")
JS_MINIFIED = $(JS_TARGETS:.js=.min.js)

all: $(JS_FINAL)

# Concat
$(JS_FINAL): $(JS_MINIFIED)
  cat $^ >$@
  rm -f $^
  
%.min.js: %.js
  uglifyjs -o $@ $<
  echo >> $@
  
clean:
  rm -f $(JS_FINAL)
  

Score

  • + ubiquitous
  • + dependency flow/declarative = brain candy
  • + file dependencies
  • +/- DSL
  • - windows
  • - frills: log, watch, etc.
  • - intermediate/temporary files (more names!)

Actually build system, but great project "menu" and tool abstraction layer

[Why use make]
# Makefile
.PHONY: migrate js

migrate:
  php artisan migrate --env=development
  
js:
  # grunt build
  gulp
  
$ make migrate

$ make js

Could also use npm/composer "scripts"

$ composer run-script migrate

Many build systems

At least one pr language

  • Rake
  • Cake
  • Shake
  • pmake
  • nmake
  • cmake
  • scons

phing, what?


    
        
        
    
    
        
        
        
        
        
        
        
    
    
        
        
            
                
            
        
        
    

New

redo, fabricate.py, Tup, ninja, shake

Aim for make, with virtual fs, etc.

Grunt, so 2013

The JavaScript task runner

Tasks only, no dependencies

Lots of plugins, though

coffee, less, stylus, jade...

// JavaScript
module.exports = function(grunt) {
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    concat: {
      options: {
        separator: ';'
      },
      dist: {
        src: ['src/**/*.js'],
        dest: 'dist/<%= pkg.name %>.js'
      }
    },
    uglify: {
      options: {
        banner: '/*! <%= pkg.name %> <%= grunt.template.today("dd-mm-yyyy") %> */\n'
      },
      dist: {
        files: {
          'dist/<%= pkg.name %>.min.js': ['<%= concat.dist.dest %>']
        }
      }
    },
    
-> Educas.dk

Score

  • + General purpose language
  • + Logging
  • + Watch*
  • - Not DSL
  • - No knowledge of dependencies
  • - Manual incremental builds (everything manual)

Gulp js

The streaming build system

Hello gulp

var gulp = require("gulp"),
  jade = require("gulp-jade");
  
gulp.task("default", function () {
  gulp.src("app/**/*.jade")
    .pipe(jade())
    .pipe(gulp.dest("public/"));
});

Parts

  • orchestrator
  • node streams
  • gaze
  • vinyl-fs

orchestrator

Concurrent tasks

// takes in a callback so the engine knows when it'll be done
orchestrator.add('one', function (cb) {
    // do stuff -- async or otherwise
    cb(err);
});

// identifies a dependent task must be complete before this one begins
orchestrator.add('two', ['one'], function () {
    // task 'one' is done now
});

orchestrator.start('one', 'two');

node streams

# .on('something', ...) "EventEmitter"
readable.on('data', function(chunk) {
  console.log('got %d bytes of data', chunk.length);
})
readable.on('end', function() {
  console.log('there will be no more data.');
});

# Readable stream -> writeable stream

Gaze

File watching (wraps fs.watch)

gulp.watch("app/**/*.coffee", function () {...});

vinyl-fs

Virtual files on streams

new File({
  cwd: "/home/tbh/educas-dk/",
  base: "/app/",
  path: "/app/file.coffee"
  contents: new Buffer("test = 123")
});

Score

  • + No temporary files
  • + pipeline-oriented
  • + logging
  • + watch
  • - incremental builds* (gulp-watch?)

Bonus!

Assets

Rails-assets.org

# Gemfile
source 'https://rails-assets.org'
gem 'rails'

gem 'rails-assets'
gem 'rails-assets-angular'
gem 'rails-assets-bootstrap'
// application.js
//= require angular

angular.module(...)
$ bundle install
$ rails server