I would like to make a Cakefile task to watch some CoffeeScript files just like if I had run coffee -c -w js/*.coffee.
Its watching and recompiling them successfully, but it doesn't log the usual output to the terminal when there's a compile error like it would if I just ran the script from the terminal. Any idea how to make this happen?
exec = require('child_process').exec
task 'watch','watch all files and compile them as needed', (options) ->
exec 'coffee -c -w js/*.coffee', (err,stdout, stderr) ->
console.log stdout
Also, if there's a better way to invoke a coffeescript command from a cakefile than running 'exec' please post that too.
spawn instead of exec?
{spawn} = require 'child_process'
task 'watch', -> spawn 'coffee', ['-cw', 'js'], customFds: [0..2]
I've used spawn to solve this, here is an example cake file:
{spawn, exec} = require 'child_process'
option '-p', '--prefix [DIR]', 'set the installation prefix for `cake install`'
task 'build', 'continually build with --watch', ->
coffee = spawn 'coffee', ['-cw', '-o', 'lib', 'src']
coffee.stdout.on 'data', (data) -> console.log data.toString().trim()
You can see it in action with the docco project:
https://github.com/jashkenas/docco/blob/master/Cakefile
The problem with your original code was that exec only calls its callback once—after the child process has terminated. (The Node docs aren't so clear on this.) So instead of defining that callback, you should instead try
child = exec 'coffee -c -w js/*.coffee'
child.stdout.on 'data', (data) -> sys.print data
Let me know if that works for you.
Related
I have a rakefile that executes some (but not all) of it's file tasks even if the files of interest have already been built. The frustrating thing, is that paring down my rake file to a MWE resolves the problem---even though I haven't altered anything wrt the filetask definition, how the files are being selected, the dependencies, or anything else. It seems that simply removing other (file)tasks from the rakefile remedies the problem.
I realize this is a really awful question, but does anyone have ideas about what might be going on here? I'd post sample code, but my MWE works as expected and I don't have any sense for what is causing the problem in the full rake file. All I can think to do is demonstrate that my MWE is literally an excerpt from the full Rakefile, unaltered...
➜ solutionmaps cat mwe/Rakefile|sed '/^$/d'|tee a
require 'rake'
require 'rake/clean'
require 'pathname'
HOME = ENV['HOME']
SHARED_ATLAS = "#{HOME}/MRI/Manchester/data/CommonBrains/MNI_EPI_funcRes.nii"
TXT = Rake::FileList["txt/nodestrength/??.mni"]
AFNI_RAW = TXT.pathmap("afni/nodestrength/%n_raw+tlrc.HEAD")
AFNI_RAW.zip(TXT).each do |target,source|
file target => [source] do
sh("3dUndump -master #{SHARED_ATLAS} -xyz -datum float -prefix #{target.sub("+tlrc.HEAD","")} #{source}")
end
CLOBBER.push(target)
CLOBBER.push(target.sub(".HEAD",".BRIK"))
CLOBBER.push(target.sub(".HEAD",".BRIK.gz"))
end
➜ solutionmaps perl -ne 'print if ($seen{$_} .= #ARGV) =~ /10$/' Rakefile mwe/Rakefile|sed '/^$/d'|tee b
require 'rake'
require 'rake/clean'
require 'pathname'
HOME = ENV['HOME']
SHARED_ATLAS = "#{HOME}/MRI/Manchester/data/CommonBrains/MNI_EPI_funcRes.nii"
TXT = Rake::FileList["txt/nodestrength/??.mni"]
AFNI_RAW = TXT.pathmap("afni/nodestrength/%n_raw+tlrc.HEAD")
AFNI_RAW.zip(TXT).each do |target,source|
file target => [source] do
sh("3dUndump -master #{SHARED_ATLAS} -xyz -datum float -prefix #{target.sub("+tlrc.HEAD","")} #{source}")
end
CLOBBER.push(target)
CLOBBER.push(target.sub(".HEAD",".BRIK"))
CLOBBER.push(target.sub(".HEAD",".BRIK.gz"))
end
➜ solutionmaps diff a b
➜ solutionmaps
And that my mwe works as expected (that is, it does not execute the file task).
➜ mwe rake --trace --dry-run afni/nodestrength/02_raw+tlrc.HEAD
** Invoke afni/nodestrength/02_raw+tlrc.HEAD (first_time, not_needed)
** Invoke txt/nodestrength/02.mni (first_time, not_needed)
But the full rakefile does not.
rake --trace --dry-run afni/nodestrength/02_raw+tlrc.HEAD
** Invoke afni/nodestrength/02_raw+tlrc.HEAD (first_time)
** Invoke txt/nodestrength/02.mni (first_time, not_needed)
** Execute (dry run) afni/nodestrength/02_raw+tlrc.HEAD
➜ solutionmaps ls afni/nodestrength/02_raw+tlrc.HEAD
afni/nodestrength/02_raw+tlrc.HEAD
Finally happened across a possible answer:
Rake determines that a file task needs to be run if the file doesn’t exist or if any of the prerequisite file tasks are newer.
Quoted from: http://madewithenvy.com/ecosystem/articles/2013/rake-file-tasks/
Since my Rakefiles are under heavy development, and my Filetasks are all pretty interrelated, this is probably why my Rakefile always wanted to rebuild everything.
I'm migrating some shell scripts to Chef recipes. Some of these scripts are fairly involved, so just to make life easier in the short term and to avoid introducing bugs in rewriting everything in Chef/Ruby, I'd like to just run some of them as-is. They're all well-written and idempotent, so honestly there's no rush, but of course, the eventual goal is to rewrite them.
One cool feature of Ruby is its __END__ keyword/method: Lines below __END__ will not be executed. Those lines will be available via the special filehandle DATA.
It would be cool to ship the shell scripts as-is inside the the recipe after __END__, maybe something like the following, which I placed in chef-repo/cookbooks/ruby-data-test/recipes/default.rb:
file = Tempfile.new(File.basename(__FILE__))
file << DATA.read
bash file.path
file.unlink
__END__
echo "Hello, world"
However when I run this (with chef-solo -c solo.rb --override-runlist 'recipe[ruby-data-test]'), I get the following error:
[2014-10-03T17:14:56+00:00] ERROR: uninitialized constant Chef::Recipe::DATA
I'm pretty new to Chef, but I'm guessing the above is something about Chef wrapping my recipe in a class, and there's something simple preventing me from accessing DATA. Since it's "global" (?) I tried putting a dollar sign ($DATA) in front of it but that failed with:
NoMethodError
-------------
undefined method `read' for nil:NilClass
So the question is: How do I access DATA in my Chef recipe? Thanks!
It appears you don't have access to DATA, but you can fake it by reading in the current file yourself and splitting on __END__, like Sinatra does.
I ended up making a Chef LWRP for reuse. I don't know if I'll actually end up using this, but I wanted to figure it out. Like I said, I'm a Chef/Ruby noob, so any better ideas or suggestions welcome!
ruby_data_test/recipes/default.rb:
ruby_data_test_execute_ruby_data __FILE__
__END__
#!/bin/bash
set -o errexit
date
echo "Hello, world"
ruby_data_test/resources/execute_ruby_data.rb:
actions :execute_ruby_data
default_action :execute_ruby_data
attribute :source, :name_attribute => true, :required => true
attribute :args, :kind_of => Array
attribute :ignore_errors, :kind_of => [TrueClass, FalseClass], :default => false
ruby_data_test/providers/execute_ruby_data.rb:
def whyrun_supported?
true
end
use_inline_resources
action :execute_ruby_data do
converge_by("Executing #{#new_resource}") do
Chef::Log.info("Executing #{#new_resource}")
file_who_called_me = #new_resource.source
io = ::IO.respond_to?(:binread) ? ::IO.binread(file_who_called_me) : ::IO.read(file_who_called_me)
app, data = io.gsub("\r\n", "\n").split(/^__END__$/, 2)
data.lstrip!
file = Tempfile.new('execute_ruby_data')
file << data
file.chmod(0755)
file.close
exit_status = ::Open3.popen2e(file.path, *#new_resource.args) do |stdin, stdout_and_stderr, wait_thr|
stdout_and_stderr.each { |line| puts line }
wait_thr.value # exit status
end
if exit_status != 0 && !#new_resource.ignore_errors
throw RuntimeError
end
end
end
Here's the output:
$ chef-solo -c solo.rb --override-runlist 'recipe[ruby_data_test]'
Starting Chef Client, version 11.12.4
[2014-10-03T21:50:29+00:00] WARN: Run List override has been provided.
[2014-10-03T21:50:29+00:00] WARN: Original Run List: []
[2014-10-03T21:50:29+00:00] WARN: Overridden Run List: [recipe[ruby_data_test]]
Compiling Cookbooks...
Converging 1 resources
Recipe: ruby_data_test::default
* ruby_data_test_execute_ruby_data[/root/chef/chef-repo/cookbooks/ruby_data_test/recipes/default.rb] action execute_ruby_dataFri Oct 3 21:50:29 UTC 2014
Hello, world
- Executing ruby_data_test_execute_ruby_data[/root/chef/chef-repo/cookbooks/ruby_data_test/recipes/default.rb]
Running handlers:
Running handlers complete
Chef Client finished, 1/1 resources updated in 1.387608 seconds
I would like to write sass and coffee-script code in Rhodes/RhoMobile and let the compiler automatically compile to js and css file before run or build task, for every platforms I am targeting
In this example you place your sass files in assets/stylesheets directory. Processed and compressed files will output in public/css. This also preserve the sub directory structure
An error during compilation will stop the process
Adjust the before hook depending on the platform your are targetting
Add this task at then very end of your Rakefile:
task :precompile_css do
Dir.chdir $app_path do
exit_code = system 'scss --force --update assets/stylesheets:public/css --style compressed'
raise 'Precompile error' unless exit_code
end
end
# Add all platforms you need.
# To get the exact name of the task to hook you can execute
# $ rake -n run:my_platform
task 'config:android' => :precompile_css
task 'config:wm' => :precompile_css
etc ...
Also possible with coffeescript
task :precompile_js do
Dir.chdir $app_path do
exit_code = system 'coffee --compile --output public/js/ assets/javascripts/'
raise 'Precompile error' unless exit_code
end
end
task 'config:android' => :precompile_js
task 'config:wm' => :precompile_js
etc...
Then just invoke your run or build tasks as usual
is it possible to execute a command line order such as 'll', 'pwd' or whatever from a Coffee script?
I've tried to find examples without luck so far.
Thanks!
If you execute CoffeeScript via Node.js you will have full access to the abilities of your OS. Use the spawn method of the child_process module to create a new process:
{spawn} = require 'child_process'
ls = spawn 'ls', ['array', 'of', 'options']
# receive all output and process
ls.stdout.on 'data', (data) -> console.log data.toString().trim()
# receive error messages and process
ls.stderr.on 'data', (data) -> console.log data.toString().trim()
Is there a way using Rake to watch depedencies for changes and execute tasks automatically?
For example, given the following Rakefile:
file 'main.o' => ["main.c", "greet.h"] do
sh "cc -c -o main.o main.c"
end
file 'greet.o' => ['greet.c'] do
sh "cc -c -o greet.o greet.c"
end
file "hello" => ["main.o", "greet.o"] do
sh "cc -o hello main.o greet.o"
end
It would be great if when I changed greet.c or main.c then hello would automatically execute.
AFAIK rake doesn't support watching files and directories with a continuous compilation approach.
The buildr project implements a continuous compilation task for java which you can get inspired by to do the job for your project.
Also, it looks like guard might help there. See the guard github profile for implementations using guard. There's even a Rake extension to do what you want!
There is also rerun https://github.com/alexch/rerun. You can do rerun rake to watch and run the default task.