File Coverage

blib/lib/GraphViz/Makefile.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             # -*- perl -*-
2              
3             #
4             # Author: Slaven Rezic
5             #
6             # Copyright (C) 2002,2003,2005,2008,2013 Slaven Rezic. All rights reserved.
7             # This program is free software; you can redistribute it and/or
8             # modify it under the same terms as Perl itself.
9             #
10             # Mail: srezic@cpan.org
11             # WWW: http://www.rezic.de/eserte/
12             #
13              
14             package GraphViz::Makefile;
15 1     1   3590 use GraphViz;
  0            
  0            
16             use Make;
17             use strict;
18              
19             use vars qw($VERSION $V);
20             $VERSION = '1.17';
21              
22             $V = 0 unless defined $V;
23              
24             sub new {
25             my($pkg, $g, $make, $prefix, %args) = @_;
26             $g = GraphViz->new unless $g;
27             if (!$make) {
28             $make = Make->new;
29             } elsif (!UNIVERSAL::isa($make, "Make")) {
30             $make = Make->new(Makefile => $make);
31             }
32              
33             my @allowed_args = qw(reversed);
34             my %allowed_args = map {($_,undef)} @allowed_args;
35             while(my($k,$v) = each %args) {
36             die "Unrecognized argument $k, known arguments are @allowed_args"
37             if !exists $allowed_args{$k};
38             }
39              
40             my $self = { GraphViz => $g,
41             Make => $make,
42             Prefix => ($prefix||""),
43             %args
44             };
45             bless $self, $pkg;
46             }
47              
48             sub GraphViz { shift->{GraphViz} }
49             sub Make { shift->{Make} }
50              
51             sub generate {
52             my($self, $target) = @_;
53             $target = "all" if !defined $target;
54             my $seen = {};
55             my $expanded_target = $self->{Make}->subsvars($target);
56             $self->_generate($target, $expanded_target, $seen);
57             }
58              
59             sub _generate {
60             my($self, $target, $expanded_target, $seen) = @_;
61             return if $seen->{$expanded_target};
62             $seen->{$expanded_target}++;
63             my $make_target = $self->{Make}->Target($target);
64             if (!$make_target) {
65             warn "Can't get make target for $target\n" if $V;
66             return;
67             }
68             my @depends = $self->_all_depends($self->{Make}, $make_target);
69             if (!@depends) {
70             warn "No depends for target $target\n" if $V;
71             return;
72             }
73             my $g = $self->{GraphViz};
74             my $prefix = $self->{Prefix};
75             $g->add_node($prefix.$expanded_target);
76             foreach my $dep_def (@depends) {
77             my $expanded_dep = $dep_def->{expanded};
78             $g->add_node($prefix.$expanded_dep) unless $seen->{$expanded_dep};
79             if ($self->{reversed}) {
80             $g->add_edge($prefix.$expanded_dep, $prefix.$expanded_target);
81             warn "$prefix$expanded_dep => $prefix$expanded_target\n" if $V >= 2;
82             } else {
83             $g->add_edge($prefix.$expanded_target, $prefix.$expanded_dep);
84             warn "$prefix$expanded_target => $prefix$expanded_dep\n" if $V >= 2;
85             }
86             }
87             $seen->{$target}++;
88             foreach my $dep_def (@depends) {
89             my($expanded_dep, $unexpanded_dep) = @{$dep_def}{qw(expanded unexpanded)};
90             $self->_generate($unexpanded_dep, $expanded_dep, $seen);
91             }
92             }
93              
94             sub guess_external_makes {
95             my($self, $make_rule, $cmd) = @_;
96             if (defined $cmd && $cmd =~ /\bcd\s+(\w+)\s*(?:;|&&)\s*make\s*(.*)/) {
97             my($dir, $makeargs) = ($1, $2);
98             my $makefile;
99             my $rule;
100             {
101             require Getopt::Long;
102             local @ARGV = split /\s+/, $makeargs;
103             $makefile = "makefile";
104             # XXX parse more options
105             Getopt::Long::GetOptions("f=s" => \$makefile);
106             my @env;
107             foreach (@ARGV) {
108             if (!defined $rule) {
109             $rule = $_;
110             } elsif (/=/) {
111             push @env, $_;
112             }
113             }
114             }
115              
116             # warn "dir: $dir, file: $makefile, rule: $rule\n";
117             my $f = "$dir/$makefile"; # XXX make better. use $make->{GNU}
118             $f = "$dir/Makefile" if !-r $f;
119             my $gm2 = GraphViz::Makefile->new($self->{GraphViz}, $f, "$dir/"); # XXX save_pwd verwenden; -f option auswerten
120             $gm2->generate($rule);
121              
122             $self->{GraphViz}->add_edge($make_rule->Name, "$dir/$rule");
123             } else {
124             warn "can't match external make command in $cmd\n" if $V;
125             }
126             }
127              
128             sub _all_depends {
129             my($self, $make, $make_target) = @_;
130             my @depends;
131             if ($make_target->colon) {
132             push @depends, $make_target->colon->depend;
133             # push @depends, $make_target->colon->exp_depend;
134             $self->guess_external_makes($make_target, $make_target->colon->exp_command);
135             } elsif ($make_target->dcolon) {
136             foreach my $rule ($make_target->dcolon) {
137             push @depends, $rule->depend;
138             #push @depends, $rule->exp_depend;
139             $self->guess_external_makes($rule, $rule->exp_command);
140             }
141             }
142             map
143             { +{ unexpanded => $_,
144             expanded => $make->subsvars($_),
145             }
146             } @depends;
147             # map { split(/\s+/,$make->subsvars($_)) } @depends;
148             # @depends;
149             }
150              
151             {
152             local $^W = 0; # no redefine warnings
153             package
154             Make;
155              
156             *subsvars = sub
157             {
158             my $self = shift;
159             local $_ = shift;
160             my @var = @_;
161             push(@var,$self->{Override},$self->{Vars},\%ENV);
162             croak("Trying to subsitute undef value") unless (defined $_);
163             while (/(?
164             {
165             my ($key,$head,$tail) = ($1,$`,$');
166             my $value;
167             if ($key =~ /^([\w._]+|\S)(?::(.*))?$/)
168             {
169             my ($var,$op) = ($1,$2);
170             foreach my $hash (@var)
171             {
172             $value = $hash->{$var};
173             if (defined $value)
174             {
175             last;
176             }
177             }
178             unless (defined $value)
179             {
180             #XXX $@ not defined?
181             #XXX die "$var not defined in '$_'" unless (length($var) > 1);
182             warn "$var not defined in '$_'" unless (length($var) > 1);
183             $value = '';
184             }
185             if (defined $op)
186             {
187             if ($op =~ /^s(.).*\1.*\1/)
188             {
189             local $_ = $self->subsvars($value);
190             $op =~ s/\\/\\\\/g;
191             eval $op.'g';
192             $value = $_;
193             }
194             else
195             {
196             die "$var:$op = '$value'\n";
197             }
198             }
199             }
200             elsif ($key =~ /wildcard\s*(.*)$/)
201             {
202             $value = join(' ',glob($self->pathname($1)));
203             }
204             elsif ($key =~ /shell\s*(.*)$/)
205             {
206             $value = join(' ',split('\n',`$1`));
207             }
208             elsif ($key =~ /addprefix\s*([^,]*),(.*)$/)
209             {
210             $value = join(' ',map($1 . $_,split('\s+',$2)));
211             }
212             elsif ($key =~ /notdir\s*(.*)$/)
213             {
214             my @files = split(/\s+/,$1);
215             foreach (@files)
216             {
217             s#^.*/([^/]*)$#$1#;
218             }
219             $value = join(' ',@files);
220             }
221             elsif ($key =~ /dir\s*(.*)$/)
222             {
223             my @files = split(/\s+/,$1);
224             foreach (@files)
225             {
226             s#^(.*)/[^/]*$#$1#;
227             }
228             $value = join(' ',@files);
229             }
230             elsif ($key =~ /^subst\s+([^,]*),([^,]*),(.*)$/)
231             {
232             my ($a,$b) = ($1,$2);
233             $value = $3;
234             $a =~ s/\./\\./;
235             $value =~ s/$a/$b/;
236             }
237             elsif ($key =~ /^mktmp,(\S+)\s*(.*)$/)
238             {
239             my ($file,$content) = ($1,$2);
240             open(TMP,">$file") || die "Cannot open $file:$!";
241             $content =~ s/\\n//g;
242             print TMP $content;
243             close(TMP);
244             $value = $file;
245             }
246             else
247             {
248             warn "Cannot evaluate '$key' in '$_'\n";
249             }
250             $_ = "$head$value$tail";
251             }
252             s/\$\$/\$/g;
253             return $_;
254             }
255             }
256              
257             1;
258              
259              
260             __END__