source: branches/species/SOURCE_TOOLS/needed_libs.pl

Last change on this file was 19619, checked in by westram, 2 months ago
  • support to show any number of inheritants / dependencies (previously only inf and 1 were supported)
  • Property svn:executable set to *
File size: 30.8 KB
Line 
1#!/usr/bin/perl
2
3use strict;
4use warnings;
5
6my $debug_verboose;
7# BEGIN {
8  # $debug_verboose = 1;
9  # $SIG{__DIE__} = sub {
10    # require Carp;
11    # if ($debug_verboose>0) { Carp::confess(@_); } # with backtrace
12    # else { Carp::croak(@_); }
13  # }
14# }
15
16my $dieOnUndefEnvvar = 1; # use '-U' to toggle
17
18my $libdepend_file = 'needs_libs';
19my $exedepend_dir  = 'BINDEP';
20
21# --------------------------------------------------------------------------------
22
23my $ARBHOME = $ENV{ARBHOME};
24defined $ARBHOME || die "Environmentvariable 'ARBHOME' is not defined";
25-d $ARBHOME || die "ARBHOME does not point to a directory ($ARBHOME)";
26if (not $ARBHOME =~ /\/$/o) { $ARBHOME .= '/'; }
27my $AH_len = length($ARBHOME);
28
29sub ARBHOME_relative($) {
30  my ($dir) = @_;
31  defined $dir || die;
32  if (substr($dir,0,$AH_len) eq $ARBHOME) { substr($dir,$AH_len); }
33  else { $dir; }
34}
35
36sub fullpath($) {
37  my ($path) = @_;
38  return $ARBHOME.ARBHOME_relative($path);
39}
40
41# --------------------------------------------------------------------------------
42
43my $LINK_STATIC = $ENV{LINK_STATIC};
44# LINK_STATIC==1 (used on OSX) ->
45# - shared ARB-libraries (e.g. lib/libWINDOW.so) are created/linked statically (as lib/libWINDOW.a)
46# - in contrast to "normal" static libs
47#   - they do NOT reside in the source-code directory and
48#   - they have a 'lib'-prefix
49
50sub fixFileSuffix($) {
51  my ($file) = @_;
52  defined $LINK_STATIC || die "Environmentvariable 'LINK_STATIC' is not defined";
53  if ($LINK_STATIC==1) { # true on OSX
54    $file =~ s/\.so/\.a/; # correct lib-suffix
55  }
56  $file;
57}
58
59# --------------------------------------------------------------------------------
60
61sub dirOf($) {
62  my ($full) = @_;
63  if ($full =~ /\/([^\/]+)$/o) { $`; }
64  else { undef; }
65}
66
67sub filenameOf($) {
68  my ($full) = @_;
69  defined $full || die;
70  if ($full =~ /\/([^\/]+)$/o) { $1; }
71  else { $full; }
72}
73
74sub trim($) {
75  my ($str) = @_;
76  $str =~ s/^\s+//go;
77  $str =~ s/\s+$//go;
78  return $str;
79}
80
81# --------------------------------------------------------------------------------
82
83sub legal_target_or_die($) {
84  my ($target) = @_;
85
86  my $ct = $target;
87  $ct =~ s/ $//g;
88  $ct =~ s/^ //g;
89
90  if ($ct ne $target) {
91    die "Invalid target '$target' (chomped='$ct')";
92  }
93}
94
95my $reg_exedepend = qr/^$exedepend_dir\/$libdepend_file\./;
96my $reg_depend = qr/\/$libdepend_file/;
97
98sub dependencyFile2target($) {
99  my ($depfile) = @_;
100
101  $depfile = ARBHOME_relative($depfile);
102  my $target = undef;
103  if ($depfile =~ $reg_exedepend) {
104    $target = $';
105  }
106  elsif ($depfile =~ $reg_depend) {
107    my ($dir,$suffix) = ($`,$');
108    my $libname = undef;
109    if ($suffix eq '') { # default (DIR/DIR.a)
110      my $lastDir = filenameOf($dir);
111      $libname = $lastDir.'.a';
112    }
113    else {
114      $libname = substr($suffix,1);
115      $libname =~ s/\_(so|a|o)$/\.$1/o;
116      if ($1 eq 'so') {
117        $dir = 'lib';
118      }
119    }
120    $target = $dir.'/'.$libname;
121    if ($target eq 'lib/lib.dummy') {
122      die "Generated invalid target '$target'";
123    }
124  }
125  else {
126    die "Illegal dependency file '$depfile'";
127  }
128
129  return $target;
130}
131
132sub target2dependencyFile($) {
133  my ($target) = @_;
134
135  $target = ARBHOME_relative($target);
136  legal_target_or_die($target);
137
138  my $depfile = undef;
139  my $dir     = dirOf($target);
140
141  if (defined $dir) {
142    if ($dir eq 'lib') {
143      if ($target =~ /^lib\/lib([A-Z]+)\.(so|a)$/) {
144        $depfile = $1.'/'.$libdepend_file.'.lib'.$1.'_so';
145      }
146      else {
147        $depfile = "failed to parse libname from '$target'";
148      }
149    }
150    else {
151      my $lastdir    = filenameOf($dir);
152      my $defaultLib = $dir.'/'.$lastdir.'.a';
153
154      if ($target eq $defaultLib) {
155        $depfile = $dir.'/'.$libdepend_file;
156      }
157      else {
158        my $targetName =  filenameOf($target);
159        $targetName    =~ s/\.(so|a|o)$/_$1/o;
160        $depfile       =  $dir.'/'.$libdepend_file.'.'.$targetName;
161      }
162    }
163  }
164  else { # no dir -> must be executable
165    $depfile = $exedepend_dir.'/'.$libdepend_file.'.'.$target;
166  }
167  return $depfile;
168}
169
170# --------------------------------------------------------------------------------
171
172my $errors = 0;
173
174sub if_errors_abortNow() {
175  if ($errors>0) { die "Errors occurred - abort!\n"; }
176}
177
178sub show_location($$$) {
179  my ($location,$message,$type) = @_;
180  if (defined $location) { print STDERR $location.': '.$type.': '.$message."\n"; }
181  else { print STDERR '(unknown source): '.$type.': '.$message."\n"; }
182}
183sub location_error($$) {
184  my ($location,$error) = @_;
185  show_location($location,$error,'Error');
186  $errors++;
187}
188sub location_warning($$) {
189  my ($location,$warning) = @_;
190  show_location($location,$warning,'Warning');
191}
192sub location_note($$) {
193  my ($location,$note) = @_;
194  show_location($location,$note,'Note');
195}
196
197# --------------------------------------------------------------------------------
198
199# values for %target_type:
200my $DYNAMIC_LIB  = 0;
201my $STATIC_LIB   = 1;
202my $EXECUTABLE   = 2;
203my $EXTRA_PARAMS = 3;
204
205my %inheritants_of      = (); # key=target; value=ref to hash (key=inheritant of target; value=location in definition file)
206my %target_type         = (); # key=target; value: see above
207my %dependencies_of     = (); # key=target; value=ref to hash (containing direct dependencies)
208my %all_dependencies_of = (); # key=target; value=ref to hash (containing all dependencies)
209
210sub get_inheritance_location($$) {
211  my ($inheritor,$dependency) = @_;
212  my $inh_r = $inheritants_of{$dependency};
213  return $$inh_r{$inheritor};
214}
215
216sub warn_bad_dependency($) {
217  my ($target) = @_;
218
219  my $directly_depends_on_r = $dependencies_of{$target};
220  foreach my $sub (keys %$directly_depends_on_r) {
221    my $sub_depends_r = $all_dependencies_of{$sub};
222
223    # check multi dependencies:
224    foreach my $some_dep (keys %$sub_depends_r) {
225      if (defined $$directly_depends_on_r{$some_dep}) { # multi dependency
226        my $some_dep_location = get_inheritance_location($target,$some_dep);
227        location_warning($some_dep_location, "Duplicated dependency on '$some_dep'");
228
229        my $sub_location = get_inheritance_location($target,$sub);
230        location_note($sub_location, "(already depends on '$some_dep' via '$sub'");
231      }
232    }
233
234    # check cyclic dependencies:
235    if (defined $$sub_depends_r{$target}) {
236      my $sub_location = get_inheritance_location($target,$sub);
237      location_warning($sub_location, "Cyclic dependency between $target ..");
238      my $target_location = get_inheritance_location($sub,$target);
239      location_warning($sub_location, ".. and $sub");
240    }
241  }
242}
243
244sub warn_bad_dependencies() {
245  foreach (keys %inheritants_of) {
246    warn_bad_dependency($_);
247  }
248}
249
250# --------------------------------------------------------------------------------
251
252sub dump_dependencies() {
253  foreach my $dependency (sort keys %inheritants_of) {
254    print STDERR "------------------------------------ inheriting $dependency\n";
255    my $inherit_locations_r = $inheritants_of{$dependency};
256    foreach my $inheritant (keys %$inherit_locations_r) {
257      my $location = $$inherit_locations_r{$inheritant};
258      location_warning($location, "'$dependency' inherited by '$inheritant'");
259    }
260  }
261}
262
263# --------------------------------------------------------------------------------
264
265sub prefix($$) {
266  my ($maybePrefix,$path) = @_;
267  if (defined $maybePrefix) { $maybePrefix.'/'.$path; }
268  else { $path; }
269}
270
271sub dynamic_name($) {
272  my ($target) = @_;
273  my $name = filenameOf($target);
274  if ($name =~ /^lib(.*)\.so$/o) { $1; }
275  else { undef; }
276}
277
278sub assert_defined($) {
279  my ($val) = @_;
280  use Carp;
281  defined $val || Carp::confess("undef");
282  return $val;
283}
284
285sub is_dynamic_lib($) { my ($target) = @_; return $target_type{$target} == $DYNAMIC_LIB; }
286sub is_static_lib($)  { my ($target) = @_; return assert_defined($target_type{$target}) == $STATIC_LIB; }
287sub is_extra_param($) { my ($target) = @_; return $target_type{$target} == $EXTRA_PARAMS; }
288sub is_executable($)  { my ($target) = @_; return $target_type{$target} == $EXECUTABLE; }
289
290sub is_library($)  { my ($target) = @_; return is_static_lib($target) || is_dynamic_lib($target); }
291sub is_file($)     { my ($target) = @_; return is_library($target) || is_executable($target); }
292
293# --------------------------------------------------------------------------------
294
295sub declare_initial_target($$) {
296  my ($target,$type) = @_;
297
298  if ($target ne '') {
299    legal_target_or_die($target);
300
301    my $inherit_locations_r = $inheritants_of{$target};
302    if (not defined $inherit_locations_r) {
303      my %new_hash = ();
304      $inheritants_of{$target} = \%new_hash;
305      $target_type{$target} = $type;
306    }
307    else {
308      $target_type{$target}==$type || die "type mismatch for initial '$target' (".$target_type{$target}." vs $type)\n";
309    }
310  }
311}
312
313sub detect_target_type($) {
314  my ($target) = @_;
315
316  if ($target =~ /\.(so|a|o)$/o) {
317    if ($1 eq 'so') { $DYNAMIC_LIB; }
318    else { $STATIC_LIB; }
319  }
320  else { $EXECUTABLE; }
321}
322
323sub detect_type_and_declare_initial_target($) {
324  my ($target) = @_;
325
326  my $type = detect_target_type($target);
327  declare_initial_target($target,$type);
328}
329
330# --------------------------------------------------------------------------------
331
332sub scan_dependsfile_entry($$$) {
333  my ($line, $inheritant, $location) = @_;
334
335  my $analyse = 1;
336  my $type = undef;
337
338  if ($line =~ /^\$\((.*)\)$/o) {
339    my $envvar = $1;
340    my $env = $ENV{$envvar};
341    if (not defined $env) {
342      if ($dieOnUndefEnvvar==1) { die "Undefined environment variable '$envvar'\n"; }
343      $type = $EXTRA_PARAMS;
344      $analyse = 0;
345    }
346    else {
347      $line = $env;
348      $line =~ s/\s\s/ /g;
349      $line =~ s/^\s//g;
350      $line =~ s/\s$//g;
351    }
352  }
353
354  if ($analyse==1) {
355    if ($line =~ /-[lL]/o) {
356      $type = $EXTRA_PARAMS;
357    }
358    else {
359      $line = ARBHOME_relative($line);
360      $type = detect_target_type($line);
361    }
362    $analyse = 0;
363  }
364
365  defined $type || die "could not detect line type\n";
366
367  my $target = $line;
368  declare_initial_target($target,$type);
369
370  my $inherit_locations_r = $inheritants_of{$target};
371  $$inherit_locations_r{$inheritant} = $location;
372}
373
374
375my %scanned = (); # key=dependsfile, value=1 -> already scanned
376
377sub scan_target($) {
378  my ($target) = @_;
379
380  my $depfile = target2dependencyFile($target);
381
382  if (not defined $scanned{$depfile}) {
383    $scanned{$depfile} = 1;
384
385    my $fulldep = fullpath($depfile);
386
387    if (not -f $fulldep) {
388      my $dir = dirOf($fulldep);
389      my $err = "Missing dependency file for '$target'";
390      if (not -d $dir) { $err .= " (No such directory '$dir')"; }
391      my $location = $fulldep.':0';
392      location_error($location,$err);
393    }
394    else {
395      open(IN,'<'.$fulldep) || die "can't read '$fulldep' (Reason: $!)";
396      my $lineno = 0;
397      foreach (<IN>) {
398        $lineno++;
399        chomp;
400        if ($_ =~ /\#/) { $_ = $`; } # remove comments
401        $_ = trim($_); # remove whitespace
402
403        if ($_ ne '') {  # skip empty lines
404          my $location = $fulldep.':'.$lineno;
405          eval {
406            scan_dependsfile_entry($_, $target, $location);
407          };
408          if ($@) {
409            location_error($location, $@);
410          }
411        }
412      }
413      close(IN);
414    }
415  }
416}
417
418sub rec_add_depends_to_hash($\%);
419sub rec_add_depends_to_hash($\%) {
420  my ($target,$hash_r) = @_;
421
422  my $all_dep_r = $all_dependencies_of{$target};
423  if (defined $all_dep_r) {
424    foreach (keys %$all_dep_r) {
425      $$hash_r{$_} = 1;
426    }
427  }
428  else {
429    my $dep_r = $dependencies_of{$target};
430    foreach (keys %$dep_r) {
431      if (not defined $$hash_r{$_}) {
432        $$hash_r{$_} = 1;
433        rec_add_depends_to_hash($_,%$hash_r);
434      }
435    }
436  }
437}
438
439sub resolve_dependencies() {
440  my $done = 0;
441  my $target;
442  while ($done==0) {
443    my %unscanned = ();
444    foreach my $target (keys %inheritants_of) {
445      if ($target_type{$target} != $EXTRA_PARAMS) { # recurse dependency
446        my $depfile = target2dependencyFile($target);
447        if (not defined $scanned{$depfile}) {
448          if ($target eq '') {
449            my $inh_r = $inheritants_of{$target};
450            foreach (keys %$inh_r) {
451              my $loc = $$inh_r{$_};
452              location_error($loc, "Empty target inherited from here");
453            }
454          }
455          else {
456            $unscanned{$target} = 1;
457          }
458        }
459      }
460    }
461
462    my @unscanned = sort keys %unscanned;
463    if (scalar(@unscanned)) {
464      foreach $target (@unscanned) { scan_target($target); }
465    }
466    else {
467      $done = 1;
468    }
469  }
470
471  # build %dependencies_of
472
473  foreach $target (keys %inheritants_of) {
474    my %new_hash = ();
475    $dependencies_of{$target} = \%new_hash;
476  }
477  foreach $target (keys %inheritants_of) {
478    my $inherit_locations_r = $inheritants_of{$target};
479    foreach my $inheritant (keys %$inherit_locations_r) {
480      if ($target eq $inheritant) {
481        my $self_include_loc = get_inheritance_location($inheritant, $target);
482        location_error($self_include_loc, "Illegal self-dependency");
483      }
484      my $dep_r = $dependencies_of{$inheritant};
485      $$dep_r{$target} = 1;
486    }
487  }
488
489  if_errors_abortNow();
490
491  # build %all_dependencies_of
492
493  my @targets = keys %dependencies_of;
494  my %depends_size = map {
495    my $dep_r = $dependencies_of{$_};
496    $_ => scalar(keys %$dep_r);
497  } @targets;
498
499  @targets = sort { $depends_size{$a} <=> $depends_size{$b}; } @targets; # smallest first
500
501  foreach $target (@targets) {
502    my %new_hash = ();
503    rec_add_depends_to_hash($target,%new_hash);
504    $all_dependencies_of{$target} = \%new_hash;
505  }
506}
507
508# --------------------------------------------------------------------------------
509
510my $reg_is_libdepend_file = qr/^($libdepend_file|$libdepend_file\..*)$/;
511
512sub find_dep_decl_files($\@);
513sub find_dep_decl_files($\@) {
514  my ($dir,$found_r) = @_;
515  $dir =~ s/\/\//\//og;
516  my @subdirs = ();
517  opendir(DIR,$dir) || die "can't read directory '$dir' (Reason: $!)";
518  foreach (readdir(DIR)) {
519    if ($_ ne '.' and $_ ne '..') {
520      my $full = $dir.'/'.$_;
521      if (not -l $full) {
522        if (-d $full) {
523          if (not $_ =~ /\.(svn|git)$/o) { push @subdirs, $full; }
524        }
525        elsif ($_ =~ $reg_is_libdepend_file) { push @$found_r, ARBHOME_relative($full); }
526      }
527    }
528  }
529  closedir(DIR);
530  foreach (@subdirs) { find_dep_decl_files($_,@$found_r); }
531}
532
533my %dep2lib = (
534               'ARBDB/needs_libs.libARBDB_so'   => 'lib/libARBDB.so',
535               'AWT/needs_libs.libAWT_so'       => 'lib/libAWT.so',
536               'CORE/needs_libs.libCORE_so'     => 'lib/libCORE.so',
537               'WINDOW/needs_libs.libWINDOW_so' => 'lib/libWINDOW.so',
538
539               'GL/glAW/needs_libs.libglAW_a'       => 'GL/glAW/libglAW.a',
540               'GL/glpng/needs_libs.libglpng_arb_a' => 'GL/glpng/libglpng_arb.a',
541
542               'PROBE_COM/needs_libs.client_a' => 'PROBE_COM/client.a',
543               'PROBE_COM/needs_libs.server_a' => 'PROBE_COM/server.a',
544               'NAMES_COM/needs_libs.client_a' => 'NAMES_COM/client.a',
545               'NAMES_COM/needs_libs.server_a' => 'NAMES_COM/server.a',
546
547               'ptpan/needs_libs.PROBE_a' => 'ptpan/PROBE.a',
548              );
549
550sub libdepend_file_2_libname($) {
551  my ($dep) = @_;
552  if (defined $dep2lib{$dep}) {
553    $dep = $dep2lib{$dep};
554  }
555  elsif ($dep =~ /^(.*)\/([^\/]*)/) {
556    my ($dir,$name) = ($1,$2);
557    if ($name =~ $reg_is_libdepend_file) {
558      if ($dep =~ /BINDEP\/needs_libs\./) {
559        $dep = $';
560      }
561      else {
562        my ($prefix,$targetdir) = ('',undef);
563
564        if ($dir =~ /^(.*)\/([^\/]*)/) {
565          my ($path,$lastdir) = ($1,$2);
566          $path =~ s/^.\///;
567          $prefix = $path.'/';
568          $targetdir = $lastdir;
569        }
570        else {
571          $targetdir = $dir;
572        }
573
574        my $target = undef;
575        if ($name =~ /^needs_libs\.(.*)_o$/) {
576          $target = $targetdir.'/'.$1.'.o';
577        }
578        else {
579          $target = $targetdir.'/'.$targetdir.'.a';
580        }
581
582        $dep = $prefix.$target;
583      }
584    }
585  }
586  return $dep;
587}
588
589sub declare_all_targets() {
590  my @depfiles = ();
591  find_dep_decl_files($ARBHOME,@depfiles);
592  foreach my $depfile (sort @depfiles) {
593    my $target = dependencyFile2target($depfile);
594    detect_type_and_declare_initial_target($target);
595  }
596}
597
598# --------------------------------------------------------------------------------
599
600sub dot_label($) {
601  my ($target) = @_;
602  if (is_file($target)) { $target = filenameOf($target); }
603  return '"'.$target.'"';
604}
605
606sub generateDependencyGraph(\@\@$$) {
607  my ($depends_r,$targets_r,$gif,$bundleExe) = @_;
608
609  my $base;
610  if ($gif =~ /\.gif$/) {
611    $base = $`;
612  }
613  else {
614    $base = $gif;
615    $gif = $base.'.gif';
616  }
617
618  my $dotfile = $base.'.dot';
619  my $maxsize_X = 20; # inch
620  my $maxsize_Y = 17; # inch
621
622  open(DOT,'>'.$dotfile) || die "can't write to '$dotfile' (Reason: $!)";
623  print DOT "digraph ARBLIBDEP {\n";
624  print DOT "  rankdir=LR;\n";
625  print DOT "  concentrate=true;\n";
626  print DOT "  searchsize=1000;\n";
627  print DOT "  Damping=2.0;\n";
628  print DOT "  size=\"$maxsize_X,$maxsize_Y\";\n";
629  print DOT "  orientation=portrait;\n";
630  print DOT "\n";
631
632  my %wanted_depend = map { $_ => 1; } @$depends_r;
633
634  my $seen_binary = 0;
635
636  my %execount = ();
637
638  foreach my $exe (keys %inheritants_of) {
639    if ($target_type{$exe}==$EXECUTABLE) {
640      my $deps_r = $all_dependencies_of{$exe};
641      foreach (keys %$deps_r) {
642        my $count = $execount{$_};
643        $count = (defined $count) ? $count+1 : 1;
644        $execount{$_} = $count;
645      }
646    }
647  }
648
649  my %is_target = map { $_ => 1; } @$targets_r;
650
651  foreach my $target (@$depends_r) {
652    my $deps_r = $dependencies_of{$target};
653    my $type = $target_type{$target};
654
655    if (not defined $type) {
656      use Carp;
657      Carp::confess("no target_type has been defined for '$target' (misspelled or directory missing?) ");
658    }
659
660    my $dtarget = dot_label($target);
661
662    my $color = 'black';
663    my $shape = 'box';
664    my $style = '';
665
666    if ($type==$EXECUTABLE)   { $color = 'green'; $shape = 'ellipse'; }
667    if ($type==$DYNAMIC_LIB)  { $color = 'blue'; }
668    if ($type==$STATIC_LIB)   { ; }
669    if ($type==$EXTRA_PARAMS) { $color = 'red'; $shape = 'box'; }
670
671    if ($is_target{$target}) {
672      $color = '"#ffbb66"';
673      $style = 'style=filled';
674    }
675
676    my $peri = $execount{$target};
677    $peri = defined $peri ? $peri : 1;
678
679    print DOT '    '.$dtarget.' [';
680    print DOT ' color='.$color;
681    print DOT ' shape='.$shape;
682    print DOT ' peripheries='.$peri;
683    print DOT ' '.$style;
684    print DOT '];'."\n";
685
686    if ($type==$EXECUTABLE) {
687      if ($bundleExe==1) {
688        if ($seen_binary==0) {
689          print DOT '    executables [';
690          print DOT ' color=yellow';
691          print DOT '];'."\n";
692          $seen_binary = 1;
693        }
694        print DOT '    executables -> '.$dtarget.';'."\n";
695      }
696    }
697
698    foreach my $depend (keys %$deps_r) {
699      if (defined $wanted_depend{$depend}) {
700        my $ddepend = dot_label($depend);
701        print DOT '    '.$dtarget.' -> '.$ddepend.';'."\n";
702      }
703    }
704  }
705  print DOT "}\n";
706  close(DOT);
707  print STDERR "Wrote $dotfile\n";
708
709  my $cmd = 'dot -Tgif -o'.$gif.' '.$dotfile;
710  print STDERR $cmd."\n";
711  system($cmd)==0 || die "Failed to execute '$cmd'";
712
713  unlink($dotfile) || die "Failed to unlink '$dotfile' (Reason: $!)";
714}
715
716# --------------------------------------------------------------------------------
717
718sub pushDirsTo($\@\@) {
719  my ($pathPrefix,$depends_r,$out_r) = @_;
720  my @out = ();
721  foreach my $dep (@$depends_r) {
722    if (is_static_lib($dep)) {
723      my $dir  = dirOf($dep);
724      defined $dir || die "no dir for '$dep'";
725      my $name = filenameOf($dir);
726      push @out, prefix($pathPrefix,$dir.'/'.$name);
727    }
728    elsif (is_dynamic_lib($dep)) {
729      if ($dep =~ /^lib\/lib([A-Z_]+)\.so/) {
730        my $base = $1;
731        push @out, prefix($pathPrefix,$base.'/'.$base);
732      }
733      else {
734        die "malformed (unexpected) dependency name for shared lib ('$dep')";
735      }
736    }
737  }
738
739  foreach (sort @out) { push @$out_r, $_; }
740}
741
742sub pushFilesTo($\@\@) {
743  my ($pathPrefix,$depends_r,$out_r) = @_;
744  foreach my $dep (@$depends_r) {
745    if (is_file($dep)) {
746      push @$out_r, fixFileSuffix(prefix($pathPrefix,$dep));
747    }
748  }
749}
750
751sub sortLibsByDependency(\@) {
752  # sort libs for linker CL (fullfilling libs behind requesting libs)
753  my ($libs_r) = @_;
754  my @order = ();
755  my %added = map { $_ => 0; } @$libs_r;
756
757  while (scalar(@order) < scalar(@$libs_r)) {
758    my $didAdd = 0;
759    foreach my $lib (@$libs_r) {
760      if ($added{$lib}==0) { # not added yet
761        my $all_dep_r = $all_dependencies_of{$lib};
762        my $missing = 0;
763      CHECK: foreach my $dep (keys %$all_dep_r) {
764          my $was_added = $added{$dep};
765          if (defined $was_added) {
766            if ($was_added==0) { $missing = 1; last CHECK; }
767          }
768        }
769        if ($missing==0) {
770          push @order, $lib;
771          $added{$lib} = 1;
772          $didAdd = 1;
773        }
774      }
775    }
776    if ($didAdd==0) {
777      die "internal error: did not add any lib in last loop (undetected nested dependency?)";
778    }
779  }
780  @$libs_r = reverse @order;
781}
782
783sub pushStaticLibsTo($\@\@) {
784  my ($pathPrefix,$depends_r,$out_r) = @_;
785  my @libs = ();
786  foreach my $dep (@$depends_r) { if (is_static_lib($dep)) { push @libs, $dep; } }
787  sortLibsByDependency(@libs);
788  foreach (@libs) { push @$out_r, prefix($pathPrefix,$_); }
789}
790
791sub pushDynamicLibsTo(\@\@) {
792  my ($depends_r,$out_r) = @_;
793  my @dep = ();
794  foreach my $dep (@$depends_r) { if (is_dynamic_lib($dep)) { push @dep, $dep; } }
795  sortLibsByDependency(@dep);
796  foreach (@dep) { push @$out_r, '-l'.dynamic_name($_); }
797  foreach my $dep (@$depends_r) { if (is_extra_param($dep)) { push @$out_r, $dep; } }
798}
799
800# --------------------------------------------------------------------------------
801
802sub die_usage($) {
803  my ($err) = @_;
804  print "Usage: needed_libs.pl [options] [lib|obj|executable]\n";
805  print "Scans directories of 'lib|obj's for files named '$libdepend_file' or '$libdepend_file.libname'\n";
806  print "In case of an executable, it scans for a file named 'BINDEP/$libdepend_file.executable'.\n";
807  print "\n";
808  print "These files have to contain the name(s) of the libraries needed by 'lib', either\n";
809  print "as ARBHOME-relative path to the lib file or\n";
810  print "as link instruction (containing '-l') for non-ARB-libraries\n";
811  print "\n";
812  print "Libraries in directory '\$ARBHOME/lib' are handled special:\n";
813  print "- they need to be specified as lib/libNAME.so\n";
814  print "- their source directory is assumed to be \$ARBHOME/NAME\n";
815  print "- they are called 'dynamic' in the context of this script (even if \$LINK_STATIC=1, where they aren't dynamic)\n";
816  print "\n";
817  print "They may as well contain \${ENVVAR}\n";
818  print "If no '$libdepend_file' exists, the 'lib' is assumed to be independent.\n";
819  print "\n";
820  print "If the filename is 'DIR/$libdepend_file', the library is assumed to be 'DIR/DIR.a'\n";
821  print "\n";
822  print "options:\n";
823  print "  -D             output dirs lib depends on (e.g. ARBDB/ARBDB SL/ARB_TREE/ARB_TREE ..)\n";
824  print "  -F             output all libfiles the given lib depends on (e.g. ARBDB/libARBDB.so SL/ARB_TREE/ARB_TREE.a ..)\n";
825  print "                 (suffix used for \"dynamic\" libs depends on setting of \$LINK_STATIC)\n";
826  print "  -l             output link params (e.g. -lARBDB SL/ARB_TREE/ARB_TREE.a ..)\n";
827  print "  -L             same as -l, but link whole archive for the specified library (i.e. for the tested unit)\n";
828  print "  -s             output names of \"static\" libs (e.g. SL/ARB_TREE/ARB_TREE.a ..)\n";
829  print "  -d             output names of \"dynamic\" libs (e.g. -lARBDB ..)\n";
830  print "  -A dir         prefix paths with 'dir' (not for \"dynamic\" libs)\n";
831  print "  -U             do not die on undefined environment variables\n";
832  print "  -G outgif      Draw dependency graph (using dot)\n";
833  print "  -V             Dump dependencies\n";
834  print "  -n [<depth>]   print nested dependencies down to depth (default=all)\n";
835  print "  -N [<depth>]   print nested inheritants up to depth (default=all)\n";
836  print "  -I             Invert (print inheritants instead of dependencies)\n";
837  print "  -B             Both (print inheritants and dependencies)\n";
838  print "  -S             Include self (lib/exe given on command line)\n";
839  print "  -T             only show terminals (=those w/o dependencies and/or inheritants)\n";
840
841  die "Error: $err\n" if defined $err;
842  exit(1);
843}
844
845
846sub main() {
847  my @targets = ();
848
849  my $printDirs       = 0;
850  my $printFiles      = 0;
851  my $printStatic     = 0;
852  my $printDynamic    = 0;
853  my $linkWholeTarget = 0;
854  my $pathPrefix      = undef;
855  my $dependencyGraph = undef;
856  my $dump            = 0;
857  my $trackDepends    = 1;
858  my $dependsNesting  = -1; # -1 means infinite
859  my $inheritsNesting = -1; # -1 means infinite
860  my $trackInherits   = 0;
861  my $includeSelf     = 0;
862  my $onlyTerminals   = 0;
863
864  if (not @ARGV) {
865    die_usage("no arguments specified");
866  }
867
868  while (scalar(@ARGV)) {
869    $_ = shift @ARGV;
870    if ($_ =~ /^-/o) {
871      my $switch = $';
872      if    ($switch eq 'D') { $printDirs = 1; }
873      elsif ($switch eq 'F') { $printFiles = 1; }
874      elsif ($switch eq 'd') { $printDynamic = 1; }
875      elsif ($switch eq 's') { $printStatic = 1; }
876      elsif ($switch eq 'l') { $printStatic = 1; $printDynamic = 1; }
877      elsif ($switch eq 'L') { $printStatic = 1; $printDynamic = 1; $linkWholeTarget = 1; }
878      elsif ($switch eq 'A') { $pathPrefix = shift @ARGV; }
879      elsif ($switch eq 'U') { $dieOnUndefEnvvar = 0; }
880      elsif ($switch eq 'G') { $dependencyGraph = shift @ARGV; }
881      elsif ($switch eq 'V') { $dump = 1; }
882      elsif ($switch eq 'n' or $switch eq 'N') {
883        my $num = 1;
884        if (scalar(@ARGV) and $ARGV[0] =~ /^[0-9]+$/o) { $num = shift @ARGV; }
885        if    ($switch eq 'n') { $dependsNesting = $num; }
886        elsif ($switch eq 'N') { $inheritsNesting = $num; }
887        else { die; }
888      }
889      elsif ($switch eq 'I') { $trackInherits = 1; $trackDepends = 0; }
890      elsif ($switch eq 'B') { $trackInherits = 1; }
891      elsif ($switch eq 'S') { $includeSelf = 1; }
892      elsif ($switch eq 'T') { $onlyTerminals = 1; }
893      elsif (grep { $switch eq $_ } qw(? -help help h)) { die_usage(undef); }
894      else { die "unknown switch '-$switch'"; }
895    }
896    else {
897      if ($_ ne '') { push @targets, $_; }
898    }
899  }
900
901  my $targets = scalar(@targets);
902  my $specified_lib = undef;
903
904  if ($targets>0) {
905    if ($targets>1) {
906      if (not defined $dependencyGraph) {
907        die_usage("You may only specify ONE target! (only with -G multiple targets are possible)");
908      }
909    }
910    @targets = map { $_ = libdepend_file_2_libname($_); } @targets;
911    @targets = map { $_ = ARBHOME_relative($_); } @targets;
912    $specified_lib = $targets[0];
913    if ($trackDepends==1 and $trackInherits==0) {
914      foreach (@targets) {
915        detect_type_and_declare_initial_target($_);
916      }
917    }
918    else {
919      declare_all_targets();
920    }
921  }
922  else {
923    declare_all_targets();
924  }
925
926  resolve_dependencies();
927  warn_bad_dependencies();
928  if_errors_abortNow();
929
930  my %track = ();
931  if ($trackDepends==1) {
932    if ($dependsNesting==-1) { # infinite
933      foreach (@targets) {
934        my $dep_r = $all_dependencies_of{$_};
935        foreach (keys %$dep_r) { $track{$_} = 1; }
936      }
937    }
938    else {
939      my @track_depends_of = @targets;
940      my $nesting = 0;
941      NESTED_DEPENDS : while (@track_depends_of) {
942        ++$nesting;
943        if ($nesting>$dependsNesting) {
944          last NESTED_DEPENDS;
945        }
946        my %nextTracks = ();
947        foreach (@track_depends_of) {
948          my $dep_r = $dependencies_of{$_};
949          foreach (keys %$dep_r) {
950            my $tracked = $track{$_};
951            if (not defined $tracked or $tracked!=1) {
952              $track{$_} = 1;
953              $nextTracks{$_} = 1;
954            }
955          }
956        }
957        @track_depends_of = keys %nextTracks;
958      }
959    }
960  }
961  my $bundleExe = 1;
962  if ($trackInherits==1) {
963    if ($targets>0) {
964      if ($inheritsNesting==-1) { # infinite
965        foreach my $inheritant (keys %inheritants_of) {
966          my $dep_r = $all_dependencies_of{$inheritant};
967          foreach (@targets) {
968            if (defined $$dep_r{$_}) { $track{$inheritant} = 1; }
969          }
970        }
971      }
972      else {
973        $bundleExe = 0;
974        my @track_inherits_of = @targets;
975        my $nesting = 0;
976        NESTED_INHERITS : while (@track_inherits_of) {
977          ++$nesting;
978          if ($nesting>$inheritsNesting) {
979            last NESTED_INHERITS;
980          }
981          my %nextTracks = ();
982          foreach (@track_inherits_of) {
983            my $inh_r = $inheritants_of{$_};
984            foreach (keys %$inh_r) {
985              my $tracked = $track{$_};
986              if (not defined $tracked or $tracked!=1) {
987                $track{$_} = 1;
988                $nextTracks{$_} = 1;
989              }
990            }
991          }
992          @track_inherits_of = keys %nextTracks;
993        }
994      }
995    }
996  }
997
998  if ($targets>0) {
999    if ($includeSelf==1) {
1000      foreach (@targets) {
1001        $track{$_} = 1;
1002      }
1003    }
1004  }
1005  else {
1006    foreach (keys %inheritants_of) { $track{$_} = 1; }
1007  }
1008
1009  if ($onlyTerminals==1) {
1010    if ($trackInherits==1) {
1011      # untrack all which have inheritants
1012      foreach (keys %track) {
1013        my $hash_r = $inheritants_of{$_};
1014        if ((defined $hash_r) and (scalar(keys %$hash_r)>0)) {
1015          delete $track{$_};
1016        }
1017      }
1018    }
1019    if ($trackDepends==1) {
1020      # untrack all which have dependencies
1021      foreach (keys %track) {
1022        my $hash_r = $dependencies_of{$_};
1023        if ((defined $hash_r) and (scalar(keys %$hash_r)>0)) {
1024          delete $track{$_};
1025        }
1026      }
1027    }
1028  }
1029
1030  my @track = sort keys %track;
1031
1032  if ($dump==1) { dump_dependencies(); }
1033  if (defined $dependencyGraph) { generateDependencyGraph(@track,@targets,$dependencyGraph,$bundleExe); }
1034
1035  my @out = ();
1036  if ($printDirs==1) { pushDirsTo($pathPrefix,@track,@out); }
1037  if ($printFiles==1) { pushFilesTo($pathPrefix,@track,@out); }
1038
1039  {
1040    my @libs = ();
1041    if ($printStatic==1) { pushStaticLibsTo($pathPrefix,@track,@libs); }
1042    if ($printDynamic==1) { pushDynamicLibsTo(@track,@libs); }
1043
1044    # detect whether to wrap specified library (in whole-archive linker flags):
1045    my $parameter_to_wrap = undef;
1046    if (defined $specified_lib and $linkWholeTarget==1) {
1047      if (is_dynamic_lib($specified_lib)) {
1048        $parameter_to_wrap = '-l'.dynamic_name($specified_lib); # as done in .@pushStaticLibsTo
1049      }
1050      elsif (is_static_lib($specified_lib)) {
1051        $parameter_to_wrap = prefix($pathPrefix,$specified_lib); # as done in .@pushDynamicLibsTo
1052      }
1053      else {
1054        die "'$specified_lib' is neither detected as dynamic nor as static";
1055      }
1056    }
1057
1058    my $seen = 0;
1059    foreach (@libs) {
1060      if (defined $parameter_to_wrap and $_ eq $parameter_to_wrap) {
1061        # force the linker to link the whole-archive for the main library (aka the tested unit):
1062        push @out, '-Wl,--whole-archive';
1063        push @out, $_;
1064        push @out, '-Wl,--no-whole-archive';
1065        $seen = 1;
1066      }
1067      else {
1068        push @out, $_;
1069      }
1070    }
1071
1072    if ($seen==0 and defined $parameter_to_wrap) {
1073      die "expected to see '$specified_lib' (required to link in whole-archive mode)";
1074    }
1075  }
1076
1077  if (scalar(@out)>0) { print join(' ',@out)."\n"; }
1078}
1079
1080main();
Note: See TracBrowser for help on using the repository browser.