Rakeファイルの依存関係を可視化する

眼力を鍛えているとRakeファイルに記述した依存関係くらいは
図として見えてくるのです。嘘です。


まず以下を見たいRakefileのあるところに
vizrake.rbなどとして保存する。

require 'graphviz_r'

module VizRake

  def self.init_graphviz
    @gvr = GraphvizR.new 'vizrake' unless @gvr
    @gvr.graph[:label=>"Generated by VizRake"]
  end
  def self.output
    @gvr.output
  end

  def self.file(arg)
    self.controller(:file,arg)
  end
  def self.task(arg)
    self.controller(:task,arg)
  end
  def self.desc(mes)
    @mes = mes
  end

  private

  def self.controller(type,arg)
    unless arg.is_a? Hash
      self.node(arg)
    else
      key = arg.keys[0]
      vals = arg[key]
      vals = [vals] unless vals.is_a? Array
      vals.each { |val|
        if type == :task
          self::node2node(key,val)
        elsif type == :file
          self::boxnode(val)
          self::boxnode(key)
          self::node2node(val,key)
        end
      }
    end
    @mes = nil
  end

  def self.node2node(node1,node2)
    @mes = '' unless @mes
    (@gvr[self::q(node1)] >> @gvr[self::q(node2)]) [:label => @mes]
  end
  def self.node(node)
    @gvr[self::q(node)]
  end
  def self.boxnode(node)
    @gvr[self::q(node)] [:shape => :record]
  end

  def self.q(arg) #"q" means "quote"
    %Q'"#{arg}"'
  end
end

[:task,:file,:desc].each { |method|
  # aliasing
  eval "alias :_#{((method.to_s)+"_org").to_s} :#{method}"
  # overloading
  eval(<<-EVL
    def #{method}(arg,&block)
      VizRake::#{method}(arg)
      _#{method}_org(arg,&block)
    end
  EVL
  )
}

VizRake.init_graphviz

at_exit {
  @mes = nil
  if defined? CLEAN
    CLEAN.each { |t|
      task :clean => t
    }
  end
  if defined? CLOBBER
    CLOBBER.each { |t|
      task :clobber => t
    }
  end
  VizRake::output
}


その後、Rakefileのあるところで

$ rake -r vizrake

として実行するとvizrake.pngというファイルができている。


例えば、この前自分が書いたのから
少し修正(cleanとclobberに対応)した以下のRakefile

require 'rake/clean'

CC    = 'gcc'
ERUBY = 'eruby'

task :default => 'soundcard.rb'

CLEAN.include(['soundcard.out','soundcard.c'])
CLOBBER.include('soundcard.rb')

desc 'output Rubyfile'
file 'soundcard.rb' => ['soundcard.out'] do
  sh './soundcard.out > soundcard.rb'
end

desc 'C source compile'
file 'soundcard.out' => 'soundcard.c' do
  sh "#{CC} -o soundcard.out soundcard.c"
end

desc 'eRuby make C source'
file 'soundcard.c' => 'soundcard.erb' do
  sh "#{ERUBY} soundcard.erb > soundcard.c"
end


フハハ、見える、見えるぞ。(飛行石の力で)



工夫したところ

  • fileは四角、taskは丸にしてみた。
  • Rakeモジュールのメソッドをaliasして退避してから上書きすることで擬似フック動作してみた。
  • at_exitって初めて使ったよ。


ただし、

  • 図の下部の名前や出力ファイル名はハードコーディングなので自分で書き変えよう。
  • Packageやdirectoryは非対応なので見えないよ。
  • あんまりテストしていないからダメな場合もあると思うし。
  • ひどく大きいサイズの画像ができる場合があったので気をつけよう。railsRakefileとか。
  • Rakefile自体が見やすいものなので根本的に便利じゃなかったりしてね。
  • 書き忘れましたが、要 graphviz_r ですよ。


Rakefileの勉強になりました。


追記:生成画像ファイルが間違っていましたので差し替えました。