summaryrefslogtreecommitdiff
path: root/usr.bin
diff options
context:
space:
mode:
authorMarc Espie <espie@cvs.openbsd.org>2012-06-19 09:30:45 +0000
committerMarc Espie <espie@cvs.openbsd.org>2012-06-19 09:30:45 +0000
commit3defa3954033e9bd8abcac5258f42a67d22d49f1 (patch)
treefd7ff1fe2d3de6d7f8d0dc5b326bd0709840094d /usr.bin
parent9de60e3c6d5b8c9f00cfb34b43989af9a75d1f6a (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/Makefile5
-rw-r--r--usr.bin/libtool/LT/Archive.pm78
-rw-r--r--usr.bin/libtool/LT/Exec.pm144
-rw-r--r--usr.bin/libtool/LT/LaFile.pm340
-rw-r--r--usr.bin/libtool/LT/LaLoFile.pm88
-rw-r--r--usr.bin/libtool/LT/Library.pm159
-rw-r--r--usr.bin/libtool/LT/LoFile.pm72
-rw-r--r--usr.bin/libtool/LT/Parser.pm310
-rw-r--r--usr.bin/libtool/LT/Program.pm188
-rw-r--r--usr.bin/libtool/LT/Trace.pm48
-rw-r--r--usr.bin/libtool/LT/Util.pm53
-rw-r--r--usr.bin/libtool/Makefile32
-rwxr-xr-xusr.bin/libtool/libtool709
-rw-r--r--usr.bin/libtool/libtool.125
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' => \&notyet,
+ '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.