3
# Program: find-cycles.pl
5
# Synopsis: Given a list of possibly cyclic dependencies, merge all the
6
# cycles. This makes it possible to topologically sort the
7
# dependencies between different parts of LLVM.
9
# Syntax: find-cycles.pl < LibDeps.txt > FinalLibDeps.txt
11
# Input: cycmem1: cycmem2 dep1 dep2
12
# cycmem2: cycmem1 dep3 dep4
15
# Output: cycmem1 cycmem2: dep1 dep2 dep3 dep4
18
# This file was written by Eric Kidd, and is placed into the public domain.
29
# Read our dependency information.
32
my ($module, $dependency_str) = /^\s*([^:]+):\s*(.*)\s*$/;
33
die "Malformed data: $_" unless defined $dependency_str;
34
my @dependencies = split(/ /, $dependency_str);
35
$DEPS{$module} = \@dependencies;
38
# Partition our raw dependencies into sets of cyclically-connected nodes.
41
# Print out the finished cycles, with their dependencies.
44
foreach my $cycle (@CYCLES) {
45
my @modules = sort keys %{$cycle};
47
# Merge the dependencies of all modules in this cycle.
49
foreach my $module (@modules) {
50
@dependencies{@{$DEPS{$module}}} = 1;
53
# Prune the known cyclic dependencies.
54
foreach my $module (@modules) {
55
delete $dependencies{$module};
58
# Warn about possible linker problems.
59
my @archives = grep(/\.a$/, @modules);
61
$cycles_found = $cycles_found + 1;
62
print STDERR "find-cycles.pl: Circular dependency between *.a files:\n";
63
print STDERR "find-cycles.pl: ", join(' ', @archives), "\n";
64
push @modules, @archives; # WORKAROUND: Duplicate *.a files. Ick.
65
} elsif (@modules > 1) {
66
$cycles_found = $cycles_found + 1;
67
print STDERR "find-cycles.pl: Circular dependency between *.o files:\n";
68
print STDERR "find-cycles.pl: ", join(' ', @modules), "\n";
69
push @modules, @modules; # WORKAROUND: Duplicate *.o files. Ick.
72
# Add to our output. (@modules is already as sorted as we need it to be.)
73
push @output, (join(' ', @modules) . ': ' .
74
join(' ', sort keys %dependencies) . "\n");
80
#==========================================================================
81
# Depedency Cycle Support
82
#==========================================================================
83
# For now, we have cycles in our dependency graph. Ideally, each cycle
84
# would be collapsed down to a single *.a file, saving us all this work.
86
# To understand this code, you'll need a working knowledge of Perl 5,
87
# and possibly some quality time with 'man perlref'.
92
sub found_cycles ($@);
95
# Find all multi-item cycles.
96
my @modules = sort keys %DEPS;
97
foreach my $module (@modules) { find_cycles($module); }
99
# Build fake one-item "cycles" for the remaining modules, so we can
100
# treat them uniformly.
101
foreach my $module (@modules) {
102
unless (defined $CYCLES{$module}) {
103
my %cycle = ($module, 1);
104
$CYCLES{$module} = \%cycle;
108
# Find all our unique cycles. We have to do this the hard way because
109
# we apparently can't store hash references as hash keys without making
112
foreach my $cycle (values %CYCLES) {
113
unless ($seen{$cycle}) {
115
push @CYCLES, $cycle;
120
# Walk through our graph depth-first (keeping a trail in @path), and report
121
# any cycles we find.
122
sub find_cycles ($@) {
123
my ($module, @path) = @_;
124
if (str_in_list($module, @path)) {
125
found_cycle($module, @path);
127
return if defined $SEEN{$module};
129
foreach my $dep (@{$DEPS{$module}}) {
130
find_cycles($dep, @path, $module);
135
# Give a cycle, attempt to merge it with pre-existing cycle data.
136
sub found_cycle ($@) {
137
my ($module, @path) = @_;
139
# Pop any modules which aren't part of our cycle.
140
while ($path[0] ne $module) { shift @path; }
141
#print join("->", @path, $module) . "\n";
143
# Collect the modules in our cycle into a hash.
145
foreach my $item (@path) {
147
if (defined $CYCLES{$item}) {
148
# Looks like we intersect with an existing cycle, so merge
150
foreach my $old_item (keys %{$CYCLES{$item}}) {
151
$cycle{$old_item} = 1;
156
# Update our global cycle table.
157
my $cycle_ref = \%cycle;
158
foreach my $item (keys %cycle) {
159
$CYCLES{$item} = $cycle_ref;
161
#print join(":", sort keys %cycle) . "\n";
164
sub str_in_list ($@) {
165
my ($str, @list) = @_;
166
foreach my $item (@list) {
167
return 1 if ($item eq $str);