File Coverage

lib/OODoc.pm
Criterion Covered Total %
statement 33 207 15.9
branch 0 116 0.0
condition 0 33 0.0
subroutine 11 24 45.8
pod 9 10 90.0
total 53 390 13.5


line stmt bran cond sub pod time code
1             # Copyrights 2003-2021 by [Mark Overmeer].
2             # For other contributors see ChangeLog.
3             # See the manual pages for details on the licensing terms.
4             # Pod stripped from pm file by OODoc 2.02.
5             # This code is part of perl distribution OODoc. It is licensed under the
6             # same terms as Perl itself: https://spdx.org/licenses/Artistic-2.0.html
7              
8             package OODoc;
9 1     1   3906 use vars '$VERSION';
  1         2  
  1         52  
10             $VERSION = '2.02';
11              
12 1     1   5 use base 'OODoc::Object';
  1         2  
  1         96  
13              
14 1     1   6 use strict;
  1         1  
  1         42  
15 1     1   34 use warnings;
  1         2  
  1         35  
16              
17 1     1   4 use Log::Report 'oodoc';
  1         1  
  1         6  
18              
19 1     1   246 use OODoc::Manifest;
  1         2  
  1         20  
20              
21 1     1   5 use File::Copy;
  1         2  
  1         70  
22 1     1   6 use File::Spec;
  1         2  
  1         32  
23 1     1   6 use File::Basename;
  1         1  
  1         52  
24 1     1   5 use IO::File;
  1         1  
  1         165  
25 1     1   7 use List::Util 'first';
  1         1  
  1         2700  
26              
27              
28             sub init($)
29 0     0 0   { my ($self, $args) = @_;
30              
31 0 0         $self->SUPER::init($args) or return;
32              
33 0           $self->{O_pkg} = {};
34              
35 0           my $distribution = $self->{O_distribution} = delete $args->{distribution};
36 0 0         defined $distribution
37             or error __x"the produced distribution needs a project description";
38              
39 0   0       $self->{O_project} = delete $args->{project} || $distribution;
40              
41 0           my $version = delete $args->{version};
42 0 0         unless(defined $version)
43 0 0         { my $fn = -f 'version' ? 'version'
    0          
44             : -f 'VERSION' ? 'VERSION'
45             : undef;
46 0 0         if(defined $fn)
47 0 0         { my $v = IO::File->new($fn, 'r')
48             or fault __x"cannot read version from file {file}", file=> $fn;
49 0           $version = $v->getline;
50 0 0         $version = $1 if $version =~ m/(\d+\.[\d\.]+)/;
51 0           chomp $version;
52             }
53             }
54              
55 0 0         defined $version
56             or error __x"no version specified for distribution '{dist}'"
57             , dist => $distribution;
58              
59 0           $self->{O_version} = $version;
60 0           $self;
61             }
62              
63             #-------------------------------------------
64              
65              
66 0     0 1   sub distribution() {shift->{O_distribution}}
67              
68              
69 0     0 1   sub version() {shift->{O_version}}
70              
71              
72 0     0 1   sub project() {shift->{O_project}}
73              
74             #-------------------------------------------
75              
76              
77             sub selectFiles($@)
78 0     0 1   { my ($self, $files) = (shift, shift);
79              
80             my $select
81 0     0     = ref $files eq 'Regexp' ? sub { $_[0] =~ $files }
82 0 0         : ref $files eq 'CODE' ? $files
    0          
    0          
83             : ref $files eq 'ARRAY' ? $files
84             : error __x"use regex, code reference or array for file selection";
85              
86 0 0         return ($select, [])
87             if ref $select eq 'ARRAY';
88              
89 0           my (@process, @copy);
90 0           foreach my $fn (@_)
91 0 0         { if($select->($fn)) {push @process, $fn}
  0            
92 0           else {push @copy, $fn}
93             }
94              
95 0           ( \@process, \@copy );
96             }
97              
98              
99             sub processFiles(@)
100 0     0 1   { my ($self, %args) = @_;
101              
102             exists $args{workdir}
103 0 0         or error __x"requires a directory to write the distribution to";
104              
105 0           my $dest = $args{workdir};
106 0           my $source = $args{source};
107 0   0       my $distr = $args{distribution} || $self->distribution;
108              
109 0           my $version = $args{version};
110 0 0         unless(defined $version)
111 0 0         { my $fn = defined $source ? File::Spec->catfile($source, 'version')
112             : 'version';
113 0 0         $fn = -f $fn ? $fn
    0          
114             : defined $source ? File::Spec->catfile($source, 'VERSION')
115             : 'VERSION';
116 0 0         if(defined $fn)
    0          
117 0 0         { my $v = IO::File->new($fn, "r")
118             or fault __x"cannot read version from {file}", file => $fn;
119 0           $version = $v->getline;
120 0 0         $version = $1 if $version =~ m/(\d+\.[\d\.]+)/;
121 0           chomp $version;
122             }
123             elsif($version = $self->version) { ; }
124             else
125 0           { error __x"there is no version defined for the source files";
126             }
127             }
128              
129 0           my $notice = '';
130 0 0         if($notice = $args{notice})
131 0           { $notice =~ s/^(\#\s)?/# /mg; # put comments if none
132             }
133              
134             #
135             # Split the set of files into those who do need special processing
136             # and those who do not.
137             #
138              
139             my $manfile
140             = exists $args{manifest} ? $args{manifest}
141 0 0         : defined $source ? File::Spec->catfile($source, 'MANIFEST')
    0          
142             : 'MANIFEST';
143              
144 0           my $manifest = OODoc::Manifest->new(filename => $manfile);
145              
146 0           my $manout;
147 0 0         if(defined $dest)
148 0           { my $manif = File::Spec->catfile($dest, 'MANIFEST');
149 0           $manout = OODoc::Manifest->new(filename => $manif);
150 0           $manout->add($manif);
151             }
152             else
153 0           { $manout = OODoc::Manifest->new(filename => undef);
154             }
155              
156 0   0       my $select = $args{select} || qr/\.(pm|pod)$/;
157 0           my ($process, $copy) = $self->selectFiles($select, @$manifest);
158              
159 0           trace @$process." files to process and ".@$copy." files to copy";
160              
161             #
162             # Copy all the files which do not contain pseudo doc
163             #
164              
165 0 0         if(defined $dest)
166 0           { foreach my $filename (@$copy)
167 0 0         { my $fn = defined $source ? File::Spec->catfile($source, $filename)
168             : $filename;
169              
170 0           my $dn = File::Spec->catfile($dest, $fn);
171 0 0         unless(-f $fn)
172 0           { warning __x"no file {file} to include in the distribution"
173             , file => $fn;
174 0           next;
175             }
176              
177 0 0 0       unless(-e $dn && ( -M $dn < -M $fn ) && ( -s $dn == -s $fn ))
      0        
178 0           { $self->mkdirhier(dirname $dn);
179              
180 0 0         copy $fn, $dn
181             or fault __x"cannot copy distribution file {from} to {to}"
182             , from => $fn, to => $dest;
183              
184 0           trace " copied $fn to $dest";
185             }
186              
187 0           $manout->add($dn);
188             }
189             }
190              
191             #
192             # Create the parser
193             #
194              
195 0   0       my $parser = $args{parser} || 'OODoc::Parser::Markov';
196 0           my $skip_links = delete $args{skip_links};
197              
198 0 0         unless(ref $parser)
199 0           { eval "require $parser";
200 0 0         error __x"cannot compile {pkg} class: {err}", pkg => $parser, err => $@
201             if $@;
202              
203 0 0         $parser = $parser->new(skip_links => $skip_links)
204             or error __x"parser {name} could not be instantiated", name=>$parser;
205             }
206              
207             #
208             # Now process the rest
209             #
210              
211 0           foreach my $filename (@$process)
212 0 0         { my $fn = $source ? File::Spec->catfile($source, $filename) : $filename;
213              
214 0 0         unless(-f $fn)
215 0           { warning __x"no file {file} to include in the distribution"
216             , file => $fn;
217 0           next;
218             }
219              
220 0           my $dn;
221 0 0         if($dest)
222 0           { $dn = File::Spec->catfile($dest, $fn);
223 0           $self->mkdirhier(dirname $dn);
224 0           $manout->add($dn);
225             }
226              
227             # do the stripping
228 0           my @manuals = $parser->parse
229             ( input => $fn
230             , output => $dn
231             , distribution => $distr
232             , version => $version
233             , notice => $notice
234             );
235              
236 0 0         trace "stripped $fn into $dn" if defined $dn;
237 0           trace $_->stats for @manuals;
238              
239 0           foreach my $man (@manuals)
240 0 0         { $self->addManual($man) if $man->chapters;
241             }
242             }
243              
244             # Some general subtotals
245 0           trace $self->stats;
246              
247 0           $self;
248             }
249              
250             #-------------------------------------------
251              
252              
253             sub prepare(@)
254 0     0 1   { my ($self, %args) = @_;
255              
256 0           info "collect package relations";
257 0           $self->getPackageRelations;
258              
259 0           info "expand manual contents";
260 0           foreach my $manual ($self->manuals)
261 0           { trace " expand manual $manual";
262 0           $manual->expand;
263             }
264              
265 0           info "Create inheritance chapters";
266 0           foreach my $manual ($self->manuals)
267 0 0         { next if $manual->chapter('INHERITANCE');
268              
269 0           trace " create inheritance for $manual";
270 0           $manual->createInheritance;
271             }
272              
273 0           $self;
274             }
275              
276              
277             sub getPackageRelations($)
278 0     0 1   { my $self = shift;
279 0           my @manuals = $self->manuals; # all
280              
281             #
282             # load all distributions (which are not loaded yet)
283             #
284              
285 0           info "compile all packages";
286              
287 0           foreach my $manual (@manuals)
288 0 0         { next if $manual->isPurePod;
289 0           trace " require package $manual";
290              
291 0           eval "require $manual";
292 0 0 0       warning __x"errors from {manual}: {err}", manual => $manual, err =>$@
      0        
293             if $@ && $@ !~ /can't locate/i && $@ !~ /attempt to reload/i;
294             }
295              
296 0           info "detect inheritance relationships";
297              
298 0           foreach my $manual (@manuals)
299             {
300 0           trace " relations for $manual";
301              
302 0 0         if($manual->name ne $manual->package) # autoloaded code
303 0           { my $main = $self->mainManual("$manual");
304 0 0         $main->extraCode($manual) if defined $main;
305 0           next;
306             }
307 0           my %uses = $manual->collectPackageRelations;
308              
309 0 0         foreach (defined $uses{isa} ? @{$uses{isa}} : ())
  0            
310 0   0       { my $isa = $self->mainManual($_) || $_;
311              
312 0           $manual->superClasses($isa);
313 0 0         $isa->subClasses($manual) if ref $isa;
314             }
315              
316 0 0         if(my $realizes = $uses{realizes})
317 0   0       { my $to = $self->mainManual($realizes) || $realizes;
318              
319 0           $manual->realizes($to);
320 0 0         $to->realizers($manual) if ref $to;
321             }
322             }
323              
324 0           $self;
325             }
326              
327             #-------------------------------------------
328              
329              
330             our %formatters =
331             ( pod => 'OODoc::Format::Pod'
332             , pod2 => 'OODoc::Format::Pod2'
333             , pod3 => 'OODoc::Format::Pod3'
334             , html => 'OODoc::Format::Html'
335             , html2 => 'OODoc::Format::Html2'
336             );
337              
338             sub create($@)
339 0     0 1   { my ($self, $format, %args) = @_;
340              
341             my $dest = $args{workdir}
342 0 0         or error __x"create requires a directory to write the manuals to";
343              
344             #
345             # Start manifest
346             #
347              
348             my $manfile = exists $args{manifest} ? $args{manifest}
349 0 0         : File::Spec->catfile($dest, 'MANIFEST');
350 0           my $manifest = OODoc::Manifest->new(filename => $manfile);
351              
352             # Create the formatter
353              
354 0 0         unless(ref $format)
355             { $format = $formatters{$format}
356 0 0         if exists $formatters{$format};
357              
358 0           eval "require $format";
359 0 0         error __x"formatter {name} has compilation errors: {err}"
360             , name => $format, err => $@ if $@;
361              
362 0   0       my $options = delete $args{format_options} || [];
363              
364 0           $format = $format->new
365             ( manifest => $manifest
366             , workdir => $dest
367             , project => $self->distribution
368             , version => $self->version
369             , @$options
370             );
371             }
372              
373             #
374             # Create the manual pages
375             #
376              
377 0     0     my $select = ! defined $args{select} ? sub {1}
378             : ref $args{select} eq 'CODE' ? $args{select}
379 0 0   0     : sub { $_[0]->name =~ $args{select}};
  0 0          
380              
381 0           foreach my $package (sort $self->packageNames)
382             {
383 0           foreach my $manual ($self->manualsForPackage($package))
384 0 0         { next unless $select->($manual);
385              
386 0 0         unless($manual->chapters)
387 0           { trace " skipping $manual: no chapters";
388 0           next;
389             }
390              
391 0           trace " creating manual $manual with ".(ref $format);
392              
393             $format->createManual
394             ( manual => $manual
395             , template => $args{manual_templates}
396             , append => $args{append}
397 0   0       , format_options => ($args{manual_format} || [])
398             );
399             }
400             }
401              
402             #
403             # Create other pages
404             #
405              
406 0           trace "creating other pages";
407             $format->createOtherPages
408             ( source => $args{other_templates}
409             , process => $args{process_files}
410 0           );
411              
412 0           $format;
413             }
414              
415              
416             sub stats()
417 0     0 1   { my $self = shift;
418 0           my @manuals = $self->manuals;
419 0           my $manuals = @manuals;
420 0           my $realpkg = $self->packageNames;
421              
422 0           my $subs = map {$_->subroutines} @manuals;
  0            
423 0           my @options = map { map {$_->options} $_->subroutines } @manuals;
  0            
  0            
424 0           my $options = @options;
425 0           my $examples = map {$_->examples} @manuals;
  0            
426              
427 0           my $diags = map {$_->diagnostics} @manuals;
  0            
428 0           my $distribution = $self->distribution;
429 0           my $version = $self->version;
430              
431 0           <
432             $distribution version $version
433             Number of package manuals: $manuals
434             Real number of packages: $realpkg
435             documented subroutines: $subs
436             documented options: $options
437             documented diagnostics: $diags
438             shown examples: $examples
439             STATS
440             }
441              
442             #-------------------------------------------
443              
444              
445             1;