diff options
author | Marc Espie <espie@cvs.openbsd.org> | 2012-06-19 09:30:45 +0000 |
---|---|---|
committer | Marc Espie <espie@cvs.openbsd.org> | 2012-06-19 09:30:45 +0000 |
commit | 3defa3954033e9bd8abcac5258f42a67d22d49f1 (patch) | |
tree | fd7ff1fe2d3de6d7f8d0dc5b326bd0709840094d /usr.bin | |
parent | 9de60e3c6d5b8c9f00cfb34b43989af9a75d1f6a (diff) |
add libtool to base, okay deraadt@, jasper@, millert@...
(as requested by matthieu@ so we can work on xenocara).
Diffstat (limited to 'usr.bin')
-rw-r--r-- | usr.bin/Makefile | 5 | ||||
-rw-r--r-- | usr.bin/libtool/LT/Archive.pm | 78 | ||||
-rw-r--r-- | usr.bin/libtool/LT/Exec.pm | 144 | ||||
-rw-r--r-- | usr.bin/libtool/LT/LaFile.pm | 340 | ||||
-rw-r--r-- | usr.bin/libtool/LT/LaLoFile.pm | 88 | ||||
-rw-r--r-- | usr.bin/libtool/LT/Library.pm | 159 | ||||
-rw-r--r-- | usr.bin/libtool/LT/LoFile.pm | 72 | ||||
-rw-r--r-- | usr.bin/libtool/LT/Parser.pm | 310 | ||||
-rw-r--r-- | usr.bin/libtool/LT/Program.pm | 188 | ||||
-rw-r--r-- | usr.bin/libtool/LT/Trace.pm | 48 | ||||
-rw-r--r-- | usr.bin/libtool/LT/Util.pm | 53 | ||||
-rw-r--r-- | usr.bin/libtool/Makefile | 32 | ||||
-rwxr-xr-x | usr.bin/libtool/libtool | 709 | ||||
-rw-r--r-- | usr.bin/libtool/libtool.1 | 25 |
14 files changed, 2249 insertions, 2 deletions
diff --git a/usr.bin/Makefile b/usr.bin/Makefile index 8535b2eb93a..c4ed87c9c61 100644 --- a/usr.bin/Makefile +++ b/usr.bin/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.123 2012/04/22 23:43:38 espie Exp $ +# $OpenBSD: Makefile,v 1.124 2012/06/19 09:30:44 espie Exp $ .include <bsd.own.mk> @@ -10,7 +10,8 @@ SUBDIR= apply apropos ar arch asa asn1_compile at aucat audioctl awk banner \ file2c find fgen finger fmt fold from fsplit fstat ftp gencat getcap \ getconf getent getopt gprof grep gzsig head hexdump id indent \ infocmp ipcrm ipcs \ - join jot kdump keynote ktrace lam last lastcomm leave less lex lndir \ + join jot kdump keynote ktrace lam last lastcomm leave less lex \ + libtool lndir \ locate lock logger login logname look lorder \ m4 mail make man mandoc mesg mg \ midiplay mixerctl mkdep mklocale mkstr mktemp modstat nc netstat \ diff --git a/usr.bin/libtool/LT/Archive.pm b/usr.bin/libtool/LT/Archive.pm new file mode 100644 index 00000000000..8eece326039 --- /dev/null +++ b/usr.bin/libtool/LT/Archive.pm @@ -0,0 +1,78 @@ +# $OpenBSD: Archive.pm,v 1.1 2012/06/19 09:30:44 espie 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); + +package LT::Archive; +use LT::Trace; +use LT::Exec; +use LT::Util; + +sub extract +{ + my ($self, $dir, $archive) = @_; + + if (! -d $dir) { + LT::Trace::debug {"mkdir -p $dir\n"}; + File::Path::mkpath($dir); + } + LT::Exec->chdir($dir)->link('ar', 'x', $archive); +} + +sub get_objlist +{ + my ($self, $a) = @_; + + open(my $arh, '-|', 'ar', 't', $a); + my @o = <$arh>; + close $arh; + map { chomp; } @o; + return @o; +} + +sub get_symbollist +{ + my ($self, $filepath, $regex, $objlist) = @_; + + if (@$objlist == 0) { + die "get_symbollist: object list is empty\n"; + } + + LT::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; + LT::Trace::debug {"$c: $line\n"}; + if ($line =~ m/\S+\s+[BCDEGRST]\s+(.*)/) { + my $s = $1; + if ($s =~ m/$regex/) { + push @$symbols, $s; + LT::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"; +} + +1; diff --git a/usr.bin/libtool/LT/Exec.pm b/usr.bin/libtool/LT/Exec.pm new file mode 100644 index 00000000000..a024ba033b8 --- /dev/null +++ b/usr.bin/libtool/LT/Exec.pm @@ -0,0 +1,144 @@ +# $OpenBSD: Exec.pm,v 1.1 2012/06/19 09:30:44 espie 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); + +package LT::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 compile +{ + my ($self, @l) = @_; + $self->command("compile", @l); +} + +sub execute +{ + my ($self, @l) = @_; + $self->command("execute", @l); +} + +sub install +{ + my ($self, @l) = @_; + $self->command("install", @l); +} + +sub link +{ + my ($self, @l) = @_; + $self->command("link", @l); +} + +sub command_run +{ + my ($self, @l) = @_; + + if ($self->{dir}) { + LT::Trace::print {"cd $self->{dir} && "}; + } + LT::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, $mode, @l) = @_; + # create an object "on the run" + if (!ref($self)) { + $self = $self->new; + } + if ($mode eq "compile"){ + say "@l" if $verbose || $dry; + } else { + say "libtool: $mode: @l" if $verbose || $dry; + } + if (!$dry) { + $self->command_run(@l); + } + $performed++; +} + +1; diff --git a/usr.bin/libtool/LT/LaFile.pm b/usr.bin/libtool/LT/LaFile.pm new file mode 100644 index 00000000000..8ff72c3da66 --- /dev/null +++ b/usr.bin/libtool/LT/LaFile.pm @@ -0,0 +1,340 @@ +# $OpenBSD: LaFile.pm,v 1.1 2012/06/19 09:30:44 espie 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); + +package LT::LaFile; +use parent qw(LT::LaLoFile); +use File::Basename; +use LT::Archive; +use LT::Util; + +# 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 $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, '.'; + LT::Trace::debug {"searching .la for $l\n"}; + LT::Trace::debug {"search path= ", join(':', @sdirs), "\n"}; + foreach my $d (@sdirs) { + foreach my $la_candidate ("$d/lib$l.la", "$d/$l.la") { + if (-f $la_candidate) { + LT::Trace::debug {"found $la_candidate\n"}; + return $la_candidate; + } + } + } + LT::Trace::debug {".la for $l not found!\n"}; + return 0; +} + +sub link +{ + my ($self, $ltprog, $la, $fname, $odir, $shared, $objs, $dirs, + $libs, $deplibs, $libdirs, $parser, $opts) = @_; + + LT::Trace::debug {"creating link command for library (linked ", + ($shared) ? "dynam" : "stat", "ically)\n"}; + + my $what = ref($self); + my @libflags; + my @cmd; + my $dst = ($odir eq '.') ? "$ltdir/$fname" : "$odir/$ltdir/$fname"; + if ($la =~ m/\.a$/) { + # probably just a convenience library + $dst = ($odir eq '.') ? "$fname" : "$odir/$fname"; + } + my $symlinkdir = $ltdir; + if ($odir ne '.') { + $symlinkdir = "$odir/$ltdir"; + } + mkdir $symlinkdir if (! -d $symlinkdir); + + LT::Trace::debug {"argvstring (pre resolve_la): @{$parser->{args}}\n"}; + my $args = $parser->resolve_la($deplibs, $libdirs); + LT::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); + LT::Trace::debug {"staticlibs = \n", join("\n", @$staticlibs), "\n"}; + LT::Trace::debug {"orderedlibs = @$orderedlibs\n"}; + my $finalorderedlibs = reverse_zap_duplicates_ref($orderedlibs); + LT::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 = basename $a; + my $xdir = "$odir/$ltdir/${la}x/$libfile"; + LT::Archive->extract($xdir, $a); + my @kobjs = LT::Archive->get_objlist($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 = LT::LaFile->parse($l->{lafile}); + next if ($lainfo->stringize('dlname') ne ''); + $l->find($dirs, 0, 0, $what); + my $a = $l->{fullpath}; + if ($a =~ m/\.a$/ && $a !~ m/_pic\.a/) { + # extract objects from archive + my $libfile = basename $a; + my $xdir = "$odir/$ltdir/${la}x/$libfile"; + LT::Archive->extract($xdir, $a); + my @kobjs = LT::Archive->get_objlist($a); + map { $_ = "$xdir/$_"; } @kobjs; + push @libflags, @kobjs; + } + } + push @cmd, @libflags if (@libflags); + push @cmd, @$objs if (@$objs); + LT::Exec->link(@cmd); + LT::Exec->link('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/; + LT::Archive->get_symbollist($symbolsfile, $opts->{'export-symbols-regex'}, $objs); + } + my $tmp = []; + while (my $k = shift @$finalorderedlibs) { + my $l = $libs->{$k}; + $l->find($dirs, 1, $opts->{'static'}, $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; + LT::Trace::debug {"libs:\n", join("\n", (keys %$libs)), "\n"}; + LT::Trace::debug {"libfiles:\n", join("\n", map { $_->{fullpath}//'UNDEF' } @libobjects), "\n"}; + + main::create_symlinks($symlinkdir, $libs); + my $prev_was_archive = 0; + my $libcounter = 0; + foreach my $k (@$finalorderedlibs) { + my $a = $libs->{$k}->{fullpath} || die "Link error: $k not found in \$libs\n"; + 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, $sharedflag, @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$symlinkdir", @libflags if (@libflags); + push @cmd, "-Wl,-retain-symbols-file,$symbolsfile" if ($symbolsfile); + LT::Exec->link(@cmd); +} + +sub install +{ + my ($class, $src, $dstdir, $instprog, $instopts, $strip) = @_; + + my $srcdir = dirname $src; + my $srcfile = basename $src; + my $dstfile = $srcfile; + + my @opts = @$instopts; + my @stripopts = ('--strip-debug'); + if ($$instprog[-1] =~ m/install([.-]sh)?$/) { + push @opts, '-m', '644'; + } + + my $lainfo = $class->parse($src); + my $sharedlib = $lainfo->{'dlname'}; + my $staticlib = $lainfo->{'old_library'}; + my $laipath = "$srcdir/$ltdir/$srcfile".'i'; + if ($staticlib) { + # do not strip static libraries, this is done below + my @realinstopts = @opts; + @realinstopts = grep { $_ ne '-s' } @realinstopts; + my $s = "$srcdir/$ltdir/$staticlib"; + my $d = "$dstdir/$staticlib"; + LT::Exec->install(@$instprog, @realinstopts, $s, $d); + LT::Exec->install('strip', @stripopts, $d) if ($strip); + } + if ($sharedlib) { + my $s = "$srcdir/$ltdir/$sharedlib"; + my $d = "$dstdir/$sharedlib"; + LT::Exec->install(@$instprog, @opts, $s, $d); + } + if ($laipath) { + # do not try to strip .la files + my @realinstopts = @opts; + @realinstopts = grep { $_ ne '-s' } @realinstopts; + my $s = $laipath; + my $d = "$dstdir/$dstfile"; + LT::Exec->install(@$instprog, @realinstopts, $s, $d); + } + # for libraries with a -release in their name + my @libnames = split /\s+/, $lainfo->{'library_names'}; + foreach my $n (@libnames) { + next if ($n eq $sharedlib); + unlink("$dstdir/$n"); + symlink($sharedlib, "$dstdir/$n"); + } +} + +1; diff --git a/usr.bin/libtool/LT/LaLoFile.pm b/usr.bin/libtool/LT/LaLoFile.pm new file mode 100644 index 00000000000..63d56a99b2d --- /dev/null +++ b/usr.bin/libtool/LT/LaLoFile.pm @@ -0,0 +1,88 @@ +# $OpenBSD: LaLoFile.pm,v 1.1 2012/06/19 09:30:44 espie 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); + +package LT::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) = @_; + + LT::Trace::debug {"parsing $filename"}; + + if (defined $cache_by_fullname->{$filename}) { + LT::Trace::debug {" (cached)\n"}; + return $cache_by_fullname->{$filename}; + } + my $key = join("/", (stat $filename)[0,1]); + if (defined $cache_by_inode->{$key}) { + LT::Trace::debug {" (cached)\n"}; + return $cache_by_inode->{$key}; + } + LT::Trace::debug {"\n"}; + return $cache_by_inode->{$key} = $cache_by_fullname->{$filename} = + $class->read($filename); +} + +sub new +{ + my $class = shift; + bless {}, $class; +} + +1; diff --git a/usr.bin/libtool/LT/Library.pm b/usr.bin/libtool/LT/Library.pm new file mode 100644 index 00000000000..5f1e7eca9ac --- /dev/null +++ b/usr.bin/libtool/LT/Library.pm @@ -0,0 +1,159 @@ +# $OpenBSD: Library.pm,v 1.1 2012/06/19 09:30:44 espie 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); + +package LT::Library; + +use LT::Util; + +# find actual library filename +# XXX pick the right one if multiple are found! +sub find +{ + my ($self, $dirs, $shared, $staticflag, $linkmode, $ldconfigdirs) = @_; + + my $libtofind = $self->{key}; + my $libfile = 0; + my @globbedlib; + + my $pic = ''; # used when finding static libraries + if ($linkmode eq 'LaFile') { + $pic = '_pic'; + } + + if (defined $self->{lafile}) { + require LT::LaFile; + # if there is a .la file, use the info from there + LT::Trace::debug {"found .la file $self->{lafile} for library key: $self->{key}\n"}; + my $lainfo = LT::LaFile->parse($self->{lafile}); + my $dlname = $lainfo->{'dlname'}; + my $oldlib = $lainfo->{'old_library'}; + my $libdir = $lainfo->{'libdir'}; + my $installed = $lainfo->{'installed'}; + my $d = abs_dir($self->{lafile}); + # get the name we need (this may include a -release) + if (!$dlname && !$oldlib) { + die "Link error: neither static nor shared library found in $self->{lafile}\n"; + } + if ($d !~ m/\Q$ltdir\E$/ && $installed eq 'no') { + $d .= "/$ltdir"; + } + if ($shared) { + if ($dlname) { + $libfile = "$d/$dlname"; + } else { + # fall back to static + $libfile = "$d/$oldlib"; + } + # if -static has been passed, don't link dynamically + # against not-installed libraries + if ($staticflag && $installed eq 'no') { + $libfile = "$d/$oldlib"; + } + } else { + $libfile = "$d/$oldlib"; + } + if (! -f $libfile) { + LT::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); + LT::Trace::debug {"searching for $libtofind\n"}; + LT::Trace::debug {"search path= ", join(':', @sdirs), "\n"}; + LT::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]) { + LT::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) { + LT::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) { + LT::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 "Link error: $libtofind not found!\n"; + } + } else { + $self->{fullpath} = $libfile; + LT::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; + + if (!defined($filename)){ + say "warning: library was specified that could not be found: $self->{key}"; + return; + } + LT::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; + } + } + LT::Trace::debug {"found ", (@deps == 0) ? 'no ' : '', + "deps for $filename\n@deps\n"}; + return @deps; +} + +sub new +{ + my ($class, $key) = @_; + bless { key => $key }, $class; +} + +1; diff --git a/usr.bin/libtool/LT/LoFile.pm b/usr.bin/libtool/LT/LoFile.pm new file mode 100644 index 00000000000..1c6151313f2 --- /dev/null +++ b/usr.bin/libtool/LT/LoFile.pm @@ -0,0 +1,72 @@ +# $OpenBSD: LoFile.pm,v 1.1 2012/06/19 09:30:44 espie 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); + +package LT::LoFile; +use parent qw(LT::LaLoFile); +use File::Basename; +use LT::Util; + +# 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) = @_; + + 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; + LT::Exec->compile(@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; + LT::Exec->compile(@cmd); + } +} + +1; diff --git a/usr.bin/libtool/LT/Parser.pm b/usr.bin/libtool/LT/Parser.pm new file mode 100644 index 00000000000..d85f5ffc808 --- /dev/null +++ b/usr.bin/libtool/LT/Parser.pm @@ -0,0 +1,310 @@ +# $OpenBSD: Parser.pm,v 1.1 2012/06/19 09:30:44 espie 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); + +package LT::Parser; +use File::Basename; +use Cwd qw(abs_path); +use LT::Util; +use LT::Library; +my $calls = 0; + +sub internal_resolve_la +{ + my ($self, $level, $result, $rdeplibs, $rlibdirs, $args) = @_; + LT::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$/; + require LT::LaFile; + my $lainfo = LT::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}); + if ($lainfo->{'libdir'} ne '') { + push(@{$lainfo->{'cached_libdirs'}}, + $lainfo->{'libdir'}); + } + if (@{$lainfo->{'cached_deplibs'}} > 50) { + $lainfo->{'cached_deplibs'} = reverse_zap_duplicates_ref($lainfo->{'cached_deplibs'}); + } + if (@{$lainfo->{'cached_libdirs'}} > 50) { + $lainfo->{'cached_libdirs'} = reverse_zap_duplicates_ref($lainfo->{'cached_libdirs'}); + } + if (@{$lainfo->{'cached_result'}} > 50) { + $lainfo->{'cached_result'} = 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 +{ + LT::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. +sub parse_linkargs1 +{ + state $seen_pthread = 0; + my ($self, $deplibs, $Rresolved, $libsearchdirs, + $dirs, $libs, $args, $level) = @_; + LT::Trace::debug {"parse_linkargs1, level: $level\n"}; + LT::Trace::debug {" args: @$args\n"}; + my $result = $self->{result}; + + # first read all directories where we can search libraries + foreach my $a (@$args) { + if ($a =~ m/^-L(.*)/) { + if (!exists $dirs->{$1}) { + $dirs->{$1} = 1; + LT::Trace::debug {" adding $a to deplibs\n"} if ($level == 0); + push @$deplibs, $a; + } + } + } + foreach my $a (@$args) { + LT::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} = LT::Library->new($key); + require LT::LaFile; + my $lafile = LT::LaFile->find($key, $dirs); + if ($lafile) { + $libs->{$key}->{lafile} = $lafile; + my $absla = abs_path($lafile); + LT::Trace::debug {" adding $absla to deplibs\n"} if ($level == 0); + push @$deplibs, $absla; + push @$result, $lafile; + next; + } else { + $libs->{$key}->find($dirs, 1, 0, 'notyet', $libsearchdirs); + my @deps = $libs->{$key}->inspect; + foreach my $d (@deps) { + my $k = basename $d; + $k =~ s/^(\S+)\.so.*$/$1/; + $k =~ s/^lib//; + push(@largs, "-l$k"); + } + } + } + LT::Trace::debug {" adding $a to deplibs\n"} if ($level == 0); + 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} = LT::Library->new($key); + } + $dirs->{abs_dir($a)} = 1; + $libs->{$key}->{fullpath} = $a; + push(@$result, $a); + } elsif ($a =~ m/(\S+\/)*(\S+)\.la$/) { + (my $key = $2) =~ s/^lib//; + $dirs->{abs_dir($a)} = 1; + my $fulla = abs_path($a); + require LT::LaFile; + my $lainfo = LT::LaFile->parse($fulla); + my $dlname = $lainfo->{'dlname'}; + my $oldlib = $lainfo->{'old_library'}; + my $libdir = $lainfo->{'libdir'}; + if ($dlname ne '') { + if (!exists $libs->{$key}) { + $libs->{$key} = LT::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//; + $dirs->{abs_dir($a)} = 1; + if (!exists $libs->{$key}) { + $libs->{$key} = LT::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 +# XXX the variable $parser->{seen_la_shared} will register whether or not +# a .la file is found which refers to a shared library and which is not +# yet installed +# this is used to decide where to link executables and create wrappers +sub parse_linkargs2 +{ + state $seen_pthread = 0; + my ($self, $Rresolved, $libsearchdirs, $orderedlibs, $staticlibs, + $dirs, $libs) = @_; + LT::Trace::debug {"parse_linkargs2\n"}; + LT::Trace::debug {" args: @{$self->{args}}\n"}; + $self->{result} = []; + my $result = $self->{result}; + + foreach my $a (@{$self->{args}}) { + LT::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} = LT::Library->new($key); + } + push @$orderedlibs, $key; + } elsif ($a =~ m/(\S+\/)*(\S+)\.a$/) { + (my $key = $2) =~ s/^lib//; + if (!exists $libs->{$key}) { + $libs->{$key} = LT::Library->new($key); + } + $libs->{$key}->{fullpath} = $a; + push(@$staticlibs, $a); + } elsif ($a =~ m/(\S+\/)*(\S+)\.la$/) { + (my $key = $2) =~ s/^lib//; + my $d = abs_dir($a); + $dirs->{$d} = 1; + my $fulla = abs_path($a); + require LT::LaFile; + my $lainfo = LT::LaFile->parse($fulla); + my $dlname = $lainfo->stringize('dlname'); + my $oldlib = $lainfo->stringize('old_library'); + my $installed = $lainfo->stringize('installed'); + if ($dlname ne '' && $installed eq 'no') { + LT::Trace::debug {"seen uninstalled la shared in $a\n"}; + $self->{seen_la_shared} = 1; + } + if ($dlname eq '' && -f "$d/$ltdir/$oldlib") { + push @$staticlibs, "$d/$ltdir/$oldlib"; + } else { + if (!exists $libs->{$key}) { + $libs->{$key} = LT::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); + } + } + LT::Trace::debug {"end parse_linkargs2\n"}; + return $self->{result}; +} + +sub new +{ + my ($class, $args) = @_; + bless { args => $args }, $class; +} +1; diff --git a/usr.bin/libtool/LT/Program.pm b/usr.bin/libtool/LT/Program.pm new file mode 100644 index 00000000000..1eb2ff0c1ed --- /dev/null +++ b/usr.bin/libtool/LT/Program.pm @@ -0,0 +1,188 @@ +# $OpenBSD: Program.pm,v 1.1 2012/06/19 09:30:44 espie 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); + +package LT::Program; +use File::Basename; +use LT::Archive; +use LT::Util; + +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 $pfile = basename $program; + my $realprogram = $ltdir . '/' . $pfile; + open(my $pw, '>', $program) or die "Cannot write $program: $!\n"; + print $pw <<EOF +#!/bin/sh + +# $program - wrapper for $realprogram +# Generated by libtool $version + +argdir=`dirname \$0` +if test -f "\$argdir/$realprogram"; 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/$realprogram" \${1+"\$\@"} + + echo "\$0: cannot exec $program \${1+"\$\@"}" + exit 1 +else + echo "\$0: error: \\\`\$argdir/$realprogram' does not exist." 1>&2 + exit 1 +fi +EOF +; + close($pw); + chmod 0755, $program; +} + +sub link +{ + my ($self, $ltprog, $dirs, $libs, $deplibs, $libdirs, $parser, + $opts) = @_; + + LT::Trace::debug {"linking program (", ($opts->{'static'}) ? "not " : "", + "dynamically linking not-installed libtool libraries)\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; + + LT::Trace::debug {"argvstring (pre resolve_la): @{$parser->{args}}\n"}; + my $args = $parser->resolve_la($deplibs, $libdirs); + LT::Trace::debug {"argvstring (post resolve_la): @{$parser->{args}}\n"}; + my $orderedlibs = []; + my $staticlibs = []; + $parser->{args} = $args; + $parser->{seen_la_shared} = 0; + $args = $parser->parse_linkargs2(\@main::Rresolved, + \@main::libsearchdirs, $orderedlibs, $staticlibs, $dirs, $libs); + LT::Trace::debug {"staticlibs = \n", join("\n", @$staticlibs), "\n"}; + LT::Trace::debug {"orderedlibs = @$orderedlibs\n"}; + my $finalorderedlibs = reverse_zap_duplicates_ref($orderedlibs); + LT::Trace::debug {"final orderedlibs = @$finalorderedlibs\n"}; + + my $symlinkdir = $ltdir; + if ($odir ne '.') { + $symlinkdir = "$odir/$ltdir"; + } + mkdir $symlinkdir if ! -d $symlinkdir; + if ($parser->{seen_la_shared}) { + $dst = ($odir eq '.') ? "$ltdir/$fname" : "$odir/$ltdir/$fname"; + $self->write_wrapper(); + } else { + $dst = ($odir eq '.') ? $fname : "$odir/$fname"; + } + + my $symbolsfile; + if ($opts->{'export-symbols'}) { + $symbolsfile = $opts->{'export-symbols'}; + } elsif ($opts->{'export-symbols-regex'}) { + ($symbolsfile = "$odir/$ltdir/$fname") =~ s/\.la$/.exp/; + LT::Archive->get_symbollist($symbolsfile, $opts->{'export-symbols-regex'}, $self->{objlist}); + } + $libdirs = 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 = reverse_zap_duplicates_ref($RPdirs); + map { $_ = "-Wl,-rpath,$_" } @$RPdirs; + foreach my $k (keys %$libs) { + LT::Trace::debug {"key = $k - "}; + my $r = ref($libs->{$k}); + LT::Trace::debug {"ref = $r\n"}; + if (!defined $libs->{$k}) { + LT::Trace::debug {"creating library object for $k\n"}; + require LT::Library; + $libs->{$k} = LT::Library->new($k); + } + my $l = $libs->{$k}; + $l->find($dirs, 1, $opts->{'static'}, $what); + } + + my @libobjects = values %$libs; + LT::Trace::debug {"libs:\n", join("\n", (keys %$libs)), "\n"}; + LT::Trace::debug {"libfiles:\n", join("\n", map { $_->{fullpath} } @libobjects), "\n"}; + + main::create_symlinks($symlinkdir, $libs); + foreach my $k (@$finalorderedlibs) { + my $a = $libs->{$k}->{fullpath} || die "Link error: $k not found in \$libs\n"; + 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, @$args if ($args); + push @cmd, @{$self->{objlist}} if (@{$self->{objlist}}); + push @cmd, @$staticlibs if (@$staticlibs); + push @cmd, "-L$symlinkdir", @libflags if (@libflags); + push @cmd, @$RPdirs if (@$RPdirs); + push @cmd, "-Wl,-retain-symbols-file,$symbolsfile" if ($symbolsfile); + LT::Exec->link(@cmd); +} + +sub install +{ + my ($class, $src, $dst, $instprog, $instopts) = @_; + + my $srcdir = dirname $src; + my $srcfile = basename $src; + my $realpath = "$srcdir/$ltdir/$srcfile"; + LT::Exec->install(@$instprog, @$instopts, $realpath, $dst); +} + +1; diff --git a/usr.bin/libtool/LT/Trace.pm b/usr.bin/libtool/LT/Trace.pm new file mode 100644 index 00000000000..99ddcb9cc7b --- /dev/null +++ b/usr.bin/libtool/LT/Trace.pm @@ -0,0 +1,48 @@ +# $OpenBSD: Trace.pm,v 1.1 2012/06/19 09:30:44 espie 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); + +package LT::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); + } +} + +1; diff --git a/usr.bin/libtool/LT/Util.pm b/usr.bin/libtool/LT/Util.pm new file mode 100644 index 00000000000..e0ff3226064 --- /dev/null +++ b/usr.bin/libtool/LT/Util.pm @@ -0,0 +1,53 @@ +# $OpenBSD: Util.pm,v 1.1 2012/06/19 09:30:44 espie 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; +package LT::Util; +require Exporter; +our @ISA = qw(Exporter); +our @EXPORT = qw(reverse_zap_duplicates_ref abs_dir $ltdir $version + @picflags $sharedflag); +use File::Basename; +use Cwd; + +our $ltdir = '.libs'; +our $version = '1.5.26'; # pretend to be this version of libtool +our @picflags = ('-fPIC', '-DPIC'); +our $sharedflag = '-shared'; + +# 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; +} + +sub abs_dir +{ + my $a = shift; + return dirname(Cwd::abs_path($a)); +} + +1; diff --git a/usr.bin/libtool/Makefile b/usr.bin/libtool/Makefile new file mode 100644 index 00000000000..eb85c7a60b6 --- /dev/null +++ b/usr.bin/libtool/Makefile @@ -0,0 +1,32 @@ +# $OpenBSD: Makefile,v 1.1 2012/06/19 09:30:44 espie Exp $ + +.include <bsd.own.mk> + +MAN=libtool.1 +NOPROG= + +PACKAGES= \ + LT/Archive.pm \ + LT/Exec.pm \ + LT/LaFile.pm \ + LT/LaLoFile.pm \ + LT/Library.pm \ + LT/LoFile.pm \ + LT/Parser.pm \ + LT/Program.pm \ + LT/Trace.pm \ + LT/Util.pm + +LIBBASE=/usr/libdata/perl5 + +realinstall: + ${INSTALL} -d -o ${LIBOWN} -g ${LIBGRP} -m ${DIRMODE} \ + ${DESTDIR}${LIBBASE}/LT +.for i in ${PACKAGES} + ${INSTALL} ${INSTALL_COPY} -o ${LIBOWN} -g ${LIBGRP} -m ${LIBMODE} \ + ${.CURDIR}/$i ${DESTDIR}${LIBBASE}/$i +.endfor + ${INSTALL} ${INSTALL_COPY} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \ + ${.CURDIR}/libtool ${DESTDIR}${BINDIR}/libtool + +.include <bsd.prog.mk> diff --git a/usr.bin/libtool/libtool b/usr.bin/libtool/libtool new file mode 100755 index 00000000000..74d2dcc77f8 --- /dev/null +++ b/usr.bin/libtool/libtool @@ -0,0 +1,709 @@ +#!/usr/bin/perl +# $OpenBSD: libtool,v 1.1 2012/06/19 09:30:44 espie 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; +use LT::Trace; +use LT::Exec; +use LT::Parser; +use LT::Util; + + +package main; + +use subs qw( + create_symlinks + generate_objlist + get_search_dirs + guess_implicit_mode + help + notyet + parse_version_info + process_deplibs + is_wrapper + ); + + + +use Config; +use constant { + OBJECT => 0, + LIBRARY => 1, + PROGRAM => 2, +}; + +my @no_shared_archs = qw(m88k vax); +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(); +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 { LT::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 "libtool (not (GNU libtool)) $version" ; exit(0); }, + ); + +if ($verbose || $D) { + LT::Exec->verbose_run; +} +# what are we going to run (cc, c++, ...) +my $ltprog = []; +# deal with multi-arg ltprog +LT::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; + LT::Trace::debug {"arg = \"$arg\"\n"}; + # if the current argument is an install program, stop immediately + if ($arg =~ /cp$/) { last; } + if ($arg =~ /install([-.]sh)?$/) { last; } +} +LT::Trace::debug {"ltprog = \"@$ltprog\"\n"}; +if (@$ltprog == 0) { die "No libtool command given.\n" }; +# make ltprog a list of elements without whitespace (prevent exec errors) +my @tmp_ltprog = @$ltprog; +@$ltprog = (); +for my $el (@tmp_ltprog) { + my @parts = split /\s+/, $el; + push @$ltprog, @parts; +} + +# check mode and guess it if needed +if (!($mode && grep { $_ eq $mode } @valid_modes)) { + $mode = guess_implicit_mode($ltprog); + if ($mode) { + LT::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') { + require LT::LoFile; + my $lofile = LT::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"; + } + LT::Trace::debug {"srcfile = $srcfile\n"} if $srcfile; + LT::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; + LT::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.\n"; + } else { + $dstdir = dirname $dst; + } + } + 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: $!\n"; + } + my $srcdir = dirname $s; + my $srcfile = basename $s; + LT::Trace::debug {"srcdir = $srcdir\nsrcfile = $srcfile\n"}; + LT::Trace::debug {"dstdir = $dstdir\ndstfile = $dstfile\n"}; + if ($srcfile =~ m/^\S+\.la$/) { + require LT::LaFile; + LT::LaFile->install($s, $dstdir, $ltprog, \@instopts, $install_opts{'s'}); + } elsif (-f "$srcdir/$ltdir/$srcfile" && is_wrapper($s)) { + require LT::Program; + LT::Program->install($s, $dst, $ltprog, \@instopts); + } else { + LT::Exec->$mode(@$ltprog, @instopts, $s, $dst); + } + } + if (defined $install_opts{'d'}) { + LT::Exec->$mode(@$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"; + } + LT::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; + } + LT::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; + if ($opts{'objectlist'}) { + my $objectlist = $opts{'objectlist'}; + open(my $ol, '<', $objectlist) or die "Cannot open $objectlist: $!\n"; + my @objlist = <$ol>; + for (@objlist) { chomp; } + generate_objlist(\@objs, \@sobjs, \@objlist); + } else { + generate_objlist(\@objs, \@sobjs, \@ARGV); + } + LT::Trace::debug {"objs = @objs\n"}; + LT::Trace::debug {"sobjs = @sobjs\n"}; + + my $parser = LT::Parser->new(\@ARGV); + $parser->{result} = []; + + if ($linkmode == PROGRAM) { + require LT::Program; + my $program = LT::Program->new; + $program->{outfilepath} = $outfile; + # XXX give higher priority to dirs of not installed libs + if ($opts{'export-dynamic'}) { + push(@{$parser->{args}}, "-Wl,-E"); + } + + $parser->parse_linkargs1($deplibs, \@Rresolved, \@libsearchdirs, + $dirs, $libs, $parser->{args}, 0); + $parser->{args} = $parser->{result}; + LT::Trace::debug {"end parse_linkargs1\n"}; + LT::Trace::debug {"deplibs = @$deplibs\n"}; + + $program->{objlist} = \@objs; + if (@objs == 0) { + if (@sobjs > 0) { + LT::Trace::debug {"no non-pic libtool objects found, trying pic objects...\n"}; + $program->{objlist} = \@sobjs; + } elsif (@sobjs == 0) { + LT::Trace::debug {"no libtool objects of any kind found\n"}; + LT::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); + } elsif ($linkmode == LIBRARY) { + require LT::LaFile; + my $lainfo = LT::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'} || $opts{'all-static'}) { + $shared = 0; + $static = 1; + } + $shared = 0 if $noshared; + + $parser->parse_linkargs1($deplibs, \@Rresolved, \@libsearchdirs, + $dirs, $libs, $parser->{args}, 0); + $parser->{args} = $parser->{result}; + LT::Trace::debug {"end parse_linkargs1\n"}; + LT::Trace::debug {"deplibs = @$deplibs\n"}; + + 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); + + LT::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); + LT::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, ($convenience && @sobjs > 0) ? \@sobjs : \@objs, $dirs, $libs, $deplibs, $libdirs, $parser, \%opts); + LT::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); + LT::Trace::debug {"deplibs = @$deplibs\n"}; + my $finaldeplibs = reverse_zap_duplicates_ref($deplibs); + LT::Trace::debug {"finaldeplibs = @$finaldeplibs\n"}; + $lainfo->set('dependency_libs', "@$finaldeplibs"); + if (@RPopts) { + if (@RPopts > 1) { + LT::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"); + } + if (! $opts{'module'}) { + $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 + LT::Exec->silent_run; + LT::Exec->$mode(@$ltprog, @ARGV); +} else { + die "MODE=$mode not implemented yet.\n"; +} + +if (LT::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}) { + require LT::LaFile; + my $lainfo = LT::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) { + LT::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/-L\./) { + } elsif ($lf =~ m/\/\S+\/(\S+\.la)/) { + my $lafile = $1; + require LT::LaFile; + my $libdir = LT::LaFile->parse($lf)->{'libdir'}; + if ($libdir eq '') { + # this drops libraries which will not be + # installed + # XXX improve checks when adding to deplibs + say "warning: $lf dropped from deplibs"; + } else { + $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 $result = []; + foreach my $a (@$objsource) { + if ($a =~ m/\S+\.lo$/) { + require LT::LoFile; + my $ofile = basename $a; + my $odir = dirname $a; + my $loinfo = LT::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; + } + } elsif ($a =~ m/\S+\.o$/) { + push @$objs, $a; + } else { + push @$result, $a; + } + } + @$objsource = @$result; +} + +# XXX reuse code from SharedLibs.pm instead +sub get_search_dirs +{ + my @libsearchdirs; + open(my $fh, '-|', '/sbin/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 run ldconfig\n"; + } + return @libsearchdirs; +} + + +# 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; +} + +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>); +} + diff --git a/usr.bin/libtool/libtool.1 b/usr.bin/libtool/libtool.1 new file mode 100644 index 00000000000..c32a231e7ba --- /dev/null +++ b/usr.bin/libtool/libtool.1 @@ -0,0 +1,25 @@ +.\" $OpenBSD: libtool.1,v 1.1 2012/06/19 09:30:44 espie Exp $ +.\" +.\" Copyright (c) 2012 Marc Espie <espie@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. +.\" +.Dd $Mdocdate: June 19 2012 $ +.Dt LIBTOOL 1 +.Os +.Sh NAME +.Nm libtool +.Nd compile and link complex libraries +.Sh DESCRIPTION +.Nm +is supposed to be a drop-in replacement for the eponymous GNU project. |