/src/cake.coffee
CoffeeScript | 88 lines | 51 code | 12 blank | 25 comment | 7 complexity | e722c6667fddfa73f29ca27743df2510 MD5 | raw file
1# `cake` is a simplified version of [Make](http://www.gnu.org/software/make/) 2# ([Rake](http://rake.rubyforge.org/), [Jake](https://github.com/280north/jake)) 3# for CoffeeScript. You define tasks with names and descriptions in a Cakefile, 4# and can call them from the command line, or invoke them from other tasks. 5# 6# Running `cake` with no arguments will print out a list of all the tasks in the 7# current directory's Cakefile. 8 9# External dependencies. 10fs = require 'fs' 11path = require 'path' 12helpers = require './helpers' 13optparse = require './optparse' 14CoffeeScript = require './' 15 16# Register .coffee extension 17CoffeeScript.register() 18 19# Keep track of the list of defined tasks, the accepted options, and so on. 20tasks = {} 21options = {} 22switches = [] 23oparse = null 24 25# Mixin the top-level Cake functions for Cakefiles to use directly. 26helpers.extend global, 27 28 # Define a Cake task with a short name, an optional sentence description, 29 # and the function to run as the action itself. 30 task: (name, description, action) -> 31 [action, description] = [description, action] unless action 32 tasks[name] = {name, description, action} 33 34 # Define an option that the Cakefile accepts. The parsed options hash, 35 # containing all of the command-line options passed, will be made available 36 # as the first argument to the action. 37 option: (letter, flag, description) -> 38 switches.push [letter, flag, description] 39 40 # Invoke another task in the current Cakefile. 41 invoke: (name) -> 42 missingTask name unless tasks[name] 43 tasks[name].action options 44 45# Run `cake`. Executes all of the tasks you pass, in order. Note that Node's 46# asynchrony may cause tasks to execute in a different order than you'd expect. 47# If no tasks are passed, print the help screen. Keep a reference to the 48# original directory name, when running Cake tasks from subdirectories. 49exports.run = -> 50 global.__originalDirname = fs.realpathSync '.' 51 process.chdir cakefileDirectory __originalDirname 52 args = process.argv[2..] 53 CoffeeScript.run fs.readFileSync('Cakefile').toString(), filename: 'Cakefile' 54 oparse = new optparse.OptionParser switches 55 return printTasks() unless args.length 56 try 57 options = oparse.parse(args) 58 catch e 59 return fatalError "#{e}" 60 invoke arg for arg in options.arguments 61 62# Display the list of Cake tasks in a format similar to `rake -T` 63printTasks = -> 64 relative = path.relative or path.resolve 65 cakefilePath = path.join relative(__originalDirname, process.cwd()), 'Cakefile' 66 console.log "#{cakefilePath} defines the following tasks:\n" 67 for name, task of tasks 68 spaces = 20 - name.length 69 spaces = if spaces > 0 then Array(spaces + 1).join(' ') else '' 70 desc = if task.description then "# #{task.description}" else '' 71 console.log "cake #{name}#{spaces} #{desc}" 72 console.log oparse.help() if switches.length 73 74# Print an error and exit when attempting to use an invalid task/option. 75fatalError = (message) -> 76 console.error message + '\n' 77 console.log 'To see a list of all tasks/options, run "cake"' 78 process.exit 1 79 80missingTask = (task) -> fatalError "No such task: #{task}" 81 82# When `cake` is invoked, search in the current and all parent directories 83# to find the relevant Cakefile. 84cakefileDirectory = (dir) -> 85 return dir if fs.existsSync path.join dir, 'Cakefile' 86 parent = path.normalize path.join dir, '..' 87 return cakefileDirectory parent unless parent is dir 88 throw new Error "Cakefile not found in #{process.cwd()}"