summaryrefslogtreecommitdiffstats
path: root/scripts/dependency-graph.in
diff options
context:
space:
mode:
authorAndreas Gruenbacher <agruen@suse.de>2006-01-14 18:44:11 +0000
committerAndreas Gruenbacher <agruen@suse.de>2006-01-14 18:44:11 +0000
commit5e61a8bda30e96bef5a1c6e265f4d6d19695102d (patch)
tree2744d8a7548af767ae1e71fad5063f5590f20f85 /scripts/dependency-graph.in
parentb73c8212ecca40dffaa1cad31693fc25fc63eab2 (diff)
downloadquilt-5e61a8bda30e96bef5a1c6e265f4d6d19695102d.tar.gz
- Move scripts directory to quilt/scripts to simplify running quilt
directly from the source tree. - test/Makefile: merge into Makefile.in (target check). - Makefile.in: clean up.
Diffstat (limited to 'scripts/dependency-graph.in')
-rw-r--r--scripts/dependency-graph.in470
1 files changed, 0 insertions, 470 deletions
diff --git a/scripts/dependency-graph.in b/scripts/dependency-graph.in
deleted file mode 100644
index a853c10..0000000
--- a/scripts/dependency-graph.in
+++ /dev/null
@@ -1,470 +0,0 @@
-#!@PERL@ -w
-
-# This script 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.
-#
-# See the COPYING and AUTHORS files for more details.
-
-# Generate a dot-style graph of dependencies between patches.
-
-use Getopt::Long;
-use FileHandle;
-use strict;
-
-# Constants
-my $short_edge_style = "color=grey";
-my $close_node_style = "color=grey";
-my $highlighted_node_style = "style=bold";
-
-# Command line arguments
-my $help = 0;
-my $use_patcher = 0; # Assume patcher format for metadata
-my $short_edge_thresh = 0; # threshold for coloring as "short", 0 = disable
-my $long_edge_thresh = 0; # threshold for coloring as "long",0 = disable
-my $edge_labels; # label all edges with filenames
-my $short_edge_labels; # label short edges with filenames
-my $long_edge_labels; # label long edges with filenames
-my $edge_length_labels; # distance between patches as edge labels
-my $node_numbers; # include sequence numbers
-my $show_isolated_nodes; # also include isolated nodes
-my $reduce; # remove transitive edges
-my $filter_patchnames; # filter for compacting filenames
-my $selected_patch; # only include patches related on this patch
-my $selected_distance = -1; # infinity
-my @highlight_patches; # a list of patches to highlight
-my $lines; # check ranges with this number of context
- # lines.
-
-unless (GetOptions(
- "h|help" => \$help,
- "patcher" => \$use_patcher,
- "short-edge=i" => \$short_edge_thresh,
- "long-edge=i" => \$long_edge_thresh,
- "edge-files" => \$edge_labels,
- "short-edge-files" => \$short_edge_labels,
- "long-edge-files" => \$long_edge_labels,
- "edge-length" => \$edge_length_labels,
- "node-numbers" => \$node_numbers,
- "isolated" => \$show_isolated_nodes,
- "reduce" => \$reduce,
- "filter-patchnames=s" => \$filter_patchnames,
- "select-patch=s" => \$selected_patch,
- "select-distance=i" => \$selected_distance,
- "highlight=s" => \@highlight_patches,
- "lines=i" => \$lines) && !$help) {
- my $basename = $0;
- $basename =~ s:.*/::;
- my $fd = $help ? *STDOUT : *STDERR;
- print $fd <<EOF;
-SYNOPSIS: $basename [-h] [--patcher] [--short-edge=num] [--long-edge=num]
- [--short-edge-files] [--long-edge-files] [--edge-length]
- [--node-numbers] [--isolated] [--reduce] [--filter-patchnames=filter]
- [--select-patch=patch] [--select-distance=num] [--highlight=patch]
- [--lines=num]
-
---patcher
- Assume patch manager is Holger Schurig's patcher script instead
- of the default quilt.
-
---short-edge=num, --long-edge=num
- Define the maximum edge length of short edges, and minimum edge
- length of long edges. Short edges are de-emphasized, and long
- edges are emphasized. The default is to treat all edges equally.
-
--edge-files, --short-edge-files, --long-edge-files
- Include conflicting filenames on all edges, short edges, or long
- edges.
-
---edge-length
- Use the edge lengths as edge labels. Cannot be used together with
- filename labels.
-
---node-numbers
- Include the sequence numbers of patches in the patch series in
- node labels.
-
---isolated
- Do not suppress isolated nodes.
-
---reduce
- Remove transitive edges.
-
---filter-patchnames=filter
- Define a filter command for transforming patch names into node
- labels. The filter is passed each patch name on a separate line,
- and must return the edge label for each patch on a separate line
- (example: sed -e 's/^prefix//').
-
---select-patch=patch
- Reduce the graph to nodes that depend on the specified patch,
- and nodes that the specified patch depends on (recursively).
-
---select-distance=num
- Limit the depth of recusion for --select-patch. The default is
- to recurse exhaustively.
-
---highlight=patch
- Highlight the specified patch. This option can be specified more
- than once.
-
---lines=num
- Check the ranges of lines that the patches modify for computing
- dependencies. Include up to num lines of context.
-EOF
- exit $help ? 0 : 1;
-}
-
-my @nodes;
-
-sub next_patch_for_file($$)
-{
- my ($n, $file) = @_;
-
- for (my $i = $n + 1; $i < @nodes; $i++) {
- return $i
- if (exists $nodes[$i]{files}{$file});
- }
- return undef;
-}
-
-# Compute the ranges of lines that a patch modifies: The patch should
-# have no context lines. The return value is a list of pairs of line
-# numbers, alternatingly marking the start and end of a modification.
-sub ranges($) {
- my ($fd) = @_;
- my (@left, @right);
- while (<$fd>) {
- if (/^\@\@ -(\d+)(?:,(\d+)?) \+(\d+)(?:,(\d+)?) \@\@/) {
- push @left, ($3, $3 + $4);
- push @right, ($1, $1 + $2);
- }
- }
- return [ [ @left ], [ @right ] ];
-}
-
-sub backup_file_name($$) {
- my ($patch, $file) = @_;
-
- if ($use_patcher) {
- return $file . "~" . $patch;
- } else {
- return $ENV{QUILT_PC} . "/" . $patch . "/" . $file;
- }
-}
-
-# Compute the lists of lines that a patch changes in a file.
-sub compute_ranges($$) {
- my ($n, $file) = @_;
- my $file1 = backup_file_name($nodes[$n]{file}, $file);
- my $file2;
- my $n2 = next_patch_for_file($n, $file);
- if (defined $n2) {
- $file2 = backup_file_name($nodes[$n2]{file}, $file);
- } else {
- $file2 = $file;
- }
-
- #print STDERR "diff -NU$lines \"$file1\" \"$file2\"\n";
- if (-z $file1) {
- $file1="/dev/null";
- return [[], []]
- if (-z $file2);
- } else {
- $file2="/dev/null"
- if (-z $file2);
- }
- my $fd = new FileHandle("diff -NU$lines \"$file1\" \"$file2\" |");
- my $ranges = ranges($fd);
- $fd->close();
- return $ranges;
-}
-
-sub is_a_conflict($$$) {
- my ($from, $to, $filename) = @_;
-
- $nodes[$from]{files}{$filename} = compute_ranges($from, $filename)
- unless @{$nodes[$from]{files}{$filename}};
- $nodes[$to]{files}{$filename} = compute_ranges($to, $filename)
- unless @{$nodes[$to]{files}{$filename}};
-
- my @a = @{$nodes[$from]{files}{$filename}[1]};
- my @b = @{$nodes[$to ]{files}{$filename}[0]};
-
- while (@a && @b) {
- if ($a[0] < $b[0]) {
- return 1 if @b % 2;
- shift @a;
- } elsif ($a[0] > $b[0]) {
- return 1 if @a % 2;
- shift @b;
- } else {
- return 1 if (@a % 2) == (@b % 2);
- shift @a;
- shift @b;
- }
- }
- return 0;
-}
-
-# Fetch the list of patches (all of them must be applied)
-my @patches;
-if (@ARGV) {
- if (@ARGV == 1 && $ARGV[0] eq "-") {
- @patches = map { chomp ; $_ } <STDIN>;
- } else {
- @patches = @ARGV;
- }
-} elsif ($use_patcher) {
- my $fh = new FileHandle("< .patches/applied")
- or die ".patches/applied: $!\n";
- @patches = map { chomp; $_ } <$fh>;
- $fh->close();
-} else {
- my $fh = new FileHandle("< $ENV{QUILT_PC}/applied-patches")
- or die ".$ENV{QUILT_PC}/applied-patches: $!\n";
- @patches = map { chomp; $_ } <$fh>;
- $fh->close();
-}
-
-# Fetch the list of files
-my $n = 0;
-foreach my $patch (@patches) {
- my @files;
- if ($use_patcher) {
- my $fh = new FileHandle("< .patches/$patch.files")
- or die ".patches/$patch.files: $!\n";
- @files = map { chomp; $_ } <$fh>;
- $fh->close();
- } else {
- if (! -d "$ENV{QUILT_PC}/$patch") {
- print STDERR "$ENV{QUILT_PC}/$patch does not exist; skipping\n";
- next;
- }
- @files = split(/\n/, `cd $ENV{QUILT_PC}/$patch ; find . -type f ! -name .timestamp`);
- @files = map { s:\./::; $_ } @files;
- }
- push @nodes, {number=>$n++, name=>$patch, file=>$patch,
- files=>{ map {$_ => []} @files } };
-}
-
-my %used_nodes; # nodes to which at least one edge is attached
-
-# If a patch is selected, limit the graph to nodes that depend on this patch,
-# and nodes that are dependent on this patch.
-if ($selected_patch) {
- for ($n = 0; $n < @nodes; $n++) {
- last if $nodes[$n]{file} eq $selected_patch;
- }
- die "Patch $selected_patch not included\n"
- if ($n == @nodes);
-
- $used_nodes{$n} = 1;
- my $selected_node = $nodes[$n];
- push @{$selected_node->{attrs}}, $highlighted_node_style;
-
- my %sel;
- map { $sel{$_} = 1 } keys %{$selected_node->{files}};
- foreach my $node (@nodes) {
- foreach my $file (keys %{$node->{files}}) {
- unless (exists $sel{$file}) {
- delete $node->{files}{$file};
- }
- }
- }
-}
-
-# Optionally highlight a list of patches
-foreach my $patch (@highlight_patches) {
- for ($n = 0; $n < @nodes; $n++) {
- last if $nodes[$n]{file} eq $patch;
- }
- die "Patch $patch not included\n"
- if ($n == @nodes);
-
- my $node = $nodes[$n];
- push @{$node->{attrs}}, $highlighted_node_style;
- $node->{colorized} = 1;
-}
-
-# If a patchname filter is selected, pipe all patchnames through
-# it.
-if ($filter_patchnames) {
- local *PIPE;
- my $pid = open(PIPE, "- |"); # fork a child to read from
- die "fork: $!\n"
- unless defined $pid;
- unless ($pid) { # child
- # open a second pipe to the actual filter
- open(PIPE, "| $filter_patchnames")
- or die "\`$filter_patchnames': $!\n";
- map { print PIPE "$_\n" } @patches;
- close(PIPE);
- exit;
- } else { # parent
- $n = 0;
- foreach my $name (<PIPE>) {
- last unless $n < @nodes;
- chomp $name;
- if ($name eq "") {
- delete $nodes[$n++]{name};
- } else {
- $nodes[$n++]{name} = $name;
- }
- }
- close(PIPE)
- or die "patchname filter failed.\n";
- die "patchname filter returned too few lines\n"
- if $n != @nodes;
- }
-}
-
-my %files_seen; # remember the last patch that touched each file
-my %edges;
-
-foreach my $node (@nodes) {
- my $number = $node->{number};
- foreach my $file (keys %{$node->{files}}) {
- if (exists $files_seen{$file}) {
- my $patches = $files_seen{$file};
- my $patch;
- # Optionally look at the line ranges the patches touch
- if (defined $lines) {
- for (my $n = $#$patches; $n >= 0; $n--) {
- if (is_a_conflict($number, $patches->[$n], $file)) {
- $patch = $patches->[$n];
- last;
- }
- }
- } else {
- $patch = $patches->[$#$patches];
- }
- if (defined $patch) {
- push @{$edges{"$number:$patch"}{names}}, $file;
- $used_nodes{$number} = 1;
- $used_nodes{$patch} = 1;
- }
- }
- push @{$files_seen{$file}}, $number;
- }
-}
-
-# Create adjacency lists
-foreach my $node (@nodes) {
- @{$node->{to}} = ();
- @{$node->{from}} = ();
-}
-foreach my $key (keys %edges) {
- my ($from, $to) = split /:/, $key;
- push @{$nodes[$from]{to}}, $to;
- push @{$nodes[$to]{from}}, $from;
-}
-
-# Colorize nodes that are close to each other
-foreach my $node (@nodes) {
- if (!exists $node->{colorized} && !exists $used_nodes{$node->{number}}) {
- $node->{colorized} = 1;
- push @{$node->{attrs}}, $close_node_style;
- }
-}
-
-# Colorize short and long edges
-foreach my $node (@nodes) {
- my $close = 1;
- foreach my $node2 (map {$nodes[$_]} @{$node->{to}}) {
- if (abs($node2->{number} - $node->{number}) > $short_edge_thresh) {
- $close = 0
- }
- }
- foreach my $node2 (map {$nodes[$_]} @{$node->{from}}) {
- if (abs($node2->{number} - $node->{number}) > $short_edge_thresh) {
- $close = 0
- }
- }
- if (!exists $node->{colorized} && $close) {
- $node->{colorized} = 1;
- push @{$node->{attrs}}, $close_node_style;
- }
-}
-
-# Add node labels
-foreach my $node (@nodes) {
- my @label = ();
- push @label, $node->{number} + 1
- if ($node_numbers);
- push @label, $node->{name}
- if exists $node->{name};
- push @{$node->{attrs}}, "label=\"" . join(": ", @label) . "\"";
-}
-
-# Add edge labels
-foreach my $key (keys %edges) {
- my ($from, $to) = split /:/, $key;
- if ($edge_length_labels) {
- push @{$edges{$key}->{attrs}}, "label=\"" . abs($to - $from) . "\""
- if abs($to - $from) > 1;
- } elsif (abs($to - $from) < $short_edge_thresh) {
- push @{$edges{$key}->{attrs}}, $short_edge_style;
- if ($edge_labels || $short_edge_labels) {
- push @{$edges{$key}->{attrs}},
- "label=\"" . join("\\n", @{$edges{$key}{names}}) . "\"";
- }
- } else {
- if ($long_edge_thresh && abs($to - $from) > $long_edge_thresh) {
- push @{$edges{$key}->{attrs}}, "style=bold";
- if ($edge_labels || $long_edge_labels) {
- push @{$edges{$key}->{attrs}},
- "label=\"" . join("\\n", @{$edges{$key}{names}}) . "\"";
- }
- } else {
- if ($edge_labels) {
- push @{$edges{$key}->{attrs}},
- "label=\"" . join("\\n", @{$edges{$key}{names}}) . "\"";
- }
- }
- }
- # Compute a pseudo edge length so that neato works acceptably.
- push @{$edges{$key}{attrs}}, "len=\"" .
- sprintf("%.2f", log(abs($to - $from) + 3)) . "\"";
-}
-
-#foreach my $node (@nodes) {
-# push @{$node->{attrs}}, "shape=box";
-#}
-
-# Open output file / pipe
-my $out;
-if ($reduce) {
- $out = new FileHandle("| tred")
- or die "tred: $!\n";
-} else {
- $out = new FileHandle("> /dev/stdout")
- or die "$!\n";
-}
-
-# Write graph
-print $out "digraph dependencies {\n";
-#print "\tsize=\"11,8\"\n";
-foreach my $node (@nodes) {
- next unless $show_isolated_nodes || exists $used_nodes{$node->{number}};
- print $out "\tn$node->{number}";
- if (exists $node->{attrs}) {
- print $out " [" .
- join(",", @{$node->{attrs}}) . "]";
- }
- print $out ";\n";
-}
-
-sub w($) {
- my @n = split /:/, shift;
- return $n[0] * 10000 + $n[1];
-}
-foreach my $key (sort { w($a) <=> w($b) } keys %edges) {
- my ($from, $to) = split /:/, $key;
- print $out "\tn$to -> n$from";
- if (exists $edges{$key}{attrs}) {
- print $out " [" . join(",", @{$edges{$key}{attrs}}) . "]";
- }
- print $out ";\n";
-}
-print $out "}\n";