90-class-dependencies: Difference between revisions
Jump to navigation
Jump to search
(class-dependencies) |
No edit summary |
||
Line 1: | Line 1: | ||
<pre> | |||
#!/usr/bin/perl | #!/usr/bin/perl | ||
Line 262: | Line 264: | ||
=cut | =cut | ||
</pre> |
Revision as of 13:02, 3 July 2010
#!/usr/bin/perl =head1 NAME fai-deps - class dependencies for FAI =head1 SYNOPSIS fai-deps [-h] [--man] [-d] =head1 ABSTRACT implements dependencies between fai classes. =head1 DESCRIPTION fai-deps uses files $FAI/class/*.deps to sort the classes in $LOGDIR/FAI_CLASSES and define additional ones. While doing so, it retains the original order as much as possible. *.deps files may contain class names, seperated by whitespace. Comments ( after # or ; ) are ignored e.g. you have a class WORDPRESS that depends on the classes VHOST and POSTGRES . VHOST again may depend on WEBSERVER. So if you want to install the blogging software wordpress, you add a file $FAI/class/WORDPRESS.deps that contains the words VHOST POSTGRES and a file $FAI/class/VHOST.deps that contains the word WEBSERVER The order often is important, so this script is taking care of it. The order of the example above would be: WEBSERVER VHOST POSTGRES WORDPRESS That way, in $FAI/scripts/ first the webserver would be configured, then the vhosts, ... It removes double entries from FAI_CLASSES and handles circular dependencies[1]. I do not recommend using circular dependencies, but if you accidentally define them, they will not break your neck. But you'll get a warning ... =head1 ENVIROMENT One non-standard perl module is required: Graph::Directed; On debian install libgraph-perl The following enviroment variables are used: $LOGDIR : path to fai temporary files $FAI : path to fai config space =cut BEGIN { unless ( $ENV{FAI} and $ENV{LOGDIR} ) { print STDERR '$ENV{FAI} and $ENV{LOGDIR} are not defined', $/; print STDERR 'This script should be called from within fai', $/; exit 1; } } use strict; use warnings; use lib "$ENV{FAI}/lib"; use Getopt::Long; use Pod::Usage; use Graph::Directed; #use Text::Glob qw(match_glob); #use Data::Dumper; #use GraphViz; my %opts; GetOptions( \%opts, 'help|h', 'man', 'debug|d' ) or pod2usage(1); pod2usage(1) if $opts{help}; pod2usage(-verbose => 2) if $opts{man}; my $debug = $opts{debug}; my $fai_classes_file = "$ENV{LOGDIR}/FAI_CLASSES"; my $class_dir = "$ENV{FAI}/class"; # main { # read classes and dependencies into $digraph # retain order of first appearance in @uniq_classes my $digraph = Graph::Directed->new; my ( @uniq_classes ) = read_fai_classes( $digraph, $fai_classes_file ); push @uniq_classes, read_dependencies( $digraph, $class_dir, @uniq_classes ); exit if not $digraph->has_edges; # debug output if ( $debug ) { print STDERR 'graph:', $/; print STDERR $digraph->stringify(), $/; print STDERR 'is strongly connected', $/ if $digraph->is_strongly_connected; # create_graphviz_output($digraph->edges); print STDERR 'unique list of classes, orderd by appearence', $/; print STDERR join('-', @uniq_classes), $/; print STDERR $/; } # warn if graph has cycles if ( $digraph->has_a_cycle ) { print STDERR 'Warning: cyclic class dependencies found:', $/; my $copy = $digraph->copy; while ( my @cycle = $copy->find_a_cycle ) { print STDERR join('-', @cycle), $/; $copy->delete_cycle(@cycle); } print STDERR 'I`ll try my best to retain your class order', $/; } # sort classes: retain order where possible, respect dependencies where necessary my @sorted_classes = sort_classes( $digraph, @uniq_classes ); # debug output if ( $debug ) { print STDERR "list of all classes after resolving dependencies:", $/; print STDERR "@sorted_classes", $/; print STDERR 'in debug mode, this script has no effect at all!', $/x5; print STDERR 'Goodbye, and thank you for the fish', $/; exit; } # rewrite $fai_classes_file open FAI_CLASSES, ">$fai_classes_file" or die "$!: $fai_classes_file"; print FAI_CLASSES join($/, @sorted_classes), $/; close FAI_CLASSES; } exit; # end main # sort_classes: # topological sort classes, retaining order as much as possible my %class_finished_for; my @order; sub sort_classes { my ( $digraph, @uniq_classes ) = @_; @order = @uniq_classes if not @order; my @sorted_classes; for my $class ( @uniq_classes ) { next if exists $class_finished_for{$class}; my %unfinished_successor_for = map { $_, 1 } grep { not exists $class_finished_for{$_} } successors($digraph, $class); # retain order for successors my @unfinished_successors = grep { $unfinished_successor_for{$_} } @order; push @sorted_classes, sort_classes( $digraph, @unfinished_successors ); push @sorted_classes, $class; $class_finished_for{$class}++; } return @sorted_classes; } # successors: find successors for a given class # handle circular dependencies: # * do not return circular connected successors # * _do_ return all successors of circular connected successors sub successors { my ( $digraph, $class ) = @_; my $component = $digraph->strongly_connected_component_by_vertex($class); # strongly connected components to all successors, except own component my %successor_components = map { $_, undef } # turn list into hash for uniqueness grep { $_ ne $component } map { $digraph->strongly_connected_component_by_vertex($_) } $digraph->successors($class); # classes for these components my %successors = map { $_, undef } # turn list into hash for uniqueness map { $digraph->strongly_connected_component_by_index($_) } keys %successor_components; return keys %successors; } # read_fai_classes: reads fai classes from $fai_classes_file # usually $LOGDIR/FAI_CLASSES sub read_fai_classes { my ( $digraph, $fai_classes_file) = @_; my @uniq_classes; # read plain classes from $LOGDIR/FAI_CLASSES open FAI_CLASSES, $fai_classes_file or die "$!: $fai_classes_file"; while ( <FAI_CLASSES> ) { chomp; # skip double classes next if $digraph->has_vertex( $_ ); push @uniq_classes, $_; $digraph->add_vertex( $_ ); } close FAI_CLASSES; return @uniq_classes; } # read_dependencies: reads dependencies and its classes from $class_dir/*.deps my %deps_file_seen_for; sub read_dependencies { my ( $digraph, $class_dir, @uniq_classes) = @_; my @new_classes; # read class dependencies from $class_dir/*.deps my $prefix = quotemeta($class_dir); my @deps_files = grep { -f "$class_dir/$_.deps" and not -x "$class_dir/$_.deps" } @uniq_classes; for my $class ( @deps_files ) { next if $deps_file_seen_for{$class}++; open DEPSFILE, "$class_dir/$class.deps" or die "$!: $class"; while ( <DEPSFILE> ) { chomp; # remove comments, leading and trailing whitespace s/(#|;).*// ; s/ ^\s+ //x; s/ \s+$ //x; # allow multiple classes per line my @deps = split m/\s+/; for my $dep ( @deps ) { push @new_classes, $dep if not $digraph->has_vertex( $dep ); $digraph->add_edge($class, $dep); } } close DEPSFILE; push @new_classes, read_dependencies( $digraph, $class_dir, @new_classes ); } return @new_classes; } #sub create_graphviz_output { # my @edges = @_; # my $g = GraphViz->new(); # for ( @edges ) { # $g->add_edge( @$_ ); # } # return $g->as_png('graph-test.png'); #} =head1 SEE ALSO http://www.informatik.uni-koeln.de/fai/ =head1 AUTHOR Copyright 2008 by Ingo Wichmann <iw@linuxhotel.de> This software ist free software; you can redistribute it and/or modify it unter the same terms as Perl itself. =cut