Jim Cownie | 5e8470a | 2013-09-27 10:38:44 +0000 | [diff] [blame^] | 1 | #!/usr/bin/env perl |
| 2 | |
| 3 | # |
| 4 | #//===----------------------------------------------------------------------===// |
| 5 | #// |
| 6 | #// The LLVM Compiler Infrastructure |
| 7 | #// |
| 8 | #// This file is dual licensed under the MIT and the University of Illinois Open |
| 9 | #// Source Licenses. See LICENSE.txt for details. |
| 10 | #// |
| 11 | #//===----------------------------------------------------------------------===// |
| 12 | # |
| 13 | |
| 14 | use strict; |
| 15 | use warnings; |
| 16 | |
| 17 | use File::Glob ":glob"; |
| 18 | use File::Temp; |
| 19 | use Cwd; |
| 20 | |
| 21 | use FindBin; |
| 22 | use lib "$FindBin::Bin/lib"; |
| 23 | |
| 24 | use tools; |
| 25 | use Uname; |
| 26 | use Platform ":vars"; |
| 27 | |
| 28 | our $VERSION = "0.005"; |
| 29 | |
| 30 | # -------------------------------------------------------------------------------------------------- |
| 31 | # Subroutines. |
| 32 | # -------------------------------------------------------------------------------------------------- |
| 33 | |
| 34 | sub windows { |
| 35 | my ( $arch, $output, @args ) = @_; |
| 36 | my %files; |
| 37 | # TODO: Check the archives are of specified architecture. |
| 38 | foreach my $arg ( @args ) { |
| 39 | foreach my $archive ( bsd_glob( $arg ) ) { |
| 40 | info( "Processing \"$archive\"..." ); |
| 41 | my $bulk; |
| 42 | execute( [ "lib.exe", "/nologo", "/list", $archive ], -stdout => \$bulk ); |
| 43 | my @members = split( "\n", $bulk ); |
| 44 | foreach my $member ( @members ) { |
| 45 | my $file = get_file( $member ); |
| 46 | my $path = cat_file( $output, $file ); |
| 47 | if ( exists( $files{ $file } ) ) { |
| 48 | runtime_error( |
| 49 | "Extraction \"$file\" member from \"$archive\" archive failed:", |
| 50 | "\"$file\" member has already been extracted from \"$files{ $file }\" archive" |
| 51 | ); |
| 52 | }; # if |
| 53 | $files{ $file } = $archive; |
| 54 | info( " Writing \"$path\"..." ); |
| 55 | execute( [ "lib.exe", "/nologo", "/extract:" . $member, "/out:" . $path, $archive ] ); |
| 56 | }; # foreach $member |
| 57 | }; # foreach $archive |
| 58 | }; # foreach $arg |
| 59 | }; # sub windows |
| 60 | |
| 61 | sub linux { |
| 62 | my ( $arch, $output, @archives ) = @_; |
| 63 | # TODO: Check the archives are of specified architecture. |
| 64 | my $cwd = Cwd::cwd(); |
| 65 | change_dir( $output ); |
| 66 | foreach my $archive ( @archives ) { |
| 67 | info( "Processing \"$archive\"..." ); |
| 68 | my $path = abs_path( $archive, $cwd ); |
| 69 | execute( [ "ar", "xo", $path ] ); |
| 70 | }; # foreach $archive |
| 71 | change_dir( $cwd ); |
| 72 | }; # sub linux |
| 73 | |
| 74 | my %mac_arch = ( |
| 75 | "32" => "i386", |
| 76 | "32e" => "x86_64" |
| 77 | ); |
| 78 | |
| 79 | sub darwin { |
| 80 | my ( $arch, $output, @archives ) = @_; |
| 81 | my $cwd = getcwd(); |
| 82 | change_dir( $output ); |
| 83 | if ( defined( $arch ) ) { |
| 84 | if ( not defined( $mac_arch{ $arch } ) ) { |
| 85 | runtime_error( "Architecture \"$arch\" is not a valid one for OS X*" ); |
| 86 | }; # if |
| 87 | $arch = $mac_arch{ $arch }; |
| 88 | }; # if |
| 89 | foreach my $archive ( @archives ) { |
| 90 | info( "Processing \"$archive\"..." ); |
| 91 | my $path = abs_path( $archive, $cwd ); |
| 92 | my $temp; |
| 93 | # Whether archive is a fat or thin? |
| 94 | my $bulk; |
| 95 | execute( [ "file", $path ], -stdout => \$bulk ); |
| 96 | if ( $bulk =~ m{Mach-O universal binary} ) { |
| 97 | # Archive is fat, extracy thin archive first. |
| 98 | if ( not defined( $arch ) ) { |
| 99 | runtime_error( |
| 100 | "\"$archive\" archive is universal binary, " . |
| 101 | "please specify architecture to work with" |
| 102 | ); |
| 103 | }; # if |
| 104 | ( undef, $temp ) = File::Temp::tempfile(); |
| 105 | execute( [ "libtool", "-static", "-arch_only", $arch, "-o", $temp, $path ] ); |
| 106 | $path = $temp; |
| 107 | }; # if |
| 108 | execute( [ "ar", "xo", $path ] ); # Extract members. |
| 109 | if ( defined( $temp ) ) { # Delete temp file, if any. |
| 110 | del_file( $temp ); |
| 111 | }; # if |
| 112 | }; # foreach $archive |
| 113 | change_dir( $cwd ); |
| 114 | }; # sub darwin |
| 115 | |
| 116 | |
| 117 | # -------------------------------------------------------------------------------------------------- |
| 118 | # Main. |
| 119 | # -------------------------------------------------------------------------------------------------- |
| 120 | |
| 121 | # Parse command line. |
| 122 | |
| 123 | my $output = "."; |
| 124 | my @args; |
| 125 | |
| 126 | get_options( |
| 127 | Platform::target_options(), |
| 128 | "o|output-directory=s" => \$output, |
| 129 | ); |
| 130 | @args = @ARGV; |
| 131 | |
| 132 | if ( not -e $output ) { |
| 133 | runtime_error( "Output directory \"$output\" does not exist" ); |
| 134 | }; # if |
| 135 | if ( not -d $output ) { |
| 136 | runtime_error( "\"$output\" is not a directory" ); |
| 137 | }; # if |
| 138 | if ( not -w $output ) { |
| 139 | runtime_error( "Output directory \"$output\" is not writable" ); |
| 140 | }; # if |
| 141 | |
| 142 | if ( $target_os eq "win" ) { |
| 143 | *process = \&windows; |
| 144 | } elsif ( $target_os eq "lin" or $target_os eq "lrb" ) { |
| 145 | *process = \&linux; |
| 146 | } elsif ( $target_os eq "mac" ) { |
| 147 | *process = \&darwin; |
| 148 | } else { |
| 149 | runtime_error( "OS \"$target_os\" not supported" ); |
| 150 | }; # if |
| 151 | |
| 152 | |
| 153 | # Do the work. |
| 154 | process( $target_arch, $output, @args ); |
| 155 | exit( 0 ); |
| 156 | |
| 157 | __END__ |
| 158 | |
| 159 | =pod |
| 160 | |
| 161 | =head1 NAME |
| 162 | |
| 163 | B<extract-objects.pl> -- Extract all object files from static library. |
| 164 | |
| 165 | =head1 SYNOPSIS |
| 166 | |
| 167 | B<extract-objects.pl> I<option>... I<archive>... |
| 168 | |
| 169 | =head1 OPTIONS |
| 170 | |
| 171 | =over |
| 172 | |
| 173 | =item B<--architecture=>I<arch> |
| 174 | |
| 175 | Specify architecture to work with. The option is mandatory on OS X* in case of universal archive. |
| 176 | In other cases the option should not be used. I<arch> may be one of C<32> or C<32e>. |
| 177 | |
| 178 | =item B<--os=>I<str> |
| 179 | |
| 180 | Specify OS name. By default OS is autodetected. |
| 181 | |
| 182 | Depending on OS, B<extract-objects.pl> uses different external tools for handling static |
| 183 | libraries: F<ar> (in case of "lin" and "mac") or F<lib.exe> (in case of "win"). |
| 184 | |
| 185 | =item B<--output-directory=>I<dir> |
| 186 | |
| 187 | Specify directory to write extracted members to. Current directory is used by default. |
| 188 | |
| 189 | =item B<--help> |
| 190 | |
| 191 | Print short help message and exit. |
| 192 | |
| 193 | =item B<--doc> |
| 194 | |
| 195 | =item B<--manual> |
| 196 | |
| 197 | Print full documentation and exit. |
| 198 | |
| 199 | =item B<--quiet> |
| 200 | |
| 201 | Do not print information messages. |
| 202 | |
| 203 | =item B<--version> |
| 204 | |
| 205 | Print version and exit. |
| 206 | |
| 207 | =back |
| 208 | |
| 209 | =head1 ARGUMENTS |
| 210 | |
| 211 | =over |
| 212 | |
| 213 | =item I<archive> |
| 214 | |
| 215 | A name of archive file (static library). Multiple archives may be specified. |
| 216 | |
| 217 | =back |
| 218 | |
| 219 | =head1 DESCRIPTION |
| 220 | |
| 221 | The script extracts all the members (object files) from archive (static library) to specified |
| 222 | directory. Commands to perform this action differ on different OSes. On Linux* OS, simple command |
| 223 | |
| 224 | ar xo libfile.a |
| 225 | |
| 226 | is enough (in case of extracting files to current directory). |
| 227 | |
| 228 | On OS X*, it is a bit compilicated with universal ("fat") binaries -- C<ar> cannot |
| 229 | operate on fat archives, so "thin" archive should be extracted from the universal binary first. |
| 230 | |
| 231 | On Windows* OS, library manager (C<lib.exe>) can extract only one object file, so operation should be |
| 232 | repeated for every object file in the library. |
| 233 | |
| 234 | B<extract-objects.pl> detects OS automatically. But detection can be overrided with B<--os> option. |
| 235 | It may be helpful in cross-build environments. |
| 236 | |
| 237 | B<extract-objects.pl> effectively encapsulates all these details and provides uniform way for |
| 238 | extracting object files from static libraries, which helps to keep makefiles simple and clean. |
| 239 | |
| 240 | =head1 EXAMPLES |
| 241 | |
| 242 | Extract object files from library F<libirc.lib>, and put them into F<obj/> directory: |
| 243 | |
| 244 | $ extract-objects.pl --output=obj libirc.lib |
| 245 | |
| 246 | Extract object files from library F<libirc.a>. Use Linux* OS tools (F<ar>), even if run on another OS: |
| 247 | |
| 248 | $ extract-objects.pl --os=lin libirc.a |
| 249 | |
| 250 | Extract object files from library F<libirc.a>, if it is a OS X* universal binary, use i386 |
| 251 | architecture. Be quiet: |
| 252 | |
| 253 | $ extract-objects.pl --quiet --arch=i386 libirc.a |
| 254 | |
| 255 | =cut |
| 256 | |
| 257 | # end of file # |
| 258 | |