File Coverage

blib/lib/Makefile/GraphViz.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package Makefile::GraphViz;
2              
3 2     2   55374 use strict;
  2         6  
  2         76  
4 2     2   11 use warnings;
  2         4  
  2         66  
5 2     2   12 use vars qw($VERSION);
  2         3  
  2         109  
6              
7             #use Smart::Comments;
8 2     2   3612 use GraphViz;
  0            
  0            
9             use base 'Makefile::Parser';
10              
11             $VERSION = '0.20';
12              
13             $Makefile::Parser::Strict = 0;
14              
15             our $IDCounter = 0;
16              
17             my %VirNodeStyle =
18             (
19             shape => 'plaintext',
20             );
21              
22             my %NormalNodeStyle =
23             (
24             shape => 'box',
25             style => 'filled',
26             fillcolor => '#f5f694',
27             );
28              
29             my %EdgeStyle =
30             (
31             color => 'red',
32             );
33              
34             my %CmdStyle =
35             (
36             shape => 'ellipse',
37             style => 'filled',
38             fillcolor => '#c7f77c',
39             );
40              
41             my %InitArgs = (
42             layout => 'dot',
43             ratio => 'auto',
44             node => \%NormalNodeStyle,
45             edge => \%EdgeStyle,
46             );
47              
48             our %Nodes;
49              
50             sub _gen_id () {
51             return ++$IDCounter;
52             }
53              
54             sub _trim_path ($) {
55             my $s = shift;
56             $s =~ s/.+(.{5}[\\\/].*)$/...$1/o;
57             $s =~ s/\\/\\\\/g;
58             return $s;
59             }
60              
61             sub _trim_cmd ($) {
62             my $s = shift;
63             $s =~ s/((?:\S+\s+){2})\S.*/$1.../o;
64             $s =~ s/\\/\\\\/g;
65             return $s;
66             }
67              
68             sub _find ($@) {
69             my $elem = shift;
70             foreach (@_) {
71             return 1 if $elem eq $_;
72             }
73             return undef;
74             }
75              
76             sub plot ($$@) {
77             my $self = shift;
78             my $root_name = shift;
79             my %opts = @_;
80             #warn "@_\n";
81              
82             # process the ``gv'' option:
83             my $gv = $opts{gv};
84              
85             # process the ``vir_nodes'' option:
86             my $val = $opts{vir_nodes};
87             my @vir_nodes = @$val if $val and ref $val;
88             my %vir_nodes;
89             map { $vir_nodes{$_} = 1 } @vir_nodes;
90              
91             # process the ``normal_nodes'' option:
92             $val = $opts{normal_nodes};
93             my @normal_nodes = @$val if $val and ref $val;
94             my %normal_nodes;
95             map { $normal_nodes{$_} = 1 } @normal_nodes;
96              
97             # process the ``init_args'' option:
98             $val = $opts{init_args};
99             my %init_args = ($val and ref $val) ? %$val : %InitArgs;
100              
101             # process the ``edge_style'' option:
102             $val = $opts{edge_style};
103             my %edge_style = ($val and ref $val) ? %$val : %EdgeStyle;
104             $init_args{edge} = \%edge_style;
105              
106             # process the ``normal_node_style'' option:
107             $val = $opts{normal_node_style};
108             my %normal_node_style = ($val and ref $val) ? %$val : %NormalNodeStyle;
109             $init_args{node} = \%normal_node_style;
110              
111             # process the ``vir_node_style'' option:
112             $val = $opts{vir_node_style};
113             my %vir_node_style = ($val and ref $val) ? %$val : %VirNodeStyle;
114              
115             # process the ``cmd_style'' option:
116             $val = $opts{cmd_style};
117             my %cmd_style = ($val and ref $val) ? %$val : %CmdStyle;
118              
119             # process the ``trim_mode'' option:
120             my $trim_mode = $opts{trim_mode};
121             #warn "TRIM MODE: $trim_mode\n";
122              
123             # process the ``end_with'' option:
124             $val = $opts{end_with};
125             my @end_with = ($val and ref $val) ? @$val : ();
126              
127             # process the ``exclude'' option:
128             $val = $opts{exclude};
129             my @exclude = ($val and ref $val) ? @$val : ();
130              
131             return $gv if _find($root_name, @exclude);
132              
133             if (!$gv) {
134             $gv = GraphViz->new(%init_args);
135             %Nodes = ();
136             }
137              
138             my $is_virtual = 0;
139             if ($Nodes{$root_name}) {
140             return $gv;
141             }
142             $Nodes{$root_name} = 1;
143             #warn "GraphViz: $gv\n";
144              
145             my @roots = ($root_name and ref $root_name) ?
146             $root_name : ($self->target($root_name));
147              
148             my $short_name = _trim_path($root_name);
149             if ($normal_nodes{$root_name}) {
150             $is_virtual = 0;
151             } elsif ($vir_nodes{$root_name} or @roots and !$roots[0]->commands) {
152             $is_virtual = 1;
153             }
154              
155             if (!@roots or _find($root_name, @end_with)) {
156             $gv->add_node(
157             $root_name,
158             label => $short_name,
159             $is_virtual ? %vir_node_style : ()
160             );
161             return $gv;
162             }
163             #my $short_name = $root_name;
164              
165             my $i = 0;
166             for my $root (@roots) {
167             #warn $i, "???\n";
168             ### $root_name
169             ### $root
170             #$short_name =~ s/\\/\//g;
171             #warn $short_name, "\n";
172             #warn $short_name, "!!!!!!!!!!!!!!!!\n";
173             $gv->add_node(
174             $root_name,
175             label => $short_name,
176             $is_virtual ? %vir_node_style : ()
177             );
178              
179             #warn $gv;
180             my $lower_node;
181             my @cmds = $root->commands;
182             if (!$trim_mode and @cmds) {
183             $lower_node = _gen_id();
184             my $cmds = join("\n", map { _trim_cmd($_); } @cmds);
185             $gv->add_node($lower_node, label => $cmds, %cmd_style);
186             $gv->add_edge(
187             $lower_node => $root_name,
188             $is_virtual ? (style => 'dashed') : ()
189             );
190             } else {
191             $lower_node = $root_name;
192             }
193              
194             my @prereqs = $root->prereqs;
195             foreach (@prereqs) {
196             #warn "$_\n";
197             next if _find($_, @exclude);
198             $gv->add_edge(
199             $_ => $lower_node,
200             $is_virtual ? (style => 'dashed') : ());
201             #warn "$_ ++++++++++++++++++++\n";
202             $self->plot($_, gv => $gv, @_);
203             }
204             #warn "END\n";
205             #warn "GraphViz: $gv\n";
206             } continue { $i++ }
207             return $gv;
208             }
209              
210             sub plot_all ($) {
211             my $self = shift;
212             my $gv = GraphViz->new(%InitArgs);
213             %Nodes = ();
214             for my $target ($self->roots) {
215             $self->plot($target, gv => $gv);
216             }
217             $gv;
218             }
219              
220             1;
221             __END__