DEPS-0.13/0000755000175000017500000000000010372224537011523 5ustar dwitchdwitchDEPS-0.13/lib/0000755000175000017500000000000010372224537012271 5ustar dwitchdwitchDEPS-0.13/lib/graphincludes/0000755000175000017500000000000010372224537015121 5ustar dwitchdwitchDEPS-0.13/lib/graphincludes/extractor/0000755000175000017500000000000010372224537017134 5ustar dwitchdwitchDEPS-0.13/lib/graphincludes/extractor/perl.pm0000644000175000017500000000430710365263062020436 0ustar dwitchdwitch# This file is part of the DEPS/graph-includes package # # (c) 2005,2006 Yann Dirson # Distributed under version 2 of the GNU GPL. package graphincludes::extractor::perl; use strict; use warnings; use File::Spec::Functions qw(catfile); use base qw(graphincludes::extractor); use graphincludes::params; # This is only a preliminary extractor for perl dependencies. It should # correctly catch: # - use # - require # - require "" # - require '' # - use base qw() # # It misses: # - all other "use base ", including qw(several classes) # - direct uses of "import base" # - require # - do # - statements not the 1st of a line # # It will be confused by "require ", trying to locate a file # with a name corresponding to the un-eval'ed expression. sub get_default_sysincludes { # since this program does mess wih @INC, we must ask another perl # what it thinks @INC is open INC, 'perl -e \'print join "\n", @INC\' |'; my @incs; while () { chomp; push @incs, $_; } close INC; return @incs; } sub pattern { '\.(pm|pl)$' } sub getdeps { my $self = shift; my ($graph) = @_; @ARGV = map {$_->{LABEL}} $graph->get_nodes(); while (<>) { my $dstfile; if (m/^\s*require\s*["'](\S+)["']/) { print STDERR "Found quoted require: $_" if $graphincludes::params::debug; $dstfile = $self->locatefile ($1, '.', @graphincludes::params::inclpath); } elsif (m/^\s*use\s+base\s+qw\(\s*([\w:]+)\s*\)/) { print STDERR "Found use_base: $_" if $graphincludes::params::debug; $dstfile = $1; $dstfile =~ s|::|/|g; $dstfile = $self->locatefile ($dstfile . '.pm', '.', @graphincludes::params::inclpath); } elsif (m/^\s*(?:use|require)\s*([\w:]+)/) { print STDERR "Found use/require: $_" if $graphincludes::params::debug; $dstfile = $1; $dstfile =~ s|::|/|g; $dstfile = $self->locatefile ($dstfile . '.pm', '.', @graphincludes::params::inclpath); } else { next; } if (defined $dstfile) { $graph->record_edge ($ARGV, $dstfile); } else { $self->record_missed_dep ($ARGV, catfile (split(/::/, $1)) . '.pm'); } } } 1; DEPS-0.13/lib/graphincludes/extractor/C.pm0000644000175000017500000000174010365264103017651 0ustar dwitchdwitch# This file is part of the DEPS/graph-includes package # # (c) 2005,2006 Yann Dirson # Distributed under version 2 of the GNU GPL. package graphincludes::extractor::C; use strict; use warnings; use base qw(graphincludes::extractor); use graphincludes::params; use File::Basename qw(dirname); sub get_default_sysincludes { return ('/usr/include'); } sub pattern { '\.([ch](pp|xx|\+\+)?|C|H|cc|hh)$' } sub getdeps { my $self = shift; my ($graph) = @_; @ARGV = map {$_->{LABEL}} $graph->get_nodes(); while (<>) { my $dstfile; if (m/^\s*#\s*include\s*"(.*)"/) { $dstfile = $self->locatefile ($1, dirname($ARGV), @graphincludes::params::inclpath); } elsif (m/^\s*#\s*include\s*<(.*)>/) { $dstfile = $self->locatefile ($1, @graphincludes::params::inclpath); } else { next; } if (defined $dstfile) { $graph->record_edge ($ARGV, $dstfile); } else { $self->record_missed_dep ($ARGV, $1); } } } 1; DEPS-0.13/lib/graphincludes/renderer/0000755000175000017500000000000010372224537016727 5ustar dwitchdwitchDEPS-0.13/lib/graphincludes/renderer/tulip.pm0000644000175000017500000000271010372211224020407 0ustar dwitchdwitch# This file is part of the DEPS/graph-includes package # # (c) 2005,2006 Yann Dirson # Distributed under version 2 of the GNU GPL. package graphincludes::renderer::tulip; use warnings; use strict; use base qw(graphincludes::renderer); use Hash::Util qw(lock_keys); use graphincludes::params; sub new { my $class = shift; my $self = {}; bless ($self, $class); lock_keys (%$self); return $self; } sub printgraph { my $self = shift; my ($graphnode, $nodestylers, $edgestylers) = @_; my $graph = eval { defined $graphnode->{DATA} } ? $graphnode->{DATA} : $graphnode; # give unique numeric IDs to nodes, starting at 1 my $nodecount = 0; my %nodeids; foreach my $node ($graph->get_nodes) { my $label = $node->{LABEL}; unless (defined $nodeids{$label}) { $nodecount++; $nodeids{$label} = $nodecount; } } # declare nodes print '(nodes'; for (my $i=1; $i<=$nodecount; $i++) { print " ", $i; } print ")\n"; # set node labels print "(property 0 string \"viewLabel\"\n"; print " (default \"\" \"\" )\n"; foreach my $node (keys %nodeids) { print " (node $nodeids{$node} \"$node\")\n"; } print ")\n"; # simple edges my $edgecount = 0; foreach my $file ($graph->get_edge_origins) { foreach my $dest ($graph->get_dep_names_from($file)) { $edgecount++; print "(edge $edgecount ", $nodeids{$file}, ' ', $nodeids{$dest}, ")\n"; } } } sub wait { } 1; DEPS-0.13/lib/graphincludes/renderer/dot.pm0000644000175000017500000000733410371750002020050 0ustar dwitchdwitch# This file is part of the DEPS/graph-includes package # # (c) 2005,2006 Yann Dirson # Distributed under version 2 of the GNU GPL. package graphincludes::renderer::dot; use warnings; use strict; use base qw(graphincludes::renderer); use Hash::Util qw(lock_keys); use graphincludes::params; sub new { my $class = shift; my $self = { DOTFLAGS => [], OUTFORMAT => undef, }; bless ($self, $class); lock_keys (%$self); return $self; } my %paper = ( a4 => '11.6,8.2', a3 => '16.5,11.6', letter => '11,8.5', ); #FIXME: also set arrow head my @paperparams = ('-Gnodesep=0.1', '-Granksep=0.1', '-Nfontsize=5', '-Efontsize=5'); sub set_multipage { my $self = shift; my ($papersize) = @_; die "Unkown paper size \`$papersize'" unless defined $paper{$papersize}; # paper output format is postscript on stdout unless otherwise specified $self->set_outputformat ('ps'); push @{$self->{DOTFLAGS}}, @paperparams, '-Gpage=' . $paper{$papersize}; } sub set_outputformat { my $self = shift; $self->{OUTFORMAT} = shift; } sub set_outputfile { my $self = shift; my ($outfile) = @_; push @{$self->{DOTFLAGS}}, '-o', $outfile; $outfile =~ m/.*\.([^.]+)$/ or die "cannot guess output format"; $self->set_outputformat ($1); } sub _node_stylestring { my ($node, $style) = @_; my @attrs; my $label = ($style->{label} or $node->{LABEL}); foreach my $key (keys %$style) { if ($key eq 'label') { next; # already handled } elsif ($key eq 'extralabel') { $label .= "\\n$style->{$key}"; } elsif ($key eq 'bgcolor') { # FIXME: should validate color (use Graphics::ColorNames) push @attrs, 'style=filled', 'fillcolor='.$style->{$key}; } elsif ($key eq 'bordercolor') { push @attrs, 'color='.$style->{$key}; } else { print STDERR "Warning: graphincludes::renderer::dot does not support attribute '$key' for nodes\n"; } } unshift @attrs, "label=\"$label\""; join ',', @attrs; } sub _edge_stylestring { my ($node, $style) = @_; my @attrs; foreach my $key (keys %$style) { if ($key eq 'label') { push @attrs, "label=\".$style->{$key}\""; } else { print STDERR "Warning: graphincludes::renderer::dot does not support style attribute '$key' for edges\n"; } } join ',', @attrs; } # apply sequence of stylers on an object # FIXME: move to where ? sub style { my ($object, $graphnode, $stylers) = @_; my $style = {}; foreach my $styler (@$stylers) { $styler->apply($object, $graphnode, $style); } return $style; } sub printgraph { my $self = shift; my ($graphnode, $nodestylers, $edgestylers) = @_; # $graphnode can be a node in the transform graph, or maybe a simple graph, # allowing to graph the transform graph itself my $graph = eval { defined $graphnode->{DATA} } ? $graphnode->{DATA} : $graphnode; push @{$self->{DOTFLAGS}}, "-T$self->{OUTFORMAT}" if defined $self->{OUTFORMAT}; if (scalar(@{$self->{DOTFLAGS}}) > 0) { my $flags = join ' ', @{$self->{DOTFLAGS}}; print STDERR "Running through \`dot $flags'\n" if $graphincludes::params::verbose; open STDOUT, "| dot $flags"; } print "strict digraph dependencies {\nrankdir=LR;\n"; foreach my $node ($graph->get_nodes) { print "\"$node->{LABEL}\" [", _node_stylestring($node, style($node, $graphnode, $nodestylers)), "]\n"; } foreach my $file ($graph->get_edge_origins) { foreach my $edge ($graph->get_edges_from($file)) { print "\"$file\" -> \"$edge->{DST}{LABEL}\" [", _edge_stylestring($edge, style($edge, $graphnode, $edgestylers)), "]\n"; } } print "}\n"; } sub wait { my $self = shift; if (scalar(@{$self->{DOTFLAGS}}) > 0) { close STDOUT; wait; } } 1; DEPS-0.13/lib/graphincludes/params.pm0000644000175000017500000000057610372223303016740 0ustar dwitchdwitch# This file is part of the DEPS/graph-includes package # # (c) 2005,2006 Yann Dirson # Distributed under version 2 of the GNU GPL. package graphincludes::params; use strict; use warnings; our $VERSION='0.13'; our $verbose=0; our $showdropped=0; our $debug=0; our ($minshow, $maxshow) = (1, 1); our (@focus, @inclpath, @sysinclpath); our $filename_regexp; 1; DEPS-0.13/lib/graphincludes/renderer.pm0000644000175000017500000000116310352504735017265 0ustar dwitchdwitch# This file is part of the graph-includes package # # (c) 2005 Yann Dirson # Distributed under version 2 of the GNU GPL. package graphincludes::renderer; use warnings; use strict; use Carp qw(croak); sub set_multipage { my $self = shift; my $class = ref $self; croak "$class does not know yet how to output a multipage graph"; } sub set_outputformat { my $self = shift; my $class = ref $self; croak "$class does not know yet how to set output format"; } sub set_outputfile { my $self = shift; my $class = ref $self; croak "$class does not know yet how to set output file"; } 1; DEPS-0.13/lib/graphincludes/project.pm0000644000175000017500000001063310371125644017126 0ustar dwitchdwitch# This file is part of the DEPS/graph-includes package # # (c) 2005,2006 Yann Dirson # Distributed under version 2 of the GNU GPL. package graphincludes::project; use strict; use warnings; use graphincludes::params; our @ISA; use graphincludes::graph; use File::Spec::Functions qw(catfile catpath splitdir splitpath canonpath); use Hash::Util qw(lock_keys); use Carp qw(croak); # set_language: class method that sets the language to be used when extracting deps. # This is a hack, which does not allow to mix several languages in a single project. # It is only a temporary measure that allows support for languages other than C/C++. our $_language; sub set_language { my $class = shift; $_language = shift; my $langmodule = "graphincludes::extractor::" . $_language; eval "require $langmodule" or die "cannot load $langmodule from " . join ':', @INC; push @ISA, $langmodule; } sub new { my $class = shift; my %args = @_; my $prefixstrip = $args{prefixstrip}; my @files = map { canonpath($_) } @{$args{files}}; # take a (cleaned up) copy of @ARGV my $self = {}; # if (defined $_language) { # $self = ("graphincludes::extractor::" . $_language)->new; # } $self->{TRANSGRAPH} = new graphincludes::graph; # graph of graph transformations my $graphnode = new DEPS::Node('files'); $self->{TRANSGRAPH}->add_node($graphnode); $self->{ROOTGRAPH} = $graphnode->{DATA} = new graphincludes::graph; # the file dependency graph $self->{ROOTGRAPH}->set_nodes_from_names(\@files); $self->{PFXSTRIP} = $prefixstrip; $self->{SPECIALEDGES} = {}; $self->{IGNOREDEDGES} = {}; # to be computed in getdeps $self->{REPORT} = { HDR => {}, SYS => {}, }; bless ($self, $class); lock_keys (%$self); return $self; } sub init { my $self = shift; $self->getdeps($self->{ROOTGRAPH}); } sub nlevels { return 0; } sub filelabel { my $self = shift; my ($file,$level) = @_; return $file; } sub locatefile { my $self = shift; my ($dst, @path) = @_; print STDERR "Trying to locate \`$dst'\n" if $graphincludes::params::debug; sub fullpath { my ($dstpath, $strip, $srcpath) = @_; catfile (@$srcpath[0..($#$srcpath-$strip)], @$dstpath); } (undef, my $dstdir, my $filename) = splitpath($dst); my @dstpath = (splitdir ($dstdir), $filename); # count number of leading "../" in the #include reference my $strip = 0; while ($dstpath[0] eq '..') { $strip++; shift @dstpath; } # find the file in @path my $dstfile; foreach my $dir (@path) { my @srcpath = splitdir ($dir); if (defined($dstfile = canonpath(fullpath(\@dstpath,$strip,\@srcpath))) and grep { $_->{LABEL} eq $dstfile } $self->{ROOTGRAPH}->get_nodes) { print STDERR " Found from $dir ($dstfile)\n" if $graphincludes::params::debug; last; } else { print STDERR " Not from $dir ($dstfile)\n" if $graphincludes::params::debug; $dstfile = undef; } } return $dstfile; # can be undef ! } sub _fileexists { my ($file, @path) = @_; foreach my $dir (@path) { my $f = catpath('', $dir, $file); return $f if -r $f; } return undef; } sub record_missed_dep { my $self = shift; my ($src, $dst) = @_; if (defined _fileexists ($dst, @graphincludes::params::sysinclpath)) { # list as system include $self->{REPORT}->{SYS}->{$dst} = 1; } else { # list as unknown header push @{$self->{REPORT}->{HDR}->{$dst}}, $src; } } sub special_edge { my $self = shift; my ($src, $dst) = @_; my $attrs = $self->{SPECIALEDGES}->{$src}->{$dst}; if (defined $attrs) { return $attrs; } else { return undef; } } sub apply_transform { my $self = shift; my ($transform, $args, $newname, @graphnames) = @_; # load the required transform package eval "require $transform" or die "cannot load '$transform': $@"; # find graphs from graphnames my @parentnodes = map { $self->{TRANSGRAPH}->get_node_from_name($_) or croak "no graph named '$_'" } @graphnames; my @graphs = map { $_->{DATA} } @parentnodes; # apply transform my $result; eval '$result = '.$transform.'::apply (graphs => \@graphs, %$args)' or croak "transform failed: $@"; # record in project's transform graph my $node = new DEPS::Node($newname); $node->{DATA} = $result; $self->{TRANSGRAPH}->add_node($node); # record deps foreach my $parent (@parentnodes) { $self->{TRANSGRAPH}->add_edge (new DEPS::Edge($parent, $node)); } return $result; } 1; DEPS-0.13/lib/graphincludes/project/0000755000175000017500000000000010372224537016567 5ustar dwitchdwitchDEPS-0.13/lib/graphincludes/project/default.pm0000644000175000017500000000302110364261476020551 0ustar dwitchdwitch# This file is part of the DEPS/graph-includes package # # (c) 2005,2006 Yann Dirson # Distributed under version 2 of the GNU GPL. package graphincludes::project::default; use strict; use warnings; use base qw(graphincludes::project); use Hash::Util qw(lock_keys unlock_keys); use File::Spec::Functions qw(splitpath); use graphincludes::params; sub new { my $class = shift; my $self; $self = $class->SUPER::new(@_); unlock_keys(%$self); bless ($self, $class); $self->{IGNOREDDEPS} = $self->ignored_deps(); lock_keys(%$self); return $self; } sub nlevels { return 2; } sub filelabel { my $self = shift; my ($file,$level) = @_; $level = $graphincludes::params::minshow unless defined $level; $file =~ s/^$self->{PFXSTRIP}// if defined $self->{PFXSTRIP}; if ($level == 0) { return $file; } elsif ($level == 1) { $file =~ s/\.[^.]*$//; return $file; } elsif ($level == 2) { (undef, my $dirname, my $filename) = splitpath($file); if ($dirname ne '') { return $dirname; } else { return '<' . $self->filelabel($file, $level - 1) . '>'; } } return undef; } sub defaultcolors { return (); } sub ignored_deps { return {}; } sub special_edge { my $self = shift; my ($src, $dst) = @_; my $lbl = $self->{IGNOREDEDGES}->{$src}->{$dst}; my $special = $self->SUPER::special_edge($src,$dst); if (defined $lbl) { $special->{color} = 'gray'; $special->{constraint} = 'false'; $special->{label} = [ $lbl ]; } return $special; } 1; DEPS-0.13/lib/graphincludes/project/uniqueincludes.pm0000644000175000017500000000225510370226565022167 0ustar dwitchdwitch# This file is part of the DEPS/graph-includes package # # (c) 2005,2006 Yann Dirson # Distributed under version 2 of the GNU GPL. package graphincludes::project::uniqueincludes; use strict; use warnings; use base qw(graphincludes::project::default); use File::Basename qw(basename); use graphincludes::params; sub nlevels { return 1; } sub filelabel { my $self = shift; my ($file,$level) = @_; $file =~ s/^$self->{PFXSTRIP}// if defined $self->{PFXSTRIP}; if ($level == 0) { return $file; } else { my $filename = basename($file); $filename =~ s!\.[^.]*$!!; # strip suffix return $filename; } } sub getdeps { my $self = shift; my ($graph) = @_; @ARGV = map {$_->{LABEL}} $graph->get_nodes(); LINE: while (<>) { if (m!^\s*#\s*include\s*[<"](.+)[>"]!) { my $dstfile = $1; foreach my $node ($graph->get_nodes) { if ( basename($node->{LABEL}) eq basename($dstfile) ) { if ($ARGV ne $node->{LABEL}) { # skip deps to self, they confuse the reduction checker $self->{ROOTGRAPH}->record_edge ($ARGV, $node->{LABEL}); } next LINE; } } $self->record_missed_dep ($ARGV, $dstfile); } } } 1; DEPS-0.13/lib/graphincludes/graph.pm0000644000175000017500000001456310370213277016566 0ustar dwitchdwitch# This file is part of the DEPS/graph-includes package # # (c) 2005,2006 Yann Dirson # Distributed under version 2 of the GNU GPL. package graphincludes::graph; use strict; use warnings; use Hash::Util qw(lock_keys); use Carp qw(croak); use DEPS::Node; use DEPS::Edge; sub new { my $class = shift; my $self = {}; $self->{_NODES} = undef; $self->{_EDGES} = undef; $self->{_REVEDGES} = undef; $self->{_DROPCOUNT} = 0; $self->{_DROPPEDEDGES} = {}; $self->{_DROPPEDREVEDGES} = {}; bless ($self, $class); lock_keys (%$self); return $self; } sub copy { my $self = shift; my %args = @_; my $class = ref $self; my %nodes = %{$self->{_NODES}}; my $copy = { _NODES => \%nodes, _DROPCOUNT => 0, _EDGES => {}, _REVEDGES => {}, _DROPPEDEDGES => {}, _DROPPEDREVEDGES => {}, }; bless ($copy, $class); if (defined $args{deep_copy_edges}) { foreach my $src ($self->get_edge_origins) { foreach my $dst ($self->get_dep_names_from($src)) { $copy->record_edge($src,$dst); } } } else { # FIXME: copy mode probably too shallow to make any sense ? my (%edges,%revedges); %edges = %{$self->{_EDGES}}; %revedges = %{$self->{_REVEDGES}}; $copy->{_EDGES} = \%edges; $copy->{_REVEDGES} = \%revedges; } lock_keys (%$copy); return $copy; } sub set_nodes { my $self = shift; # store nodes as a hash indexed by label my %nodes = map { ($_->{LABEL} => $_) } @_; $self->{_NODES} = \%nodes; } sub set_nodes_from_names { my $self = shift; my ($files) = @_; $self->set_nodes(map { new DEPS::Node($_); } @$files); } sub record_node { my $self = shift; my ($name) = @_; # sanity check croak "node name must not be an object or reference" if ref $name; $self->{_NODES}{$name} = new DEPS::Node($name) unless defined $self->{_NODES}{$name}; return $self->{_NODES}{$name}; } sub add_node { my $self = shift; my ($node) = @_; # sanity checks unless (ref $node) { printf STDERR "Non-object: %s\n", $node->dump; croak "Trying to add a non-object as node"; } if (defined $self->{_NODES}{$node->{LABEL}}) { printf STDERR "Already have %s\n", $self->{_NODES}{$node->{LABEL}}->dump; printf STDERR "Want to add %s\n", $node->dump; croak "Cannot add another node labelled $node->{LABEL}"; } $self->{_NODES}{$node->{LABEL}} = $node; } sub get_nodes { my $self = shift; values %{$self->{_NODES}}; } sub get_node_from_name { my $self = shift; my ($name) = @_; $self->{_NODES}{$name}; } sub has_node { my $self = shift; my ($name) = @_; defined get_node_from_name($name); } sub record_edge { my $self = shift; my ($src, $dst) = @_; # if (defined $self->{IGNOREDDEPS}{$src}{$dst}) { # print STDERR "ignoring $src -> $dst\n" if $graphincludes::params::debug; # $self->{IGNOREDEDGES}{$src}{$dst} = # $self->{IGNOREDDEPS}{$src}{$dst}; # } # sanity check croak "edge src name must not be an object or reference" if ref $src; croak "edge dst name must not be an object or reference" if ref $dst; unless (defined $self->{_EDGES}{$src}{$dst}) { # more sanity check my $srcnode = $self->{_NODES}{$src} or croak "Source node not found '$src'"; my $dstnode = $self->{_NODES}{$dst} or croak "Destination node not found '$dst' (source was '$src')"; my $edge = new DEPS::Edge ($srcnode, $dstnode); $self->{_EDGES}{$src}{$dst} = $self->{_REVEDGES}{$dst}{$src} = $edge; } return $self->{_EDGES}{$src}{$dst}; } sub add_edge { my $self = shift; my ($edge) = @_; # sanity checks unless (ref $edge) { printf STDERR "Non-object: %s\n", $edge; croak "Trying to add a non-object as edge"; } if ($self->has_edge($edge->{SRC}{LABEL},$edge->{DST}{LABEL})) { printf STDERR "Already have %s\n", $self->get_edge($edge->{SRC}{LABEL},$edge->{DST}{LABEL})->dump; printf STDERR "Want to add %s\n", $edge->dump; croak "Request to add duplicate edge"; } # do add $self->{_EDGES}{$edge->{SRC}{LABEL}}{$edge->{DST}{LABEL}} = $self->{_REVEDGES}{$edge->{DST}{LABEL}}{$edge->{SRC}{LABEL}} = $edge; } sub has_children { my $self = shift; my ($src) = @_; # FIXME: not 100% correct - that could be an empty hash defined $self->{_EDGES}{$src}; } sub has_parents { my $self = shift; my ($dst) = @_; defined $self->{_REVEDGES}{$dst}; } sub get_edge { my $self = shift; my ($src, $dst) = @_; return $self->{_EDGES}{$src}{$dst}; } sub has_edge { my $self = shift; my ($src, $dst) = @_; croak "has_edge: uninitialized src" unless defined $src; croak "has_edge: uninitialized dst" unless defined $dst; defined $self->{_EDGES}{$src}{$dst}; } sub has_path { my $self = shift; my ($from, $to, @seen) = @_; # @seen is a private parameter return ($from) if $from eq $to; return () if grep { $_ eq $from } @seen; return ($from, $to) if $self->has_edge($from,$to); # superfluous ? foreach my $child ($self->get_dep_names_from($from)) { if (my @path = $self->has_path($child, $to, (@seen, $from))) { return ($from, @path); } } return (); # no child (left) to look at } sub drop_edge { my $self = shift; my ($src, $dst) = @_; $self->{_DROPPEDEDGES}{$src}{$dst} = $self->{_EDGES}{$src}{$dst}; $self->{_DROPPEDREVEDGES}{$dst}{$src} = $self->{_REVEDGES}{$dst}{$src}; delete $self->{_EDGES}{$src}{$dst}; delete $self->{_REVEDGES}{$dst}{$src}; } sub get_edge_origins { my $self = shift; keys %{$self->{_EDGES}}; } sub get_edges_from { my $self = shift; my ($origin) = @_; values %{$self->{_EDGES}{$origin}}; } sub get_dep_names_from { my $self = shift; my ($origin) = @_; keys %{$self->{_EDGES}{$origin}}; } sub get_edge_weight { my $self = shift; my ($src,$dst) = @_; return $self->get_edge($src,$dst)->weight(); } sub is_reduction_of { my $self = shift; my ($complete) = @_; print STDERR "Verifying validity of transitive reduction " if $graphincludes::params::verbose; my $ok = 1; foreach my $node ($complete->get_edge_origins) { print STDERR '.' if $graphincludes::params::verbose; foreach my $child ($complete->get_dep_names_from($node)) { if (!$self->has_path($node, $child)) { print STDERR "ERROR: missing edge from $node to $child\n" if $graphincludes::params::debug; $ok = 0; } } } printf STDERR " %s.\n", ($ok ? "ok" : "FAILED") if $graphincludes::params::verbose; return $ok; } 1; DEPS-0.13/lib/graphincludes/graph/0000755000175000017500000000000010372224537016222 5ustar dwitchdwitchDEPS-0.13/lib/graphincludes/graph/grouped.pm0000644000175000017500000000227310371741521020225 0ustar dwitchdwitch# This file is part of the DEPS/graph-includes package # # (c) 2006 Yann Dirson # Distributed under version 2 of the GNU GPL. package graphincludes::graph::grouped; use strict; use warnings; use base qw(graphincludes::graph); use Set::Object qw(); use Hash::Util qw(lock_keys unlock_keys); use Carp qw(croak); sub new { my $class = shift; my $self = $class->SUPER::new(@_); unlock_keys (%$self); $self->{_REVGROUP} = undef; bless ($self, $class); lock_keys (%$self); return $self; } sub register_group_member { my $self = shift; my ($group, $member) = @_; if (defined $self->{_REVGROUP}{$member}) { # already registered: sanity check croak "Node '$member->{LABEL}' already member of group '$self->{_REVGROUP}{$member}{LABEL}'" if $self->{_REVGROUP}{$member} ne $group; } else { # do register $self->{_REVGROUP}{$member} = $group; } } sub who_contains { my $self = shift; my ($member) = @_; $self->{_REVGROUP}{$member}; } sub register_intragroup_edge { my $self = shift; my ($group, $edge) = @_; $group->{intraedges} = new Set::Object unless defined $group->{intraedges}; $group->{intraedges}->insert($edge); } 1; DEPS-0.13/lib/graphincludes/extractor.pm0000644000175000017500000000066010352504735017473 0ustar dwitchdwitch# This file is part of the graph-includes package # # (c) 2005 Yann Dirson # Distributed under version 2 of the GNU GPL. package graphincludes::extractor; use strict; use warnings; use graphincludes::params; sub pattern { '^$' } sub accepts_file { my $class = shift; my $pat = $graphincludes::params::filename_regexp || ${class}->pattern; m/$pat/; } sub get_default_sysincludes { return (); } 1; DEPS-0.13/lib/DEPS/0000755000175000017500000000000010372224537013024 5ustar dwitchdwitchDEPS-0.13/lib/DEPS/Style.pm0000644000175000017500000000142210371136341014453 0ustar dwitchdwitch# This file is part of the DEPS/graph-includes package # # (c) 2006 Yann Dirson # Distributed under version 2 of the GNU GPL. package DEPS::Style; use warnings; use strict; use Hash::Util qw(lock_keys); use Carp qw(croak); sub new { my $class = shift; my $required = shift; my $optional = shift; my %args = @_; my $self = {}; # import %args foreach my $key (keys %args) { if (grep { $key eq $_ } @$required, @$optional) { $self->{$key} = $args{$key}; } else { croak "${class}::new does not understand attribute '$key'"; } } # sanity check foreach my $key (@$required) { croak "no value provided for '$key'" unless defined $self->{$key}; } bless ($self, $class); lock_keys (%$self); return $self; } 1; DEPS-0.13/lib/DEPS/Transform/0000755000175000017500000000000010372224537014777 5ustar dwitchdwitchDEPS-0.13/lib/DEPS/Transform/CompatGroup.pm0000644000175000017500000000653710371740742017610 0ustar dwitchdwitch# This file is part of the DEPS/graph-includes package # # (c) 2006 Yann Dirson # Distributed under version 2 of the GNU GPL. package DEPS::Transform::CompatGroup; use warnings; use strict; use graphincludes::graph::grouped; use Carp qw(croak); sub apply { my %args = @_; # sanity checks if (!defined $args{graphs} or scalar @{$args{graphs}} != 1) { use Data::Dumper; print STDERR Dumper(keys %args); croak "graphincludes::transform::compatgroup applies to a single graph"; } my $src = $args{graphs}[0]; # the low-level graph my $lvl = $args{level}; my $prj = $args{labeller}; my @prev = map { $prj->{TRANSGRAPH}->get_node_from_name($_)->{DATA} or croak "no graph named '$_'" } @{$args{previous}}; my $new = new graphincludes::graph::grouped; # for each low-level node: # - record the group, and the correct $lvl-1 group as ingredient # - record the groups for the llnode dependencies # - record dependencies between the groups, and the correct edge as ingredient # - record intra-node dependencies in groups foreach my $llnode ($src->get_nodes) { my $groupname = $prj->filelabel($llnode->{LABEL}, $lvl); next if !defined $groupname; my ($newnode, $newsubnode) = _register_node ($new, $llnode, $groupname, $lvl, $prj, \@prev); # find the immediate subedge foreach my $edge ($src->get_edges_from($llnode->{LABEL})) { my $newdepname = $prj->filelabel($edge->{DST}{LABEL}, $lvl); next if !defined $newdepname; my ($newdep, $newsubdep) = _register_node ($new, $edge->{DST}, $newdepname, $lvl, $prj, \@prev); # record the dep, as part of node if intra-group, or as edge ingredient my $subedge = $prev[$#prev]->get_edge($newsubnode->{LABEL}, $newsubdep->{LABEL}); if ($newdep eq $newnode) { # the subedge is taken from level $lvl-1, and will not be found there if it was # aleady intra-node at that level, so skip it if we do not find it $new->register_intragroup_edge($newnode, $subedge) if defined $subedge; } else { $new->record_edge($groupname, $newdepname)->add_ingredients($subedge); } } } return $new; } # Hack to get the dependency graph in corect shape. Not to be # mis-used: the compatgroup mechanism allows for inconsistencies if # this fixup is used, when the filelabel function does not define a # proper group hierarchy. sub fixup_dep { my ($graph, $src, $dst, $orig) = @_; $graph->has_edge($orig, $dst) or croak "no dependency exists from $orig to $dst"; $graph->drop_edge($orig, $dst); $graph->record_edge($src, $dst); } sub _register_node { my ($new, $node, $groupname, $lvl, $prj, $prev) = @_; # find the immediate subnode my ($subnode, $prevlevel); for ($prevlevel = $lvl - 1; $prevlevel >= 0 and !defined ($subnode = $prev->[$prevlevel]-> get_node_from_name($prj->filelabel($node->{LABEL}, $prevlevel))); --$prevlevel) { } # hack necessary because prefixstrip is applied to level 0 - we # would need another graph between 0 and 1 for this $subnode = $prev->[$prevlevel]->get_node_from_name($node->{LABEL}) unless defined $subnode; die "No ingredient node found for $groupname for $node->{LABEL}" unless defined $subnode; my $newnode = $new->record_node($groupname); $new->register_group_member($newnode, $subnode); return ($newnode->add_ingredients($subnode), $subnode); } 1; DEPS-0.13/lib/DEPS/Transform/Consolidate.pm0000644000175000017500000000720310371131751017575 0ustar dwitchdwitch# This file is part of the DEPS/graph-includes package # # (c) 2006 Yann Dirson # Distributed under version 2 of the GNU GPL. package DEPS::Transform::Consolidate; use warnings; use strict; use Set::Object qw(); use Carp qw(croak); use graphincludes::graph; sub apply { my %args = @_; my $srcs = $args{graphs}; sub _addnodes { my ($nodeset,$graph) = @_; foreach my $node ($graph->get_nodes) { my $newnode = $node->copy; $newnode->{ORIGINGGRAPH} = $graph; $nodeset->insert($node); } } my $nodeset = new Set::Object; # start with all nodes from lower graph my $prevgraph = $srcs->[0]; _addnodes($nodeset,$prevgraph); # successively add each other one, lower-to-higher level my $newgraph; # exported from the loop foreach my $graph (@$srcs[1..$#$srcs]) { _addnodes($nodeset,$graph); # sanity check: nodes in lower-level graph should not have # ingredients from the set foreach my $node ($prevgraph->get_nodes) { foreach my $ingredient ($node->ingredients) { # FIXME: error message should pinpoint the problem croak "graphs must be ordered from lower-level to higher-level in graphincludes::transform::consolidate" if $nodeset->has($ingredient); } } my %replacements; # track to which node each ingredient node is mapped # remove all nodes that are ingredients of another foreach my $node ($graph->get_nodes) { $nodeset->remove ($node->ingredients); foreach my $ingredient ($node->ingredients) { $replacements{$ingredient} = $node; } } $newgraph = new graphincludes::graph; # add the nodes in the graph foreach my $node ($nodeset->elements) { $newgraph->add_node($node); } # edges from the top graph foreach my $src ($graph->get_edge_origins) { foreach my $edge ($graph->get_edges_from($src)) { if ($newgraph->has_edge($src, $edge->{DST}{LABEL})) { # already added, just add ingredient edge reference $newgraph->get_edge($src, $edge->{DST}{LABEL})->add_ingredients($edge); } else { # create new one $newgraph->add_edge(new DEPS::Edge($graph->get_node_from_name($src), $edge->{DST}) ->add_ingredients($edge)); } } } # add the edges from lower graph, using %replacements foreach my $src ($prevgraph->get_edge_origins) { # internal consistency check croak "edge origin name '$src' is invalid in graph" unless (defined $prevgraph->get_node_from_name($src)); # look in %replacements to resolve groups, # then look for a node by that name to catch the ungrouped my $newsrc = ( $replacements{$prevgraph->get_node_from_name($src)} or $newgraph->get_node_from_name($src) ); foreach my $edge ($prevgraph->get_edges_from($src)) { unless (ref $edge->{DST}) { use Data::Dumper; print STDERR "From $src:", Dumper ($prevgraph->get_edges_from($src)); die; } my $newdst = ( $replacements{$edge->{DST}} or $newgraph->get_node_from_name($edge->{DST}{LABEL}) ); # do not add an edge if there is no match in upper graph # FIXME: check - does it cause problems when there would be a match at upper+1 ? unless (defined $newsrc and defined $newdst) { next; } # ignore intra-node deps next if $newsrc eq $newdst; if ($newgraph->has_edge($newsrc->{LABEL}, $newdst->{LABEL})) { # already added, just add ingredient edge reference $newgraph->get_edge($newsrc->{LABEL}, $newdst->{LABEL})->add_ingredients($edge); } else { # create new one $newgraph->add_edge(new DEPS::Edge($newsrc, $newdst) ->add_ingredients($edge)); } } } $prevgraph = $newgraph; } return $newgraph; } 1; DEPS-0.13/lib/DEPS/Transform/TransitiveReduction.pm0000644000175000017500000000747510371131645021353 0ustar dwitchdwitch# This file is part of the DEPS/graph-includes package # # (c) 2006 Yann Dirson # Distributed under version 2 of the GNU GPL. package DEPS::Transform::TransitiveReduction; use warnings; use strict; use graphincludes::graph; use Carp qw(croak); sub apply { my %args = @_; # sanity checks if (!defined $args{graphs} or scalar @{$args{graphs}} != 1) { croak "graphincludes::transform::transitivereduction applies to a single graph"; } my $src = $args{graphs}[0]; my $reduced = $src->copy(deep_copy_edges => 1); print STDERR "Doing transitive reduction " if $graphincludes::params::verbose; foreach my $node ($reduced->get_edge_origins) { print STDERR '.' if $graphincludes::params::verbose; print STDERR "node $node\n" if $graphincludes::params::debug; if ($reduced->has_children($node)) { my %droppedchildren; # hash (indexed list) of children to drop # first, get the list of children to be dropped my @considered = ($node); foreach my $child ($reduced->get_dep_names_from($node)) { # do not explore children already removed, or some circles cause lost edges next if defined $droppedchildren{$child}; print STDERR " child $child\n" if $graphincludes::params::debug; if ($reduced->has_children($child)) { foreach my $gchild ($reduced->get_dep_names_from($child)) { if ($gchild ne $node and $gchild ne $child) { # XXX print STDERR " gchild $gchild\n" if $graphincludes::params::debug; $reduced->_suppress (\%droppedchildren, $gchild, \@considered, ($node, $child, $gchild)); } } } } # then drop those children we just marked foreach my $child (keys %droppedchildren) { $reduced->drop_edge ($node, $child); } } } print STDERR " $reduced->{_DROPCOUNT} cleared.\n" if $graphincludes::params::verbose; $reduced->is_reduction_of ($src) or die "internal error in transitive reduction (please use --debug)"; return $reduced; } package graphincludes::graph; # FIXME ! sub _suppress { my $self = shift; my ($dropped, # hash (indexed list) of children to drop $suspect, # node to consider this time $considered, # graph nodes already seen, not to reconsider @context) # current path = @_; # Do not consider $suspect twice, prevent looping on circular deps. # We must take care of the special case of the child that led us to # the current node, or we would have to do special things to $gchild return if $suspect eq $context[1] or grep { $suspect eq $_ } (@$considered); push @$considered, $suspect; # mark $suspect for removal if ($self->has_edge($context[0],$suspect) and !defined $dropped->{$suspect}) { if ($graphincludes::params::showdropped) { $self->{SPECIALEDGES}{$context[0]}{$suspect} = {color => "#FFCCCC", constraint => 'false'}; } elsif (grep { $_ eq $context[0] } @graphincludes::params::focus) { $self->{SPECIALEDGES}{$context[0]}{$suspect} = {color => "#FFCCCC"}; } else { $self->{_DROPCOUNT}++; # increment "use count" on each step of the alternate path in @context my $dropped_edge = $self->get_edge($context[0],$suspect); my $weight = $dropped_edge->weight; for (my $i = 0; $i < $#context; $i++) { $self->{_EDGES}{$context[$i]}{$context[$i+1]}->add_ingredients($dropped_edge); } # remove it $dropped->{$suspect} = 1; } print STDERR " --$suspect (", join (',', @context), ")\n" if $graphincludes::params::debug; } # look at $suspect's children if ($self->has_children($suspect)) { foreach my $child ($self->get_dep_names_from($suspect)) { if ($graphincludes::params::debug) { foreach (@context) { print STDERR " "; } print STDERR "$child\n"; } $self->_suppress ($dropped, $child, $considered, (@context, $child)); } } } 1; DEPS-0.13/lib/DEPS/Edge.pm0000644000175000017500000000170210363536045014226 0ustar dwitchdwitch# This file is part of the DEPS/graph-includes package # # (c) 2005,2006 Yann Dirson # Distributed under version 2 of the GNU GPL. package DEPS::Edge; use strict; use warnings; use base qw(DEPS::Object); use List::Util qw(sum); use Set::Object qw(); use Carp qw(croak); # Objects keys of this class are intentionally not locked, since many # attributes will be used for various purposes, and it probably makes # no sense to make things more complicated. sub new { my $class = shift; my $self = {}; ($self->{SRC},$self->{DST}) = @_; # sanity checks croak "DEPS::Edge::new: src not an object" unless ref $self->{SRC}; croak "DEPS::Edge::new: dst not an object" unless ref $self->{DST}; bless ($self, $class); return $self; } sub weight { my $self = shift; if (defined $self->{_INGREDIENTS}) { return ( 1 + sum map { $_->weight() } $self->{_INGREDIENTS}->members() ); } else { return 1; } } 1; DEPS-0.13/lib/DEPS/Ingredientable.pm0000644000175000017500000000146210371732267016304 0ustar dwitchdwitch# This file is part of the DEPS/graph-includes package # # (c) 2006 Yann Dirson # Distributed under version 2 of the GNU GPL. # DEPS::Ingredientable # # A base class for simple hash-based objects that may be the result of # applying a transform to ingredients package DEPS::Ingredientable; use strict; use warnings; use Set::Object qw(); # An ingredient is an object from which the current object is built/derived sub add_ingredients { my $self = shift; my (@ingredients) = @_; $self->{_INGREDIENTS} = new Set::Object unless defined $self->{_INGREDIENTS}; $self->{_INGREDIENTS}->insert(@ingredients); return $self; } sub ingredients { my $self = shift; my @return; if (defined $self->{_INGREDIENTS}) { @return = $self->{_INGREDIENTS}->members(); } @return; } 1; DEPS-0.13/lib/DEPS/Object.pm0000644000175000017500000000124210364260737014572 0ustar dwitchdwitch# This file is part of the DEPS/graph-includes package # # (c) 2006 Yann Dirson # Distributed under version 2 of the GNU GPL. # DEPS::Object # A base class for simple hash-based objects that belong to a graph package DEPS::Object; use strict; use warnings; use base qw(DEPS::Ingredientable); sub copy { my $self = shift; my $class = ref $self; my $copy = {}; foreach my $field (keys %$self) { $copy->{$field} = $self->{$field}; } bless ($copy, $class); return $copy; } sub dump { my $obj = shift->copy; $obj->{ORIGINGGRAPH} = "$obj->{ORIGINGGRAPH}" if defined $obj->{ORIGINGGRAPH}; use Data::Dumper; Dumper $obj; } 1; DEPS-0.13/lib/DEPS/Style/0000755000175000017500000000000010372224537014124 5ustar dwitchdwitchDEPS-0.13/lib/DEPS/Style/Edge/0000755000175000017500000000000010372224537014770 5ustar dwitchdwitchDEPS-0.13/lib/DEPS/Style/Edge/WeightLabel.pm0000644000175000017500000000152710371746522017524 0ustar dwitchdwitch# This file is part of the DEPS/graph-includes package # # (c) 2006 Yann Dirson # Distributed under version 2 of the GNU GPL. package DEPS::Style::Edge::WeightLabel; use warnings; use strict; use base qw(DEPS::Style); use Hash::Util qw(lock_keys unlock_keys); use Carp qw(croak); sub new { my $class = shift; my $self = $class->SUPER::new([], [], @_); unlock_keys (%$self); bless ($self, $class); lock_keys (%$self); return $self; } # FIXME: should be able to specify the level relative to which counting is done sub apply { my $self = shift; my ($edge, $graphnode, $style) = @_; my $nedges = $edge->weight; # FIXME: be more friendly print STDERR "Warning; overriding label from DEPS::Style::Edge::WeightLabel" if defined $style->{label}; $style->{label} = "[$nedges]"; return $style; } 1; DEPS-0.13/lib/DEPS/Style/Node/0000755000175000017500000000000010372224537015011 5ustar dwitchdwitchDEPS-0.13/lib/DEPS/Style/Node/PerGroup.pm0000644000175000017500000000423710371727503017120 0ustar dwitchdwitch# This file is part of the DEPS/graph-includes package # # (c) 2006 Yann Dirson # Distributed under version 2 of the GNU GPL. package DEPS::Style::Node::PerGroup; use warnings; use strict; use base qw(DEPS::Style); use Hash::Util qw(lock_keys unlock_keys); use Carp qw(croak); # Arguments: # attribute (eg. 'bgcolor', 'bordercolor') # valuemap (hash of name => color) # refgraph (reference graph in which to look for names) # FIXME: optionally forbid overriding ? sub new { my $class = shift; my $self = $class->SUPER::new([qw(attribute valuemap transgraph graph refgraph)], [], @_); unlock_keys (%$self); $self->{PATHS} = undef; # cache - double-hash of path-arrays # to the graphs used as base for coloring bless ($self, $class); lock_keys (%$self); return $self; } sub _get_path { my $self = shift; my ($src, $dst) = @_; # look in cache first my $path = $self->{PATHS}{$src}{$dst}; if (!defined $path) { my @path = $self->{transgraph}->has_path($src, $dst); if (@path) { # we have nothing to look in the 1st node in the graph shift @path; # get an graphs from nodes in the transform graph @path = map { $self->{transgraph}->get_node_from_name($_)->{DATA} } @path; # fill the cache and keep path $path = $self->{PATHS}{$src}{$dst} = \@path; } else { croak "no path found from $src to $dst"; } } return $path; } # FIXME: would be more clean and straightforward to apply on a # formalized "group hierarchy" sub apply { my $self = shift; my ($node, $graphnode, $style) = @_; my $path = $self->_get_path($self->{graph}, $self->{refgraph}); # iterate on path to find the node in $refgraph foreach my $graph (@$path) { $node = ($graph->who_contains($node) or $node); } my $checknode; unless (defined ($checknode = $self->{transgraph}->get_node_from_name($self->{refgraph}) ->{DATA}->get_node_from_name($node->{LABEL})) and $checknode eq $node) { return $style; } my $color = $self->{valuemap}{$node->{LABEL}}; if (defined $color) { $style->{$self->{attribute}} = $color; } return $style; } 1; DEPS-0.13/lib/DEPS/Style/Node/GroupStats.pm0000644000175000017500000000257010371745124017465 0ustar dwitchdwitch# This file is part of the DEPS/graph-includes package # # (c) 2006 Yann Dirson # Distributed under version 2 of the GNU GPL. package DEPS::Style::Node::GroupStats; use warnings; use strict; use base qw(DEPS::Style); use Hash::Util qw(lock_keys unlock_keys); use Carp qw(croak); sub new { my $class = shift; my $self = $class->SUPER::new([], [qw(expectednodes expectedeges)], @_); unlock_keys (%$self); # FIXME: sane default values depend on language, and of grouping level $self->{expectednodes} = 1 unless defined $self->{expectednodes}; $self->{expectedeges} = 0 unless defined $self->{expectedeges}; bless ($self, $class); lock_keys (%$self); return $self; } # FIXME: should be able to specify the level relative to which counting is done sub apply { my $self = shift; my ($node, $graphnode, $style) = @_; my $nnodes = scalar($node->ingredients); my $nedges = 0; $nedges = scalar(my @edges=$node->{intraedges}->members()) if defined $node->{intraedges}; if ($nnodes != $self->{expectednodes} and $nedges != $self->{expectedeges}) { # FIXME: be more friendly print STDERR "Warning; overriding extralabel from DEPS::Style::Node::GroupStats" if defined $style->{extralabel}; $style->{extralabel} = '[' . $nnodes . (($nedges != $self->{expectedeges}) ? (':'.$nedges) : '' ) . ']'; } return $style; } 1; DEPS-0.13/lib/DEPS/Node.pm0000644000175000017500000000124110363535701014243 0ustar dwitchdwitch# This file is part of the DEPS/graph-includes package # # (c) 2005,2006 Yann Dirson # Distributed under version 2 of the GNU GPL. package DEPS::Node; use strict; use warnings; use base qw(DEPS::Object); use Carp qw(croak); use Set::Object qw(); # Objects keys of this class are intentionally not locked, since many # attributes will be used for various purposes, and it probably makes # no sense to make things more complicated. sub new { my $class = shift; my $self = {}; ($self->{LABEL}) = @_; # sanity check croak "node name must not be an object or reference" if ref $self->{LABEL}; bless ($self, $class); return $self; } 1; DEPS-0.13/MANIFEST0000644000175000017500000000154310372224176012656 0ustar dwitchdwitchdoc/README.dbk.xml examples/graphincludes/project/wesnoth.pm graph-includes lib/DEPS/Edge.pm lib/DEPS/Ingredientable.pm lib/DEPS/Node.pm lib/DEPS/Object.pm lib/DEPS/Style.pm lib/DEPS/Style/Edge/WeightLabel.pm lib/DEPS/Style/Node/GroupStats.pm lib/DEPS/Style/Node/PerGroup.pm lib/DEPS/Transform/CompatGroup.pm lib/DEPS/Transform/Consolidate.pm lib/DEPS/Transform/TransitiveReduction.pm lib/graphincludes/extractor.pm lib/graphincludes/extractor/C.pm lib/graphincludes/extractor/perl.pm lib/graphincludes/graph.pm lib/graphincludes/graph/grouped.pm lib/graphincludes/params.pm lib/graphincludes/project.pm lib/graphincludes/project/default.pm lib/graphincludes/project/uniqueincludes.pm lib/graphincludes/renderer.pm lib/graphincludes/renderer/dot.pm lib/graphincludes/renderer/tulip.pm Makefile.PL MANIFEST META.yml Module meta-data (added by MakeMaker) NEWS README DEPS-0.13/META.yml0000644000175000017500000000046410372224537013000 0ustar dwitchdwitch# http://module-build.sourceforge.net/META-spec.html #XXXXXXX This is a prototype!!! It will change in the future!!! XXXXX# name: DEPS version: 0.13 version_from: lib/graphincludes/params.pm installdirs: site requires: distribution_type: module generated_by: ExtUtils::MakeMaker version 6.17 DEPS-0.13/examples/0000755000175000017500000000000010372224537013341 5ustar dwitchdwitchDEPS-0.13/examples/graphincludes/0000755000175000017500000000000010372224537016171 5ustar dwitchdwitchDEPS-0.13/examples/graphincludes/project/0000755000175000017500000000000010372224537017637 5ustar dwitchdwitchDEPS-0.13/examples/graphincludes/project/wesnoth.pm0000644000175000017500000000444710231257354021672 0ustar dwitchdwitch# This file is part of the graph-includes package # # (c) 2005 Yann Dirson # Distributed under version 2 of the GNU GPL. package graphincludes::project::wesnoth; use strict; use warnings; use base qw(graphincludes::project::default); use graphincludes::params; sub filelabel { my $self = shift; my ($file,$level) = @_; $level = $graphincludes::params::minshow unless defined $level; # 0: file $file =~ s/^$self->{PFXSTRIP}// if defined $self->{PFXSTRIP}; return $file if $level == 0; # 1: compilation unit $file =~ s/\.[^.]*$//; $file='ai' if $file =~ m/^ai_(move|attack)$/; return $file if $level == 1; # 2: small groups if ($file =~ m!^(variable|server/variable)\.! ) { $file='variable'; } elsif ($file =~ m!^(multiplayer|ai).*!) { $file=$1; } elsif ($file =~ m!^(mapgen|mapgen_dialog|cavegen|map_create)\..*!) { $file='mapcreator'; } elsif ($file =~ m!^(serialization|widgets)/.*!) { $file=$1; } return $file if $level == 2; # 3: big groups if ($file =~ m!^(array|astarnode|config|filesystem|game_config|game_errors|gettext|global|language|log|map|pathfind|pathutils|race|random|serialization|scoped_resource|terrain|thread|tstring|unit|unit_types|util|variable|wassert|wml_separators|(.*/xcoll))$!) { $file='core'; } elsif ($file =~ m!^(clipboard|cursor|font|image|sdl_utils|tooltips|video)$!) { $file='graphics'; } elsif ($file =~ m!^(about|builder|display|events|preferences|show_dialog|sound|theme|widgets)$!) { $file='uicore'; } elsif ($file =~ m!^(ai|game|help|multiplayer|titlescreen)$!) { $file='gameclient'; } elsif ($file =~ m!^(campaign_server|editor|server|tools)/.*!) { $file=$1; } return $file; } sub defaultcolors { my @colors; $colors[2] = { serialization => 'steelblue1', mapcreator => 'gold', widgets => 'linen', multiplayer => 'palegreen', }; $colors[3] = { core => 'steelblue3', graphics => 'peachpuff', uicore => 'lavenderblush', gameclient => 'yellow', editor => 'cyan', server => 'pink', }; return @colors; } sub ignored_deps { return {'src/font.cpp' => {'src/team.hpp' => 'team colors' }, 'src/language.cpp' => {'src/preferences.hpp' => 'split out graph. stuff from preferences'}, }; } 1; DEPS-0.13/doc/0000755000175000017500000000000010372224537012270 5ustar dwitchdwitchDEPS-0.13/doc/README.dbk.xml0000644000175000017500000013307110372222310014476 0ustar dwitchdwitch
DEPS Dependency Extraction and Processing System Formerly graph-includes toolkit Yann Dirson ydirson@altern.org This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 2005 2006 Yann Dirson
In short DEPS is a set of perl libraries which allows to extract dependency information from arbitrary material (eg. program source files), apply various transformations on this graph to make it more readable or put emphasis on one aspect of it, and draw it. The graph-includes tool is the current command-line interface to DEPS. It is quite limited, in that it only extracts dependency information from files, and applies predefined transformations, on which the user has limited influence through flags. A more generic tool is planned, but will probably wait until various aspects of the DEPS design gets finalized. Currently available graph transformations are: multi-level grouping of source files consolidation of groups of different levels, effectively allowing to show eg. files not in any group transitive reduction, drastically reducing the number of edges to be drawn It currently supports graphing the C/C++ #include relationship, and to a certain extent perl inter-module dependencies, using graphviz or tulip.
Important notice This tool has evolved from a 50-line script written for a particular project (Battle for Wesnoth). Although it has been generalized much, there are still somewhat ad-hoc heuristics harcoded here and there, especially in the default project class (see class descriptions below). Although work is under way to make this tool as generic as possible, work still has to be done at all levels. It is still under development, and may not suit your needs (at least, not yet).
Installation instructions Be sure you have a recent version of Perl installed. At least List::Util is missing from versions earlier than 5.8. You can also just fetch this additional package from CPAN if you cannot upgrade. If you notice that another package is missing from your installation, please report it, so it can be listed here. Other required perl modules: Set::Object Installation is like many other perl packages: Sample install session $ perl Makefile.PL prefix=/usr/local $ make $ su # make install Be sure that the directory in which the library modules got installed is in your perl library path. Eg, if graph-includes --version does not give the expected result, try setting the PERL5LIB environment variable to (in the above example) /usr/local/share/perl/5.8.4/. New versions can be found at https://alioth.debian.org/projects/deps/. A darcs repository is available at http://deps.alioth.debian.org/darcs/deps/. To be able to format the produced graphs, you will need one of graphviz and tulip
How to take advantage of this tool to improve your code Graph-includes is only a supporting tool for a refactoring effort. It can be useful in helping a developper to see where he should put its efforts in order to get cleaner and saner dependencies in a project. In this respect, it is quite similar to a microscope: if you don't look at the right place, you won't see anything interesting. But if you start with a small magnifying factor, you can locate regions of interest, and then zoom on those to get to the interesting stuff.
On the spirit of dependency cleanup
First look at a dependency graph When developping a project of medium size (we'll talk mostly C/C++ here, but that will apply to most languages), expecially with many people writing code, it is quite easy to get to a point where each file (out of several tens of hundreds of files) depends on too many other files. The most obvious relation is the #include one. The more #includes a file has, the more time it takes to build - especially when those included files #include themselves a bunch of other files. For a project of about 100 files, just producing a graph of all those files, with arrows representing the #include dependencies, will usually give an unreadable graph, and will show very little about possible improvements. This is why this tool has been written: to make it possible to get to the useful information hidden in this unusable dependency graph.
Looking further A less obvious relation appears more clearly when you consider not files by themselves, but the set of files made of an interface and the matching implementation. Let's consider two such sets, made of the files a.h, a.c, b.h, b.c. a.c includes b.h, and b.c includes a.h, and each implementation, following good practice, includes its own interface. A simple dependency graph as described above would show such a graph: a.c -> b.h \ /| \/ / / \| b.c -> a.h If OTOH we represent those sets of files instead of the files themselves, we now have something like: a <--> b This shows much more clearly that those two modules are intrinsicately related. In many cases, this will express that whenever you use the a.o file resulting from the build of a.c, you'll need to link b.o as well, and vice versa. This will be the case when each file uses the headers to get function prototypes. Then hunting for abusive dependencies will allow, for example, to select with finer grain which of those modules of code will need to go into which executable, thus producing lighter executables. Note that such a reciprocal dependency may not be pathological. Many projects tend to split a large module into several files for clarity, even when those files are inter-dependant. It is much often in cycles of unidirectional dependencies that we find dependencies that should not be there. In other cases, headers would just have been used to access a type definition from b.h, and the associated b.o would not be needed. In such cases, you may want to consider splitting such "low-level" declarations into their own headers. Not only this would simplify the graph, allowing you to get a better grasp on your source code, but it can also lead to faster compilations, since each file will be able include less unrelated definitions.
Tuning the "files" and "includes" parameters Your first run will surely looks somewhat like: graph-includes -o project.ps src/ lib/ You will take care of specifying all directories or individual source files that make up your project. In addition to an initial graph in the project.ps file, which is quite likely to be incomplete by far, you will find a file named project.ps.graph-includes.report. It is a text file, which will help us to finetune ou command-line. Its first section will look something like: General statistics: ------------------- 412 files, 353 nodes (14% dropped) 245 dependencies, 137 edges (44% dropped) 225 leaf node(s) 280 dependencies not found 0 dependencies identified as system headers As you can see, many dependencies are declared as "not found". What happens is quite similar to running a C compiler without any -I flags: most header files are not located. We have in graph-includes two different flags to specify paths where to look for the dependencies. -I (aka -Include) specifies directories that are part of the project, and will allow to find all of our include-style dependencies. OTOH, -sysI (aka -sysInclude) specifies system directories; included files found in such a directory will of course not result in an intra-project dependency, and will add no edge to our graph, but will stop being displayed as part of the "dependencies not found" count. Thus, they will help us to see how far we are from specifying all the -I flags. Now you will most likely require several iterations of adding -I/-sysI flags and checking the results. But that alone may not be sufficient to reach the ultimate "0 dependencies not found": multi-platform source often have conditional #include directives, and eg. win32 headers will probably not be located on a Un*x box. some generated files will require the source tree to be configured in some way, or even to be partly or completely built (eg. config.h generated by a "configure" script, or Qt source generated by the meta-object compiler) When you are confident that those remaining missing dependencies are system headers for other platforms, you can go on and look at the graph.
Possible strategies to help locating abusive dependencies Keeping in mind that we are essentially looking for dependency loops, we expect to obtain in then end a graph that will be wihout cycles, that is, with all (or, at least, most of) arrows pointing from left to right in our graph. Then we will look for those arrows pointing backwards, as a sure sign for a cycle. Remember that if the cycle is not a long one, it may be legitimate; only if you judge that some of the modules in this cycle are really unrelated, should your consider it pathological. Those backward arrows are not necessarily directly pointing to the abusive dependency, but they can surely be used to locate the culprit: by finding the various cycles of which our backward arrows are part of, and checking one by one all the dependencies in those cycles, you can bet at least one that, with some work, could be cleared. Then, the way to modifications to do are really dependant on your code. Some possibilities include: removing an #include which is not necessary, perhaps remaining from a revious code reorganization splitting a file in two parts, when you can easily split the components of the file into distinct sets. One productive distinction to look for is to find a couple of really high-level parts, that not all the parts depending on this module would need. This will most probably be related to the dependency that you found abusive when looking at the cycle in the graph.
Tool architecture
Overview: Graph-includes was initially developped with only a handful of ideas, and then started to grow as I noticed where useful things were missing. That initial phase was useful for me to get a grasp on the domain of dependency graphing, and provided the ground for a (hopefully) decent design, which still has to be completely implemented. Together with blocking issues marked + in the TODO list, the implementation of this design shall be the goal for a 1.0 release. The planned design is architectured as successive layers, all of which should be pluggable to allow a high degree of customization. source locator | v language selector | v dependency extractor | v graph transformations | v styling | v layout engine | v rendering We will then be able to consider DEPS as being made of a number of parts: core classes and glue, implementing the above design standard or third-party classes, doing the real work command-line and gui tools to allow easy use of the whole This will hopefully make it easy for anyone to plug their own work at any place in the architecture.
State of things Currently, transformations, styling and the renderer are properly customizable. A couple of transformations and styles are already available. The extractors are customizable as well, but the currently implemented mechanism is far from being generic enough. Language selectors are intermixed with extractors. The current source locator is a local-tree one (alternatives would include SCM-aware locators), is hardcoded in the graph-includes script, and takes parameters from per-language extractors to find files. There is no distinction (yet ?) between the layout engine and the renderer. In fact, it may not be easy to do this, since most layout engines are tied to a particular renderer.
Command-line usage for <command>graph-includes</command> See "graph-includes --help". Please note that this tool does not allow do access the full power and flexibility of DEPS. A new one, to be called deps-grapher is planned for subsequent releases, but not enough of the new design has been implemented yet to start working on this new tool.
output type The default output is a .dot file on standard output, suitable for formatting by dot (from the graphviz toolkit), or interactive editing by dotty (also from graphviz). Alternatively, a graph file for the Tulip graph visualizer can be generated instead using "--renderer=tulip". You can ask graph-includes to do the formatting for you, eg. using "--output=<file>.<suffix>". It will run "dot -T<suffix>", so that "--output=mydeps.ps" or "--output=mydeps.jpg" will have the expected behaviour. If your suffix is not known to dot, it will complain itself, so asking for --output=foo.bar will cause a message like: Warning: language bar not recognized, use one of: canon cmap cmapx dia dot fig gd gd2 gif hpgl imap ismap jpeg jpg mif mp pcl pic plain plain-ext png ps ps2 svg svgz vrml vtx wbmp xdot If you intend to print the result on paper, the default layout will likely be too large. You can use --paper=a4 to select parameters that will produce a smaller graph and spilt it into pages. This flag also changes the default output format to postscript. Be warned that dot may not honor the page-splitting parameter for all output formats. Since the transitive reduction can take time, you may like the --verbose switch, which will show a progress bar.
what to draw The files to be analyzed are given as non-option arguments, and can be explicitely specified, or found by recursing in directories. Eg, to analyse foo.c in current directory, as well as all C/C++ files in the src/ directory, use: $ graph-includes foo.c src/ When an directory argument is specified, it is searched for files whose name matches a specific regexp pattern, whose default value depends on the specified language (see --language below). This pattern can be overriden using the --fileregexp option. Eg, to match in addition to .c and .h files, those with an additional .tmpl suffix, you could write: $ graph-includes -I src -fileregexp '\.[ch](\.tmpl)?$' src/ How dependencies get extracted from the source files depend on the language used in those files. You can specify it with the --language flag. Default value is C (which should also be used for other languages based on the C preprocessor, like C++). There is also some partial support for perl - see comments in lib/graphincludes/extractor/perl.pm for more details. In order to tell the #include resolver where to look for included files, you can use the cpp-like -I (aka. --Include) flag. Eg: $ graph-includes -I src src/ Dependencies not found in the project (ie. files appearing in #include but not given on command-line) are listed as "not found" in the graph-includes.report file for diagnostics purposes, unless they are found in a system directory. System directories are declared in a similar fashion, with the --sysInclude option. Eg: $ graph-includes -I src -sysI /opt/foo/include src/ Language extractor have some knowledge about default system include dirs: the C extractor knows about /usr/include, and the Perl extractor asks perl itself. To avoid having useless information on the graph, --prefixstrip=<prefix> can be used to avoid repeating a given prefix in all node labels. Typically: $ graph-includes --prefixstrip=src/ src/
how to draw Files and their inter-dependency build up the first of all graphs, named files. Transformations can then be applied to such graphs, producing new graphs. Production of those new graphs define a transformation graph, whose nodes are our graphs, and whose edges denotes which graphs were used in producing which other graphs. Typically, files will be grouped in a hierarchy of groups. "Level 0 groups" typically containing just one file, are the nodes of the files graph. Groups hierarchies are defined by the selected project class, selected by the --class=<class> option. See below for descriptions of the project classes available by default, and for instructions to write customized project classes. In graph-includes 0.11 and earlier, when a given node of level n is not part of a group at level n+1, it still appears at that level, as if it was the only member of a group. This is not the case any more. To achieve a similar result in DEPS 0.12, the relevant graphs must be merged using the consolidate transformation. The range of group levels to be drawn is selected with --consolidate=<min>-<max>, which defaults to 1-1. Eg, for class "default", whose group levels are defined as: 0 one file per group 1 what/ever.* go into a what/ever group (usually interface + implementation) 2 what/* go into a what group, supposing top-level directories denote modules of some sort Group levels below "min" or above "max" are not displayed as nodes. If a file is not a member of any group between "min" and "max" levels, it will simply not be represented. Another way of using the grouping feature is to color nodes according to the group(s) they belong to, using a class-defined color scheme, possibly modified by --color <n>:<label>=<color>[,<label>=<color>…] options, where <n> is the group level in which the group name <label> will receive a background of the specified color, which can be defined either by a named X11 color (like "blue" or "palegreen"), or by a RGB color using the standard X11 "#RRGGBB" syntax. The number of grouping levels to be colored is limited by the renderer to be used. As of 0.11, the dot renderer only supports coloring 2 group levels. Groups of a lower level than the minimal level requested to --consolidate cannot be colored, for obvious reasons. For those wanting to see what edges the transitive reduction dropped, the --showdropped will add them to the graph in a different color. Be prepared for your computer room to get a noticeable temperature increase for anything else than a small set of files with only few dependencies. OTOH, will do the same, but only for the dependencies of a specified node. That should prevent the nasty effects described above, and will be useful for various purposes, including debugging the transitive reducer. People still getting cold may also like to circumvent the transitive-reduction engine completely, using --alldeps. The author assumes no responsibility for losses of mental health induced by trying to make any serious use of the resulting graph.
Existing plugins
Transformations
CompatGroup This transformation is a temporary one, allowing to use existing project classes (defining groups through a filelabel() method), to be used in the new design.
Consolidate Standard group transformations may not include all nodes from a graph into group nodes. Thus, a group graph does not as such describe all the files fed into the tool. This transformation will merge graphs describing different grouping levels, so that each node from the lowest-level graph requested will be represented by a node in the result graph: if it is part of a group, the highest-level group of which it is a member will be appear, if not the node itself will appear instead.
TransitiveReduction Dependency graphs are often hard to read because of the large number of edges. Since the depndency relation can be seen as being transitive (if A depends on B and B on C, then A depends on C), we can make the graph more readable by not drawing those edges which the transitivity property makes redundant. This is what this transformation achieves.
Styles
Node styles
PerGroup Allows to set style attributes (eg. color) based on group membership.
GroupStats Adds in group nodes some statistics: the number of nodes that make up this group, and the number of edges between those member nodes (ie. dependencies internal to the group). Note that this is relative to the group-level immediately below in the group hierarchy, NOT to the files themselves - parameters will be added later to makes this more customizable.
Edge styles
WeightLabel Adds a label on edges, showing how many dependencies between the lowest-level nodes (eg. effective number of include relationship between files) are represented by each edge. Contrast this with the intra-group counts currently reported by GroupStats on nodes.
Project classes The concept of project classes will most probably be replaced by project files in the near future, as functionality currently located there will move into more adequate areas.
class "default" As implied by its name, it is the one which will be used unless you use the --class option. Although it is the default one, it may still be quite rough at the moment, still using some ad-hoc heuristics, and will be improved in the near future. Here are its main characteristics: looks at C-style #include lines creates level-1 groups for all files sharing the same path and (disregarding the suffix) filename. Eg, files "foo/bar.c" and "foo/bar.h" would be grouped in a "foo/bar" level-1 group. In clear, it won't connect include files if they are all located in an include/ directory. creates by-directory level-2 groups. Eg. in the above example, a group "foo" would exist at level-2.
class "uniqueincludes" Built on top of the default class, it is meant for projects where file names are kept unique across all directories. If the ad-hoc #include processing of the default class does not suit your project, it is the only out-of-the-box alternative available today. Here are its main characteristics: provides a single grouping level based on filenames, disregarding all the directory hierarchy. Note that it is not meant for general use, as: it will group any files with the same name in the same level-0 group, possibly causing confusion. it does not make any directory name appear in the node names
Examples of use
Pure command-line examples
Graphing graph-includes itself: $ ./graph-includes -lang perl -I lib -prefixstrip lib/ -o deps.ps graph-includes lib/ graph-includes does not know in advance which classes it will use
Rather clean ones: a rather clean dependency graph Maelstrom-3.0.6$ graph-includes -v -sysI /usr/include/SDL -I . -I ./netlogic -I ./maclib -I ./screenlib -o deps.ps . more work has to be put in the wesnoth example class: wesnoth-0.9.1$ graph-includes -v --class wesnoth --consolidate 1-1 -sysI /usr/include/c++/3.3 -sysI /usr/include/SDL --prefixstrip src/ -I src -o deps.ps src/
Examples only here as a reminder to write proper project classes for them needs supporting features for multi-arch source trees: qemu-0.7.0$ graph-includes -v -sysI /usr/include/SDL $(find -name CVS -prune -o -type d -printf -I %p\n) -o deps.ps . needs proper file-grouping: mesag-6.2.1$ graph-includes -o -I ./include -I ./include/GL -I ./src/mesa -I ./src/mesa/main -I ./src/glu/sgi/include -I ./src/glu/sgi/libnurbs/internals -I ./src/mesa/glapi -o deps.ps .
Customization examples See graphincludes::project::wesnoth in the examples/ dir as an example of a custom project class. Keep in mind that the API is not frozen yet, and will probably be overhauled more than once before an official API gets blessed.
Caveats This toolkit only handles explicitely-declared dependencies, it won't detect it if eg. a prototype was cut'n'pasted instead of using the correct #include, but you shouldn't do that anyway :) Dynamic dependencies computed at runtime cannot be detected by current plugins. This explains why the graph for DEPS itself is so particular.
TODO
general continue merging the verbose/debug behaviour into the global report file continue moving classes into DEPS namespace as the API stabilizes finalize filename portability support, using File::Spec volume information (or, possibly better but using non-core module, using Path::Class)
core engine allow to associate attributes to files (eg. an ARCH attribute for multi-architecture trees, like kernels, development tools and emulators) modularization (finish the restructuring into a cleaner and more modular design) allow passing options to modules (-O param=value ?) allow to define several views in a project, several of which can be generated by default. find out whether we can declare protocols/pure-virtual-classes in some way, to cleanup the class graph generalize --prefix-strip give consistent access to all commonly-needed features through command-line and class customization Maybe allow to use as nodes other objects than files (eg. URI objects ?), for ultimate generalization. Provide a mechanism for any transform to record informations for the report file. Allow to plug verifiers (probes ?) in a clean way (eg. verify the transitive reduction, or any consistency check)
graph-includes tool allow to run from a build directory --class does not allow to find the project file (need to set PERL5LIB) caller must prepend path to source tree to --prefixstrip and all relative -I flags find the accessory classes as easily as possible (like bugzilla ?) better robustness to incorrect arguments (eg. --consolidate 1:2) automate --help production (see Pod::Usage ?) multi-sheet paper support may be broken use an existing source of paper formats (libpaper, LC_PAPER, whatever) maybe use graphviz' tred(1) to check our transitive reductions, and/or as alternative transform plugin. autodetection of the language to use based on filenames provide an initial list of system directories to avoid repeating them (ask compiler)
extractors allow -I syntax for programs using eg. -I. from source subdirectory write other extractors (java, python, …) C-like extractor some support for CPP symbol conditionals (mostly #ifdef), perhaps coupling this with attributes write an openc++-based dependency extractor extract more fine-grained dependency (depending on a header does not necessarily imply depending on code) handle (warn about) the case where the declarations for a given implementation file are scattered in more than one header detect undeclared dependencies (eg. manually inserted prototypes) check necessity of declared includes perl extractor remove arbitrary limitations report use/require lines we could not completely parse do some invariant analysis when importing a module using a variable name. Investigate drawing 1->n links in this case.
project classes proper way to define include paths in project class make default project-class consider multiple levels of directories as group levels, but only if they (consistently ?) have multiple subgroups ? write a linux-kernel class and others as examples :) provide a simple hash-based filelabel implementation
grouping Abstract the grouping process into GroupHierarchy objects Provide a generic regexp-based grouper provide tools for automatic grouping (eg. using cycles, or selected external deps, or from leaves) keep group members as close as possible (style) give more weight to intra-group edges Improve transitive reductions implying multiple edges into a single cycle, to prefer an edge into the lowest-level common group
styling specify restrictions on allowed deps at a given grouping level, and show violations in red. allow styling through font color, node shape (dot/tulip), number of peripheries (dot) allow to draw non-consecutive group levels (eg. --consolidate 1,3) (tool issue to be resolved by deps-grapher) allow different node shapes when mixing high-level nodes with lower-level ones through the default singleton groups (Node::PerGraph style, similar to PerGroup) optionally show labels (using attributes ?) for node ingredients and color edges according to them optionally show external deps (deps on files not on command-line) limit graph to one or more given group(s) of files (specified by <level>:<label>) draw cycles in a given color draw a specific path provide automatic coloring schemes color intra-group edges with the same color as nodes (post-processing ?) allow to request drawing of who in a high-level node points to another node (ie. violates some constraint) propagate excuses in some way when they are dropped by the transitive reducer investigate candidate tools for hyperbolic layout ? allow to show the count of deps in a given edge using line width instead of labels
documentation write more documentation
testsuite write a testsuite. ensure that all provided non-abstract classes are self-contained
gui The standard GUI should be able to navigate the project definition, visualizing the hierarchy of groups, (un)folding groups and edges, displaying single culprits from a group, and anything you can think of. For an engine, maybe with graphviz' lefty, or write a specialized tulip gui ? A self-customizable GUI like entity may be a good idea. Since entity has support for OpenGL areas, maybe it can be made to embed tulip graphs.
Known bugs volume names in paths are not handled yet (eg. on windows) on windows, backslash path separator in -prefixstrip argument interferes with regexp ("Trailing \ in regex m/ ... at wesnoth.pm line 21") (is that really a bug, or just a normal side-effect of cycles ?) traversal counts on edges are unreasonably high on cycles (real graph traversal issue, but unlikely to get fixed before we get another visual way to spot cycles ;) the colored style of a node of level < min is not shown when that node is displayed because it is not part of any node > min and < max. (is that still true now that we use transforms and the like ?) --showdropped mode draws too many edges as dropped (ie. does not consider marked edges as dropped when deciding whether to consider subsequent edges as dropped) transitive reduction may not be complete, some more edges could possibly be dropped - wesnoth tree at 2005-03-25 exhibits the problem with the "display -> builder -> animated -> image" path
DEPS-0.13/NEWS0000644000175000017500000002432310372223667012231 0ustar dwitchdwitchVersion 0.13 - 2006-01-08 End-user-visible changes * Added a node style to display the number of ingredient nodes and of internal edges in a group (GroupStats), activated by default. * Updated documentation. Bugs fixed: * Ship all files (the 0.12 tarball missed all transforms and styles !) * The path-handling functions in the default project class were confused by leading "./" in paths and failed to idendify some dependencies. * The root graph can now be included in arguments to Transform::Consolidate, and "--consolidate 0-" is now valid. * The tulip renderer had been broken by an API change in 0.12. * Workaround for a strange behaviour or ActivePerl 5.8.7 on Windows, which caused dependencies in subdirs not to be found. * Ingredientable::ingredients() method now always returns a list, even in scalar context when there is only one ingredient. Changes for the plugin writer: * New node style attributes "label" and "extralabel" for the dot renderer. * Added support for edge styling, and support for "label" attribute in the dot renderer. New WeightLabel edge styler to replace old ad-hoc code. * Class moves and renames: transforms, styles. ================================================== Version 0.12 - 2006-02-03 End-user-visible changes * Start of a renaming of the whole toolkit to DEPS (Dependency Extraction and Processing System), graph-includes being only the current user frontend. * Graphs of grouped nodes now only include the defined groups. The new --consolidate flag is now available to merge such graphs in a manner such that nodes of a given group-level, not included in any higher-level group, are shown as well. As a side-effect, this flag also controls the highest group-level of the nodes to be drawn, in place of the --group flag. * The --group flag to render groups as node clusters is not available any more. It will come back in a later release, under a more suitable name. * The documentation previously found in the README file is now available in DocBook XML format, in the doc/ directory. Bugs fixed: * Fixed the uniqueincludes project class, which has been broken since the 0.6 reorganization. * Calculation of edge weights was wrong. Changes for the class writer: * A project is now made of a set of graphs, themselves connected as a graph representing the transformations used to produce them from the other graphs. Most APIs changed consequently. * Projects are now expected to provide a nlevels() method matching the number of grouping levels defined, and the filelabel() methods are now allowed to return undef when a node does not belong to any group at the requested level. * Arbitrary transformations and styles can now be defined, although no facility is provided yet by the master graph-includes script to apply them, short of editing the script. Currently available transformations are "compatgroup" (use the old filelabel() mechanism to define grouping levels), "consolidate" (merge several group levels in a single graph to show non-grouped nodes as well), and "transitivereduction". The only currently available styler allows to apply style attributes (like colors) to a node according to the group it belongs to at a given grouping level. Under the hood: * Objects now get their hash locked in the constructors, to catch typos, unwanted modifications, and other errors likely to occur during subsequent refactorings. * Introduced graph, node, and edge objects (splitted out from project, along with relevant methods). A project now has a stack of graphs. * Many other changes, fixing some bugs, and introducing brand new ones. ================================================== Version 0.11 - 2005-12-06 End-user-visible changes * Generalized support for node coloring, with renderer::dot supporting 2 coloring levels (background and outline) * Improved portability to non-UNIX platforms, was successfully run on win32. * The C extractor now looks in for system headers in /usr/include by default. * The Perl extractor uses the default @INC value to locate system headers. Changes for the class writer: * Per-language extractors can now declare a default system-include path. Bugs fixed: * The --color flag was broken. ================================================== Version 0.10 - 2005-11-29 End-user-visible changes * Added a --renderer flag to select another renderer than dot. * Added preliminary support to use tulip as renderer. ================================================== Version 0.9.2 - 2005-11-2 * Start of a "tutorial" or "how to use" section in the doc. * Explicitely written down the plan for the upcoming redesign. ================================================== Version 0.9.1 - 2005-06-01 Bugs fixed: * Fixed a typo preventing the recursive search to see .c and .h files. ================================================== Version 0.9 - 2005-06-01 Bugs fixed: * Now fails nicely when no dependencies are found for any reason. Other end-user-visible changes * Command-line and version are now logged in the report file. * New --version flag. * Can now be run from source dir with no effort. * Exit with usage on command-line. * Updated usage summary with recent options. ================================================== Version 0.8 - 2005-05-26 End-user-visible changes * Directory arguments are now searched for files, allowing to handle projects with a large number of files. * New --fileregexp option to override the per-language regexp used to look for files inside directories. * More statistics are available in the report file. Changes for the class writer: * extractors' getdeps methods cannot rely any more on @ARGV containing the files list, and must get it from the FILES instance variable. ================================================== Version 0.7 - 2005-05-10 Bugs fixed: * The '.graph-includes.report' suffix used when using --output did not include the first dot. Other end-user-visible changes * Visible edges are now labelled with the number of real dependencies they represent, so we have some visual feedback of transitive reduction. * Huge performance boost for the transitive reduction. * When cycles are present, the specific reduction selected is usually a different one than in 0.6.1 and below, as a side-effect of implementation changes. * More documentation has been written. Changes for the class writer: * project::record_missing_dep was split out of project::record_dep. * Dependencies are now stored in a hash instead of an array. * The "label" special_edge attribute is now an array of strings, to be presented on several lines. ================================================== Version 0.6.1 - 2005-04-26 Bugs fixed: * Fixed the distribution which misses the C and perl extractors. ================================================== Version 0.6 - 2005-04-24 Bugs fixed: * A typo in the graphincludes::params package prevented the parameters default values to be used. Command-line changes: * New --language flag to select syntax of source files. * Preliminary support for extracting perl dependencies, using "--language perl" Changes for the class writer: * C-specific behaviour is now located in new extractor::C class. * Changed special_edge() return value to an hash of graphviz node attribute/value pairs * More methods were moved from project::default up to ancestor classes. ================================================== Version 0.5 - 2005-04-20 Command-line changes: * --Include is now recognized as long form for -I. * New --sysInclude option to specify system directories. Included files not found in the project, but found in those directories are not considered as "not found". Other end-user-visible changes * Removed inconditional duplicate warnings flooding the output. * Verbose diagnostics are now output unconditionally into a graph-includes.report (or .graph-includes.report) file. Changes for the class writer: * The constructor for the project classes now uses named parameters, instead of positional parameters. * Global parameters were moved to a new graphincludes::params package. ================================================== Version 0.4 - 2005-04-17 Command-line changes: * Implemented cpp-like "-I " syntax for #include lookup, dropping the infamous former ad-hoc heuristic. Other end-user-visible changes * `#include "..."' are now analyzed using the standard cpp semantics, and `#include <...>' line are now considered as well. Under the hood * Many code cleanups. ================================================== Version 0.3 - 2005-04-14 Bugs fixed: * Fixed a bug in the ad-hoc #include resolver which caused some dependencies to be ignored Command-line changes: * New --paper option to get a graph printable on paper (for now, only a4, a3 and letter) Other end-user-visible changes * More documentation has been written. ================================================== Version 0.2 - 2005-04-01 Bugs fixed: * --output would reject its argument. * --focus and --showdropped caused invalid output. Command-line changes: * New --prefixstrip option to make the graph more readable by stripping a common prefix from all filenames. * Obsolete (pre 0.1 !) --allfiles option was completely dropped. Other end-user-visible changes * More documentation has been written. * The default project-class (and the sample wesnoth class) now provide default singleton level-2 groups (so "--group 2-2" should work on all projects). Changes for the class writer: * The tool expect a project class to build its dependency graph in an init() method, instead of in the constructor. * The project class constructor now takes an additional prefixstrip argument. More changes to come in this area to cause such changes to be less disruptive in the future. * A project class can now specify excuses for abusive dependencies, to be shown in as edges of a different color, and with the excuse as a label. ================================================== Version 0.1 - 2005-03-28 Initial public release. DEPS-0.13/graph-includes0000755000175000017500000002413010372206754014357 0ustar dwitchdwitch#!/usr/bin/env perl # graph-includes - create a graphviz graph of source-files # dependencies, with an emphasis on getting usable graphs even for # large projects # (c) 2005,2006 Yann Dirson # Distributed under version 2 of the GNU GPL. use warnings; use strict; use File::Basename qw(dirname); use File::Spec::Functions qw(catdir canonpath); use lib catdir(dirname($0), 'lib'); #BEGIN { print STDERR '@INC=', join (':', @INC)} use Getopt::Long qw(GetOptions); use List::Util qw(sum); use File::Find qw(find); use graphincludes::params; our $showalldeps=0; our $class='default'; our $language='C'; our (@colors, @nodestylers, @edgestylers); our ($outfile, $prefixstrip, $paper); our $rendererclass = 'graphincludes::renderer::dot'; our $usage = <} Select "class" of source code -language Select language syntax for dependency extraction (default: C) -fileregexp Use this regexp to identify interesting files inside directories (overrides per-language default regexp) -Include Adds a directory to the path where to look for project's include files -sysInclude Adds a directory to the path where to look for system include files -prefixstrip Strip (eg. "src/") from filenames in the graph -consolidate - Consolidate file groups of levels through (default: 1-1) -color :