PageRenderTime 25ms CodeModel.GetById 14ms app.highlight 7ms RepoModel.GetById 1ms app.codeStats 1ms

/src/cake.coffee

http://github.com/jashkenas/coffee-script
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()}"