File Coverage

blib/lib/Devel/Depend/Cpp.pm
Criterion Covered Total %
statement 12 73 16.4
branch 0 32 0.0
condition 0 13 0.0
subroutine 4 7 57.1
pod 3 3 100.0
total 19 128 14.8


line stmt bran cond sub pod time code
1              
2             package Devel::Depend::Cpp;
3              
4 1     1   10423 use 5.006;
  1         4  
  1         47  
5 1     1   8 use warnings ;
  1         2  
  1         36  
6 1     1   5 use strict ;
  1         6  
  1         41  
7 1     1   285 use Carp ;
  1         2  
  1         1518  
8              
9             require Exporter;
10              
11             our @ISA = qw(Exporter);
12             our %EXPORT_TAGS = ( 'all' => [ qw() ] );
13             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
14             our @EXPORT = qw($PreprocessorDepend);
15              
16             our $VERSION = '0.10';
17              
18             =head1 NAME
19              
20             Devel::Depend::Cpp - Extract dependency trees from c files
21              
22             =head1 SYNOPSIS
23              
24             use Devel::Depend::Cpp;
25            
26             my ($success, $includ_levels, $included_files)
27             = Devel::Depend::Cpp::Depend
28             (
29             undef, # use default 'cpp' command
30             '/usr/include/stdio.h',
31             '', # switches to cpp
32             0, # include system includes
33             0, # dump 'cpp' output in terminal
34             ) ;
35            
36              
37             =head1 OUTPUT
38              
39             include levels for '/usr/include/stdio.h':
40             |- 1
41             | |- /usr/include/bits/stdio_lim.h
42             | |- /usr/include/bits/sys_errlist.h
43             | |- /usr/include/bits/types.h
44             | |- /usr/include/features.h
45             | |- /usr/include/libio.h
46             | `- /usr/lib/gcc/i686-pc-linux-gnu/3.4.5/include/stddef.h
47             |- 2
48             | |- /usr/include/_G_config.h
49             | |- /usr/include/bits/typesizes.h
50             | |- /usr/include/bits/wordsize.h
51             | |- /usr/include/gnu/stubs.h
52             | |- /usr/include/sys/cdefs.h
53             | |- /usr/lib/gcc/i686-pc-linux-gnu/3.4.5/include/stdarg.h
54             | `- /usr/lib/gcc/i686-pc-linux-gnu/3.4.5/include/stddef.h
55             |- 3
56             | |- /usr/include/gconv.h
57             | |- /usr/include/wchar.h
58             | `- /usr/lib/gcc/i686-pc-linux-gnu/3.4.5/include/stddef.h
59             |- 4
60             | |- /usr/include/bits/wchar.h
61             | |- /usr/include/wchar.h
62             | `- /usr/lib/gcc/i686-pc-linux-gnu/3.4.5/include/stddef.h
63             `- 5
64             `- /usr/lib/gcc/i686-pc-linux-gnu/3.4.5/include/stddef.h
65            
66             '/usr/include/stdio.h' included files:
67             |- /usr/include/_G_config.h
68             |- /usr/include/bits/stdio_lim.h
69             |- /usr/include/bits/sys_errlist.h
70             |- /usr/include/bits/types.h
71             |- /usr/include/bits/typesizes.h
72             |- /usr/include/bits/wchar.h
73             |- /usr/include/bits/wordsize.h
74             |- /usr/include/features.h
75             |- /usr/include/gconv.h
76             |- /usr/include/gnu/stubs.h
77             |- /usr/include/libio.h
78             |- /usr/include/sys/cdefs.h
79             |- /usr/include/wchar.h
80             |- /usr/lib/gcc/i686-pc-linux-gnu/3.4.5/include/stdarg.h
81             `- /usr/lib/gcc/i686-pc-linux-gnu/3.4.5/include/stddef.h
82            
83             '/usr/include/stdio.h' included files tree:
84             |- /usr/include/bits/stdio_lim.h
85             |- /usr/include/bits/sys_errlist.h
86             |- /usr/include/bits/types.h
87             | |- /usr/include/bits/typesizes.h
88             | |- /usr/include/bits/wordsize.h
89             | `- /usr/lib/gcc/i686-pc-linux-gnu/3.4.5/include/stddef.h
90             |- /usr/include/features.h
91             | |- /usr/include/gnu/stubs.h
92             | `- /usr/include/sys/cdefs.h
93             |- /usr/include/libio.h
94             | |- /usr/include/_G_config.h
95             | | |- /usr/include/gconv.h
96             | | | |- /usr/include/wchar.h
97             | | | | |- /usr/include/bits/wchar.h
98             | | | | `- /usr/lib/gcc/i686-pc-linux-gnu/3.4.5/include/stddef.h
99             | | | `- /usr/lib/gcc/i686-pc-linux-gnu/3.4.5/include/stddef.h
100             | | |- /usr/include/wchar.h
101             | | `- /usr/lib/gcc/i686-pc-linux-gnu/3.4.5/include/stddef.h
102             | `- /usr/lib/gcc/i686-pc-linux-gnu/3.4.5/include/stdarg.h
103             `- /usr/lib/gcc/i686-pc-linux-gnu/3.4.5/include/stddef.h
104              
105             =head1 DESCRIPTION
106              
107             Extract dependency trees from c files.
108              
109             =head1 MEMBER FUNCTIONS
110              
111             =cut
112              
113             #------------------------------------------------------------------------------------------------
114              
115             sub Depend
116             {
117              
118             =head2 Depend
119              
120             B calls I (the c pre-processor) to extract all the included files.
121              
122             =head3 Returns
123              
124             =over 2
125              
126             =item * Success flag
127              
128             =item * A reference to a hash where the included files are sorted perl level. A file can appear at different levels
129              
130             =item * A reference to a hash with one file per entry
131              
132             =item * A reference to a hash representing an include tree
133              
134             =item * A string containing an error message, if any
135              
136             =back
137              
138             =head3 Arguments
139              
140             =over 2
141              
142             =item * the name of the 'cpp' binary to use. undef to use the first 'cpp' in your path
143              
144             =item * The name of the file to depend
145              
146             =item * A string to be passed to cpp, ex: '-DDEBUG'
147              
148             =item * A boolean indicating if the system include files should be included in the result (anything under /usr/)
149              
150             =item * a sub reference to be called everytime a node is added (see I for an example)
151              
152             =item * A boolean indicating if the output of B should be dumped on the screen
153              
154             =back
155              
156             This sub is a wrapper around B.
157              
158             =cut
159              
160 0   0 0 1   my $cpp = shift || 'cpp' ;
161 0   0       my $file_to_depend = shift || confess "No file to depend!\n" ;
162 0           my $switches = shift ;
163 0           my $include_system_includes = shift ;
164 0           my $add_child_callback = shift ;
165 0           my $display_cpp_output = shift ;
166              
167             #handle OS differences
168 0 0         my $redir_to_null = $^O eq "MSWin32" ? '> nul' : '1>/dev/null';
169 0           my $command = "$cpp -H -M $switches $file_to_depend 2>&1 $redir_to_null" ;
170              
171             return
172             (
173 0           RunAndParse
174             (
175             $file_to_depend
176             , $command
177             , $include_system_includes
178             , $add_child_callback
179             , $display_cpp_output
180             )
181             ) ;
182             }
183              
184             our $PreprocessorDepend = \&Depend;
185              
186             #------------------------------------------------------------------------------------------------
187              
188             sub RunAndParse
189             {
190              
191             =head2 RunAndParse
192              
193             This sub runs a, preprocessor, command passed as an argument and parses its output. The output is expected to follow
194             the I output format. This sub allows finer control of the preprocessing. Try L first.
195              
196             =cut
197              
198 0   0 0 1   my $file_to_depend = shift || confess "****No file to depend!\n" ;
199 0   0       my $command = shift || die "No command defined!" ;
200 0           my $include_system_includes = shift ;
201 0           my $add_child_callback = shift ;
202 0           my $display_cpp_output = shift ;
203              
204 0           my $errors = '';
205              
206 0           my @cpp_output = `$command` ;
207              
208 0 0         $errors .= "command: $command : " . join("\n", @cpp_output) if $?;
209 0 0         $errors .= "command: $command : $!" if $! ;
210              
211 0 0         print STDERR "$command\n" if($display_cpp_output) ;
212            
213 0           for(@cpp_output)
214             {
215 0 0         print STDERR $_ if($display_cpp_output) ;
216 0 0         $errors .= $_ if(/No such file or directory/) ;
217             }
218            
219 0 0         if($include_system_includes)
220             {
221 0           @cpp_output = grep {/^\./} @cpp_output ;
  0            
222             }
223             else
224             {
225             =comment
226             Default search path from cpp Info pages:
227             /usr/local/include
228             /usr/lib/gcc-lib/TARGET/VERSION/include
229             /usr/TARGET/include
230             /usr/include
231             /usr/include/g++-v3
232             =cut
233 0 0         @cpp_output = grep {! m~\.+\s+/usr/~ && /^\./} @cpp_output ;
  0            
234              
235 0 0 0       if ($^O eq 'MSWin32' && defined $ENV{INCLUDE})
236             {
237 0           my @includes = split(';', $ENV{INCLUDE});
238 0           for my $include (@includes)
239             {
240 0           $include =~ s|\\|/|g;
241 0 0         @cpp_output = grep { ! m~\.+\s+\Q$include~ && /^\./} @cpp_output ;
  0            
242             }
243             }
244             }
245            
246 0           my %node_levels ;
247             my %nodes ;
248 0           my %parent_at_level = (0 => {__NAME => $file_to_depend}) ;
249              
250 0           for(@cpp_output)
251             {
252 0 0         print STDERR $_ if($display_cpp_output) ;
253            
254 0           chomp ;
255 0           my ($level, $name) = /^(\.+)\s+(.*)/ ;
256 0           $name =~ s|\\|/|g;
257 0           $name = CollapsePath($name) ;
258            
259 0           $level = length $level ;
260            
261 0           my $node ;
262 0 0         unless (exists $nodes{$name})
263             {
264 0           $nodes{$name} = {__NAME => $name} ;
265             }
266            
267 0           $node = $nodes{$name} ;
268            
269 0 0         $node_levels{$level}{$name} = $node unless exists $node_levels{$level}{$name} ;
270            
271 0           $parent_at_level{$level} = $node ;
272            
273 0           my $parent = $parent_at_level{$level - 1} ;
274            
275 0 0         unless(exists $parent->{$name})
276             {
277 0           $parent->{$name} = $node ;
278 0 0         $add_child_callback->($parent->{__NAME} => $name) if(defined $add_child_callback) ;
279             }
280             }
281            
282 0           return(($errors eq ''), \%node_levels, \%nodes, $parent_at_level{0}, $errors) ;
283             }
284              
285             #-------------------------------------------------------------------------------
286              
287             sub CollapsePath
288             {
289              
290             =head2 CollapsePath
291              
292             Removes '.' and '..' from a path.
293              
294             =cut
295              
296 0     0 1   my $collapsed_path = $_[0] ;
297              
298 0           $collapsed_path =~ s~(?
299 0           $collapsed_path =~ s~/\.$~~ ;
300              
301 0           1 while($collapsed_path =~ s~[^/]+/\.\./~~) ;
302 0           $collapsed_path =~ s~[^/]+/\.\.$~~ ;
303             #
304             ## collaps to root
305 0           $collapsed_path =~ s~^/(\.\./)+~/~ ;
306              
307             #remove trailing '/'
308 0 0         $collapsed_path =~ s~/$~~ unless $collapsed_path eq '/' ;
309              
310 0           return($collapsed_path) ;
311             }
312              
313             #-------------------------------------------------------------------------------
314              
315             1 ;
316              
317             =head2 EXPORT
318              
319             $PreprocessorDepend, a scalar containing a reference to the B sub.
320              
321             =head1 DEPENDENCIES
322              
323             I.
324              
325             =head1 AUTHOR
326              
327             Khemir Nadim ibn Hamouda
328             CPAN ID: NKH
329             mailto:nadim@khemir.net
330             http:// no web site
331              
332             =head1 COPYRIGHT
333              
334             This program is free software; you can redistribute
335             it and/or modify it under the same terms as Perl itself.
336              
337             The full text of the license can be found in the
338             LICENSE file included with this module.
339              
340             =head1 SEE ALSO
341              
342             B.
343              
344             =cut
345