07.16.08
Fixing “undefined method `exitstatus’ for nil:NilClass”
I’m trying to get some more practical experience with Ruby so I’ve started converting a few batch files that automated instrumenting some .NET assemblies for code coverage. Basically it comes down to cleaning out some directories, running a child process in a loop, and pushing some binaries out to a sandbox test area.
It seemed like a good opportunity to also get some experience writing rake tasks.
The interface right now is:
coverage coverage:all # Perform a complete code coverage pass from scratch coverage coverage:clean # Clean the coverage temp directory and sandbox coverage coverage:cover # Instrument the assemblies for code coverage coverage coverage:merge # Merge the test results coverage coverage:run # Run the test suite coverage coverage:start # Start the coverage service coverage coverage:stop # Stop the coverage service
During development I was doing something I thought was pretty simple - executing a process in a loop for each file in a FileList.
C:\ruby\coverage>rake coverage:cover "E:\Program Files/Dev Suite}/instrument.exe" "foo.dll" /COVERAGE "/OUTPUTPATH:C:/output/coverage" rake aborted! undefined method `exitstatus' for nil:NilClass C:/ruby/coverage/rakefile:36 (See full trace by running task with --trace)
See the problem? I didn’t … (now it’s all I see!)
I re-ran rake with –trace and got a little more detail. The top of the stack is:
rake aborted! undefined method `exitstatus' for nil:NilClass c:/ruby/lib/ruby/gems/1.8/gems/rake-0.8.1/lib/rake.rb:899:in `sh'
That makes sense … I’m calling sh.
In rake.rb line 899 is for this method:
def sh(*cmd, &block)
options = (Hash === cmd.last) ? cmd.pop : {}
unless block_given?
show_command = cmd.join(" ")
show_command = show_command[0,42] + “…”
# TODO code application logic heref show_command.length > 45
block = lambda { |ok, status|
ok or fail “Command failed with status (#{status.exitstatus}): [#{show_command}]”
}
end
rake_check_options options, :noop, :verbose
rake_output_message cmd.join(” “) if options[:verbose]
unless options[:noop]
res = system(*cmd)
block.call(res, $?)
end
end
This is where I spent 5 minutes not making the mandatory mental leap. That’s how long it took to realize that
block = lambda { |ok, status|
is creating a block object which will be called later at the block.call.
So status.exitstatus is “$?” and “$?” is the exit status of the child process … and it’s NilClass so that must mean the process never executed … which means I need to start with the most obvious culprit: the command line (specifically the executable path).
So go back to the origional error message … now do you see it? (if you don’t - there is a “}” after “Dev Suite”
C:\ruby\coverage>rake coverage:cover "E:\Program Files/Dev Suite}/instrument.exe" "foo.dll" /COVERAGE "/OUTPUTPATH:C:/output/coverage" rake aborted! undefined method `exitstatus' for nil:NilClass C:/ruby/coverage/rakefile:36 (See full trace by running task with --trace)
It sucks that rake is not resiliant to this. So I updated my local copy to check for status.nil? like so:
block = lambda { |ok, status|
ok or fail "Command failed with status (#{status.nil? ? "nil" : status.exitstatus}): [#{show_command}]”
}
Summary …
If you are running a rake and get the message “undefined method `exitstatus’ for nil:NilClass” it (probably) means that the program it was trying to run was not found.
Luis Lavena said,
July 16, 2008 at 5:55 pm
Actually, you got hit by a Ruby for Windows bug
http://blog.mmediasys.com/2008/05/24/random-bits-and-experiments/
See under “Less quirk for us” section.
And the whole issue is covered here:
http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/16786
HTH.
Robert Horvick said,
July 16, 2008 at 6:14 pm
@Luis
Thanks! I appreciate the pointers. Great to see that it’s already being taken care of.
Robert.