lib/teapot/build.rb in teapot-1.0.0.pre.rc7 vs lib/teapot/build.rb in teapot-1.0.0.pre.rc9

- old
+ new

@@ -1,17 +1,17 @@ # Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com> -# +# # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: -# +# # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. -# +# # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, @@ -24,10 +24,11 @@ require 'build/graph' require 'build/makefile' require 'teapot/name' +require 'graphviz' require 'process/group' require 'system' module Teapot module Build @@ -65,10 +66,14 @@ if @callback scope.instance_exec(@arguments, &@callback) end end + + def inspect + @rule.name.inspect + end end class Top < Graph::Node def initialize(controller, task_class, &update) @update = update @@ -85,10 +90,14 @@ # Top level nodes are always considered dirty. This ensures that enclosed nodes are run if they are dirty. The top level node has no inputs or outputs by default, so children who become dirty wouldn't mark it as dirty and thus wouldn't be run. def requires_update? true end + + def inspect + @task_class.name.inspect + end end class Task < Graph::Task def initialize(controller, walker, node, group = nil) super(controller, walker, node) @@ -137,11 +146,13 @@ end end def visit super do + @controller.enter(self, @node) @node.apply!(self) + @controller.exit(self, @node) end end end class Controller < Graph::Controller @@ -159,10 +170,12 @@ super() end attr :top + attr :visualisation + # Because we do a depth first traversal, we can capture global state per branch, such as `@task_class`. def traverse!(walker) @top.each do |node| # Capture the task class for each top level node: @task_class = node.task_class @@ -184,17 +197,56 @@ super do |walker, node| @task_class.new(self, walker, node) end end + def enter(task, node) + return unless @g + + parent_node = @hierarchy.last + + task_node = @g.nodes[node] || @g.add_node(node, shape: 'box') + + if parent_node + parent_node.connect(task_node) + end + + node.inputs.map{|path| path.shortest_path(Dir.pwd)}.each do |path| + input_node = @g.nodes[path.to_s] || @g.add_node(path.to_s, shape: 'box') + input_node.connect(task_node) + end + + @hierarchy << task_node + end + + def exit(task, node) + return unless @g + + @hierarchy.pop + + task_node = @g.nodes[node] || @g.add_node(node, shape: 'box') + + node.outputs.map{|path| path.shortest_path(Dir.pwd)}.each do |path| + output_node = @g.nodes[path.to_s] || @g.add_node(path.to_s, shape: 'box') + output_node.connect(task_node) + end + end + def update! group = Process::Group.new + @g = Graphviz::Graph.new('G', rankdir: "LR") + @hierarchy = [] + walker = super do |walker, node| @task_class.new(self, walker, node, group) end group.wait + + if ENV['BUILD_GRAPH_PDF'] + Graphviz::output(@g, path: ENV['BUILD_GRAPH_PDF']) rescue nil + end return walker end end end