2

How do I run a rake script automatically every time I save a file in Visual Studio? I can create a batch file that wraps the command. And I'd like to trigger it on CTRL + S. But, Visual Studio 2012 doesn't have macros.

JP Boodhoo has done it in many of his screen casts, but hasn't shared the implementation.


FYI, my rakefile looks like this

require 'albacore'

desc 'Build Solution'
msbuild :Build do |msb| 
  msb.properties :configuration => :Release 
  msb.targets :Clean, :Build 
  msb.solution = 'ProjoBot.sln' 
end 

desc 'Run Unit Tests' 
mspec :Test do |mspec| 
  mspec.command = 'Lib/Tools/MSpec/mspec-clr4.exe' 
  mspec.assemblies 'Src/Tests/ProjoBot.UnitSpecifications/bin/Release/ProjoBot.UnitSpecifications.dll'
end 

task :default => [:Build, :Test]
Anthony Mastrean
  • 21,850
  • 21
  • 110
  • 188
Afraz Ali
  • 2,672
  • 2
  • 27
  • 47

2 Answers2

1

I used the External Tools to run a batch file that executes the default Rake task.

@ECHO OFF
rake

I assigned the shortcut key CTRL + S to the tool, so that, when I save, the rake task is triggered! I hope this helps someone looking to do the same thing.

Anthony Mastrean
  • 21,850
  • 21
  • 110
  • 188
Afraz Ali
  • 2,672
  • 2
  • 27
  • 47
  • Does this implementation still execute the built-in save? Or have you lost that ability? It would be nice to see the batch file contents. – Anthony Mastrean Feb 11 '13 at 20:38
  • Anthony, my bat file is just a single liner all it contains is Rake All I am doing is calling the default rake task. – Afraz Ali Feb 12 '13 at 10:42
  • my rake file is as follows ----- require 'albacore' desc "Build Solution" msbuild :Build do |msb| msb.properties :configuration => :Release msb.targets :Clean, :Build msb.solution = "ProjoBot.sln" end desc "Run Unit Tests" mspec :Test do |mspec| mspec.command = "Lib/Tools/MSpec/mspec-clr4.exe" mspec.assemblies "Src/Tests/ProjoBot.UnitSpecifications/bin/Release/ProjoBot.UnitSpecifications.dll" end task :default => [:Build, :Test] And yes Ctrl + S still works fine, the good thing is that if there is no batch file in a solution nothing will break and there will be no errors. – Afraz Ali Feb 12 '13 at 10:46
  • Cool, so you're depending on Ruby/Rake installed system-wide, so it's in your PATH. And you can call it from any command line. That's a good way to get started! I'm surprised that `CTRL + S` can be reassigned to your External Tool *and* still save the files. I didn't know VS could do that. I'll have to check it out. – Anthony Mastrean Feb 12 '13 at 16:35
1

There may be some options that integrate with the command line that are ignorant of Visual Studio.

The Ruby/Guard Way

I was playing with the Guard gem last night. You basically install guard and the Guard rake plugin.

gem install guard
gem install guard-rake

You can create a Guard "template", a Guardfile with a plain Rake task in it

guard init rake

You can edit it to watch .cs files in the source directory, for example. (and

guard 'rake', :task => 'default', :run_on_start => false do
  watch(%r{^source/.+\.cs$})
end

Then start Guard

guard

You may need to use -i to turn off this "interactive" mode, which may generate errors on Windows!

guard -i

Guard runs like a little local server, showing logs

12:07:08 - INFO - Guard uses TerminalTitle to send notifications.
12:07:08 - INFO - Starting guard-rake default
12:07:08 - INFO - Guard is now watching at 'D:/temp'
[1] guard(main)>

And if you force a file change (I'm going to touch a fake file I set up in my test directory), you'll get the output of your rake task!

12:07:08 - INFO - Guard uses TerminalTitle to send notifications.
12:07:08 - INFO - Starting guard-rake default
12:07:08 - INFO - Guard is now watching at 'D:/temp'
12:07:54 - INFO - running default
building!...
[1] guard(main)>

The PowerShell Way

There's nothing fancy out there that wraps up filesystem polling triggered actions, but that doesn't mean you can't build your own! I wrote a .\guard.ps1 file that sits in my solution root. It has a FileSystemWatcher and waits for changes in a loop.

$here = Split-Path $MyInvocation.MyCommand.Definition

$watcher = New-Object System.IO.FileSystemWatcher
$watcher.Path = "$here\source"
$watcher.IncludeSubdirectories = $true

while($true) {
  # timeout here lets the console process kill commands and such
  $result = $watcher.WaitForChanged('All', 3000)

  if($result.TimedOut) {
    continue
  }

  # this is where you'd put your rake command
  Write-Host "$($result.ChangeType):: $($result.Name)"

  # and a delay here is good for the CPU :)
  Start-Sleep -Seconds 3
}

You can see it working and printing when you touch or create (New-Item <name> -Type File) files. But, we can also execute rake very simply

rake

PowerShell will just go ahead and execute that as a native command. You could get fancy and make this script look and feel more like Guard (ok, not a lot more, but a little!)

param(
  [string] $path = (Split-Path $MyInvocation.MyCommand.Definition),
  [string] $task = 'default'
)

$watcher = New-Object System.IO.FileSystemWatcher
$watcher.Path = $path
$watcher.IncludeSubdirectories = $true

while($true) {
  $result = $watcher.WaitForChanged('All', 3000)

  if($result.TimedOut) {
    continue
  }

  rake $task

  Start-Sleep -Seconds 3
}

And you'd execute it like this

.\guard.ps1 -Path "$pwd\source" -Task 'build'
Anthony Mastrean
  • 21,850
  • 21
  • 110
  • 188
  • Wow Anthony! Thanks for sharing your implementations. I guess your solutions are more geekier than mine :) certainly more professional but I went for a simpler solution which works great for me. The best thing is that because my external command looks for a batch file in solution directory, if it does not find it nothing breaks. I like your implementation also, I will give it a try when I am messing with powershell. Thanks :) – Afraz Ali Feb 13 '13 at 11:01
  • You asked a good question. I think I'm going to try this for my solution. If you like it, please upvote! – Anthony Mastrean Feb 13 '13 at 17:52