Refactor Cake tasks (#4440)

* Node comes with NPM nowadays, so there’s not really a reason to install CoffeeScript the non-NPM way

* The cake documentation tasks should each have build and watch modes following the same form

* Refactor the build tasks to be more foolproof, including the parser unless it’s explicitly excluded

* Abstract out testing built code, to prepare for watching the build task

* Cake task to cut a new release

* cake build:watch, based on https://github.com/GeoffreyBooth/coffeescript-gulp

* Coding style

* Tests shouldn’t write files in a watched folder

* Don’t crash if the REPL test history file is already gone by the time we try to delete it
This commit is contained in:
Geoffrey Booth 2017-02-18 02:47:02 -05:00 committed by GitHub
parent d84c94dc6d
commit 98c1a3a045
6 changed files with 139 additions and 82 deletions

165
Cakefile
View File

@ -24,65 +24,31 @@ header = """
*/ */
""" """
# Used in folder names like docs/v1 # Used in folder names like `docs/v1`.
majorVersion = parseInt CoffeeScript.VERSION.split('.')[0], 10 majorVersion = parseInt CoffeeScript.VERSION.split('.')[0], 10
# Build the CoffeeScript language from source.
build = (cb) ->
files = fs.readdirSync 'src'
files = ('src/' + file for file in files when file.match(/\.(lit)?coffee$/))
run ['-c', '-o', 'lib/coffee-script'].concat(files), cb
# Run a CoffeeScript through our node/coffee interpreter.
run = (args, cb) ->
proc = spawn 'node', ['bin/coffee'].concat(args)
proc.stderr.on 'data', (buffer) -> console.log buffer.toString()
proc.on 'exit', (status) ->
process.exit(1) if status isnt 0
cb() if typeof cb is 'function'
# Log a message with a color. # Log a message with a color.
log = (message, color, explanation) -> log = (message, color, explanation) ->
console.log color + message + reset + ' ' + (explanation or '') console.log color + message + reset + ' ' + (explanation or '')
option '-p', '--prefix [DIR]', 'set the installation prefix for `cake install`'
task 'install', 'install CoffeeScript into /usr/local (or --prefix)', (options) -> spawnNodeProcess = (args, output = 'stderr', callback) ->
base = options.prefix or '/usr/local' relayOutput = (buffer) -> console.log buffer.toString()
lib = "#{base}/lib/coffee-script" proc = spawn 'node', args
bin = "#{base}/bin" proc.stdout.on 'data', relayOutput if output is 'both' or output is 'stdout'
node = "~/.node_libraries/coffee-script" proc.stderr.on 'data', relayOutput if output is 'both' or output is 'stderr'
console.log "Installing CoffeeScript to #{lib}" proc.on 'exit', (status) -> callback(status) if typeof callback is 'function'
console.log "Linking to #{node}"
console.log "Linking 'coffee' to #{bin}/coffee" # Run a CoffeeScript through our node/coffee interpreter.
exec([ run = (args, callback) ->
"mkdir -p #{lib} #{bin}" spawnNodeProcess ['bin/coffee'].concat(args), 'stderr', (status) ->
"cp -rf bin lib LICENSE README.md package.json src #{lib}" process.exit(1) if status isnt 0
"ln -sfn #{lib}/bin/coffee #{bin}/coffee" callback() if typeof callback is 'function'
"ln -sfn #{lib}/bin/cake #{bin}/cake"
"mkdir -p ~/.node_libraries"
"ln -sfn #{lib}/lib/coffee-script #{node}"
].join(' && '), (err, stdout, stderr) ->
if err then console.log stderr.trim() else log 'done', green
)
task 'build', 'build the CoffeeScript language from source', build # Build the CoffeeScript language from source.
buildParser = ->
task 'build:full', 'rebuild the source twice, and run the tests', ->
build ->
build ->
csPath = './lib/coffee-script'
csDir = path.dirname require.resolve csPath
for mod of require.cache when csDir is mod[0 ... csDir.length]
delete require.cache[mod]
unless runTests require csPath
process.exit 1
task 'build:parser', 'rebuild the Jison parser (run build first)', ->
helpers.extend global, require 'util' helpers.extend global, require 'util'
require 'jison' require 'jison'
parser = require('./lib/coffee-script/grammar').parser.generate() parser = require('./lib/coffee-script/grammar').parser.generate()
@ -95,8 +61,62 @@ task 'build:parser', 'rebuild the Jison parser (run build first)', ->
source = fs""" source = fs"""
fs.writeFileSync 'lib/coffee-script/parser.js', parser fs.writeFileSync 'lib/coffee-script/parser.js', parser
buildExceptParser = (callback) ->
files = fs.readdirSync 'src'
files = ('src/' + file for file in files when file.match(/\.(lit)?coffee$/))
run ['-c', '-o', 'lib/coffee-script'].concat(files), callback
task 'build:browser', 'rebuild the merged script for inclusion in the browser', -> build = (callback) ->
buildParser()
buildExceptParser callback
testBuiltCode = (watch = no) ->
csPath = './lib/coffee-script'
csDir = path.dirname require.resolve csPath
for mod of require.cache when csDir is mod[0 ... csDir.length]
delete require.cache[mod]
testResults = runTests require csPath
unless watch
process.exit 1 unless testResults
buildAndTest = (includingParser = yes, harmony = no) ->
process.stdout.write '\x1Bc' # Clear terminal screen.
execSync 'git checkout lib/*', stdio: [0,1,2] # Reset the generated compiler.
buildArgs = ['bin/cake']
buildArgs.push if includingParser then 'build' else 'build:except-parser'
log "building#{if includingParser then ', including parser' else ''}...", green
spawnNodeProcess buildArgs, 'both', ->
log 'testing...', green
testArgs = if harmony then ['--harmony'] else []
testArgs = testArgs.concat ['bin/cake', 'test']
spawnNodeProcess testArgs, 'both'
watchAndBuildAndTest = (harmony = no) ->
buildAndTest yes, harmony
fs.watch 'src/', interval: 200, (eventType, filename) ->
if eventType is 'change'
log "src/#{filename} changed, rebuilding..."
buildAndTest (filename is 'grammar.coffee'), harmony
fs.watch 'test/', {interval: 200, recursive: yes}, (eventType, filename) ->
if eventType is 'change'
log "test/#{filename} changed, rebuilding..."
buildAndTest no, harmony
task 'build', 'build the CoffeeScript compiler from source', build
task 'build:parser', 'build the Jison parser only', buildParser
task 'build:except-parser', 'build the CoffeeScript compiler, except for the Jison parser', buildExceptParser
task 'build:full', 'build the CoffeeScript compiler from source twice, and run the tests', ->
build ->
build testBuiltCode
task 'build:browser', 'build the merged script for inclusion in the browser', ->
code = """ code = """
require['../../package.json'] = (function() { require['../../package.json'] = (function() {
return #{fs.readFileSync "./package.json"}; return #{fs.readFileSync "./package.json"};
@ -137,8 +157,14 @@ task 'build:browser', 'rebuild the merged script for inclusion in the browser',
console.log "built ... running browser tests:" console.log "built ... running browser tests:"
invoke 'test:browser' invoke 'test:browser'
task 'build:watch', 'watch and continually rebuild the CoffeeScript compiler, running tests on each build', ->
watchAndBuildAndTest()
task 'doc:site', 'watch and continually rebuild the documentation for the website', -> task 'build:watch:harmony', 'watch and continually rebuild the CoffeeScript compiler, running harmony tests on each build', ->
watchAndBuildAndTest yes
buildDocs = (watch = no) ->
# Constants # Constants
indexFile = 'documentation/index.html' indexFile = 'documentation/index.html'
versionedSourceFolder = "documentation/v#{majorVersion}" versionedSourceFolder = "documentation/v#{majorVersion}"
@ -211,12 +237,19 @@ task 'doc:site', 'watch and continually rebuild the documentation for the websit
fs.symlinkSync "v#{majorVersion}/index.html", 'docs/index.html' fs.symlinkSync "v#{majorVersion}/index.html", 'docs/index.html'
catch exception catch exception
if watch
for target in [indexFile, versionedSourceFolder, examplesSourceFolder, sectionsSourceFolder] for target in [indexFile, versionedSourceFolder, examplesSourceFolder, sectionsSourceFolder]
fs.watch target, interval: 200, renderIndex fs.watch target, interval: 200, renderIndex
log 'watching...', green log 'watching...', green
task 'doc:site', 'build the documentation for the website', ->
buildDocs()
task 'doc:test', 'watch and continually rebuild the browser-based tests', -> task 'doc:site:watch', 'watch and continually rebuild the documentation for the website', ->
buildDocs yes
buildDocTests = (watch = no) ->
# Constants # Constants
testFile = 'documentation/test.html' testFile = 'documentation/test.html'
testsSourceFolder = 'test' testsSourceFolder = 'test'
@ -254,14 +287,40 @@ task 'doc:test', 'watch and continually rebuild the browser-based tests', ->
fs.writeFileSync "#{outputFolder}/test.html", output fs.writeFileSync "#{outputFolder}/test.html", output
log 'compiled', green, "#{testFile} → #{outputFolder}/test.html" log 'compiled', green, "#{testFile} → #{outputFolder}/test.html"
if watch
for target in [testFile, testsSourceFolder] for target in [testFile, testsSourceFolder]
fs.watch target, interval: 200, renderTest fs.watch target, interval: 200, renderTest
log 'watching...', green log 'watching...', green
task 'doc:test', 'build the browser-based tests', ->
buildDocTests()
task 'doc:source', 'rebuild the annotated source documentation', -> task 'doc:test:watch', 'watch and continually rebuild the browser-based tests', ->
buildDocTests yes
buildAnnotatedSource = (watch = no) ->
do generateAnnotatedSource = ->
exec "node_modules/docco/bin/docco src/*.*coffee --output docs/v#{majorVersion}/annotated-source", (err) -> throw err if err exec "node_modules/docco/bin/docco src/*.*coffee --output docs/v#{majorVersion}/annotated-source", (err) -> throw err if err
log 'generated', green, "annotated source in docs/v#{majorVersion}/annotated-source/"
if watch
fs.watch 'src/', interval: 200, generateAnnotatedSource
log 'watching...', green
task 'doc:source', 'build the annotated source documentation', ->
buildAnnotatedSource()
task 'doc:source:watch', 'watch and continually rebuild the annotated source documentation', ->
buildAnnotatedSource yes
task 'release', 'build and test the CoffeeScript source, and build the documentation', ->
invoke 'build:full'
invoke 'build:browser'
invoke 'doc:site'
invoke 'doc:test'
invoke 'doc:source'
task 'bench', 'quick benchmark of compilation time', -> task 'bench', 'quick benchmark of compilation time', ->
{Rewriter} = require './lib/coffee-script/rewriter' {Rewriter} = require './lib/coffee-script/rewriter'

View File

@ -25,15 +25,10 @@ CoffeeScript is a little language that compiles into JavaScript.
If you have the node package manager, npm, installed: If you have the node package manager, npm, installed:
```shell ```shell
npm install -g coffee-script npm install --global coffee-script
``` ```
Leave off the `-g` if you don't wish to install globally. If you don't wish to use npm: Leave off the `--global` if you dont wish to install globally.
```shell
git clone https://github.com/jashkenas/coffeescript.git
sudo coffeescript/bin/cake install
```
## Getting Started ## Getting Started
@ -53,7 +48,7 @@ For documentation, usage, and examples, see: http://coffeescript.org/
To suggest a feature or report a bug: http://github.com/jashkenas/coffeescript/issues To suggest a feature or report a bug: http://github.com/jashkenas/coffeescript/issues
If you'd like to chat, drop by #coffeescript on Freenode IRC. If youd like to chat, drop by #coffeescript on Freenode IRC.
The source repository: https://github.com/jashkenas/coffeescript.git The source repository: https://github.com/jashkenas/coffeescript.git

View File

@ -2584,9 +2584,9 @@ task(<span class="string">'build:parser'</span>, <span class="string">'rebuild t
<li><p><a href="http://github.com/jashkenas/coffeescript/">Source Code</a><br> <li><p><a href="http://github.com/jashkenas/coffeescript/">Source Code</a><br>
Use <code>bin/coffee</code> to test your changes,<br> Use <code>bin/coffee</code> to test your changes,<br>
<code>bin/cake test</code> to run the test suite,<br> <code>bin/cake test</code> to run the test suite,<br>
<code>bin/cake build</code> to rebuild the CoffeeScript compiler, and<br> <code>bin/cake build</code> to rebuild the full CoffeeScript compiler, and<br>
<code>bin/cake build:parser</code> to regenerate the Jison parser if youre working on the grammar.</p> <code>bin/cake build:except-parser</code> to recompile much faster if youre not editing <code>grammar.coffee</code>.</p>
<p><code>git checkout lib &amp;&amp; bin/cake build:full</code> is a good command to run when youre working on the core language. Itll refresh the lib directory (in case you broke something), build your altered compiler, use that to rebuild itself (a good sanity test) and then run all of the tests. If they pass, theres a good chance youve made a successful change.</p> <p><code>git checkout lib &amp;&amp; bin/cake build:full</code> is a good command to run when youre working on the core language. Itll refresh the <code>lib</code> folder (in case you broke something), build your altered compiler, use that to rebuild itself (a good sanity test) and then run all of the tests. If they pass, theres a good chance youve made a successful change.</p>
</li> </li>
<li><a href="v1/test.html">Browser Tests</a><br> <li><a href="v1/test.html">Browser Tests</a><br>
Run CoffeeScripts test suite in your current browser.</li> Run CoffeeScripts test suite in your current browser.</li>

View File

@ -3,10 +3,10 @@
* [Source Code](http://github.com/jashkenas/coffeescript/)<br> * [Source Code](http://github.com/jashkenas/coffeescript/)<br>
Use `bin/coffee` to test your changes,<br> Use `bin/coffee` to test your changes,<br>
`bin/cake test` to run the test suite,<br> `bin/cake test` to run the test suite,<br>
`bin/cake build` to rebuild the CoffeeScript compiler, and<br> `bin/cake build` to rebuild the full CoffeeScript compiler, and<br>
`bin/cake build:parser` to regenerate the Jison parser if youre working on the grammar. `bin/cake build:except-parser` to recompile much faster if youre not editing `grammar.coffee`.
`git checkout lib && bin/cake build:full` is a good command to run when youre working on the core language. Itll refresh the lib directory (in case you broke something), build your altered compiler, use that to rebuild itself (a good sanity test) and then run all of the tests. If they pass, theres a good chance youve made a successful change. `git checkout lib && bin/cake build:full` is a good command to run when youre working on the core language. Itll refresh the `lib` folder (in case you broke something), build your altered compiler, use that to rebuild itself (a good sanity test) and then run all of the tests. If they pass, theres a good chance youve made a successful change.
* [Browser Tests](v<%= majorVersion %>/test.html)<br> * [Browser Tests](v<%= majorVersion %>/test.html)<br>
Run CoffeeScripts test suite in your current browser. Run CoffeeScripts test suite in your current browser.
* [CoffeeScript Issues](http://github.com/jashkenas/coffeescript/issues)<br> * [CoffeeScript Issues](http://github.com/jashkenas/coffeescript/issues)<br>

View File

@ -71,20 +71,21 @@ if require?
test "#2849: compilation error in a require()d file", -> test "#2849: compilation error in a require()d file", ->
# Create a temporary file to require(). # Create a temporary file to require().
ok not fs.existsSync 'test/syntax-error.coffee' tempFile = path.join os.tmpdir(), 'syntax-error.coffee'
fs.writeFileSync 'test/syntax-error.coffee', 'foo in bar or in baz' ok not fs.existsSync tempFile
fs.writeFileSync tempFile, 'foo in bar or in baz'
try try
assertErrorFormat ''' assertErrorFormat """
require './test/syntax-error' require '#{tempFile}'
''', """,
""" """
#{path.join __dirname, 'syntax-error.coffee'}:1:15: error: unexpected in #{fs.realpathSync tempFile}:1:15: error: unexpected in
foo in bar or in baz foo in bar or in baz
^^ ^^
""" """
finally finally
fs.unlinkSync 'test/syntax-error.coffee' fs.unlinkSync tempFile
test "#3890 Error.prepareStackTrace doesn't throw an error if a compiled file is deleted", -> test "#3890 Error.prepareStackTrace doesn't throw an error if a compiled file is deleted", ->
# Adapted from https://github.com/atom/coffee-cash/blob/master/spec/coffee-cash-spec.coffee # Adapted from https://github.com/atom/coffee-cash/blob/master/spec/coffee-cash-spec.coffee

View File

@ -109,4 +109,6 @@ testRepl "keeps running after runtime error", (input, output) ->
eq 'undefined', output.lastWrite() eq 'undefined', output.lastWrite()
process.on 'exit', -> process.on 'exit', ->
try
fs.unlinkSync historyFile fs.unlinkSync historyFile
catch exception # Already deleted, nothing else to do.