8618d9ee5f
symlinks to all of them. this fixes kde/multimedia, which builds a libmpeg-0.3.0.so (using -release), also known as libmpeg.so, and then links something with -lmpeg ... (before this commit, we only had a symlink to the former) it should really just use libmpeg.la instead, but let's have libtool handle such attempts anyway. might fix other stuff i'm not yet aware of, too.
1874 lines
49 KiB
Perl
Executable File
1874 lines
49 KiB
Perl
Executable File
#!/usr/bin/perl
|
|
# $OpenBSD: libtool,v 1.4 2010/09/19 17:30:52 steven Exp $
|
|
|
|
# Copyright (c) 2007-2010 Steven Mestdagh <steven@openbsd.org>
|
|
#
|
|
# Permission to use, copy, modify, and distribute this software for any
|
|
# purpose with or without fee is hereby granted, provided that the above
|
|
# copyright notice and this permission notice appear in all copies.
|
|
#
|
|
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
use strict;
|
|
use warnings;
|
|
use feature qw(say switch state);
|
|
use Cwd qw(getcwd abs_path);
|
|
use File::Basename;
|
|
use File::Glob ':glob';
|
|
use File::Path;
|
|
use Getopt::Long;
|
|
use Getopt::Std;
|
|
|
|
package main;
|
|
|
|
use subs qw(
|
|
create_symlinks
|
|
generate_objlist
|
|
get_search_dirs
|
|
guess_implicit_mode
|
|
help
|
|
notyet
|
|
parse_version_info
|
|
process_deplibs
|
|
reverse_zap_duplicates_ref
|
|
);
|
|
|
|
package Trace;
|
|
|
|
sub print(&)
|
|
{
|
|
my $val = shift;
|
|
if (defined $ENV{'TRACE_LIBTOOL'}) {
|
|
state $trace_file;
|
|
if (!defined $trace_file) {
|
|
open $trace_file, '>>', $ENV{'TRACE_LIBTOOL'};
|
|
}
|
|
if (defined $trace_file) {
|
|
print $trace_file (&$val);
|
|
}
|
|
}
|
|
}
|
|
|
|
sub debug(&;$)
|
|
{
|
|
my ($args, $level) = @_;
|
|
|
|
$level = 1 if !defined $level;
|
|
|
|
if (defined $main::D && $main::D >= $level) {
|
|
print (&$args);
|
|
}
|
|
}
|
|
|
|
{
|
|
package Exec;
|
|
|
|
my $dry = 0;
|
|
my $verbose = 0;
|
|
my $performed = 0;
|
|
|
|
sub performed
|
|
{
|
|
return $performed;
|
|
}
|
|
|
|
sub dry_run
|
|
{
|
|
$dry = 1;
|
|
}
|
|
|
|
sub verbose_run
|
|
{
|
|
$verbose = 1;
|
|
}
|
|
|
|
sub silent_run
|
|
{
|
|
$verbose = 0;
|
|
}
|
|
|
|
sub new
|
|
{
|
|
my $class = shift;
|
|
bless {}, $class;
|
|
}
|
|
|
|
sub chdir
|
|
{
|
|
my ($self, $dir) = @_;
|
|
my $class = ref($self) || $self;
|
|
bless {dir => $dir}, $class;
|
|
}
|
|
|
|
sub command_run
|
|
{
|
|
my ($self, @l) = @_;
|
|
|
|
if ($self->{dir}) {
|
|
Trace::print {"cd $self->{dir} && "};
|
|
}
|
|
Trace::print { "@l\n" };
|
|
my $pid = fork();
|
|
if ($pid == -1) {
|
|
die "Couldn't fork while running @l\n";
|
|
}
|
|
if ($pid == 0) {
|
|
if ($self->{dir}) {
|
|
CORE::chdir($self->{dir}) or die "Can't chdir to $self->{dir}\n";
|
|
}
|
|
exec(@l);
|
|
die "Exec failed @l\n";
|
|
} else {
|
|
my $kid = waitpid($pid, 0);
|
|
if ($? != 0) {
|
|
die "Error while executing @l\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
sub shell
|
|
{
|
|
my ($self, @cmds) = @_;
|
|
# create an object "on the run"
|
|
if (!ref($self)) {
|
|
$self = $self->new;
|
|
}
|
|
for my $c (@cmds) {
|
|
say $c if $verbose || $dry;
|
|
if (!$dry) {
|
|
$self->command_run($c);
|
|
}
|
|
}
|
|
$performed++;
|
|
}
|
|
|
|
sub command
|
|
{
|
|
my ($self, @l) = @_;
|
|
# create an object "on the run"
|
|
if (!ref($self)) {
|
|
$self = $self->new;
|
|
}
|
|
say "@l" if $verbose || $dry;
|
|
if (!$dry) {
|
|
$self->command_run(@l);
|
|
}
|
|
$performed++;
|
|
}
|
|
}
|
|
|
|
{
|
|
package Parser;
|
|
my $calls = 0;
|
|
|
|
sub internal_resolve_la
|
|
{
|
|
my ($self, $level, $result, $rdeplibs, $rlibdirs, $args) = @_;
|
|
Trace::debug {"resolve level: $level\n"};
|
|
my $seen_pthread = 0;
|
|
foreach my $a (@$args) {
|
|
if ($a eq '-pthread') {
|
|
$seen_pthread++;
|
|
next;
|
|
}
|
|
push(@$result, $a);
|
|
next if $a !~ m/\.la$/;
|
|
my $lainfo = LaFile->parse($a);
|
|
if (!exists $lainfo->{'cached_deplibs'}) {
|
|
$lainfo->{'cached_deplibs'} = [];
|
|
$lainfo->{'cached_result'} = [];
|
|
$lainfo->{'cached_libdirs'} = [];
|
|
$lainfo->{'cached_pthread'} =
|
|
$self->internal_resolve_la($level+1,
|
|
$lainfo->{'cached_result'},
|
|
$lainfo->{'cached_deplibs'},
|
|
$lainfo->{'cached_libdirs'},
|
|
$lainfo->deplib_list);
|
|
push(@{$lainfo->{'cached_deplibs'}},
|
|
@{$lainfo->deplib_list});
|
|
push(@{$lainfo->{'cached_libdirs'}},
|
|
$lainfo->{'libdir'});
|
|
if (@{$lainfo->{'cached_deplibs'}} > 50) {
|
|
$lainfo->{'cached_deplibs'} = main::reverse_zap_duplicates_ref($lainfo->{'cached_deplibs'});
|
|
}
|
|
if (@{$lainfo->{'cached_libdirs'}} > 50) {
|
|
$lainfo->{'cached_libdirs'} = main::reverse_zap_duplicates_ref($lainfo->{'cached_libdirs'});
|
|
}
|
|
if (@{$lainfo->{'cached_result'}} > 50) {
|
|
$lainfo->{'cached_result'} = main::reverse_zap_duplicates_ref($lainfo->{'cached_result'});
|
|
}
|
|
}
|
|
$seen_pthread += $lainfo->{'cached_pthread'};
|
|
push(@$result, @{$lainfo->{'cached_result'}});
|
|
push(@$rdeplibs, @{$lainfo->{'cached_deplibs'}});
|
|
push(@$rlibdirs, @{$lainfo->{'cached_libdirs'}});
|
|
}
|
|
$calls++;
|
|
return $seen_pthread;
|
|
}
|
|
|
|
END
|
|
{
|
|
Trace::print { "Calls to resolve_la: $calls\n" } if $calls;
|
|
}
|
|
|
|
# resolve .la files until a level with empty dependency_libs is reached.
|
|
sub resolve_la
|
|
{
|
|
my ($self, $deplibs, $libdirs) = @_;
|
|
$self->{result} = [];
|
|
if ($self->internal_resolve_la(0, $self->{result}, $deplibs, $libdirs, $self->{args})) {
|
|
unshift(@{$self->{result}}, '-pthread');
|
|
unshift(@$deplibs, '-pthread');
|
|
}
|
|
return $self->{result};
|
|
}
|
|
|
|
# parse link flags and arguments
|
|
# eliminate all -L and -l flags in the argument string and add the
|
|
# corresponding directories and library names to the dirs/libs hashes.
|
|
# fill deplibs, to be taken up as dependencies in the resulting .la file...
|
|
# set up a hash for library files which haven't been found yet.
|
|
# deplibs are formed by collecting the original -L/-l flags, plus
|
|
# any .la files passed on the command line, EXCEPT when the .la file
|
|
# does not point to a shared library.
|
|
# pass 1
|
|
# -Lfoo, -lfoo, foo.a, foo.la
|
|
# recursively find .la files corresponding to -l flags; if there is no .la
|
|
# file, just inspect the library file itself for any dependencies.
|
|
# XXX the variable $lashared will register whether or not a .la file is
|
|
# found which refers to a shared library
|
|
# this is used to decide where to link executables and create wrappers
|
|
sub parse_linkargs1
|
|
{
|
|
state $seen_pthread = 0;
|
|
my ($self, $deplibs, $Rresolved, $libsearchdirs,
|
|
$dirs, $libs, $args, $level) = @_;
|
|
Trace::debug {"parse_linkargs1, level: $level\n"};
|
|
Trace::debug {" args: @$args\n"};
|
|
my $result = $self->{result};
|
|
my $lashared = $self->{seen_la_shared};
|
|
|
|
# first read all directories where we can search libraries
|
|
foreach my $a (@$args) {
|
|
if ($a =~ m/^-L(.*)/) {
|
|
if (!exists $dirs->{$1}) {
|
|
$dirs->{$1} = 1;
|
|
push @$deplibs, $a;
|
|
}
|
|
}
|
|
}
|
|
foreach my $a (@$args) {
|
|
Trace::debug {" processing $a\n"};
|
|
if (!$a || $a eq '' || $a =~ m/^\s+$/) {
|
|
# skip empty arguments
|
|
} elsif ($a eq '-pthread' && !$seen_pthread) {
|
|
# XXX special treatment since it's not a -l flag
|
|
push @$deplibs, $a;
|
|
$seen_pthread = 1;
|
|
push(@$result, $a);
|
|
} elsif ($a =~ m/^-L(.*)/) {
|
|
# already read earlier, do nothing
|
|
} elsif ($a =~ m/^-R(.*)/) {
|
|
# -R options originating from .la resolution
|
|
# those from @ARGV are in @Ropts
|
|
push @$Rresolved, $1;
|
|
} elsif ($a =~ m/^-l(\S+)/) {
|
|
my @largs = ();
|
|
my $key = $1;
|
|
if (!exists $libs->{$key}) {
|
|
$libs->{$key} = Library->new($key);
|
|
my $lafile = LaFile->find($key, $dirs);
|
|
if ($lafile) {
|
|
$libs->{$key}->{lafile} = $lafile;
|
|
my $absla = main::abs_path($lafile);
|
|
push @$deplibs, $absla;
|
|
push @$result, $lafile;
|
|
next;
|
|
} else {
|
|
$libs->{$key}->find($dirs, 1, 'notyet', $libsearchdirs);
|
|
my @deps = $libs->{$key}->inspect;
|
|
foreach my $d (@deps) {
|
|
my $k = main::basename $d;
|
|
$k =~ s/^(\S+)\.so.*$/$1/;
|
|
$k =~ s/^lib//;
|
|
push(@largs, "-l$k");
|
|
}
|
|
push @$deplibs, $a;
|
|
}
|
|
}
|
|
push(@$result, $a);
|
|
my $dummy = []; # no need to add deplibs recursively
|
|
$self->parse_linkargs1($dummy, $Rresolved,
|
|
$libsearchdirs, $dirs, $libs,
|
|
\@largs, $level+1) if @largs;
|
|
} elsif ($a =~ m/(\S+\/)*(\S+)\.a$/) {
|
|
(my $key = $2) =~ s/^lib//;
|
|
if (!exists $libs->{$key}) {
|
|
$libs->{$key} = Library->new($key);
|
|
}
|
|
my $d = main::abs_path(main::dirname($a));
|
|
$dirs->{$d} = 1;
|
|
$libs->{$key}->{fullpath} = $a;
|
|
push(@$result, $a);
|
|
} elsif ($a =~ m/(\S+\/)*(\S+)\.la$/) {
|
|
(my $key = $2) =~ s/^lib//;
|
|
my $d = main::abs_path(main::dirname($a));
|
|
$dirs->{$d} = 1;
|
|
my $fulla = main::abs_path($a);
|
|
my $lainfo = LaFile->parse($fulla);
|
|
my $dlname = $lainfo->{'dlname'};
|
|
my $oldlib = $lainfo->{'old_library'};
|
|
my $libdir = $lainfo->{'libdir'};
|
|
if ($dlname ne '') {
|
|
$$lashared = 1;
|
|
if (!exists $libs->{$key}) {
|
|
$libs->{$key} = Library->new($key);
|
|
$libs->{$key}->{lafile} = $fulla;
|
|
}
|
|
}
|
|
push(@$result, $a);
|
|
push(@$deplibs, $fulla) if ($libdir ne '');
|
|
} elsif ($a =~ m/(\S+\/)*(\S+)\.so(\.\d+){2}/) {
|
|
(my $key = $2) =~ s/^lib//;
|
|
my $d = main::abs_path(main::dirname($a));
|
|
$dirs->{$d} = 1;
|
|
if (!exists $libs->{$key}) {
|
|
$libs->{$key} = Library->new($key);
|
|
}
|
|
# not really normal argument
|
|
# -lfoo should be used instead, so convert it
|
|
push(@$result, "-l$key");
|
|
} else {
|
|
push(@$result, $a);
|
|
}
|
|
}
|
|
}
|
|
|
|
# pass 2
|
|
# -Lfoo, -lfoo, foo.a
|
|
# no recursion in pass 2
|
|
# fill orderedlibs array, which is the sequence of shared libraries
|
|
# after resolving all .la
|
|
# (this list may contain duplicates)
|
|
# fill staticlibs array, which is the sequence of static and convenience
|
|
# libraries
|
|
sub parse_linkargs2
|
|
{
|
|
state $seen_pthread = 0;
|
|
my ($self, $Rresolved, $libsearchdirs, $orderedlibs, $staticlibs,
|
|
$dirs, $libs) = @_;
|
|
Trace::debug {"parse_linkargs2\n"};
|
|
Trace::debug {" args: @{$self->{args}}\n"};
|
|
$self->{result} = [];
|
|
my $result = $self->{result};
|
|
my $ltdir = $main::ltdir;
|
|
|
|
foreach my $a (@{$self->{args}}) {
|
|
Trace::debug {" processing $a\n"};
|
|
if (!$a || $a eq '' || $a =~ m/^\s+$/) {
|
|
# skip empty arguments
|
|
} elsif ($a eq '-lc') {
|
|
# don't link explicitly with libc (just remove -lc)
|
|
} elsif ($a eq '-pthread' && !$seen_pthread) {
|
|
# XXX special treatment since it's not a -l flag
|
|
$seen_pthread = 1;
|
|
push(@$result, $a);
|
|
} elsif ($a =~ m/^-L(.*)/) {
|
|
if (!exists $dirs->{$1}) {
|
|
$dirs->{$1} = 1;
|
|
}
|
|
} elsif ($a =~ m/^-R(.*)/) {
|
|
# -R options originating from .la resolution
|
|
# those from @ARGV are in @Ropts
|
|
push @$Rresolved, $1;
|
|
} elsif ($a =~ m/^-l(.*)/) {
|
|
my @largs = ();
|
|
my $key = $1;
|
|
if (!exists $libs->{$key}) {
|
|
$libs->{$key} = Library->new($key);
|
|
}
|
|
push @$orderedlibs, $key;
|
|
} elsif ($a =~ m/(\S+\/)*(\S+)\.a$/) {
|
|
(my $key = $2) =~ s/^lib//;
|
|
if (!exists $libs->{$key}) {
|
|
$libs->{$key} = Library->new($key);
|
|
}
|
|
$libs->{$key}->{fullpath} = $a;
|
|
push(@$staticlibs, $a);
|
|
} elsif ($a =~ m/(\S+\/)*(\S+)\.la$/) {
|
|
(my $key = $2) =~ s/^lib//;
|
|
my $d = main::abs_path(main::dirname($a));
|
|
$dirs->{$d} = 1;
|
|
my $fulla = main::abs_path($a);
|
|
my $lainfo = LaFile->parse($fulla);
|
|
my $dlname = $lainfo->stringize('dlname');
|
|
my $oldlib = $lainfo->stringize('old_library');
|
|
if ($dlname eq '' && -f "$d/$ltdir/$oldlib") {
|
|
push @$staticlibs, "$d/$ltdir/$oldlib";
|
|
} else {
|
|
if (!exists $libs->{$key}) {
|
|
$libs->{$key} = Library->new($key);
|
|
$libs->{$key}->{lafile} = $fulla;
|
|
}
|
|
push @$orderedlibs, $key;
|
|
}
|
|
} elsif ($a =~ m/^-Wl,(\S+)/) {
|
|
# libtool accepts a list of -Wl options separated
|
|
# by commas, and possibly with a trailing comma
|
|
# which is not accepted by the linker
|
|
my @Wlflags = split(/,/, $1);
|
|
foreach my $f (@Wlflags) {
|
|
push(@$result, "-Wl,$f");
|
|
}
|
|
} else {
|
|
push(@$result, $a);
|
|
}
|
|
}
|
|
Trace::debug {"end parse_linkargs2\n"};
|
|
return $self->{result};
|
|
}
|
|
|
|
sub new
|
|
{
|
|
my ($class, $args) = @_;
|
|
bless { args => $args }, $class;
|
|
}
|
|
}
|
|
|
|
package LaLoFile;
|
|
my %file_cache; # which files have been parsed
|
|
my $cache_by_fullname = {};
|
|
my $cache_by_inode = {};
|
|
|
|
# allows special treatment for some keywords
|
|
sub set
|
|
{
|
|
my ($self, $k, $v) = @_;
|
|
|
|
$self->{$k} = $v;
|
|
}
|
|
|
|
sub stringize
|
|
{
|
|
my ($self, $k) = @_;
|
|
if (defined $self->{$k}) {
|
|
return $self->{$k};
|
|
}
|
|
return '';
|
|
}
|
|
|
|
sub read
|
|
{
|
|
my ($class, $filename) = @_;
|
|
my $info = $class->new;
|
|
open(my $fh, '<', $filename) or die "cannot read $filename: $!\n";
|
|
my $_;
|
|
while (<$fh>) {
|
|
chomp;
|
|
next if /^\#/;
|
|
next if /^\s*$/;
|
|
if (m/^(\S+)\=\'(.*)\'$/) {
|
|
$info->set($1, $2);
|
|
} elsif (m/^(\S+)\=(\S+)$/) {
|
|
$info->set($1, $2);
|
|
}
|
|
}
|
|
return $info;
|
|
}
|
|
|
|
sub parse
|
|
{
|
|
my ($class, $filename) = @_;
|
|
|
|
Trace::debug {"parsing $filename"};
|
|
|
|
if (defined $cache_by_fullname->{$filename}) {
|
|
Trace::debug {" (cached)\n"};
|
|
return $cache_by_fullname->{$filename};
|
|
}
|
|
my $key = join("/", (stat $filename)[0,1]);
|
|
if (defined $cache_by_inode->{$key}) {
|
|
Trace::debug {" (cached)\n"};
|
|
return $cache_by_inode->{$key};
|
|
}
|
|
Trace::debug {"\n"};
|
|
return $cache_by_inode->{$key} = $cache_by_fullname->{$filename} =
|
|
$class->read($filename);
|
|
}
|
|
|
|
sub new
|
|
{
|
|
my $class = shift;
|
|
bless {}, $class;
|
|
}
|
|
|
|
package LaFile;
|
|
our @ISA=(qw(LaLoFile));
|
|
use File::Basename;
|
|
|
|
# allows special treatment for some keywords
|
|
sub set
|
|
{
|
|
my ($self, $k, $v) = @_;
|
|
|
|
$self->SUPER::set($k, $v);
|
|
if ($k eq 'dependency_libs') {
|
|
my @l = split /\s+/, $v;
|
|
$self->{deplib_list} = \@l;
|
|
}
|
|
}
|
|
|
|
sub deplib_list
|
|
{
|
|
my $self = shift;
|
|
return $self->{deplib_list}
|
|
}
|
|
|
|
# XXX not sure how much of this cruft we need
|
|
sub write
|
|
{
|
|
my ($lainfo, $filename, $name) = @_;
|
|
|
|
my $libname = $lainfo->stringize('libname');
|
|
my $sharedlibname = $lainfo->stringize('dlname');
|
|
my $staticlibname = $lainfo->stringize('old_library');
|
|
my $librarynames = $lainfo->stringize('library_names');
|
|
my $deplibs = $lainfo->stringize('dependency_libs');
|
|
my $current = $lainfo->stringize('current');
|
|
my $revision = $lainfo->stringize('revision');
|
|
my $age = $lainfo->stringize('age');
|
|
my $installed = $lainfo->stringize('installed');
|
|
my $shouldnotlink = $lainfo->stringize('shouldnotlink');
|
|
my $libdir = $lainfo->stringize('libdir');
|
|
|
|
open(my $la, '>', $filename) or die "cannot write $filename: $!\n";
|
|
say "creating $filename" if $main::verbose || $main::D;
|
|
print $la <<EOF
|
|
# $name - libtool library file
|
|
# Generated by libtool $main::version
|
|
#
|
|
# Please DO NOT delete this file!
|
|
# It is necessary for linking the library.
|
|
|
|
# The name that we can dlopen(3).
|
|
dlname='$sharedlibname'
|
|
|
|
# Names of this library.
|
|
library_names='$librarynames'
|
|
|
|
# The name of the static archive.
|
|
old_library='$staticlibname'
|
|
|
|
# Libraries that this one depends upon.
|
|
dependency_libs='$deplibs'
|
|
|
|
# Version information for $libname.
|
|
current=$current
|
|
age=$age
|
|
revision=$revision
|
|
|
|
# Is this an already installed library?
|
|
installed=$installed
|
|
|
|
# Should we warn about portability when linking against -modules?
|
|
shouldnotlink=$shouldnotlink
|
|
|
|
# Files to dlopen/dlpreopen
|
|
dlopen=''
|
|
dlpreopen=''
|
|
|
|
# Directory that this library needs to be installed in:
|
|
libdir='$libdir'
|
|
EOF
|
|
;
|
|
}
|
|
|
|
sub write_shared_libs_log
|
|
{
|
|
my ($self, $origv) = @_;
|
|
my $libname = $self->stringize('libname');
|
|
my $v = $self->stringize('current') .'.'. $self->stringize('revision');
|
|
if (!defined $ENV{'SHARED_LIBS_LOG'}) {
|
|
return;
|
|
}
|
|
my $logfile = $ENV{'SHARED_LIBS_LOG'};
|
|
my $fh;
|
|
if (! -f $logfile) {
|
|
open ($fh, '>', $logfile);
|
|
print $fh "# SHARED_LIBS+= <libname> <obsd version> # <orig version>\n";
|
|
close $fh;
|
|
}
|
|
open ($fh, '>>', $logfile);
|
|
# Remove first leading 'lib', we don't want that in SHARED_LIBS_LOG.
|
|
$libname =~ s/^lib//;
|
|
printf $fh "SHARED_LIBS +=\t%-20s %-8s # %s\n", $libname, $v, $origv;
|
|
}
|
|
|
|
# find .la file associated with a -llib flag
|
|
# XXX pick the right one if multiple are found!
|
|
sub find
|
|
{
|
|
my ($self, $l, $dirs) = @_;
|
|
|
|
# sort dir search order by priority
|
|
# XXX not fully correct yet
|
|
my @sdirs = sort { $dirs->{$b} <=> $dirs->{$a} } keys %$dirs;
|
|
# search in cwd as well
|
|
unshift @sdirs, '.';
|
|
Trace::debug {"searching .la for $l\n"};
|
|
Trace::debug {"search path= ", join(':', @sdirs), "\n"};
|
|
foreach my $d (@sdirs) {
|
|
foreach my $la_candidate ("$d/lib$l.la", "$d/$l.a") {
|
|
if (-f $la_candidate) {
|
|
Trace::debug {"found $la_candidate\n"};
|
|
return $la_candidate;
|
|
}
|
|
}
|
|
}
|
|
Trace::debug {".la for $l not found!\n"};
|
|
return 0;
|
|
}
|
|
|
|
sub link
|
|
{
|
|
my $self = shift;
|
|
my $ltprog = shift;
|
|
my $la = shift;
|
|
my $fname = shift;
|
|
my $odir = shift;
|
|
my $shared = shift;
|
|
my $objs = shift;
|
|
my $dirs = shift;
|
|
my $libs = shift;
|
|
my $deplibs = shift;
|
|
my $libdirs = shift;
|
|
my $parser = shift;
|
|
my $opts = shift;
|
|
|
|
Trace::debug {"creating link command for library (linked ",
|
|
($shared) ? "dynam" : "stat", "ically)\n"};
|
|
|
|
my $what = ref($self);
|
|
my @libflags;
|
|
my @cmd;
|
|
my $ltdir = $main::ltdir;
|
|
my $dst = ($odir eq '.') ? "$ltdir/$fname" : "$odir/$ltdir/$fname";
|
|
if ($la =~ m/\.a$/) {
|
|
# probably just a convenience library
|
|
$dst = ($odir eq '.') ? "$fname" : "$odir/$fname";
|
|
}
|
|
mkdir "$odir/$ltdir" if (! -d "$odir/$ltdir");
|
|
|
|
Trace::debug {"argvstring (pre resolve_la): @{$parser->{args}}\n"};
|
|
my $args = $parser->resolve_la($deplibs, $libdirs);
|
|
Trace::debug {"argvstring (post resolve_la): @{$parser->{args}}\n"};
|
|
my $orderedlibs = [];
|
|
my $staticlibs = [];
|
|
$parser->{args} = $args;
|
|
$args = $parser->parse_linkargs2(\@main::Rresolved,
|
|
\@main::libsearchdirs, $orderedlibs, $staticlibs, $dirs, $libs);
|
|
Trace::debug {"staticlibs = \n", join("\n", @$staticlibs), "\n"};
|
|
Trace::debug {"orderedlibs = @$orderedlibs\n"};
|
|
my $finalorderedlibs = main::reverse_zap_duplicates_ref($orderedlibs);
|
|
Trace::debug {"final orderedlibs = @$finalorderedlibs\n"};
|
|
|
|
# static linking
|
|
if (!$shared) {
|
|
@cmd = ('ar', 'cru', $dst);
|
|
foreach my $a (@$staticlibs) {
|
|
if ($a =~ m/\.a$/ && $a !~ m/_pic\.a/) {
|
|
# extract objects from archive
|
|
my $libfile = main::basename $a;
|
|
my $xdir = "$odir/$ltdir/${la}x/$libfile";
|
|
main::extract_archive($xdir, $a);
|
|
my @kobjs = main::get_objlist_from_archive($a);
|
|
map { $_ = "$xdir/$_"; } @kobjs;
|
|
push @libflags, @kobjs;
|
|
}
|
|
}
|
|
foreach my $k (@$finalorderedlibs) {
|
|
my $l = $libs->{$k};
|
|
# XXX improve test
|
|
# this has to be done probably only with
|
|
# convenience libraries
|
|
next if (!defined $l->{lafile});
|
|
my $lainfo = LaFile->parse($l->{lafile});
|
|
next if ($lainfo->stringize('dlname') ne '');
|
|
$l->find($dirs, 0, $what);
|
|
my $a = $l->{fullpath};
|
|
if ($a =~ m/\.a$/ && $a !~ m/_pic\.a/) {
|
|
# extract objects from archive
|
|
my $libfile = main::basename $a;
|
|
my $xdir = "$odir/$ltdir/${la}x/$libfile";
|
|
main::extract_archive($xdir, $a);
|
|
my @kobjs = main::get_objlist_from_archive($a);
|
|
map { $_ = "$xdir/$_"; } @kobjs;
|
|
push @libflags, @kobjs;
|
|
}
|
|
}
|
|
push @cmd, @libflags if (@libflags);
|
|
push @cmd, @$objs if (@$objs);
|
|
Exec->command(@cmd);
|
|
Exec->command('ranlib', $dst);
|
|
return;
|
|
}
|
|
|
|
# dynamic linking
|
|
my $symbolsfile;
|
|
if ($opts->{'export-symbols'}) {
|
|
$symbolsfile = $opts->{'export-symbols'};
|
|
} elsif ($opts->{'export-symbols-regex'}) {
|
|
($symbolsfile = "$odir/$ltdir/$la") =~ s/\.la$/.exp/;
|
|
main::get_symbollist($symbolsfile, $opts->{'export-symbols-regex'}, $objs);
|
|
}
|
|
my $tmp = [];
|
|
while (my $k = shift @$finalorderedlibs) {
|
|
my $l = $libs->{$k};
|
|
$l->find($dirs, 1, $what);
|
|
if ($l->{dropped}) {
|
|
# remove library if dependency on it has been dropped
|
|
delete $libs->{$k};
|
|
} else {
|
|
push(@$tmp, $k);
|
|
}
|
|
}
|
|
$finalorderedlibs = $tmp;
|
|
|
|
my @libobjects = values %$libs;
|
|
Trace::debug {"libs:\n", join("\n", (keys %$libs)), "\n"};
|
|
Trace::debug {"libfiles:\n", join("\n", map { $_->{fullpath}//'UNDEF' } @libobjects), "\n"};
|
|
|
|
main::create_symlinks($ltdir, $libs);
|
|
my $prev_was_archive = 0;
|
|
my $libcounter = 0;
|
|
foreach my $k (@$finalorderedlibs) {
|
|
my $a = $libs->{$k}->{fullpath} || die "ERROR: $k not found in \$libs";
|
|
if ($a =~ m/\.a$/) {
|
|
# don't make a -lfoo out of a static library
|
|
push @libflags, '-Wl,-whole-archive' unless $prev_was_archive;
|
|
push @libflags, $a;
|
|
if ($libcounter == @$finalorderedlibs - 1) {
|
|
push @libflags, '-Wl,-no-whole-archive';
|
|
}
|
|
$prev_was_archive = 1;
|
|
} else {
|
|
push @libflags, '-Wl,-no-whole-archive' if $prev_was_archive;
|
|
$prev_was_archive = 0;
|
|
my $lib = basename $a;
|
|
if ($lib =~ m/^lib(.*)\.so(\.\d+){2}/) {
|
|
$lib = $1;
|
|
} else {
|
|
say "warning: cannot derive -l flag from library filename, assuming hash key";
|
|
$lib = $k;
|
|
}
|
|
push @libflags, "-l$lib";
|
|
}
|
|
$libcounter++;
|
|
}
|
|
|
|
@cmd = @$ltprog;
|
|
push @cmd, $main::sharedflag, @main::picflags;
|
|
push @cmd, '-o', $dst;
|
|
push @cmd, @$args if ($args);
|
|
push @cmd, @$objs if (@$objs);
|
|
push @cmd, '-Wl,-whole-archive', @$staticlibs, '-Wl,-no-whole-archive'
|
|
if (@$staticlibs);
|
|
push @cmd, "-L$ltdir", @libflags if (@libflags);
|
|
push @cmd, "-Wl,-retain-symbols-file,$symbolsfile" if ($symbolsfile);
|
|
Exec->command(@cmd);
|
|
}
|
|
|
|
package LoFile;
|
|
our @ISA=(qw(LaLoFile));
|
|
use File::Basename;
|
|
|
|
# write a libtool object file
|
|
sub write
|
|
{
|
|
my ($self, $filename) = @_;
|
|
my $picobj = $self->stringize('picobj');
|
|
my $nonpicobj = $self->stringize('nonpicobj');
|
|
|
|
my $name = basename $filename;
|
|
|
|
open(my $lo, '>', $filename) or die "cannot write $filename: $!\n";
|
|
say "creating $filename" if $main::verbose || $main::D;
|
|
print $lo <<EOF
|
|
# $name - a libtool object file
|
|
# Generated by libtool $main::version
|
|
#
|
|
pic_object='$picobj'
|
|
non_pic_object='$nonpicobj'
|
|
EOF
|
|
;
|
|
}
|
|
|
|
sub compile
|
|
{
|
|
my ($self, $compiler, $odir, $args) = @_;
|
|
|
|
my $ltdir = $main::ltdir;
|
|
mkdir "$odir/$ltdir" if (! -d "$odir/$ltdir");
|
|
if (defined $self->{picobj}) {
|
|
my @cmd = @$compiler;
|
|
push @cmd, @$args if (@$args);
|
|
push @cmd, @main::picflags, '-o';
|
|
my $o = ($odir eq '.') ? '' : "$odir/";
|
|
$o .= $self->{picobj};
|
|
push @cmd, $o;
|
|
Exec->command(@cmd);
|
|
}
|
|
if (defined $self->{nonpicobj}) {
|
|
my @cmd = @$compiler;
|
|
push @cmd, @$args if (@$args);
|
|
push @cmd, '-o';
|
|
my $o = ($odir eq '.') ? '' : "$odir/";
|
|
$o .= $self->{nonpicobj};
|
|
push @cmd, $o;
|
|
Exec->command(@cmd);
|
|
}
|
|
}
|
|
|
|
package Program;
|
|
use File::Basename;
|
|
|
|
sub new
|
|
{
|
|
my $class = shift;
|
|
bless {}, $class;
|
|
}
|
|
|
|
# write a wrapper script for an executable so it can be executed within
|
|
# the build directory
|
|
sub write_wrapper
|
|
{
|
|
my $self = shift;
|
|
|
|
my $program = $self->{outfilepath};
|
|
my $ltdir = $main::ltdir;
|
|
my $version = $main::version;
|
|
open(my $pw, '>', $program) or die "cannot write $program: $!\n";
|
|
print $pw <<EOF
|
|
#!/bin/sh
|
|
|
|
# $program - wrapper for $ltdir/$program
|
|
# Generated by libtool $version
|
|
|
|
argdir=`dirname \$0`
|
|
if test -f "\$argdir/$ltdir/$program"; then
|
|
# Add our own library path to LD_LIBRARY_PATH
|
|
LD_LIBRARY_PATH=\$argdir/$ltdir
|
|
export LD_LIBRARY_PATH
|
|
|
|
# Run the actual program with our arguments.
|
|
exec "\$argdir/$ltdir/$program" \${1+"\$\@"}
|
|
|
|
echo "\$0: cannot exec $program \${1+"\$\@"}"
|
|
exit 1
|
|
else
|
|
echo "\$0: error: \\\`\$argdir/$ltdir/$program' does not exist." 1>&2
|
|
exit 1
|
|
fi
|
|
EOF
|
|
;
|
|
close($pw);
|
|
chmod 0755, $program;
|
|
}
|
|
|
|
sub is_wrapper
|
|
{
|
|
# my $self = shift;
|
|
my $program = shift;
|
|
|
|
open(my $pw, '<', $program) or die "cannot open $program: $!\n";
|
|
return eval(grep { m/wrapper\sfor/ } <$pw>);
|
|
}
|
|
|
|
sub link
|
|
{
|
|
my $self = shift;
|
|
my $ltprog = shift;
|
|
my $dirs = shift;
|
|
my $libs = shift;
|
|
my $deplibs = shift;
|
|
my $libdirs = shift;
|
|
my $parser = shift;
|
|
my $opts = shift;
|
|
my $seen_la_shared = shift;
|
|
|
|
my $ltdir = $main::ltdir;
|
|
Trace::debug {"linking program (",
|
|
($self->{shared}) ? "dynam" : "stat", "ically)\n"};
|
|
|
|
my $what = ref($self);
|
|
my $fpath = $self->{outfilepath};
|
|
my $RPdirs = $self->{RPdirs};
|
|
|
|
my $odir = dirname $fpath;
|
|
my $fname = basename $fpath;
|
|
|
|
my @libflags;
|
|
my @cmd;
|
|
my $dst;
|
|
if ($self->{shared} && $seen_la_shared) {
|
|
$dst = ($odir eq '.') ? "$ltdir/$fname" : "$odir/$ltdir/$fname";
|
|
mkdir "$odir/$ltdir" if (! -d "$odir/$ltdir");
|
|
} else {
|
|
$dst = ($odir eq '.') ? $fname : "$odir/$fname";
|
|
}
|
|
|
|
Trace::debug {"argvstring (pre resolve_la): @{$parser->{args}}\n"};
|
|
my $args = $parser->resolve_la($deplibs, $libdirs);
|
|
Trace::debug {"argvstring (post resolve_la): @{$parser->{args}}\n"};
|
|
my $orderedlibs = [];
|
|
my $staticlibs = [];
|
|
$parser->{args} = $args;
|
|
$args = $parser->parse_linkargs2(\@main::Rresolved,
|
|
\@main::libsearchdirs, $orderedlibs, $staticlibs, $dirs, $libs);
|
|
Trace::debug {"staticlibs = \n", join("\n", @$staticlibs), "\n"};
|
|
Trace::debug {"orderedlibs = @$orderedlibs\n"};
|
|
my $finalorderedlibs = main::reverse_zap_duplicates_ref($orderedlibs);
|
|
Trace::debug {"final orderedlibs = @$finalorderedlibs\n"};
|
|
|
|
my $symbolsfile;
|
|
if ($opts->{'export-symbols'}) {
|
|
$symbolsfile = $opts->{'export-symbols'};
|
|
} elsif ($opts->{'export-symbols-regex'}) {
|
|
($symbolsfile = "$odir/$ltdir/$fname") =~ s/\.la$/.exp/;
|
|
main::get_symbollist($symbolsfile, $opts->{'export-symbols-regex'}, $self->{objlist});
|
|
}
|
|
$libdirs = main::reverse_zap_duplicates_ref($libdirs);
|
|
# add libdirs to rpath if they are not in standard lib path
|
|
for my $l (@$libdirs) {
|
|
my $found = 0;
|
|
for my $d (@main::libsearchdirs) {
|
|
if ($l eq $d) { $found = 1; last; }
|
|
}
|
|
if (!$found) { push @$RPdirs, $l; }
|
|
}
|
|
$RPdirs = main::reverse_zap_duplicates_ref($RPdirs);
|
|
map { $_ = "-Wl,-rpath,$_" } @$RPdirs;
|
|
foreach my $k (keys %$libs) {
|
|
Trace::debug {"key = $k - "};
|
|
my $r = ref($libs->{$k});
|
|
Trace::debug {"ref = $r\n"};
|
|
if (!defined $libs->{$k}) {
|
|
Trace::debug {"creating library object for $k\n"};
|
|
$libs->{$k} = Library->new($k);
|
|
}
|
|
my $l = $libs->{$k};
|
|
$l->find($dirs, $self->{shared}, $what);
|
|
}
|
|
|
|
my @libobjects = values %$libs;
|
|
Trace::debug {"libs:\n", join("\n", (keys %$libs)), "\n"};
|
|
Trace::debug {"libfiles:\n", join("\n", map { $_->{fullpath} } @libobjects), "\n"};
|
|
|
|
main::create_symlinks($ltdir, $libs);
|
|
foreach my $k (@$finalorderedlibs) {
|
|
my $a = $libs->{$k}->{fullpath} || die "ERROR: $k not found in \$libs";
|
|
if ($a =~ m/\.a$/) {
|
|
# don't make a -lfoo out of a static library
|
|
push @libflags, $a;
|
|
} else {
|
|
my $lib = basename $a;
|
|
if ($lib =~ m/^lib(.*)\.so(\.\d+){2}/) {
|
|
$lib = $1;
|
|
} else {
|
|
say "warning: cannot derive -l flag from library filename, assuming hash key";
|
|
$lib = $k;
|
|
}
|
|
push @libflags, "-l$lib";
|
|
}
|
|
}
|
|
|
|
@cmd = @$ltprog;
|
|
push @cmd, '-o', $dst;
|
|
push @cmd, '-static' if (!$self->{shared});
|
|
push @cmd, @$args if ($args);
|
|
push @cmd, @{$self->{objlist}} if (@{$self->{objlist}});
|
|
push @cmd, @$staticlibs if (@$staticlibs);
|
|
push @cmd, "-L$ltdir", @libflags if (@libflags);
|
|
push @cmd, @$RPdirs if (@$RPdirs);
|
|
push @cmd, "-Wl,-retain-symbols-file,$symbolsfile" if ($symbolsfile);
|
|
Exec->command(@cmd);
|
|
}
|
|
|
|
package Library;
|
|
|
|
# find actual library filename
|
|
# XXX pick the right one if multiple are found!
|
|
sub find
|
|
{
|
|
my ($self, $dirs, $shared, $linkmode, $ldconfigdirs) = @_;
|
|
|
|
my $libtofind = $self->{key};
|
|
my $libfile = 0;
|
|
my @globbedlib;
|
|
my $ltdir = $main::ltdir;
|
|
|
|
my $pic = ''; # used when finding static libraries
|
|
if ($linkmode eq 'LaFile') {
|
|
$pic = '_pic';
|
|
}
|
|
|
|
if (defined $self->{lafile}) {
|
|
# if there is a .la file, use the info from there
|
|
Trace::debug {"found .la file $self->{lafile} for library key: $self->{key}\n"};
|
|
my $lainfo = LaFile->parse($self->{lafile});
|
|
my $dlname = $lainfo->{'dlname'};
|
|
my $oldlib = $lainfo->{'old_library'};
|
|
my $libdir = $lainfo->{'libdir'};
|
|
my $d = main::abs_path(main::dirname($self->{lafile}));
|
|
# get the name we need (this may include a -release)
|
|
if (!$dlname && !$oldlib) {
|
|
die "neither static nor shared library found in $self->{lafile}\n";
|
|
}
|
|
if ($d !~ m/\Q$ltdir\E$/ && $lainfo->{'installed'} eq 'no') {
|
|
$d .= "/$ltdir";
|
|
}
|
|
if ($shared) {
|
|
if ($dlname) {
|
|
$libfile = "$d/$dlname";
|
|
} else {
|
|
# fall back to static
|
|
$libfile = "$d/$oldlib";
|
|
}
|
|
} else {
|
|
$libfile = "$d/$oldlib";
|
|
}
|
|
if (! -f $libfile) {
|
|
Trace::debug {".la file $self->{lafile} points to nonexistent file $libfile !\n"};
|
|
}
|
|
} else {
|
|
# otherwise, search the filesystem
|
|
# sort dir search order by priority
|
|
# XXX not fully correct yet
|
|
my @sdirs = sort { $dirs->{$b} <=> $dirs->{$a} } keys %$dirs;
|
|
# search in .libs when priority is high
|
|
map { $_ = "$_/$ltdir" if (exists $dirs->{$_} && $dirs->{$_} > 3) } @sdirs;
|
|
push @sdirs, @$ldconfigdirs if ($ldconfigdirs);
|
|
Trace::debug {"searching for $libtofind\n"};
|
|
Trace::debug {"search path= ", join(':', @sdirs), "\n"};
|
|
Trace::debug {"search type= ", ($shared) ? 'shared' : 'static', "\n"};
|
|
foreach my $sd (@sdirs) {
|
|
if ($shared) {
|
|
# select correct library by sorting by version number only
|
|
@globbedlib = sort { my ($x,$y) =
|
|
map { /\.so\.(\d+\.\d+)$/; $1 } ($a,$b); $y <=> $x }
|
|
glob "$sd/lib$libtofind.so.*.*";
|
|
if ($globbedlib[0]) {
|
|
Trace::debug {"found $libtofind in $sd\n"};
|
|
$libfile = $globbedlib[0];
|
|
last;
|
|
} else { # XXX find static library instead?
|
|
my $spath = "$sd/lib$libtofind$pic.a";
|
|
if (-f $spath) {
|
|
Trace::debug {"found static $libtofind in $sd\n"};
|
|
$libfile = $spath;
|
|
last;
|
|
}
|
|
}
|
|
} else {
|
|
# look for a static library
|
|
my $spath = "$sd/lib$libtofind.a";
|
|
if (-f $spath) {
|
|
Trace::debug {"found static $libtofind in $sd\n"};
|
|
$libfile = $spath;
|
|
last;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!$libfile) {
|
|
if (defined $self->{fullpath}) { delete $self->{fullpath}; }
|
|
if ($linkmode eq 'LaFile') {
|
|
say "warning: dependency on $libtofind dropped";
|
|
$self->{dropped} = 1;
|
|
} elsif ($linkmode eq 'Program') {
|
|
die "$libtofind not found!\n";
|
|
}
|
|
} else {
|
|
$self->{fullpath} = $libfile;
|
|
Trace::debug {"\$libs->{$self->{key}}->{fullpath} = ", $self->{fullpath}, "\n"};
|
|
}
|
|
}
|
|
|
|
# give a list of library dependencies found in the actual shared library
|
|
sub inspect
|
|
{
|
|
my $self = shift;
|
|
|
|
my $filename = $self->{fullpath};
|
|
my @deps;
|
|
Trace::debug {"inspecting $filename for library dependencies...\n"};
|
|
open(my $fh, '-|', "objdump -p $filename");
|
|
while (<$fh>) {
|
|
if (m/\s+NEEDED\s+(\S+)\s*$/) {
|
|
push @deps, $1;
|
|
}
|
|
}
|
|
Trace::debug {"found ", (@deps == 0) ? 'no ' : '',
|
|
"deps for $filename\n@deps\n"};
|
|
return @deps;
|
|
}
|
|
|
|
sub new
|
|
{
|
|
my ($class, $key) = @_;
|
|
bless { key => $key }, $class;
|
|
}
|
|
|
|
package main;
|
|
|
|
use Config;
|
|
use constant {
|
|
OBJECT => 0,
|
|
LIBRARY => 1,
|
|
PROGRAM => 2,
|
|
};
|
|
|
|
our $version = '1.5.26'; # pretend to be this version of libtool
|
|
my @no_shared_archs = qw(m88k vax);
|
|
# XXX my $machine_arch = `machine -a`;
|
|
my $machine_arch = $Config{'ARCH'};
|
|
(my $gnu_arch = $machine_arch) =~ s/amd64/x86_64/;
|
|
my @valid_modes = qw(clean compile execute finish install link uninstall);
|
|
my @valid_src = qw(asm c cc cpp cxx f s);
|
|
my $cwd = getcwd();
|
|
our $ltdir = '.libs';
|
|
our @picflags = ('-fPIC', '-DPIC');
|
|
our $sharedflag = '-shared';
|
|
my $instlibdir = '/usr/local/lib';
|
|
my @libsearchdirs;
|
|
$instlibdir = $ENV{'LIBDIR'} if defined $ENV{'LIBDIR'};
|
|
|
|
my $mode;
|
|
our $D = 0; # debug flag
|
|
my $verbose = 1;
|
|
|
|
my %opts; # options passed to libtool
|
|
my @tags; # list of --tag options passed to libtool
|
|
|
|
# just to be clear:
|
|
# when building a library:
|
|
# * -R libdir records libdir in dependency_libs
|
|
# * -rpath is the path where the (shared) library will be installed
|
|
# when building a program:
|
|
# * both -R libdir and -rpath libdir add libdir to the run-time path
|
|
# -Wl,-rpath,libdir will bypass libtool.
|
|
|
|
# build static/shared objects?
|
|
my $static = 1;
|
|
my $shared = 0;
|
|
my $convenience = 0;
|
|
my $noshared = 0;
|
|
if (grep { $_ eq $machine_arch } @no_shared_archs) {
|
|
$noshared = 1;
|
|
}
|
|
|
|
my $gp = new Getopt::Long::Parser;
|
|
# require_order so we stop parsing at the first non-option or argument,
|
|
# instead of parsing the whole ARGV.
|
|
$gp->configure( 'no_ignore_case',
|
|
'pass_through',
|
|
'no_auto_abbrev',
|
|
'require_order'
|
|
);
|
|
$gp->getoptions('config' => \&config,
|
|
'debug' => \$D,
|
|
'dry-run|n' => sub { Exec->dry_run },
|
|
'features' => \¬yet,
|
|
'finish' => sub { $mode = 'finish'; },
|
|
'help' => \&help, # does not return
|
|
'mode=s{1}' => \$mode,
|
|
'quiet' => sub { $verbose = 0; },
|
|
'silent' => sub { $verbose = 0; },
|
|
'tag=s{1}' => \@tags,
|
|
'version' => sub { say $version ; exit(0); },
|
|
);
|
|
|
|
if ($verbose || $D) {
|
|
Exec->verbose_run;
|
|
}
|
|
# what are we going to run (cc, c++, ...)
|
|
my $ltprog = ();
|
|
# deal with multi-arg ltprog
|
|
Trace::debug {"ARGV = @ARGV\n"};
|
|
while (@ARGV) {
|
|
# just read arguments until the next option...
|
|
if ($ARGV[0] =~ m/^\-/) { last; }
|
|
# XXX improve checks
|
|
if ($ARGV[0] =~ m/^\S+\.la/) { last; }
|
|
my $arg = shift @ARGV;
|
|
push @$ltprog, $arg;
|
|
Trace::debug {"arg = \"$arg\"\n"};
|
|
# if the current argument is an install program, stop immediately
|
|
if ($arg =~ /cp$/) { last; }
|
|
if ($arg =~ /install([-.]sh)?$/) { last; }
|
|
}
|
|
Trace::debug {"ltprog = \"@$ltprog\"\n"};
|
|
if (@$ltprog == 0) { die "no libtool command\n" };
|
|
|
|
# check mode and guess it if needed
|
|
if (!($mode && grep { $_ eq $mode } @valid_modes)) {
|
|
$mode = guess_implicit_mode($ltprog);
|
|
if ($mode) {
|
|
Trace::debug {"implicit mode: $mode\n"};
|
|
} else {
|
|
die "MODE must be one of:\n@valid_modes\n";
|
|
}
|
|
}
|
|
|
|
# from here, options may be intermixed with arguments
|
|
$gp->configure('permute');
|
|
|
|
if ($mode eq 'compile') {
|
|
my $lofile = LoFile->new;
|
|
|
|
$gp->getoptions('o=s' => \$opts{'o'},
|
|
'prefer-pic' => \$opts{'prefer-pic'},
|
|
'prefer-non-pic'=> \$opts{'prefer-non-pic'},
|
|
'static' => \$opts{'static'},
|
|
);
|
|
# XXX options ignored: -prefer-pic and -prefer-non-pic
|
|
my $pic = 0;
|
|
my $nonpic = 1;
|
|
# assume we need to build pic objects
|
|
$pic = 1 if (!$noshared);
|
|
$nonpic = 0 if ($pic && grep { $_ eq 'disable-static' } @tags);
|
|
$pic = 0 if ($nonpic && grep { $_ eq 'disable-shared' } @tags);
|
|
$nonpic = 1 if ($opts{'static'});
|
|
|
|
my ($outfile, $odir, $ofile, $srcfile, $srcext);
|
|
# XXX check whether -c flag is present and if not, die?
|
|
if ($opts{'o'}) {
|
|
# fix extension if needed
|
|
($outfile = $opts{'o'}) =~ s/\.o$/.lo/;
|
|
$odir = dirname $outfile;
|
|
$ofile = basename $outfile;
|
|
} else {
|
|
# XXX sometimes no -o flag is present and we need another way
|
|
my $srcre = join '|', @valid_src;
|
|
my $found = 0;
|
|
foreach my $a (@ARGV) {
|
|
if ($a =~ m/\.($srcre)$/i) {
|
|
$srcfile = $a;
|
|
$srcext = $1;
|
|
$found = 1;
|
|
last;
|
|
}
|
|
}
|
|
$found or die "cannot find source file in command\n";
|
|
# the output file ends up in the current directory
|
|
$odir = '.';
|
|
($ofile = basename $srcfile) =~ s/\.($srcext)$/.lo/i;
|
|
$outfile = "$odir/$ofile";
|
|
}
|
|
Trace::debug {"srcfile = $srcfile\n"} if $srcfile;
|
|
Trace::debug {"outfile = $outfile\n"};
|
|
(my $nonpicobj = $ofile) =~ s/\.lo$/.o/;
|
|
my $picobj = "$ltdir/$nonpicobj";
|
|
|
|
$lofile->{picobj} = $picobj if $pic;
|
|
$lofile->{nonpicobj} = $nonpicobj if $nonpic;
|
|
$lofile->compile($ltprog, $odir, \@ARGV);
|
|
$lofile->write($outfile);
|
|
} elsif ($mode eq 'install') {
|
|
# we just parse the options in order to find the actual arguments
|
|
my @argvcopy = @ARGV;
|
|
my %install_opts;
|
|
Trace::debug {"ltprog[-1] = $$ltprog[-1]\n"};
|
|
if ($$ltprog[-1] =~ m/install([.-]sh)?$/) {
|
|
getopts('BbCcdf:g:m:o:pSs', \%install_opts);
|
|
if (@ARGV < 2 && (!defined $install_opts{'d'} && @ARGV == 1)) {
|
|
die "wrong number of arguments for install\n";
|
|
}
|
|
} elsif ($$ltprog[-1] =~ m/cp$/) {
|
|
getopts('HLPRfipr', \%install_opts);
|
|
if (@ARGV < 2) {
|
|
die "wrong number of arguments for install\n";
|
|
}
|
|
} else {
|
|
die "unsupported install program $$ltprog[-1]\n";
|
|
}
|
|
my @instopts = @argvcopy[0 .. (@argvcopy - @ARGV - 1)];
|
|
my $dst = pop @ARGV;
|
|
my @src = @ARGV;
|
|
my $dstdir;
|
|
if (-d $dst) {
|
|
$dstdir = $dst;
|
|
} else {
|
|
# dst is not a directory, i.e. a file
|
|
if (@src > 1) {
|
|
# XXX not really libtool's task to check this
|
|
die "multiple source files combined with file destination";
|
|
} else {
|
|
$dstdir = dirname $dst;
|
|
}
|
|
}
|
|
my $toinstall = {};
|
|
my $tosymlink = {}; # for libraries with a -release in their name
|
|
my $addedmode = 0;
|
|
foreach my $s (@src) {
|
|
my $dstfile = basename $s;
|
|
# resolve symbolic links, so we don't try to test later
|
|
# whether the symlink is a program wrapper etc.
|
|
if (-l $s) {
|
|
$s = readlink($s) or die "Cannot readlink $s";
|
|
}
|
|
my $srcdir = dirname $s;
|
|
my $srcfile = basename $s;
|
|
Trace::debug {"srcdir = $srcdir\nsrcfile = $srcfile\n"};
|
|
Trace::debug {"dstdir = $dstdir\ndstfile = $dstfile\n"};
|
|
if ($srcfile =~ m/^\S+\.la$/) {
|
|
# we are installing a .la library
|
|
if ($$ltprog[-1] =~ m/install([.-]sh)?$/) {
|
|
push @instopts, '-m', '644' unless $addedmode;
|
|
$addedmode = 1;
|
|
}
|
|
my $lainfo = LaFile->parse($s);
|
|
# replace info where needed when installing the .la file
|
|
my $sharedlib = $lainfo->{'dlname'};
|
|
my $staticlib = $lainfo->{'old_library'};
|
|
my @libnames = split /\s+/, $lainfo->{'library_names'};
|
|
my $laipath = "$srcdir/$ltdir/$srcfile".'i';
|
|
$toinstall->{"$srcdir/$ltdir/$staticlib"} = "$dstdir/$staticlib"
|
|
if ($staticlib);
|
|
$toinstall->{"$srcdir/$ltdir/$sharedlib"} = "$dstdir/$sharedlib"
|
|
if ($sharedlib);
|
|
$toinstall->{"$laipath"} = "$dstdir/$dstfile";
|
|
foreach my $n (@libnames) {
|
|
$tosymlink->{$n} = $sharedlib if ($n ne $sharedlib);
|
|
}
|
|
} elsif (-f "$srcdir/$ltdir/$srcfile" && Program::is_wrapper($s)) {
|
|
$toinstall->{"$srcdir/$ltdir/$srcfile"} = $dst;
|
|
} else {
|
|
$toinstall->{$s} = $dst;
|
|
}
|
|
}
|
|
while (my ($s, $d) = each %$toinstall) {
|
|
my @realinstopts = @instopts;
|
|
# do not try to strip .la files
|
|
if ($s =~ m/\.la$/ || $d =~ m /\.la$/) {
|
|
@realinstopts = grep { $_ ne '-s' } @realinstopts;
|
|
}
|
|
Exec->command(@$ltprog, @realinstopts, $s, $d);
|
|
}
|
|
while (my ($d, $s) = each %$tosymlink) {
|
|
unlink("$dstdir/$d");
|
|
symlink($s, "$dstdir/$d");
|
|
}
|
|
if (defined $install_opts{'d'}) {
|
|
Exec->command(@$ltprog, @instopts, @ARGV);
|
|
}
|
|
} elsif ($mode eq 'link') {
|
|
my $cmd;
|
|
my @Ropts; # -R options on the command line
|
|
my @Rresolved; # -R options originating from .la resolution
|
|
my @RPopts; # -rpath options
|
|
my $deplibs = []; # list of dependent libraries (both -L and -l flags)
|
|
my $libdirs = []; # list of libdirs
|
|
my $libs = {}; # libraries
|
|
my $dirs = {}; # paths to find libraries
|
|
# put a priority in the dir hash
|
|
# always look here
|
|
$dirs->{'/usr/lib'} = 3;
|
|
|
|
$gp->getoptions('all-static' => \$opts{'all-static'},
|
|
'avoid-version' => \$opts{'avoid-version'},
|
|
'dlopen=s{1}' => \$opts{'dlopen'},
|
|
'dlpreopen=s{1}' => \$opts{'dlpreopen'},
|
|
'export-dynamic' => \$opts{'export-dynamic'},
|
|
'export-symbols=s' => \$opts{'export-symbols'},
|
|
'export-symbols-regex=s'=> \$opts{'export-symbols-regex'},
|
|
'module' => \$opts{'module'},
|
|
'no-fast-install' => \$opts{'no-fast-install'},
|
|
'no-install' => \$opts{'no-install'},
|
|
'no-undefined' => \$opts{'no-undefined'},
|
|
'o=s' => \$opts{'o'},
|
|
'objectlist=s' => \$opts{'objectlist'},
|
|
'precious-files-regex=s'=> \$opts{'precious-files-regex'},
|
|
'prefer-pic' => \$opts{'prefer-pic'},
|
|
'prefer-non-pic' => \$opts{'prefer-non-pic'},
|
|
'release=s' => \$opts{'release'},
|
|
'rpath=s' => \@RPopts,
|
|
'R=s' => \@Ropts,
|
|
'shrext=s' => \$opts{'shrext'},
|
|
'static' => \$opts{'static'},
|
|
'thread-safe' => \$opts{'thread-safe'},
|
|
'version-info=s{1}' => \$opts{'version-info'},
|
|
'version_info=s{1}' => \$opts{'version-info'},
|
|
'version-number=s{1}' => \$opts{'version-info'},
|
|
);
|
|
# XXX options ignored: dlopen, dlpreopen, no-fast-install,
|
|
# no-install, no-undefined, precious-files-regex,
|
|
# shrext, thread-safe, prefer-pic, prefer-non-pic
|
|
|
|
@libsearchdirs = get_search_dirs();
|
|
# add the .libs dir as well in case people try to link directly
|
|
# with the real library instead of the .la library
|
|
push @libsearchdirs, './.libs';
|
|
|
|
my $outfile = $opts{'o'};
|
|
if (!$outfile) {
|
|
die "no output file given.\n";
|
|
}
|
|
Trace::debug {"outfile = $outfile\n"};
|
|
my $odir = dirname $outfile;
|
|
my $ofile = basename $outfile;
|
|
|
|
# what are we linking?
|
|
my $linkmode = PROGRAM;
|
|
if ($ofile =~ m/\.l?a$/) {
|
|
$linkmode = LIBRARY;
|
|
}
|
|
Trace::debug {"linkmode: $linkmode\n"};
|
|
|
|
# eat multiple version-info arguments, we only accept the first.
|
|
map { $_ = '' if ($_ =~ m/\d+:\d+:\d+/); } @ARGV;
|
|
|
|
my @objs;
|
|
my @sobjs;
|
|
my $allpicobj;
|
|
if ($opts{'objectlist'}) {
|
|
my $objectlist = $opts{'objectlist'};
|
|
open(my $ol, '<', $objectlist) or die "cannot open $objectlist: $!\n";
|
|
my @objlist = <$ol>;
|
|
for (@objlist) { chomp; }
|
|
$allpicobj = generate_objlist(\@objs, \@sobjs, \@objlist);
|
|
} else {
|
|
$allpicobj = generate_objlist(\@objs, \@sobjs, \@ARGV);
|
|
}
|
|
Trace::debug {"objs = @objs\n"};
|
|
Trace::debug {"sobjs = @sobjs\n"};
|
|
|
|
my $parser = Parser->new(\@ARGV);
|
|
$parser->{result} = [];
|
|
my $seen_la_shared = 0;
|
|
$parser->{seen_la_shared} = \$seen_la_shared;
|
|
|
|
if ($linkmode == PROGRAM) {
|
|
my $program = Program->new;
|
|
$program->{outfilepath} = $outfile;
|
|
# XXX give higher priority to dirs of not installed libs
|
|
if ($opts{'export-dynamic'}) {
|
|
push(@{$parser->{args}}, "-Wl,-E");
|
|
}
|
|
if ($opts{'static'}) {
|
|
$program->{shared} = 0;
|
|
} else {
|
|
$program->{shared} = 1;
|
|
}
|
|
|
|
$parser->parse_linkargs1($deplibs, \@Rresolved, \@libsearchdirs,
|
|
$dirs, $libs, $parser->{args}, 0);
|
|
$parser->{args} = $parser->{result};
|
|
Trace::debug {"end parse_linkargs1\n"};
|
|
Trace::debug {"deplibs = @$deplibs\n"};
|
|
$seen_la_shared = ${$parser->{seen_la_shared}};
|
|
|
|
$program->{objlist} = \@objs;
|
|
if (@objs == 0) {
|
|
if (@sobjs > 0) {
|
|
Trace::debug {"no non-pic libtool objects found, trying pic objects...\n"};
|
|
$program->{objlist} = \@sobjs;
|
|
} elsif (@sobjs == 0) {
|
|
Trace::debug {"no libtool objects of any kind found\n"};
|
|
Trace::debug {"hoping for real objects in ARGV...\n"};
|
|
}
|
|
}
|
|
my $RPdirs = [];
|
|
@$RPdirs = (@Ropts, @RPopts, @Rresolved);
|
|
$program->{RPdirs} = $RPdirs;
|
|
|
|
$program->link($ltprog, $dirs, $libs, $deplibs, $libdirs, $parser, \%opts, $seen_la_shared);
|
|
if ($program->{shared} && $seen_la_shared) {
|
|
$program->write_wrapper();
|
|
}
|
|
} elsif ($linkmode == LIBRARY) {
|
|
my $lainfo = LaFile->new;
|
|
|
|
$shared = 1 if ($opts{'version-info'} ||
|
|
$opts{'avoid-version'} ||
|
|
$opts{'module'});
|
|
if (!@RPopts) {
|
|
$convenience = 1;
|
|
$noshared = 1;
|
|
$static = 1;
|
|
$shared = 0;
|
|
} else {
|
|
$shared = 1;
|
|
}
|
|
if ($ofile =~ m/\.a$/ && !$convenience) {
|
|
$ofile =~ s/\.a$/.la/;
|
|
$outfile =~ s/\.a$/.la/;
|
|
}
|
|
(my $libname = $ofile) =~ s/\.l?a$//; # remove extension
|
|
my $staticlib = $libname.'.a';
|
|
my $sharedlib = $libname.'.so';
|
|
my $sharedlib_symlink;
|
|
|
|
if ($opts{'static'}) {
|
|
$shared = 0;
|
|
$static = 1;
|
|
}
|
|
$shared = 0 if $noshared;
|
|
|
|
$parser->parse_linkargs1($deplibs, \@Rresolved, \@libsearchdirs,
|
|
$dirs, $libs, $parser->{args}, 0);
|
|
$parser->{args} = $parser->{result};
|
|
Trace::debug {"end parse_linkargs1\n"};
|
|
Trace::debug {"deplibs = @$deplibs\n"};
|
|
$seen_la_shared = ${$parser->{seen_la_shared}};
|
|
|
|
my $sover = '0.0';
|
|
my $origver = 'unknown';
|
|
# environment overrides -version-info
|
|
(my $envlibname = $libname) =~ s/[.+-]/_/g;
|
|
my ($current, $revision, $age) = (0, 0, 0);
|
|
if ($opts{'version-info'}) {
|
|
($current, $revision, $age) = parse_version_info($opts{'version-info'});
|
|
$origver = "$current.$revision";
|
|
$sover = $origver;
|
|
}
|
|
if ($ENV{"${envlibname}_ltversion"}) {
|
|
# this takes priority over the previous
|
|
$sover = $ENV{"${envlibname}_ltversion"};
|
|
($current, $revision) = split /\./, $sover;
|
|
$age = 0;
|
|
}
|
|
if (defined $opts{'release'}) {
|
|
$sharedlib_symlink = $sharedlib;
|
|
$sharedlib = $libname.'-'.$opts{'release'}.'.so';
|
|
}
|
|
if ($opts{'avoid-version'} ||
|
|
(defined $opts{'release'} && !$opts{'version-info'})) {
|
|
# don't add a version in these cases
|
|
} else {
|
|
$sharedlib .= ".$sover";
|
|
if (defined $opts{'release'}) {
|
|
$sharedlib_symlink .= ".$sover";
|
|
}
|
|
}
|
|
|
|
# XXX add error condition somewhere...
|
|
$static = 0 if ($shared && grep { $_ eq 'disable-static' } @tags);
|
|
$shared = 0 if ($static && grep { $_ eq 'disable-shared' } @tags);
|
|
|
|
Trace::debug {"SHARED: $shared\nSTATIC: $static\n"};
|
|
|
|
$lainfo->{'libname'} = $libname;
|
|
if ($shared) {
|
|
$lainfo->{'dlname'} = $sharedlib;
|
|
$lainfo->{'library_names'} = $sharedlib;
|
|
$lainfo->{'library_names'} .= " $sharedlib_symlink"
|
|
if (defined $opts{'release'});
|
|
$lainfo->link($ltprog, $ofile, $sharedlib, $odir, 1, \@sobjs, $dirs, $libs, $deplibs, $libdirs, $parser, \%opts);
|
|
Trace::debug {"sharedlib: $sharedlib\n"};
|
|
$lainfo->{'current'} = $current;
|
|
$lainfo->{'revision'} = $revision;
|
|
$lainfo->{'age'} = $age;
|
|
}
|
|
if ($static) {
|
|
$lainfo->{'old_library'} = $staticlib;
|
|
$lainfo->link($ltprog, $ofile, $staticlib, $odir, 0, ($allpicobj) ? \@sobjs : \@objs, $dirs, $libs, $deplibs, $libdirs, $parser, \%opts);
|
|
Trace::debug {($convenience ? "convenience" : "static")." lib: $staticlib\n"};
|
|
}
|
|
$lainfo->{'installed'} = 'no';
|
|
$lainfo->{'shouldnotlink'} = $opts{'module'} ? 'yes' : 'no';
|
|
map { $_ = "-R$_" } @Ropts;
|
|
unshift @$deplibs, @Ropts if (@Ropts);
|
|
Trace::debug {"deplibs = @$deplibs\n"};
|
|
my $finaldeplibs = reverse_zap_duplicates_ref($deplibs);
|
|
Trace::debug {"finaldeplibs = @$finaldeplibs\n"};
|
|
$lainfo->set('dependency_libs', "@$finaldeplibs");
|
|
if (@RPopts) {
|
|
if (@RPopts > 1) {
|
|
Trace::debug {"more than 1 -rpath option given, taking the first: ", $RPopts[0], "\n"};
|
|
}
|
|
$lainfo->{'libdir'} = $RPopts[0];
|
|
}
|
|
if (!($convenience && $ofile =~ m/\.a$/)) {
|
|
$lainfo->write($outfile, $ofile);
|
|
unlink("$odir/$ltdir/$ofile");
|
|
symlink("../$ofile", "$odir/$ltdir/$ofile");
|
|
}
|
|
my $lai = "$odir/$ltdir/$ofile".'i';
|
|
if ($shared) {
|
|
my $pdeplibs = process_deplibs($finaldeplibs);
|
|
if (defined $pdeplibs) {
|
|
$lainfo->set('dependency_libs', "@$pdeplibs");
|
|
}
|
|
$lainfo->write_shared_libs_log($origver);
|
|
}
|
|
$lainfo->{'installed'} = 'yes';
|
|
# write .lai file (.la file that will be installed)
|
|
$lainfo->write($lai, $ofile);
|
|
}
|
|
} elsif ($mode eq 'finish' || $mode eq 'clean' || $mode eq 'uninstall') {
|
|
# don't do anything
|
|
exit 0;
|
|
} elsif ($mode eq 'execute') {
|
|
# XXX check whether this is right
|
|
Exec->silent_run;
|
|
Exec->command(@$ltprog, @ARGV);
|
|
} else {
|
|
die "MODE=$mode not implemented yet.\n";
|
|
}
|
|
|
|
if (Exec->performed == 0) {
|
|
die "no commands to execute.\n"
|
|
}
|
|
|
|
###########################################################################
|
|
|
|
sub help
|
|
{
|
|
print <<EOF
|
|
Usage: $0 [options]
|
|
--config - print configuration
|
|
--debug - turn on debugging output
|
|
--dry-run - don't do anything, only show what would be done
|
|
--help - this message
|
|
--mode=MODE - use operation mode MODE
|
|
--quiet - do not print informational messages
|
|
--silent - same as `--quiet'
|
|
--tag -
|
|
--version - print version of libtool
|
|
EOF
|
|
;
|
|
exit 1;
|
|
}
|
|
|
|
sub notyet
|
|
{
|
|
die "option not implemented yet.\n";
|
|
}
|
|
|
|
# XXX incomplete
|
|
sub config
|
|
{
|
|
print "objdir=$ltdir\n";
|
|
print "arch=$machine_arch\n";
|
|
print "...\n";
|
|
exit 0;
|
|
}
|
|
|
|
# convert 4:5:8 into a list of numbers
|
|
sub parse_version_info
|
|
{
|
|
my $vinfo = shift;
|
|
|
|
if ($vinfo =~ m/^(\d+):(\d+):(\d+)$/) {
|
|
return ($1, $2, $3);
|
|
} elsif ($vinfo =~ m/^(\d+):(\d+)$/) {
|
|
return ($1, $2, 0);
|
|
} elsif ($vinfo =~ m/^(\d+)$/) {
|
|
return ($1, 0, 0);
|
|
} else {
|
|
die "error parsing -version-info $vinfo\n";
|
|
}
|
|
}
|
|
|
|
sub create_symlinks
|
|
{
|
|
my $dir = shift;
|
|
my $libs = shift;
|
|
|
|
if (! -d $dir) {
|
|
mkdir $dir or die "cannot create directory: $!\n";
|
|
}
|
|
foreach my $l (values %$libs) {
|
|
my $f = $l->{fullpath};
|
|
next if (!defined $f);
|
|
next if ($f =~ m/\.a$/);
|
|
my $libnames = [];
|
|
if (defined $l->{lafile}) {
|
|
my $lainfo = LaFile->parse($l->{lafile});
|
|
my $librarynames = $lainfo->stringize('library_names');
|
|
@$libnames = split /\s/, $librarynames;
|
|
$libnames = reverse_zap_duplicates_ref($libnames);
|
|
} else {
|
|
push @$libnames, basename $f;
|
|
}
|
|
foreach my $libfile (@$libnames) {
|
|
Trace::debug {"ln -s $f $dir/$libfile\n"};
|
|
if (! -f "$dir/$libfile") {
|
|
symlink abs_path($f), "$dir/$libfile" or die "cannot create symlink: $!\n";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# prepare dependency_libs information for the .la file which is installed
|
|
# i.e. remove any .libs directories and use the final libdir for all the
|
|
# .la files
|
|
sub process_deplibs
|
|
{
|
|
my $linkflags = shift;
|
|
|
|
my $result;
|
|
|
|
foreach my $lf (@$linkflags) {
|
|
if ($lf =~ m/-L\S+\Q$ltdir\E$/) {
|
|
} elsif ($lf =~ m/\/\S+\/(\S+\.la)/) {
|
|
my $lafile = $1;
|
|
$lf = LaFile->parse($lf)->{'libdir'}.'/'.$lafile;
|
|
push @$result, $lf;
|
|
} else {
|
|
push @$result, $lf;
|
|
}
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
# populate arrays of non-pic and pic objects and remove these from @ARGV
|
|
sub generate_objlist
|
|
{
|
|
my $objs = shift;
|
|
my $sobjs = shift;
|
|
my $objsource = shift;
|
|
my $allpic = 1;
|
|
|
|
my $result = [];
|
|
foreach my $a (@$objsource) {
|
|
if ($a =~ m/\S+\.lo$/) {
|
|
my $ofile = basename $a;
|
|
my $odir = dirname $a;
|
|
my $loinfo = LoFile->parse($a);
|
|
if ($loinfo->{'non_pic_object'}) {
|
|
my $o;
|
|
$o .= "$odir/" if ($odir ne '.');
|
|
$o .= $loinfo->{'non_pic_object'};
|
|
push @$objs, $o;
|
|
}
|
|
if ($loinfo->{'pic_object'}) {
|
|
my $o;
|
|
$o .= "$odir/" if ($odir ne '.');
|
|
$o .= $loinfo->{'pic_object'};
|
|
push @$sobjs, $o;
|
|
} else {
|
|
$allpic = 0;
|
|
}
|
|
} else {
|
|
push @$result, $a;
|
|
}
|
|
}
|
|
@$objsource = @$result;
|
|
return $allpic;
|
|
}
|
|
|
|
# XXX reuse code from SharedLibs.pm instead
|
|
sub get_search_dirs
|
|
{
|
|
my @libsearchdirs;
|
|
open(my $fh, '-|', 'ldconfig -r');
|
|
if (defined $fh) {
|
|
while (<$fh>) {
|
|
if (m/^\s*search directories:\s*(.*?)\s*$/o) {
|
|
foreach my $d (split(/\:/o, $1)) {
|
|
push @libsearchdirs, $d;
|
|
}
|
|
last;
|
|
}
|
|
}
|
|
close($fh);
|
|
} else {
|
|
die "Can't find ldconfig\n";
|
|
}
|
|
return @libsearchdirs;
|
|
}
|
|
|
|
sub extract_archive
|
|
{
|
|
my $dir = shift;
|
|
my $archive = shift;
|
|
|
|
if (! -d $dir) {
|
|
Trace::debug {"mkdir -p $dir\n"};
|
|
File::Path::mkpath($dir);
|
|
}
|
|
Exec->chdir($dir)->command('ar', 'x', $archive);
|
|
}
|
|
|
|
sub get_objlist_from_archive
|
|
{
|
|
my $a = shift;
|
|
|
|
open(my $arh, '-|', "ar t $a");
|
|
my @o = <$arh>;
|
|
close $arh;
|
|
map { chomp; } @o;
|
|
return @o;
|
|
}
|
|
|
|
sub get_symbollist
|
|
{
|
|
my $filepath = shift;
|
|
my $regex = shift;
|
|
my $objlist = shift;
|
|
|
|
Trace::debug {"generating symbol list in file: $filepath\n"};
|
|
my $symbols = [];
|
|
open(my $sh, '-|', 'nm', @$objlist) or die "error running nm on object list\n";
|
|
my $c = 0;
|
|
while (my $line = <$sh>) {
|
|
chomp $line;
|
|
Trace::debug {"$c: $line\n"};
|
|
if ($line =~ m/\S+\s+[BCDEGRST]\s+(.*)/) {
|
|
my $s = $1;
|
|
if ($s =~ m/$regex/) {
|
|
push @$symbols, $s;
|
|
Trace::debug {"matched\n"};
|
|
}
|
|
}
|
|
$c++;
|
|
}
|
|
$symbols = reverse_zap_duplicates_ref($symbols);
|
|
@$symbols = sort @$symbols;
|
|
open(my $fh, '>', $filepath) or die "cannot open $filepath\n";
|
|
print $fh join("\n", @$symbols), "\n";
|
|
}
|
|
|
|
# walk a list from back to front, removing any duplicates
|
|
# this should make sure a library's dependencies are behind the library itself
|
|
sub reverse_zap_duplicates_ref
|
|
{
|
|
my $arglist = shift;
|
|
my $h = {};
|
|
my $r = [];
|
|
for my $el (reverse @$arglist) {
|
|
next if defined $h->{$el};
|
|
unshift @$r, $el;
|
|
$h->{$el} = 1;
|
|
}
|
|
return $r;
|
|
}
|
|
|
|
# try to guess libtool mode when it is not specified
|
|
sub guess_implicit_mode
|
|
{
|
|
my $ltprog = shift;
|
|
my $m = 0;
|
|
for my $a (@$ltprog) {
|
|
if ($a =~ m/(install([.-]sh)?|cp)$/) {
|
|
$m = 'install';
|
|
} elsif ($a =~ m/cc|c\+\+/) { # XXX improve test
|
|
if (grep { $_ eq '-c' } @ARGV) {
|
|
$m = 'compile';
|
|
} else {
|
|
$m = 'link';
|
|
}
|
|
}
|
|
}
|
|
return $m;
|
|
}
|