|  | #!/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 # | 
|  |  |