blob: 04be6ea82daa607102fa064eff2704cf0f908b7a [file] [log] [blame]
#!/usr/bin/env perl
#
#//===----------------------------------------------------------------------===//
#//
#// The LLVM Compiler Infrastructure
#//
#// This file is dual licensed under the MIT and the University of Illinois Open
#// Source Licenses. See LICENSE.txt for details.
#//
#//===----------------------------------------------------------------------===//
#
use strict;
use warnings;
use File::Glob ":glob";
use File::Temp;
use Cwd;
use FindBin;
use lib "$FindBin::Bin/lib";
use tools;
use Uname;
use Platform ":vars";
our $VERSION = "0.005";
# --------------------------------------------------------------------------------------------------
# Subroutines.
# --------------------------------------------------------------------------------------------------
sub windows {
my ( $arch, $output, @args ) = @_;
my %files;
# TODO: Check the archives are of specified architecture.
foreach my $arg ( @args ) {
foreach my $archive ( bsd_glob( $arg ) ) {
info( "Processing \"$archive\"..." );
my $bulk;
execute( [ "lib.exe", "/nologo", "/list", $archive ], -stdout => \$bulk );
my @members = split( "\n", $bulk );
foreach my $member ( @members ) {
my $file = get_file( $member );
my $path = cat_file( $output, $file );
if ( exists( $files{ $file } ) ) {
runtime_error(
"Extraction \"$file\" member from \"$archive\" archive failed:",
"\"$file\" member has already been extracted from \"$files{ $file }\" archive"
);
}; # if
$files{ $file } = $archive;
info( " Writing \"$path\"..." );
execute( [ "lib.exe", "/nologo", "/extract:" . $member, "/out:" . $path, $archive ] );
}; # foreach $member
}; # foreach $archive
}; # foreach $arg
}; # sub windows
sub linux {
my ( $arch, $output, @archives ) = @_;
# TODO: Check the archives are of specified architecture.
my $cwd = Cwd::cwd();
change_dir( $output );
foreach my $archive ( @archives ) {
info( "Processing \"$archive\"..." );
my $path = abs_path( $archive, $cwd );
execute( [ "ar", "xo", $path ] );
}; # foreach $archive
change_dir( $cwd );
}; # sub linux
my %mac_arch = (
"32" => "i386",
"32e" => "x86_64"
);
sub darwin {
my ( $arch, $output, @archives ) = @_;
my $cwd = getcwd();
change_dir( $output );
if ( defined( $arch ) ) {
if ( not defined( $mac_arch{ $arch } ) ) {
runtime_error( "Architecture \"$arch\" is not a valid one for OS X*" );
}; # if
$arch = $mac_arch{ $arch };
}; # if
foreach my $archive ( @archives ) {
info( "Processing \"$archive\"..." );
my $path = abs_path( $archive, $cwd );
my $temp;
# Whether archive is a fat or thin?
my $bulk;
execute( [ "file", $path ], -stdout => \$bulk );
if ( $bulk =~ m{Mach-O universal binary} ) {
# Archive is fat, extracy thin archive first.
if ( not defined( $arch ) ) {
runtime_error(
"\"$archive\" archive is universal binary, " .
"please specify architecture to work with"
);
}; # if
( undef, $temp ) = File::Temp::tempfile();
execute( [ "libtool", "-static", "-arch_only", $arch, "-o", $temp, $path ] );
$path = $temp;
}; # if
execute( [ "ar", "xo", $path ] ); # Extract members.
if ( defined( $temp ) ) { # Delete temp file, if any.
del_file( $temp );
}; # if
}; # foreach $archive
change_dir( $cwd );
}; # sub darwin
# --------------------------------------------------------------------------------------------------
# Main.
# --------------------------------------------------------------------------------------------------
# Parse command line.
my $output = ".";
my @args;
get_options(
Platform::target_options(),
"o|output-directory=s" => \$output,
);
@args = @ARGV;
if ( not -e $output ) {
runtime_error( "Output directory \"$output\" does not exist" );
}; # if
if ( not -d $output ) {
runtime_error( "\"$output\" is not a directory" );
}; # if
if ( not -w $output ) {
runtime_error( "Output directory \"$output\" is not writable" );
}; # if
if ( $target_os eq "win" ) {
*process = \&windows;
} elsif ( $target_os eq "lin" or $target_os eq "lrb" ) {
*process = \&linux;
} elsif ( $target_os eq "mac" ) {
*process = \&darwin;
} else {
runtime_error( "OS \"$target_os\" not supported" );
}; # if
# Do the work.
process( $target_arch, $output, @args );
exit( 0 );
__END__
=pod
=head1 NAME
B<extract-objects.pl> -- Extract all object files from static library.
=head1 SYNOPSIS
B<extract-objects.pl> I<option>... I<archive>...
=head1 OPTIONS
=over
=item B<--architecture=>I<arch>
Specify architecture to work with. The option is mandatory on OS X* in case of universal archive.
In other cases the option should not be used. I<arch> may be one of C<32> or C<32e>.
=item B<--os=>I<str>
Specify OS name. By default OS is autodetected.
Depending on OS, B<extract-objects.pl> uses different external tools for handling static
libraries: F<ar> (in case of "lin" and "mac") or F<lib.exe> (in case of "win").
=item B<--output-directory=>I<dir>
Specify directory to write extracted members to. Current directory is used by default.
=item B<--help>
Print short help message and exit.
=item B<--doc>
=item B<--manual>
Print full documentation and exit.
=item B<--quiet>
Do not print information messages.
=item B<--version>
Print version and exit.
=back
=head1 ARGUMENTS
=over
=item I<archive>
A name of archive file (static library). Multiple archives may be specified.
=back
=head1 DESCRIPTION
The script extracts all the members (object files) from archive (static library) to specified
directory. Commands to perform this action differ on different OSes. On Linux* OS, simple command
ar xo libfile.a
is enough (in case of extracting files to current directory).
On OS X*, it is a bit compilicated with universal ("fat") binaries -- C<ar> cannot
operate on fat archives, so "thin" archive should be extracted from the universal binary first.
On Windows* OS, library manager (C<lib.exe>) can extract only one object file, so operation should be
repeated for every object file in the library.
B<extract-objects.pl> detects OS automatically. But detection can be overrided with B<--os> option.
It may be helpful in cross-build environments.
B<extract-objects.pl> effectively encapsulates all these details and provides uniform way for
extracting object files from static libraries, which helps to keep makefiles simple and clean.
=head1 EXAMPLES
Extract object files from library F<libirc.lib>, and put them into F<obj/> directory:
$ extract-objects.pl --output=obj libirc.lib
Extract object files from library F<libirc.a>. Use Linux* OS tools (F<ar>), even if run on another OS:
$ extract-objects.pl --os=lin libirc.a
Extract object files from library F<libirc.a>, if it is a OS X* universal binary, use i386
architecture. Be quiet:
$ extract-objects.pl --quiet --arch=i386 libirc.a
=cut
# end of file #