1. Jun 6th, 2007

    Rake hack: I’m not sleeping, just caught in a circular dependency

    Rake is great, but as far as error reporting goes, it could use some design aesthetics. Case in point, every so often, Rake decides to take a little nap:

    stopping only thread
       note: use sleep to stop forever

    So the “only thread” part makes sense, since I’m not running any multitask. But I don’t remember asking it to sleep, or for that matter take the eternal nap. All I did was to accidentally make a circular dependency.

    So here’s a little hack you can use with Rake, to make it report a circular dependency:

    class Rake::Task
      def invoke()
        fail "Circular dependency " + (stack + [name]).join(”=>”) if stack.include?(name)
        @lock.synchronize do
          puts “** Invoke #{name} #{format_trace_flags}” if application.options.trace
          return if @already_invoked
          begin
            stack.push name
            @already_invoked = true
            invoke_prerequisites
            execute if needed?
          ensure
            stack.pop
          end
        end
      end
    
      def invoke_prerequisites()
        prerequisites.each { |n| application[n, @scope].invoke }
      end
    
    protected
    
      def stack()
        Thread.current[:rake_stack] ||= []
      end
    end
    
    class Rake::MultiTask
      def invoke_prerequisites()
        threads = @prerequisites.collect do |p|
          copy = stack.dup
          Thread.new(p) { |r| stack.replace copy ; application[r].invoke }
        end
        threads.each { |t| t.join }
      end
    end

    Now it will just say “Circular dependency foo=>bar=>foo”. It doesn’t fix your code, but at least you know what’s happening.

    Another trick, not specific to Rake but still very useful for Rakefiles:

    require "highline"
    require "facet/module/alias_method_chain"
    
    if PLATFORM =~ /linux|darwin|cywin/
      module Kernel
        def warn_with_color(message)
          warn_without_color HighLine.new.color(message.to_s, :red)
        end
        alias_method_chain :warn, :color
      end
    end

    It colors all your warnings red. Not on Windows, though, but then again …

    Flexible Rails
    • Flex 3 and Ruby on Rails 2 integrated with HTTPService and XML
    • RESTful Rails controllers that support Flex and HTML clients
    • Coverage of how to use Cairngorm to architect larger Flex applications
    • A full application--not just a toy--developed and refactored iteratively
    1. Jun 8th, 2007

      Mat Schaffer

      I was thinking of making a patch on rake’s trunk for this, but then I noticed something.

      Why did you remove setup_arguments from invoke_prerequisites?

    2. Jun 8th, 2007

      Assaf

      Mat,

      That would be great. Feel free to use this code under the Rake license. If you need test cases (we use RSpec), we also have that.

      I’m running against Rake 0.7.3, which doesn’t have setup_arguments. Actually, the invoke_prerequisite change is a separate enhancement not related to circular dependencies. By calling the method instead of accessing the array, the task can decide on the prerequisites when invoked, which we use to share prerequisites between tasks (late binding).

    3. Aug 28th, 2007

      Sean Reque

      This behavior is inconsistent with Rake’s documentation. According to the documentation, “If the secondary task has already been run, it will not be run again, even though it has been explicitly invoked. Rake tasks can only be invoked once per run”. However, as this article points out, rake crashes very ungracefully upon encountering a task that has already been built. The documented behavior seems very proper as that’s exactly how make handles it, so you would think that this glaring bug would have been fixed already!

    4. Aug 28th, 2007

      Assaf

      If you try to invoke the same task twice, Rake does nothing on the second invocation, and it shows as such in the log, which is perfectly fine. But if you try to invoke the same task twice from itself, it gives a very unhelpful error messages.

      For someone new to Rake who obviously is not expecting a circular dependency in their tasks (and who is?), the “I’m sleeping” error sends them down the wrong path. Even for someone experienced, on a complex Rake project, good luck finding where the circular dependency happens (the second invocation doesn’t appear in the trace).

      So this fix simply handles the error message, making it clear what happened and why, to help you find the error in the Rakefile and fix it.

    Leave a Reply | Trackback | Track with co.mments

    Where's my comment? I get too much comment spam, so I have to moderate comments. Damn those spammers. If you don't see your comment immediately, be patient. I'll approve it the minute I see it. Want to know when your comment shows up, or check if anyone responded? Track it.

    Or using OpenID