1 #!/usr/bin/perl -w 2 # 3 # Program: GenLibDeps.pl 4 # 5 # Synopsis: Generate HTML output that shows the dependencies between a set of 6 # libraries. The output of this script should periodically replace 7 # the similar content in the UsingLibraries.html document. 8 # 9 # Syntax: GenLibDeps.pl [-flat] <directory_with_libraries_in_it> [path_to_nm_binary] 10 # 11 use strict; 12 use warnings; 13 # Parse arguments... 14 my $FLAT = 0; 15 my $WHY = 0; 16 my $PEROBJ = 0; 17 my $PEROBJINCL = 0; 18 while (scalar(@ARGV) and ($_ = $ARGV[0], /^[-+]/)) { 19 shift; 20 last if /^--$/; # Stop processing arguments on -- 21 22 # List command line options here... 23 if (/^-flat$/) { $FLAT = 1; next; } 24 if (/^-why/) { $WHY = 1; $FLAT = 1; next; } 25 if (/^-perobj$/) { $PEROBJ = 1; next; } 26 if (/^-perobjincl/) { $PEROBJINCL = 1; next;} 27 print "Unknown option: $_ : ignoring!\n"; 28 } 29 30 # Give first option a name. 31 my $Directory = $ARGV[0]; 32 if (!defined($Directory) || ! -d "$Directory") { 33 die "First argument must specify the directory containing LLVM libs\n"; 34 } 35 36 my $nmPath = $ARGV[1]; 37 38 # Find the "dot" program 39 my $DotPath=""; 40 if (!$FLAT) { 41 chomp($DotPath = `which dot`); 42 die "Can't find 'dot'" if (! -x "$DotPath"); 43 } 44 45 if (defined($ENV{NM})) { 46 chomp($nmPath=$ENV{NM}); 47 } 48 49 if (!defined($nmPath) || $nmPath eq "") { 50 chomp($nmPath=`which nm`); 51 die "Can't find 'nm'" if (! -x "$nmPath"); 52 } 53 54 my $ranlibPath; 55 if ($PEROBJ) { 56 $ranlibPath = $ARGV[2]; 57 if (defined($ENV{RANLIB})) { 58 chomp($ranlibPath=$ENV{RANLIB}); 59 } 60 61 if (!defined($ranlibPath) || $ranlibPath eq "") { 62 chomp($ranlibPath=`which ranlib`); 63 die "Can't find 'ranlib'" if (! -x "$ranlibPath"); 64 } 65 } 66 67 # Open the directory and read its contents, sorting by name and differentiating 68 # by whether its a library (.a) or an object file (.o) 69 opendir DIR,$Directory; 70 my @files = readdir DIR; 71 closedir DIR; 72 my @libs = grep(/libLLVM.*\.(dylib|so|a)$/,sort(@files)); 73 # Omit the all-of-llvm shared library. 74 @libs = grep(!/libLLVM-\d\.\d(svn)?\.(dylib|so)/, @libs); 75 my @objs = grep(/LLVM.*\.o$/,sort(@files)); 76 77 # Declare the hashes we will use to keep track of the library and object file 78 # symbol definitions. 79 my %libdefs; 80 my %objdefs; 81 82 my %libobjs; 83 my %objdeps=(); 84 # Gather library definitions at object file granularity (optional) 85 if ($PEROBJ) { 86 foreach my $lib (@libs ) { 87 `$ranlibPath $Directory/$lib`; 88 my $libpath = $lib; 89 $libpath =~ s/^libLLVM(.*)\.a/$1/; 90 $libpath =~ s/(.+)CodeGen$/Target\/$1/; 91 $libpath =~ s/(.+)AsmPrinter$/Target\/$1\/AsmPrinter/; 92 $libpath =~ s/(.+)AsmParser$/Target\/$1\/AsmParser/; 93 $libpath =~ s/(.+)Info$/Target\/$1\/TargetInfo/; 94 $libpath =~ s/(.+)Disassembler$/Target\/$1\/Disassembler/; 95 $libpath =~ s/SelectionDAG/CodeGen\/SelectionDAG/; 96 $libpath =~ s/^AsmPrinter/CodeGen\/AsmPrinter/; 97 $libpath =~ s/^BitReader/Bitcode\/Reader/; 98 $libpath =~ s/^BitWriter/Bitcode\/Writer/; 99 $libpath =~ s/^CppBackend/Target\/CppBackend/; 100 $libpath =~ s/^MSIL/Target\/MSIL/; 101 $libpath =~ s/^Core/VMCore/; 102 $libpath =~ s/^Instrumentation/Transforms\/Instrumentation/; 103 $libpath =~ s/^Interpreter/ExecutionEngine\/Interpreter/; 104 $libpath =~ s/^JIT/ExecutionEngine\/JIT/; 105 $libpath =~ s/^ScalarOpts/Transforms\/Scalar/; 106 $libpath =~ s/^TransformUtils/Transforms\/Utils/; 107 $libpath =~ s/^ipa/Analysis\/IPA/; 108 $libpath =~ s/^ipo/Transforms\/IPO/; 109 $libpath = "lib/".$libpath."/"; 110 open DEFS, "$nmPath -sg $Directory/$lib|"; 111 while (<DEFS>) { 112 chomp; 113 if (/^([^ ]*) in ([^ ]*)/) { 114 my $objfile = $libpath.$2; 115 $objdefs{$1} = $objfile; 116 $objdeps{$objfile} = {}; 117 $libobjs{$lib}{$objfile}=1; 118 # my $p = "../llvm/".$objfile; 119 # $p =~ s/Support\/reg(.*).o/Support\/reg$1.c/; 120 # $p =~ s/.o$/.cpp/; 121 # unless (-e $p) { 122 # die "$p\n" 123 # } 124 } 125 } 126 close DEFS or die "nm failed"; 127 } 128 foreach my $lib (@libs ) { 129 my $libpath = $lib; 130 $libpath =~ s/^libLLVM(.*)\.a/$1/; 131 $libpath =~ s/(.+)CodeGen$/Target\/$1/; 132 $libpath =~ s/(.+)AsmPrinter$/Target\/$1\/AsmPrinter/; 133 $libpath =~ s/(.+)AsmParser$/Target\/$1\/AsmParser/; 134 $libpath =~ s/(.+)Info$/Target\/$1\/TargetInfo/; 135 $libpath =~ s/(.+)Disassembler$/Target\/$1\/Disassembler/; 136 $libpath =~ s/SelectionDAG/CodeGen\/SelectionDAG/; 137 $libpath =~ s/^AsmPrinter/CodeGen\/AsmPrinter/; 138 $libpath =~ s/^BitReader/Bitcode\/Reader/; 139 $libpath =~ s/^BitWriter/Bitcode\/Writer/; 140 $libpath =~ s/^CppBackend/Target\/CppBackend/; 141 $libpath =~ s/^MSIL/Target\/MSIL/; 142 $libpath =~ s/^Core/VMCore/; 143 $libpath =~ s/^Instrumentation/Transforms\/Instrumentation/; 144 $libpath =~ s/^Interpreter/ExecutionEngine\/Interpreter/; 145 $libpath =~ s/^JIT/ExecutionEngine\/JIT/; 146 $libpath =~ s/^ScalarOpts/Transforms\/Scalar/; 147 $libpath =~ s/^TransformUtils/Transforms\/Utils/; 148 $libpath =~ s/^ipa/Analysis\/IPA/; 149 $libpath =~ s/^ipo/Transforms\/IPO/; 150 $libpath = "lib/".$libpath."/"; 151 open UDEFS, "$nmPath -Aup $Directory/$lib|"; 152 while (<UDEFS>) { 153 chomp; 154 if (/:([^:]+):/) { 155 my $obj = $libpath.$1; 156 s/[^ ]+: *U //; 157 if (defined($objdefs{$_})) { 158 $objdeps{$obj}{$objdefs{$_}}=1; 159 } 160 } 161 } 162 close UDEFS or die "nm failed" 163 } 164 } else { 165 # Gather definitions from the libraries 166 foreach my $lib (@libs ) { 167 open DEFS, "$nmPath -g $Directory/$lib|"; 168 while (<DEFS>) { 169 next if (! / [ABCDGRST] /); 170 s/^[^ ]* [ABCDGRST] //; 171 s/\015?\012//; # not sure if <DEFS> is in binmode and uses LF or CRLF. 172 # this strips both LF and CRLF. 173 $libdefs{$_} = $lib; 174 } 175 close DEFS or die "nm failed"; 176 } 177 } 178 179 # Gather definitions from the object files. 180 foreach my $obj (@objs ) { 181 open DEFS, "$nmPath -g $Directory/$obj |"; 182 while (<DEFS>) { 183 next if (! / [ABCDGRST] /); 184 s/^[^ ]* [ABCDGRST] //; 185 s/\015?\012//; # not sure if <DEFS> is in binmode and uses LF or CRLF. 186 # this strips both LF and CRLF. 187 $objdefs{$_} = $obj; 188 } 189 close DEFS or die "nm failed"; 190 } 191 192 # Generate one entry in the <dl> list. This generates the <dt> and <dd> elements 193 # for one library or object file. The <dt> provides the name of the library or 194 # object. The <dd> provides a list of the libraries/objects it depends on. 195 sub gen_one_entry { 196 my $lib = $_[0]; 197 my $lib_ns = $lib; 198 $lib_ns =~ s/(.*)\.[oa]/$1/; 199 if ($FLAT) { 200 print "$lib:"; 201 if ($WHY) { print "\n"; } 202 } else { 203 print " <dt><b>$lib</b></dt><dd><ul>\n"; 204 } 205 open UNDEFS, 206 "$nmPath -u $Directory/$lib | sed -e 's/^[ 0]* U //' | sort | uniq |"; 207 my %DepLibs; 208 while (<UNDEFS>) { 209 chomp; 210 my $lib_printed = 0; 211 if (defined($libdefs{$_}) && $libdefs{$_} ne $lib) { 212 $DepLibs{$libdefs{$_}} = [] unless exists $DepLibs{$libdefs{$_}}; 213 push(@{$DepLibs{$libdefs{$_}}}, $_); 214 } elsif (defined($objdefs{$_}) && $objdefs{$_} ne $lib) { 215 if ($PEROBJ && !$PEROBJINCL) { 216 # -perobjincl makes .a files depend on .o files they contain themselves 217 # default is don't depend on these. 218 next if defined $libobjs{$lib}{$objdefs{$_}}; 219 } 220 my $libroot = $lib; 221 $libroot =~ s/lib(.*).a/$1/; 222 if ($objdefs{$_} ne "$libroot.o") { 223 $DepLibs{$objdefs{$_}} = [] unless exists $DepLibs{$objdefs{$_}}; 224 push(@{$DepLibs{$objdefs{$_}}}, $_); 225 } 226 } 227 } 228 close UNDEFS or die "nm failed"; 229 unless(keys %DepLibs) { 230 # above failed 231 open UNDEFS, "$nmPath -u $Directory/$lib |"; 232 while (<UNDEFS>) { 233 # to bypass non-working sed 234 if (' ' eq substr($_,0,2) and index($_,'U ')) { 235 $_ = substr($_,index($_,'U ')+2) 236 }; 237 $_ = substr($_,index($_,' *U ')+5) if -1!=index($_,' *U '); 238 239 chomp; 240 my $lib_printed = 0; 241 if (defined($libdefs{$_}) && $libdefs{$_} ne $lib) { 242 $DepLibs{$libdefs{$_}} = [] unless exists $DepLibs{$libdefs{$_}}; 243 push(@{$DepLibs{$libdefs{$_}}}, $_); 244 } elsif (defined($objdefs{$_}) && $objdefs{$_} ne $lib) { 245 my $libroot = $lib; 246 $libroot =~ s/lib(.*).a/$1/; 247 if ($objdefs{$_} ne "$libroot.o") { 248 $DepLibs{$objdefs{$_}} = [] unless exists $DepLibs{$objdefs{$_}}; 249 push(@{$DepLibs{$objdefs{$_}}}, $_); 250 } 251 } 252 } 253 close UNDEFS or die "nm failed"; 254 } 255 if ($PEROBJINCL) { 256 # include the .a's objects 257 for my $obj (keys %{$libobjs{$lib}}) { 258 $DepLibs{$obj} = ["<.a object>"] unless exists $DepLibs{$obj}; 259 } 260 my $madechange = 1; 261 while($madechange) { 262 $madechange = 0; 263 my %temp = %DepLibs; 264 foreach my $obj (keys %DepLibs) { 265 foreach my $objdeps (keys %{$objdeps{$obj}}) { 266 next if defined $temp{$objdeps}; 267 push(@{$temp{$objdeps}}, $obj); 268 $madechange = 1; 269 } 270 } 271 %DepLibs = %temp; 272 } 273 } 274 275 for my $key (sort keys %DepLibs) { 276 if ($FLAT) { 277 print " $key"; 278 if ($WHY) { 279 print "\n"; 280 my @syms = @{$DepLibs{$key}}; 281 foreach my $sym (@syms) { 282 print " $sym\n"; 283 } 284 } 285 } else { 286 print " <li>$key</li>\n"; 287 } 288 my $suffix = substr($key,length($key)-1,1); 289 $key =~ s/(.*)\.[oa]/$1/; 290 if ($suffix eq "a") { 291 if (!$FLAT) { print DOT "$lib_ns -> $key [ weight=0 ];\n" }; 292 } else { 293 if (!$FLAT) { print DOT "$lib_ns -> $key [ weight=10];\n" }; 294 } 295 } 296 if ($FLAT) { 297 if (!$WHY) { 298 print "\n"; 299 } 300 } else { 301 print " </ul></dd>\n"; 302 } 303 } 304 305 # Make sure we flush on write. This is slower but correct based on the way we 306 # write I/O in gen_one_entry. 307 $| = 1; 308 309 # Print the definition list tag 310 if (!$FLAT) { 311 print "<dl>\n"; 312 313 open DOT, "| $DotPath -Tgif > libdeps.gif"; 314 315 print DOT "digraph LibDeps {\n"; 316 print DOT " size=\"40,15\"; \n"; 317 print DOT " ratio=\"1.33333\"; \n"; 318 print DOT " margin=\"0.25\"; \n"; 319 print DOT " rankdir=\"LR\"; \n"; 320 print DOT " mclimit=\"50.0\"; \n"; 321 print DOT " ordering=\"out\"; \n"; 322 print DOT " center=\"1\";\n"; 323 print DOT "node [shape=\"box\",\n"; 324 print DOT " color=\"#000088\",\n"; 325 print DOT " fillcolor=\"#FFFACD\",\n"; 326 print DOT " fontcolor=\"#3355BB\",\n"; 327 print DOT " style=\"filled\",\n"; 328 print DOT " fontname=\"sans\",\n"; 329 print DOT " fontsize=\"24\"\n"; 330 print DOT "];\n"; 331 print DOT "edge [dir=\"forward\",style=\"solid\",color=\"#000088\"];\n"; 332 } 333 334 # Print libraries first 335 foreach my $lib (@libs) { 336 gen_one_entry($lib); 337 } 338 339 if ($PEROBJ) { 340 foreach my $obj (keys %objdeps) { 341 print "$obj:"; 342 if (!$PEROBJINCL) { 343 foreach my $dep (keys %{$objdeps{$obj}}) { 344 print " $dep"; 345 } 346 } 347 print "\n"; 348 } 349 } 350 351 if (!$FLAT) { 352 print DOT "}\n"; 353 close DOT; 354 open DOT, "| $DotPath -Tgif > objdeps.gif"; 355 print DOT "digraph ObjDeps {\n"; 356 print DOT " size=\"8,10\";\n"; 357 print DOT " margin=\"0.25\";\n"; 358 print DOT " rankdir=\"LR\";\n"; 359 print DOT " mclimit=\"50.0\";\n"; 360 print DOT " ordering=\"out\";\n"; 361 print DOT " center=\"1\";\n"; 362 print DOT "node [shape=\"box\",\n"; 363 print DOT " color=\"#000088\",\n"; 364 print DOT " fillcolor=\"#FFFACD\",\n"; 365 print DOT " fontcolor=\"#3355BB\",\n"; 366 print DOT " fontname=\"sans\",\n"; 367 print DOT " style=\"filled\",\n"; 368 print DOT " fontsize=\"24\"\n"; 369 print DOT "];\n"; 370 print DOT "edge [dir=\"forward\",style=\"solid\",color=\"#000088\"];\n"; 371 } 372 373 # Print objects second 374 foreach my $obj (@objs) { 375 gen_one_entry($obj); 376 } 377 378 if (!$FLAT) { 379 print DOT "}\n"; 380 close DOT; 381 382 # Print end tag of definition list element 383 print "</dl>\n"; 384 } 385