#!/usr/bin/perl
require
5.6.1;
our
$VERSION
=
"0.64"
;
my
$Options
=
{
add_uncoverable_point
=> [],
delete_uncoverable_point
=> [],
annotation
=> [],
coverage
=> [],
delete
=> 0,
gcov
=>
$Config
{gccversion},
ignore
=> [],
ignore_re
=> [],
select
=> [],
select_re
=> [],
report
=>
""
,
summary
=> 1,
uncoverable_file
=> [
".uncoverable"
,
glob
(
"~/.uncoverable"
)],
};
sub
get_options
{
Getopt::Long::Configure(
"pass_through"
);
die
"Bad option"
unless
GetOptions(
$Options
,
"write:s"
=>
sub
{
@$Options
{
qw( write summary )
} = (
$_
[1], 0)
},
qw(
add_uncoverable_point=s
annotation=s
clean_uncoverable_points!
coverage=s
delete!
delete_uncoverable_point=s
dump_db!
gcov!
help|h!
ignore_re=s
ignore=s
info|i!
outputdir=s
report=s
select_re=s
select=s
silent!
summary!
test!
uncoverable_file=s
version|v!
charset=s
)
);
Getopt::Long::Configure(
"nopass_through"
);
$Options
->{report} ||=
"html"
unless
exists
$Options
->{
write
};
}
sub
delete_db
{
for
my
$del
(
@_
)
{
print
"Deleting database $del\n"
unless
$Options
->{silent};
my
$db
= Devel::Cover::DB->new(
db
=>
$del
);
unless
(
$db
->is_valid)
{
print
"Devel::Cover: $del is an invalid database - ignoring\n"
unless
$Options
->{silent};
next
;
}
$db
->
delete
;
rmtree(
$del
);
}
}
sub
test_command { -e
"Build.PL"
? mb_test_command() : mm_test_command() }
sub
gcov_args {
"-fprofile-arcs\\ -ftest-coverage"
}
sub
mm_test_command
{
my
$test
=
"make test"
;
if
(
$Options
->{gcov})
{
my
$o
= gcov_args();
$test
.=
" OPTIMIZE=-O0\\ $o OTHERLDFLAGS=$o"
;
}
$test
}
sub
mb_test_command
{
my
$test
=
'./Build test'
;
if
(
$Options
->{gcov})
{
my
$o
= gcov_args();
$test
.=
" --extra_compiler_flags=-O0\\ $o --extra_linker_flags=$o"
;
}
$test
}
sub
main
{
if
(
$INC
{
"Devel/Cover.pm"
})
{
my
$err
=
"$0 shouldn't be run with coverage turned on.\n"
;
eval
{
print
STDERR
$err
;
POSIX::_exit(1);
};
die
$err
;
}
get_options;
if
(
my
$charset
=
$Options
->{charset}) {
CGI::self_or_default()->charset(
$charset
);
}
$Devel::Cover::Silent
= 1
if
$Options
->{silent};
my
$format
=
"Devel::Cover::Report::\u$Options->{report}"
;
if
(
length
$Options
->{report})
{
eval
(
"use $format"
);
if
($@)
{
print
"Error: $Options->{report} "
,
"is not a recognised output format\n\n$@"
;
exit
1;
}
}
$format
->get_options(
$Options
)
if
$format
->can(
"get_options"
);
$Options
->{annotations} = [];
for
my
$a
(@{
$Options
->{annotation}})
{
my
$annotation
=
"Devel::Cover::Annotation::\u$a"
;
eval
(
"use $annotation"
);
if
($@)
{
print
"Error: $a is not a recognised annotation\n\n$@"
;
exit
1;
}
my
$ann
=
$annotation
->new;
$ann
->get_options(
$Options
)
if
$ann
->can(
"get_options"
);
push
@{
$Options
->{annotations}},
$ann
;
}
print
"$0 version $VERSION\n"
and
exit
0
if
$Options
->{version};
pod2usage(
-exitval
=> 0,
-verbose
=> 1)
if
$Options
->{help};
pod2usage(
-exitval
=> 0,
-verbose
=> 2)
if
$Options
->{info};
my
$dbname
= Cwd::abs_path(
@ARGV
?
shift
@ARGV
:
"cover_db"
);
die
"Can't open database $dbname\n"
if
!
$Options
->{
delete
} && !
$Options
->{test} && !-d
$dbname
;
$Options
->{outputdir} =
$dbname
unless
exists
$Options
->{outputdir};
my
$od
= Cwd::abs_path(
$Options
->{outputdir});
$Options
->{outputdir} =
$od
if
defined
$od
;
mkpath(
$Options
->{outputdir})
unless
-d
$Options
->{outputdir};
if
(
$Options
->{
delete
})
{
delete_db(
$dbname
,
@ARGV
);
exit
0
}
if
(
$Options
->{test})
{
delete_db(
$dbname
,
@ARGV
);
local
$ENV
{ -d
"t"
?
"HARNESS_PERL_SWITCHES"
:
"PERL5OPT"
} =
"-MDevel::Cover"
;
my
$test
= test_command();
if
(
$Options
->{gcov})
{
my
$t
= $] > 5.7 ?
undef
:
time
;
my
$xs
=
sub
{
utime
$t
,
$t
,
$_
if
/\.(xs|c|h)$/ };
File::Find::find({
wanted
=>
$xs
,
no_chdir
=> 0 },
"."
);
}
print
STDERR
"cover: running $test\n"
;
system
$test
;
$Options
->{report} ||=
"html"
;
}
if
(
$Options
->{gcov})
{
my
$gc
=
sub
{
return
unless
/\.(xs|c|h)$/;
my
$graph_file
=
$_
;
$graph_file
=~ s{\.\w+$}{.gcno};
return
unless
-e
$graph_file
;
my
$c
=
"gcov $_"
;
print
STDERR
"cover: running $c\n"
;
system
$c
;
};
File::Find::find({
wanted
=>
$gc
,
no_chdir
=> 0 },
"."
);
my
@gc
;
my
$gp
=
sub
{
return
unless
/\.gcov$/;
my
$xs
=
$_
;
return
if
$xs
=~ s/\.(c|h)\.gcov$/.xs.gcov/ && -e
$xs
;
s/^\.\///;
push
@gc
,
$_
;
};
File::Find::find({
wanted
=>
$gp
,
no_chdir
=> 1 },
"."
);
if
(
@gc
)
{
my
$c
=
"gcov2perl @gc"
;
print
STDERR
"cover: running $c\n"
;
system
$c
;
}
}
print
"Reading database from $dbname\n"
unless
$Options
->{silent};
my
$db
= Devel::Cover::DB->new
(
db
=>
$dbname
,
uncoverable_file
=>
$Options
->{uncoverable_file},
);
$db
=
$db
->merge_runs;
$db
->add_uncoverable (
$Options
->{add_uncoverable_point} );
$db
->delete_uncoverable (
$Options
->{delete_uncoverable_point});
$db
->clean_uncoverable
if
$Options
->{clean_uncoverable_points} ;
exit
0
if
@{
$Options
->{add_uncoverable_point}} ||
@{
$Options
->{delete_uncoverable_point}} ||
$Options
->{clean_uncoverable_points};
for
my
$merge
(
@ARGV
)
{
print
"Merging database from $merge\n"
unless
$Options
->{silent};
my
$mdb
= Devel::Cover::DB->new(
db
=>
$merge
);
$mdb
=
$mdb
->merge_runs;
$db
->merge(
$mdb
);
}
if
(
$Options
->{dump_db})
{
my
$d
= Data::Dumper->new([
$db
], [
"db"
]);
$d
->Indent(1);
$d
->Sortkeys(1)
if
$] >= 5.008;
print
$d
->Dump;
my
$structure
= Devel::Cover::DB::Structure->new(
base
=>
$dbname
);
$structure
->read_all;
my
$s
= Data::Dumper->new([
$structure
], [
"structure"
]);
$s
->Indent(1);
$s
->Sortkeys(1)
if
$] >= 5.008;
print
$s
->Dump;
exit
0
}
if
(
exists
$Options
->{
write
})
{
$dbname
=
$Options
->{
write
}
if
length
$Options
->{
write
};
print
"Writing database to $dbname\n"
unless
$Options
->{silent};
$db
->
write
(
$dbname
);
}
return
unless
$Options
->{summary} ||
$Options
->{report};
my
%f
=
map
{
$_
=> 1 } (@{
$Options
->{
select
}}
?
map
glob
, @{
$Options
->{
select
}}
:
$db
->cover->items);
delete
@f
{
map
glob
, @{
$Options
->{ignore}}};
my
$keep
=
sub
{
my
(
$f
) =
@_
;
for
(@{
$Options
->{ignore_re}})
{
return
0
if
$f
=~ /
$_
/
}
for
(@{
$Options
->{select_re}})
{
return
1
if
$f
=~ /
$_
/
}
!@{
$Options
->{select_re}}
};
my
@selected
= @{
$Options
->{file}} =
sort
grep
$keep
->(
$_
),
keys
%f
;
$Options
->{coverage} = [
$db
->collected ]
unless
@{
$Options
->{coverage}};
$Options
->{show} = {
map
{
$_
=> 1 } @{
$Options
->{coverage}} };
$Options
->{show}{total} = 1
if
keys
%{
$Options
->{show}};
CALC:
{
my
$self
=
$db
;
my
%options
=
map
{
$_
=> 1 } @{
$Options
->{coverage}};
my
$s
=
$self
->{summary} = {};
for
my
$file
(
@selected
) {
$self
->cover->get(
$file
)->calculate_summary(
$self
,
$file
, \
%options
);
}
for
my
$file
(
@selected
) {
$self
->cover->get(
$file
)->calculate_percentage(
$self
,
$s
->{
$file
});
}
my
$t
=
$self
->{summary}{Total};
for
my
$criterion
(
$self
->criteria) {
next
unless
exists
$t
->{
$criterion
};
my
$c
=
"Devel::Cover::\u$criterion"
;
$c
->calculate_percentage(
$self
,
$t
->{
$criterion
});
}
Devel::Cover::Criterion->calculate_percentage(
$self
,
$t
->{total});
}
print
"\n\n"
unless
$Options
->{silent};
return
unless
length
$Options
->{report};
$format
->report(
$db
,
$Options
)
}
main