Skip to content

Commit

Permalink
Refactor Cake tasks (#4440)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
GeoffreyBooth authored Feb 18, 2017
1 parent d84c94d commit 98c1a3a
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 81 deletions.
177 changes: 118 additions & 59 deletions Cakefile
Original file line number Diff line number Diff line change
Expand Up @@ -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

# 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 = (message, color, explanation) ->
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) ->
base = options.prefix or '/usr/local'
lib = "#{base}/lib/coffee-script"
bin = "#{base}/bin"
node = "~/.node_libraries/coffee-script"
console.log "Installing CoffeeScript to #{lib}"
console.log "Linking to #{node}"
console.log "Linking 'coffee' to #{bin}/coffee"
exec([
"mkdir -p #{lib} #{bin}"
"cp -rf bin lib LICENSE README.md package.json src #{lib}"
"ln -sfn #{lib}/bin/coffee #{bin}/coffee"
"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

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]
spawnNodeProcess = (args, output = 'stderr', callback) ->
relayOutput = (buffer) -> console.log buffer.toString()
proc = spawn 'node', args
proc.stdout.on 'data', relayOutput if output is 'both' or output is 'stdout'
proc.stderr.on 'data', relayOutput if output is 'both' or output is 'stderr'
proc.on 'exit', (status) -> callback(status) if typeof callback is 'function'

unless runTests require csPath
process.exit 1
# Run a CoffeeScript through our node/coffee interpreter.
run = (args, callback) ->
spawnNodeProcess ['bin/coffee'].concat(args), 'stderr', (status) ->
process.exit(1) if status isnt 0
callback() if typeof callback is 'function'


task 'build:parser', 'rebuild the Jison parser (run build first)', ->
# Build the CoffeeScript language from source.
buildParser = ->
helpers.extend global, require 'util'
require 'jison'
parser = require('./lib/coffee-script/grammar').parser.generate()
Expand All @@ -95,8 +61,62 @@ task 'build:parser', 'rebuild the Jison parser (run build first)', ->
source = fs"""
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

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'

task 'build:browser', 'rebuild the merged script for inclusion in the browser', ->
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 = """
require['../../package.json'] = (function() {
return #{fs.readFileSync "./package.json"};
Expand Down Expand Up @@ -137,8 +157,14 @@ task 'build:browser', 'rebuild the merged script for inclusion in the browser',
console.log "built ... running browser tests:"
invoke 'test:browser'

task 'build:watch', 'watch and continually rebuild the CoffeeScript compiler, running tests on each build', ->
watchAndBuildAndTest()

task 'build:watch:harmony', 'watch and continually rebuild the CoffeeScript compiler, running harmony tests on each build', ->
watchAndBuildAndTest yes


task 'doc:site', 'watch and continually rebuild the documentation for the website', ->
buildDocs = (watch = no) ->
# Constants
indexFile = 'documentation/index.html'
versionedSourceFolder = "documentation/v#{majorVersion}"
Expand Down Expand Up @@ -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'
catch exception

for target in [indexFile, versionedSourceFolder, examplesSourceFolder, sectionsSourceFolder]
fs.watch target, interval: 200, renderIndex
log 'watching...' , green
if watch
for target in [indexFile, versionedSourceFolder, examplesSourceFolder, sectionsSourceFolder]
fs.watch target, interval: 200, renderIndex
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
testFile = 'documentation/test.html'
testsSourceFolder = 'test'
Expand Down Expand Up @@ -254,14 +287,40 @@ task 'doc:test', 'watch and continually rebuild the browser-based tests', ->
fs.writeFileSync "#{outputFolder}/test.html", output
log 'compiled', green, "#{testFile}#{outputFolder}/test.html"

for target in [testFile, testsSourceFolder]
fs.watch target, interval: 200, renderTest
log 'watching...' , green
if watch
for target in [testFile, testsSourceFolder]
fs.watch target, interval: 200, renderTest
log 'watching...', green

task 'doc:test', 'build the browser-based tests', ->
buildDocTests()

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
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 'doc:source', 'rebuild the annotated source documentation', ->
exec "node_modules/docco/bin/docco src/*.*coffee --output docs/v#{majorVersion}/annotated-source", (err) -> throw err if err

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', ->
{Rewriter} = require './lib/coffee-script/rewriter'
Expand Down
11 changes: 3 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,10 @@ CoffeeScript is a little language that compiles into JavaScript.
If you have the node package manager, npm, installed:

```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:

```shell
git clone https://github.com/jashkenas/coffeescript.git
sudo coffeescript/bin/cake install
```
Leave off the `--global` if you don’t wish to install globally.

## Getting Started

Expand All @@ -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

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

Expand Down
6 changes: 3 additions & 3 deletions docs/v1/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2584,9 +2584,9 @@ <h2>Resources</h2><ul>
<li><p><a href="http://github.com/jashkenas/coffeescript/">Source Code</a><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 build</code> to rebuild the CoffeeScript compiler, and<br>
<code>bin/cake build:parser</code> to regenerate the Jison parser if you’re working on the grammar.</p>
<p><code>git checkout lib &amp;&amp; bin/cake build:full</code> is a good command to run when you’re working on the core language. It’ll 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, there’s a good chance you’ve made a successful change.</p>
<code>bin/cake build</code> to rebuild the full CoffeeScript compiler, and<br>
<code>bin/cake build:except-parser</code> to recompile much faster if you’re 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 you’re working on the core language. It’ll 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, there’s a good chance you’ve made a successful change.</p>
</li>
<li><a href="v1/test.html">Browser Tests</a><br>
Run CoffeeScript’s test suite in your current browser.</li>
Expand Down
6 changes: 3 additions & 3 deletions documentation/sections/resources.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
* [Source Code](http://github.com/jashkenas/coffeescript/)<br>
Use `bin/coffee` to test your changes,<br>
`bin/cake test` to run the test suite,<br>
`bin/cake build` to rebuild the CoffeeScript compiler, and<br>
`bin/cake build:parser` to regenerate the Jison parser if you’re working on the grammar.
`bin/cake build` to rebuild the full CoffeeScript compiler, and<br>
`bin/cake build:except-parser` to recompile much faster if you’re not editing `grammar.coffee`.

`git checkout lib && bin/cake build:full` is a good command to run when you’re working on the core language. It’ll 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, there’s a good chance you’ve made a successful change.
`git checkout lib && bin/cake build:full` is a good command to run when you’re working on the core language. It’ll 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, there’s a good chance you’ve made a successful change.
* [Browser Tests](v<%= majorVersion %>/test.html)<br>
Run CoffeeScript’s test suite in your current browser.
* [CoffeeScript Issues](http://github.com/jashkenas/coffeescript/issues)<br>
Expand Down
15 changes: 8 additions & 7 deletions test/error_messages.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -71,20 +71,21 @@ if require?

test "#2849: compilation error in a require()d file", ->
# Create a temporary file to require().
ok not fs.existsSync 'test/syntax-error.coffee'
fs.writeFileSync 'test/syntax-error.coffee', 'foo in bar or in baz'
tempFile = path.join os.tmpdir(), 'syntax-error.coffee'
ok not fs.existsSync tempFile
fs.writeFileSync tempFile, 'foo in bar or in baz'

try
assertErrorFormat '''
require './test/syntax-error'
''',
assertErrorFormat """
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
^^
"""
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", ->
# Adapted from https://github.com/atom/coffee-cash/blob/master/spec/coffee-cash-spec.coffee
Expand Down
4 changes: 3 additions & 1 deletion test/repl.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -109,4 +109,6 @@ testRepl "keeps running after runtime error", (input, output) ->
eq 'undefined', output.lastWrite()

process.on 'exit', ->
fs.unlinkSync historyFile
try
fs.unlinkSync historyFile
catch exception # Already deleted, nothing else to do.

0 comments on commit 98c1a3a

Please sign in to comment.