Annotation of src/usr.bin/libtool/libtool, Revision 1.19
1.1 espie 1: #!/usr/bin/perl
1.19 ! espie 2: # $OpenBSD: libtool,v 1.18 2012/07/07 21:09:27 jasper Exp $
1.1 espie 3:
4: # Copyright (c) 2007-2010 Steven Mestdagh <steven@openbsd.org>
1.13 espie 5: # Copyright (c) 2012 Marc Espie <espie@openbsd.org>
1.1 espie 6: #
7: # Permission to use, copy, modify, and distribute this software for any
8: # purpose with or without fee is hereby granted, provided that the above
9: # copyright notice and this permission notice appear in all copies.
10: #
11: # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12: # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13: # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14: # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15: # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16: # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17: # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18:
19: use strict;
20: use warnings;
21: use feature qw(say switch state);
22: use Cwd qw(getcwd abs_path);
23: use File::Basename;
24: use File::Glob ':glob';
25: use File::Path;
1.3 espie 26:
1.1 espie 27: use LT::Trace;
28: use LT::Exec;
29: use LT::Util;
30:
1.3 espie 31: $SIG{__DIE__} = sub {
32: require Carp;
33:
34: my $_ = pop @_;
35: s/(.*)( at .*? line .*?\n$)/$1/s;
36: push @_, $_;
37: die &Carp::longmess;
38: };
39:
1.16 jasper 40: package LT::OSConfig;
41:
42: use Config;
1.18 jasper 43: use LT::Util;
1.16 jasper 44:
45: sub new
46: {
47: my $class = shift;
1.18 jasper 48: # XXX: incomplete
49: my $self = {
1.19 ! espie 50: machine_arch => $Config{ARCH},
1.18 jasper 51: ltdir => $ltdir,
52: };
1.19 ! espie 53: ($self->{gnu_arch} = $self->{machine_arch}) =~ s/amd64/x86_64/;
1.18 jasper 54: bless $self, $class;
1.16 jasper 55: }
56:
1.19 ! espie 57: package LT::Mode;
! 58:
! 59: sub new
! 60: {
! 61: my ($class, $origin) = @_;
! 62: # XXX autoload *if needed*.
! 63: eval "require $class; ";
! 64: bless {origin => $origin }, $class;
! 65: }
! 66:
! 67: my $mode_maker = { compile => 'LT::Mode::Compile',
! 68: clean => 'LT::Mode::Clean',
! 69: execute => 'LT::Mode::Execute',
! 70: finish => 'LT::Mode::Finish',
! 71: install => 'LT::Mode::Install',
! 72: link => 'LT::Mode::Link',
! 73: uninstall => 'LT::Mode::Uninstall' };
! 74:
! 75: sub factory
! 76: {
! 77: my ($class, $mode, $origin) = @_;
! 78: if (defined $mode_maker->{$mode}) {
! 79: return $mode_maker->{$mode}->new($origin);
! 80: } else {
! 81: # XXX invokved from getopt, can't die yet.
! 82: say STDERR "Mode=$mode not implemented yet.\n";
! 83: exit 1;
! 84: }
! 85: }
! 86:
! 87: package LT::Mode::Empty;
! 88: our @ISA = qw(LT::Mode);
! 89: sub run
! 90: {
! 91: exit 0;
! 92: }
! 93: package LT::Mode::Compile;
! 94: our @ISA = qw(LT::Mode);
! 95:
! 96: package LT::Mode::Clean;
! 97: our @ISA = qw(LT::Mode::Empty);
! 98:
! 99: package LT::Mode::Execute;
! 100: our @ISA = qw(LT::Mode);
! 101: sub run
! 102: {
! 103: my ($class, $ltprog, $gp, $noshared) = @_;
! 104: # XXX check whether this is right
! 105: LT::Exec->silent_run;
! 106: LT::Exec->execute(@$ltprog, @main::ARGV);
! 107: }
! 108:
! 109: package LT::Mode::Finish;
! 110: our @ISA = qw(LT::Mode::Empty);
! 111:
! 112: package LT::Mode::Install;
! 113: our @ISA = qw(LT::Mode);
! 114:
! 115: package LT::Mode::Link;
! 116: our @ISA = qw(LT::Mode);
! 117:
! 118: package LT::Mode::Uninstall;
! 119: our @ISA = qw(LT::Mode::Empty);
! 120:
1.10 espie 121: package LT::Options;
122: use Getopt::Long;
123:
124: my @valid_modes = qw(compile clean execute finish install link uninstall);
1.19 ! espie 125:
1.10 espie 126: my @known_tags = qw(disable-shared disable-static CC CXX F77 FC GO GCJ RC);
127:
128: sub new
129: {
130: my $class = shift;
131: my $o = bless { gp => Getopt::Long::Parser->new }, $class;
132: # require_order so we stop parsing at the first non-option or argument,
133: # instead of parsing the whole ARGV.
134: $o->{gp}->configure('no_ignore_case',
135: 'pass_through',
136: 'no_auto_abbrev',
137: 'require_order');
138: return $o;
139: }
140:
141: sub add_tag
142: {
143: my ($self, $value) = @_;
144: if ($value =~ m/[^\-\w,\/]/) {
145: # XXX stupid Getopt pre-empts die !
146: say STDERR "invalid tag name: $value";
147: exit 1;
148: }
149: if (grep {$value eq $_} @known_tags) {
150: $self->{tags}{$value} = 1;
151: } else {
152: say STDERR "ignoring unknown tag: $value";
153: }
154: }
155:
156: sub has_tag
157: {
158: my ($self, $tag) = @_;
159: return defined $self->{tags}{$tag};
160: }
161:
162: sub configure
163: {
164: my $o = shift;
165: $o->{gp}->configure(@_);
166: }
167:
168: sub getoptions
169: {
170: my $o = shift;
171: $o->{gp}->getoptions(@_);
172: }
173:
174: sub is_abreviated_mode
175: {
176: my ($self, $arg) = @_;
1.15 jasper 177: return undef if !$arg;
1.10 espie 178: for my $m (@valid_modes) {
179: next if length $arg > length $m;
180: if ($arg eq substr($m, 0, length $arg)) {
1.19 ! espie 181: return LT::Mode->factory($m, $arg);
1.10 espie 182: }
183: }
184: return undef;
185: }
186:
187: # XXX this should always fail if we are libtool2 !
188: # try to guess libtool mode when it is not specified
189: sub guess_implicit_mode
190: {
191: my ($self, $ltprog) = @_;
192: my $m;
193: for my $a (@$ltprog) {
194: if ($a =~ m/(install([.-]sh)?|cp)$/) {
1.19 ! espie 195: $m = LT::Mode::Install->new("implicit $a");
1.10 espie 196: } elsif ($a =~ m/cc|c\+\+/) { # XXX improve test
197: if (grep { $_ eq '-c' } @ARGV) {
1.19 ! espie 198: $m = LT::Mode::Compile->new("implicit");
1.10 espie 199: } else {
1.19 ! espie 200: $m = LT::Mode::Link->new("implicit");
1.10 espie 201: }
202: }
203: }
204: return $m;
205: }
206:
207: sub valid_modes
208: {
209: my $self = shift;
210: return join(' ', @valid_modes);
211: }
212:
213: package main;
1.1 espie 214:
215: use subs qw(
216: create_symlinks
217: help
218: notyet
219: );
220:
1.17 espie 221: my $ltconfig = LT::OSConfig->new;
1.1 espie 222: my @no_shared_archs = qw(m88k vax);
223: my $cwd = getcwd();
1.19 ! espie 224: my $instlibdir = $ENV{LIBDIR} // '/usr/local/lib';
1.1 espie 225:
226: my $mode;
227: my $verbose = 1;
228:
229: # just to be clear:
230: # when building a library:
231: # * -R libdir records libdir in dependency_libs
232: # * -rpath is the path where the (shared) library will be installed
233: # when building a program:
234: # * both -R libdir and -rpath libdir add libdir to the run-time path
235: # -Wl,-rpath,libdir will bypass libtool.
236:
237: # build static/shared objects?
238: my $noshared = 0;
1.19 ! espie 239: if (grep { $_ eq $ltconfig->{machine_arch} } @no_shared_archs) {
1.1 espie 240: $noshared = 1;
241: }
242:
1.10 espie 243:
244: if ($mode = LT::Options->is_abreviated_mode($ARGV[0])) {
245: shift @ARGV;
246: }
247:
248: my $gp = LT::Options->new;
1.1 espie 249: $gp->getoptions('config' => \&config,
1.10 espie 250: 'debug' => sub {
251: LT::Trace->set($_[1]);
252: LT::Exec->verbose_run;
253: },
254: 'dry-run|n' => sub { LT::Exec->dry_run; },
255: 'features' => sub {
256: my $v = `uname -r`;
257: chomp $v;
1.18 jasper 258: say "host: $ltconfig->{gnu_arch}-unknown-openbsd$v";
1.10 espie 259: say "enable shared libraries" unless $noshared;
260: say "enable static libraries";
261: exit 0;
262: },
1.19 ! espie 263: 'finish' => sub { $mode = LT::Mode::Finish->new('--finish'); },
1.1 espie 264: 'help' => \&help, # does not return
1.19 ! espie 265: 'mode=s{1}' => sub {
! 266: $mode = LT::Mode->factory($_[1], "--mode=$_[1]");
! 267: },
1.1 espie 268: 'quiet' => sub { $verbose = 0; },
269: 'silent' => sub { $verbose = 0; },
1.10 espie 270: 'tag=s{1}' => sub { $gp->add_tag($_[1]); },
271: 'version' => sub {
272: say "libtool (not (GNU libtool)) $version" ;
273: exit 0;
274: },
1.1 espie 275: );
276:
1.10 espie 277: if ($verbose) {
1.1 espie 278: LT::Exec->verbose_run;
279: }
1.10 espie 280:
1.1 espie 281: # what are we going to run (cc, c++, ...)
282: my $ltprog = [];
283: # deal with multi-arg ltprog
1.10 espie 284: tsay {"ARGV = \"@ARGV\""};
1.1 espie 285: while (@ARGV) {
286: # just read arguments until the next option...
287: if ($ARGV[0] =~ m/^\-/) { last; }
288: # XXX improve checks
289: if ($ARGV[0] =~ m/^\S+\.la/) { last; }
290: my $arg = shift @ARGV;
291: push @$ltprog, $arg;
1.10 espie 292: tsay {"arg = \"$arg\""};
1.1 espie 293: # if the current argument is an install program, stop immediately
294: if ($arg =~ /cp$/) { last; }
295: if ($arg =~ /install([-.]sh)?$/) { last; }
296: }
1.10 espie 297: tsay {"ltprog = \"@$ltprog\""};
1.14 espie 298:
299: # XXX compat game to satisfy both libtool 1 and libtool 2
300: # let libtool install work as both libtool 1 and libtool 2
1.19 ! espie 301: if (@$ltprog == 0 && defined $mode && $mode->{origin} eq 'install') {
! 302: $ltprog = [ 'install' ];
1.14 espie 303: }
1.6 jasper 304: if (@$ltprog == 0) { die "No libtool command given.\n" .
305: "Use `libtool --help' for more information.\n" };
1.1 espie 306: # make ltprog a list of elements without whitespace (prevent exec errors)
307: my @tmp_ltprog = @$ltprog;
308: @$ltprog = ();
309: for my $el (@tmp_ltprog) {
310: my @parts = split /\s+/, $el;
311: push @$ltprog, @parts;
312: }
313:
1.10 espie 314: if (!defined $mode) {
315: $mode = $gp->guess_implicit_mode($ltprog);
1.19 ! espie 316: tsay {"implicit mode: ", $mode->{origin}} if $mode;
1.1 espie 317: }
318:
319: # from here, options may be intermixed with arguments
320: $gp->configure('permute');
321:
1.19 ! espie 322: $mode->run($ltprog, $gp, $noshared);
1.1 espie 323:
324: if (LT::Exec->performed == 0) {
325: die "No commands to execute.\n"
326: }
327:
328: ###########################################################################
329:
330: sub help
331: {
332: print <<EOF
333: Usage: $0 [options]
334: --config - print configuration
335: --debug - turn on debugging output
336: --dry-run - don't do anything, only show what would be done
337: --help - this message
338: --mode=MODE - use operation mode MODE
339: --quiet - do not print informational messages
340: --silent - same as `--quiet'
1.5 jasper 341: --tag=TAG - specify a configuration variable TAG
1.1 espie 342: --version - print version of libtool
343: EOF
344: ;
345: exit 1;
346: }
347:
348: sub notyet
349: {
350: die "Option not implemented yet.\n";
351: }
352:
353: sub config
354: {
1.18 jasper 355: while ( my ($key, $value) = each(%$ltconfig) ) {
356: print "$key = $value\n";
357: }
1.1 espie 358: exit 0;
359: }
360:
361: sub create_symlinks
362: {
1.7 espie 363: my ($dir, $libs) = @_;
1.1 espie 364: if (! -d $dir) {
1.7 espie 365: mkdir($dir) or die "Cannot mkdir($dir) : $!\n";
1.1 espie 366: }
1.7 espie 367:
1.1 espie 368: foreach my $l (values %$libs) {
369: my $f = $l->{fullpath};
1.7 espie 370: next if !defined $f;
371: next if $f =~ m/\.a$/;
1.1 espie 372: my $libnames = [];
373: if (defined $l->{lafile}) {
374: require LT::LaFile;
375: my $lainfo = LT::LaFile->parse($l->{lafile});
376: my $librarynames = $lainfo->stringize('library_names');
377: @$libnames = split /\s/, $librarynames;
378: $libnames = reverse_zap_duplicates_ref($libnames);
379: } else {
1.3 espie 380: push @$libnames, basename($f);
1.1 espie 381: }
382: foreach my $libfile (@$libnames) {
1.4 espie 383: my $link = "$dir/$libfile";
1.10 espie 384: tsay {"ln -s $f $link"};
1.7 espie 385: next if -f $link;
386: my $p = abs_path($f);
1.8 espie 387: if (!symlink($p, $link)) {
388: die "Cannot create symlink($p, $link): $!\n"
1.9 espie 389: unless $!{EEXIST};
1.8 espie 390: }
1.1 espie 391: }
392: }
1.7 espie 393: return $dir;
1.1 espie 394: }
395: