Re: [ROOT] Libraries

From: Matthew D. Langston (langston@SLAC.stanford.edu)
Date: Sun Apr 16 2000 - 20:25:00 MEST


Hi Ingo,

Ingo Strauch wrote:
> 
> Is there a way to 'look' at all symbols in a shared library? If not:
> somebody must have built that library and she could tell me what she
> has put in it...

There is no portable way to "look" inside a library to find the class
that you want.  The hints provided by others (e.g. "nm -C" under Linux)
are indeed very useful hacks and should be a part of your toolbox.
However, IMHO such hacks should not be a replacement for proper
documentation, since documentation is portable across all systems.

I have attached a utility to this e-mail (which is yet another hack in
the spirit of "nm -C" et. al.) that I wrote called "rootlib_lookup"
which solves the exact problem you are having, i.e. "in which library
does a particular class reside".

For example, if you didn't know which library contained "TVector3", you
would do this:

  $ ./rootlib_lookup TVector3 Make-macros 
  TVector3 resides in libPhysics

The "rootlib_lookup" utility takes two mandatory arguments: the name of
a class, and the name of a ROOT Makefile (which is almost always named
"Make-macros").  If the class is defined in the Makefile, then the name
of the library where the class can be found is printed on stdout.

I tried to attach the "Make-macros" file from the ROOT 2.24.02 source
tarball for your convenience, but the roottalk mailing-list software
didn't allow files that big.  If you don't have the ROOT source tarball
for the version of ROOT that you are using, then you will have to
download it to use the "rootlib_lookup" utility, since the utility
requires the "Make-macros" file.

IMHO the proper way to determine which library contains a specific class
is by going to the documentation for that class.  This is how other
class libraries that I use solve this problem.  Unfortunately, to my
knowledge the only documentation currently available to lookup which
ROOT library contains a specific class is the file "Make-macros" in the
"src" subdirectory of the ROOT source distribution.

In other words, looking at the control file (i.e. Make-macros in this
case) that actually puts a class into a shared library will always be
the definitive answer to "in which library does a class reside".

It would be nice if the automatically generated HTML documentation
contained the "class <=> library" relationship - maybe this could be
added to the ROOT documentation system in the future.  The
"rootlib_lookup" utility could easily be adapted to do this.

--
Matthew D. Langston
SLD, Stanford Linear Accelerator Center
langston@SLAC.Stanford.EDU

#! /usr/bin/perl -w

# Copyright (C) 2000 Matthew D. Langston <langston@SLAC.Stanford.EDU>
#
# This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or (at your
# option) any later version.
#
# This file is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this file; if not, write to:
#
#   Free Software Foundation, Inc.
#   Suite 330
#   59 Temple Place
#   Boston, MA 02111-1307, USA.
# 

# This utility takes two mandatory arguments: the name of a class, and
# the name of a ROOT Makefile (which is almost always named
# "Make-macros").  If the class is defined in the Makefile, then the
# name of the library where the class can be found is printed on stdout.


require 5.005;

use strict;


package FieldHash;

use base   qw( Tie::Hash );
use fields qw( rh );


sub TIEHASH
{
    my $proto  = shift;
    my $class  = ref( $proto ) || $proto;

    my $self;
    {
        no strict 'refs';
        $self  = [ \%{"$class\::FIELDS"} ];
        bless( $self, $class );
    }

    $self->{ rh } = {};

    $self->_Init( @_ )
	if $self->can( qw ( _Init ) );
    
    return $self;
}


sub STORE    { $_[ 0 ]->{ rh }->{ $_[ 1 ] } = $_[ 2 ];                              }
sub FETCH    { $_[ 0 ]->{ rh }->{ $_[ 1 ] };                                        }
sub FIRSTKEY { my $a = scalar keys %{ $_[ 0 ]->{ rh } }; each %{ $_[ 0 ]->{ rh } }; }
sub NEXTKEY  { each %{ $_[ 0 ]->{ rh } };                                           }
sub EXISTS   { exists $_[ 0 ]->{ rh }->{ $_[ 1 ] };                                 }
sub DELETE   { delete $_[ 0 ]->{ rh }->{ $_[ 1 ] };                                 }
sub CLEAR    { %{ $_[ 0 ]->{ rh } } = ();                                           }


package Makefile;

use vars qw( @ISA $AUTOLOAD );

use Carp;
use IO::File;

use base qw( FieldHash );


sub _Init
{
    my $this     = shift;
    my $makefile = shift;

    croak "Makefile name missing!"
	unless defined( $makefile );

    $this->_Parse( $makefile );
}


sub Print
{
    my $this    = shift;
    my $ra_keys = shift;
    my $fh      = shift;
    my $rh      = $this->{ rh };

    $ra_keys = keys %{ $rh }
    unless defined $ra_keys;
    
    $fh = *STDOUT
	unless defined( $fh );

    print $fh $rh->{ NAME }, $/;

    my $width = 0;
    foreach ( @{ $ra_keys } )
    {
	$width = length( $_ ) if length( $_ ) > $width;
    }

    $width += 2;
    map { printf $fh "\t%-${width}s%s$/", $_, join( ' ', @{ $rh->{ $_ } } ); } @{ $ra_keys };
}


# Private member function to parse the Makefile passed to the
# constructor and populate the %{ $this } hash.  The keys are the macro
# names, while the values are the (unsplit) rhs of the Make macros.
sub _Parse
{
    my $this     = shift;
    my $makefile = shift;

    my $fh = new IO::File( "< $makefile" );
    croak "Makefile \"$makefile\" not found!"
	unless defined( $fh );

    my $rh = $this->{ rh };

    $rh->{ NAME } = $makefile;

    my $line;
    while ( defined( $line = <$fh> ) )
    {
	chomp $line;
	if ( $line =~ s/\\\s*$// )
	{
	    $line .= <$fh>;
	    redo unless eof;
	}

	$line =~ s/#.*//;	# no comments
	$line =~ s/^\s+//;	# no leading whitespace
	$line =~ s/\s+$//;	# no trailing whitespace

	next unless length( $line ); # anything left?

	# Only look for Makefile macros (e.g. skip "include"
	# statements).
	if ( $line =~ /^\s*(\w+)\s*=\s*(.*)$/ )
	{
	    warn "$makefile:$. Macro $1 redefined!"
		if exists $rh->{ $1 };

	    $rh->{ $1 } = defined( $2 ) ? [ split( ' ', $2 ) ] : [];
	}
    }

    croak "NAME redefined in Makefile as $rh->{ NAME }!"
	if $makefile ne $rh->{ NAME };
}


package main;

my ( $class, $makefile ) = @ARGV;

usage() if !defined( $class    );
usage() if !defined( $makefile ) || ! -e $makefile;

my %makefile;
tie( %makefile, 'Makefile', $makefile );


my $header_macro;
foreach my $macro ( keys %makefile )
{
    next unless ref( $makefile{ $macro } ) eq 'ARRAY';
    if ( grep { /$class/ } @{ $makefile{ $macro } } )
    {
	$header_macro = $macro;
	last;
    }
}


die "$class not present in $makefile", $/
    if not defined( $header_macro );

# Get the name of the macro defining the library.
( my $lib_macro = $header_macro ) =~ s/H$/LIB/o;

# Turn the array reference into a string.
$lib_macro = @{ $makefile{ $lib_macro } } [ 0 ];

# Strip off leading "$(LPATH)/" and trailing ".$(SOEXT)",
# e.g. "$(LPATH)/libHist.$(SOEXT)" becomes "libHist".

( my $library = $lib_macro ) =~ s{^[^/]*/}{}o;
$library =~ s{\.[^.]*$}{}o;

print "$class resides in $library", $/;


exit( 0 );


sub usage()
{
    warn "useage: rootlib_lookup class Makefile", $/;
    print $/, <DATA>;
    exit 1;
}


__END__
This utility takes two mandatory arguments: the name of a class, and the
name of a ROOT Makefile (which is almost always named "Make-macros").
If the class is defined in the Makefile, then the name of the library
where the class can be found is printed on stdout.



This archive was generated by hypermail 2b29 : Tue Jan 02 2001 - 11:50:23 MET