#!/usr/bin/env perl # apbuild - a GCC wrapper for creating portable binaries # Copyright (c) 2003,2004,2005 Hongli Lai # Distributed under the GNU General Public License use warnings; use strict; use FindBin qw($RealBin); use File::Spec; use lib $RealBin; use lib "$RealBin/../share/apbuild"; use IPC::Open2; use POSIX; use Cwd; use Apbuild::GCC; use Apbuild::Utils; # Don't forget to bump the version in Makefile too. our $APBUILD_VERSION = "2.0.9"; ######## Initialization ######## # In C mode: # $gcc is the default compiler. # $gxx2 is not set. # # In C++ mode: # $gcc is g++ 3.2. # $gxx2 is g++ 3.4, and it might not be set. # Double compiling is only enabled if both are set. # # Note that $APBUILD_CXX_MODE is an internal environment variable # which should not be set by the user. our ($gcc, $gxx2); if ($ENV{APBUILD_CXX_MODE}) { # C++ support; user can: # - Not set APBUILD_CXX1/2 at all -> use g++ as default, and don't double compile C++ sources. # - Set APBUILD_CXX1 and APBUILD_CXX2 -> enable double compiling. # - Set only APBUILD_CXX1 -> don't double compile. # - Set only APBUILD_CXX2 -> use 'g++' as default for APBUILD_CXX1, and enable double compiling. if (empty($ENV{APBUILD_CXX1}) && empty($ENV{APBUILD_CXX2})) { $gcc = new Apbuild::GCC('g++'); } elsif (!empty($ENV{APBUILD_CXX1}) && !empty($ENV{APBUILD_CXX2})) { $gcc = new Apbuild::GCC($ENV{APBUILD_CXX1}); $gxx2 = new Apbuild::GCC($ENV{APBUILD_CXX2}); } elsif (!empty($ENV{APBUILD_CXX1})) { $gcc = new Apbuild::GCC($ENV{APBUILD_CXX1}); } elsif (!empty($ENV{APBUILD_CXX2})) { $gcc = new Apbuild::GCC('g++'); $gxx2 = new Apbuild::GCC($ENV{APBUILD_CXX2}); } } else { my $gcc_command = 'gcc'; $gcc_command = $ENV{APBUILD_CC} if (!empty($ENV{APBUILD_CC})); $gcc = new Apbuild::GCC($gcc_command); } our $appath; our $libgcc = $ENV{APBUILD_STATIC_LIBGCC} ? '-static-libgcc' : '-shared-libgcc'; # Find out where apgcc is located and determine the apbuild header # path relative to the apgcc script location. $appath = $ENV{APBUILD_PATH} if (!empty($ENV{APBUILD_PATH})); if (!defined $appath) { $appath = $FindBin::Bin; $appath =~ s/\/*$//g; $appath =~ s/^(.*)\/.*?$/$1/; $appath .= '/include/apbuild'; if (! -f "$appath/apsymbols.h" && -f "$FindBin::Bin/apsymbols.h") { $appath = $FindBin::Bin; } } my $arch=`uname -m`; chomp $arch; `cp $appath/apsymbols.h.$arch $appath/apsymbols.h`; # Special constants our @linking = ('-Wl,--enable-new-dtags,--rpath,${ORIGIN}/../lib,--rpath,${ORIGIN}/../lib/autopackage'); our @include = ("-I$appath", '-include', "$appath/apsymbols.h", "-DAPBUILD_VERSION=\"$APBUILD_VERSION\""); our $extraTypes = $Apbuild::GCC::extraTypes; our $srcTypes = $Apbuild::GCC::srcTypes; @linking = () if ($ENV{APBUILD_NO_RPATH}); if (!empty($ENV{APBUILD_INCLUDE})) { foreach my $dir (split /:+/, $ENV{APBUILD_INCLUDE}) { if ($dir =~ /=/) { # allow people to force changing a path with oldpath=newpath, e.g.,: # $echo $APBUILD_INCLUDE # /opt/kde/include=/home/taj/kde-headers/kde-3.3.2/kde/include # would change -I/opt/kde/include to my KDE 3.3 headers. # this works around problems where the order of the include # directories is important. Added for amarok 1.4.3 which has a # header named scriptmanager.h. KDE also has a header named # scriptmanager. Order of -I flags forces amarok header to be # included. my @splitpaths = split(/=/, $dir); if (-d $splitpaths[1]) { foreach (@ARGV) { s/-I$splitpaths[0]$/-I$splitpaths[1]/g; } } } else { push @include, "-I$dir" if (-d $dir); } } } # Include the apbuild include folder, and all folders inside that folder. if (opendir D, $appath) { foreach my $dir (readdir D) { if ($dir !~ /^\./ && -d "$appath/$dir") { push @include, "-I$appath/$dir"; } } closedir D; } if (!empty($ENV{APBUILD_PROJECTNAME})) { push @linking, '-Wl,--rpath,${ORIGIN}/../lib/' . $ENV{APBUILD_PROJECTNAME}; } our %capabilities = $gcc->capabilities; if ($capabilities{hash_style}) { # By default FC6 only generates a .gnu.hash section, not # .hash (which is the only thing that FC5 and most other # distros understand). # Force generation of both sections if the linker supports it # so that the binary will run on other systems. # See http://fcp.surfsite.org/modules/newbb/viewtopic.php?topic_id=29929&forum=10&post_id=128939 # for more details push @linking, '-Wl,--hash-style=both'; } if ($capabilities{stack_protector}) { # gcc-4.1 introduced stack protection to help check for # buffer overflows. This introduces a silent dependency on # glibc 2.4 (__stack_chk_fail@@GLIBC_2.4) # Not many people have glibc 2.4 yet, so disable it. push @include, "-fno-stack-protector"; } if ($capabilities{fortify_source}) { # Some distros (e.g. Ubuntu 8.10) activate a bufferoverflow protection # which is only available since glibc 2.3.4. Apbuild removes all symbols # newer then 2.3, so this can't be used. push @include, "-U_FORTIFY_SOURCE"; } our %capabilities_gxx2; %capabilities_gxx2 = $gxx2->capabilities if ($gxx2); push @linking, $libgcc if ($capabilities{libgcc}); ############# Detect compilation situation ############# our $situation = $gcc->situation(\@ARGV); if ($ENV{APBUILD_CXX_MODE}) { debug "apg++ @ARGV\n"; } else { debug "apgcc @ARGV\n"; } debug "Situation: $situation\n"; # Handle each situation # Only situations that involve compiling or linking need to be treated specially if ($situation eq 'compile') { # Extract the parameters and files my (@files, @params); $gcc->splitParams(\@ARGV, \@files, \@params); # Compile each source file to an object file. # Force GCC to compile C/C++ source files with older glibc symbols. debug "\@files: is @files\n"; foreach my $file (@files) { if ($gxx2 && $gxx2->isCxxSource($file)) { # This is a C++ source file. Compile the file twice with different ABIs. my $src = $file; my $old_gcc; my %old_cap; compileSource($src, \@ARGV); $old_gcc = $gcc; %old_cap = %capabilities; $gcc = $gxx2; %capabilities = %capabilities_gxx2; beginDoubleCompiling(); compileSource($src, \@ARGV, '.GCCABI2'); endDoubleCompiling(); $gcc = $old_gcc; %capabilities = %old_cap; } else { compileSource($file, \@ARGV); } } exit; } elsif ($situation eq 'linking') { linkObjects(\@ARGV); if ($gxx2) { # Check whether there are .GCCABI2 objects. Link them if there are. my @options; my $doubleCompile; my $callback = sub { my $type = shift; if ($type eq 'param' && ($gxx2->isLibrary($_[0]) || $gxx2->isObject($_[0])) && !$gxx2->isStaticLib($_[0]) && -f "$_[0].GCCABI2") { push @options, "$_[0].GCCABI2"; $doubleCompile = 1; } else { push @options, @_; } }; $gcc = $gxx2; %capabilities = %capabilities_gxx2; $gcc->foreach(\@ARGV, $callback); if ($doubleCompile) { debug "Double compiling.\n"; beginDoubleCompiling(); linkObjects(\@options, ".GCCABI2"); endDoubleCompiling(); } } exit; } elsif ($situation eq 'precompiled header') { # Extract the parameters and files my (@files, @params); $gcc->splitParams(\@ARGV, \@files, \@params); # Compile each source file to an object file. # Force GCC to compile C/C++ source files with older glibc symbols. debug "\@files: is @files\n"; foreach my $file (@files) { compileSource($file, \@ARGV); } } elsif ($situation eq 'compile and link') { # Extract the parameters and files my (@params, @files, @linking2); my @command; my $status; # Seperate files and linker options. # @params are all options except linker options, and is used for compilation of each individual source file. # @files are the source files. $gcc->splitParams(\@ARGV, \@files, \@params); $gcc->stripLinkerParams(\@params, \@linking2); # Compile & link only one source file if (@files == 1) { my @options = modifyLinkerOptions(@params); push @options, @linking2; if ($gxx2 && $gxx2->isCxxSource($files[0])) { # This is a C++ file. Compile twice with different ABIs. compileSource($files[0], \@ARGV); manipulateDeps(@options); $gcc = $gxx2; %capabilities = %capabilities_gxx2; beginDoubleCompiling(); compileSource($files[0], \@ARGV, '.GCCABI2'); manipulateDeps(@options); endDoubleCompiling(); } else { compileSource($files[0], [@linking, @options], undef); manipulateDeps($files[0], @options); } exit; } # Compile individual files into objects my $cxx; debug "Multiple source files: @files\n"; foreach my $file (@files) { my $out = $file; $out =~ s/^(.*)\..*?$/$1.o/; if ($gxx2 && $gxx2->isCxxSource($file)) { # This is a C++ file. Compile twice with different ABIs. my $old_gcc; my %old_cap; $cxx = 1; compileSource($file, [@ARGV, '-c']); $old_gcc = $gcc; %old_cap = %capabilities; $gcc = $gxx2; %capabilities = %capabilities_gxx2; beginDoubleCompiling(); compileSource($file, [@ARGV, '-c'], '.GCCABI2'); endDoubleCompiling(); $gcc = $old_gcc; %capabilities = %old_cap; } else { compileSource($file, [@params, '-c'], undef); } $file = $out; } # Finally, link all objects together. my @options = (@params, @linking2); linkObjects([@files, @options]); if ($cxx) { $gcc = $gxx2; %capabilities = %capabilities_gxx2; # Also link the objects with ABI 2 together foreach (@files) { $_ .= ".GCCABI2"; } beginDoubleCompiling(); linkObjects([@files, @options], '.GCCABI2'); endDoubleCompiling(); } exit; } else { my $ret = run($gcc->command, @ARGV); if (defined $ARGV[0] && $ARGV[0] eq '--help') { print "\napbuild environment variables:\n"; print " APBUILD_PATH=path Specifies the include path for apsymbols.h\n" . " (like: /usr/local/include/apbuild)\n" . " APBUILD_DEBUG=1 Enable debugging messages\n" . " APBUILD_BOGUS_DEPS=deps Specify a list of whitespace-seperated bogus\n" . " library dependancies (like: X11 ICE png). These\n" . " libraries will not be linked.\n" . " APBUILD_STATIC=deps Specify a list of whitespace-seperated libraries\n" . " to statically link to (like: popt z). You can also\n" . " explicitly specify a filename to the static library.\n" . " Example: popt=/usr/lib/libpopt.a\n" . " APBUILD_STATIC_X=1 Force static linking of some X extension libraries\n" . " Don't use this unless you know what you're doing.\n" . " APBUILD_DISABLE_BOGUS_DETECTOR=1 Disable the automatic bogus dependancy\n" . " detector. This is useful when linking to libraries\n" . " don't have correct DT_NEEDED entries, like GTK 1.2.\n" . " APBUILD_NOT_BOGUS=deps If you want to use the automatic bogus dependancy\n" . " dectector anyway, then you can specify a list of\n" . " dependancies here that are not bogus.\n" . " APBUILD_STATIC_LIBGCC=1 Link all binaries with -static-libgcc. See the gcc\n" . " info page for more info about this option.\n" . " APBUILD_PROJECTNAME If non-empty, apbuild will add\n" . " \$ORIGIN/../lib/\$APBUILD_PROJECTNAME to the library\n" . " search path.\n" . " APBUILD_INCLUDE Prepend the specified directory to the compiler's\n" . " header search path. The compiler will search this\n" . " directory first, before searching any other\n" . " directory. This is useful in combination with the\n" . " older GTK headers package (see the autopackage\n" . " website). You can specify multiple directories,\n" . " seperated by a ':', just like the \$PATH environment\n" . " variable.\n" . " APBUILD_NO_RPATH Do not add rpath entries during linking.\n" . " APBUILD_CC Use the specified C compiler. Default value: gcc\n" . " APBUILD_CXX1,APBUILD_CXX2 Use the specified C++ compiler. Default value: g++\n" . " Set both variables to enable double compiling. The\n" . " first should be set to the g++ 3.2 compiler and the\n" . " second should be set to the g++ 3.4 (or newer)\n" . " compiler.\n" . " APBUILD_RESOLVE_LIBPATH A whitespace-separated list of regular expressions which\n" . " specify the libraries whose path must be resolved into\n" . " an absolute path.\n"; } exit $ret; } ######## Functions ######## sub modifyLinkerOptions { my @argv = @_; # Remove manually specified bogus library dependancies my @bogusDeps; @bogusDeps = split / +/, $ENV{APBUILD_BOGUS_DEPS} if (!empty($ENV{APBUILD_BOGUS_DEPS})); # We call removeLibraries() twice because it may detect # some dependancies after we've resolved the library names @argv = removeLibraries(\@bogusDeps, @argv); @argv = translateLibNames(@argv); @argv = removeLibraries(\@bogusDeps, @argv); @argv = removeStaticGlibc(@argv); if ($capabilities{as_needed}) { @argv = rearrangeForAsNeeded(@argv); @argv = forceStatic(@argv); } return @argv; } sub removeLibraries { my $blacklist = shift; return @_ if (@{$blacklist} == 0); my @args; my $callback = sub { my $type = shift; if ($type ne "param") { push @args, @_; return; } my $lib; $_ = $_[0]; if (/^-l(.+)/) { $lib = $1 } elsif (/(.*)\/lib(.+)\.so/) { $gcc->addSearchPaths($1); $lib = $2; } else { push @args, @_; return; } # We now have a library parameter; remove this parameter # if the library's in the blacklist foreach my $dep (@{$blacklist}) { return if ($lib eq $dep); } push @args, @_; }; $gcc->foreach(\@_, $callback); return @args; } # This function translates library linker options to something saner. # - On my system, -lpng links to libpng.so. However, libpng.so is a symlink to libpng12.so. # This function translates -lpng to -lpng12 so that the automatic bogus dependancy stripper # can detect this as a bogus dependancy. # - Translate /usr/lib/libpng.so to /usr/lib/libpng12.so because the soname is different. # - Translates -pthread to -lpthread. # - When in C++ mode, removes libstdc++.so from the argument list. This causes trouble when # double compiling, unless the -nostdlib option is specified and we're not double compiling. # libtool can put us in this configuration. # TODO: correctly handle static libraries. # apg++ ... -L/usr/lib -Wl,-Bstatic -lphysfs -Wl,-Bdynamic # -> /usr/lib/gcc-lib/i686-pc-linux-gnu/3.3.5/../../../../i686-pc-linux-gnu/bin/ld: cannot find -lphysfs-1.0 sub translateLibNames { my (@args, @searchPaths); # Get a list of search paths $gcc->getSearchPaths(\@_, \@searchPaths); my $staticMode = 0; my $callback = sub { my $type = shift; my $dontAdd; if ($type ne 'param') { push @args, @_; return; } $_ = $_[0]; if (/^-Wl,(.+)/) { # Detect whether the next library will be linked statically or dynamically foreach my $arg (split /,/, $1) { if ($arg eq '-Bdynamic') { $staticMode = 0; } elsif ($arg eq '-Bstatic') { $staticMode = 1; } } } elsif ($staticMode) { # Don't try to resolve library name if it's linked statically } elsif (/^-l(.+)/ || /--library=(.+)/) { my $libname = $1; # Resolve libname if explicitely asked to, through APBUILD_RESOLVE_LIBPATH. my @libtosolve = split / +/, $ENV{APBUILD_RESOLVE_LIBPATH} if (!empty($ENV{APBUILD_RESOLVE_LIBPATH})); foreach (@libtosolve) { my $regexp = $_; if ($libname =~ /($regexp)/) { my $file = searchLib("lib$libname.a", \@searchPaths); if ($file && -f $file) { debug "resolved", $_[0], "as", $file; # Replace -lXXX with the absolute path for libXXX.a $_[0] = $file; last; } } } # Library is a symlink; check whether the sonames match my $lib = searchLib("lib$libname.so", \@searchPaths); if ($lib && -l $lib) { my ($soname1) = $lib =~ /.*\/lib(.+)\.so/; my $lib2 = soname($lib); my ($soname2) = $lib2 =~ /lib(.+)\.so/; if ($soname1 ne $soname2 && defined searchLib("lib$soname2.so", \@searchPaths)) { $_[0] = "-l$soname2"; } } } elsif ($_ eq '-pthread') { $_[0] = "-lpthread"; } elsif ($ENV{APBUILD_CXX_MODE} && /\/?libstdc\+\+\.so(.[56])?/) { # drop this in double compile mode as it can cause issues # in single-compile mode leave it alone, otherwise, libtool can break # ^^^ That used to be true # Now drop it and add the compilers stdc++ # Works around libtool problems where the wrong libstdc++.so is # picked up from .la files of dependencies my @command = ($gcc->command, "--print-file-name=libstdc++.so"); chomp($_[0] = `@command`); } elsif (/(.*)\/?(lib.+\.so.*)/) { $gcc->addSearchPaths ($1); push @searchPaths, $1; my $lib = searchLib($2, \@searchPaths); if (defined $lib) { my $soname = soname($lib); $lib = searchLib($soname, \@searchPaths); $_[0] = $lib if (defined $lib); } } push @args, @_ if (!$dontAdd); }; $gcc->foreach(\@_, $callback); return @args; } # Replace -static with something else. We can't statically link to glibc! # So we statically link to everything but glibc. sub removeStaticGlibc { my $hasStatic = 0; foreach (@_) { if ($_ eq '-static') { $hasStatic = 1; last; } } return @_ if (!$hasStatic); my @argv; foreach (@_) { if ((/^-l(.+)/ || /^--library=(.+)/) && defined $1 && $1 ne 'c') { push @argv, '-Wl,-Bstatic'; push @argv, $_; push @argv, '-Wl,-Bdynamic'; } elsif ($_ ne "-static") { push @argv, $_; } } return @argv; } # 'gcc -Wl,--as-needed foo.o -lpng' breaks the binary. # 'gcc foo.o -Wl,--as-needed -lpng' doesn't. # Move object files to before the first library flag. # # Furthermore, -lbfd and -liberty must be the last arguments, or # an app won't link properly in some cases. XRender needs to be # after XCursor for some reason, so push it to the end too. sub rearrangeForAsNeeded { my @args; my @nonParams; my @last; my $callback = sub { my $type = shift; if ($type ne "param") { push @nonParams, @_; } elsif ($_[0] eq "-lbfd" || $_[0] eq "-liberty" || $_[0] eq "-lXrender") { push @last, @_; } elsif ($gcc->isLibrary($_[0])) { push @args, @_; } elsif ($gcc->isObject($_[0]) || $gcc->linkOrderIsImportant($_[0])) { push @nonParams, @_; } else { push @args, @_; } }; $gcc->foreach(\@_, $callback); unshift @args, "-Wl,--as-needed"; unshift @args, @nonParams; push @args, @last; return @args; } ################ Automatic bogus dependancy stripper ################ # Automatically detecting bogus dependancies & force static linking to certain X libraries sub manipulateDeps { return if ($capabilities{as_needed}); my @searchPaths; my $output = 'a.out'; my $i = 0; my @deps; my @argv; if ($ENV{APBUILD_DISABLE_BOGUS_DETECTOR}) { @argv = @_; goto FINAL; } # Get a list of search paths and the output filename for ($i = 0; $i < @_; $i++) { if ($_[$i] eq "-L") { push (@searchPaths, $_[$i + 1]) if (defined $_[$i + 1]); $i++; } elsif ($_[$i] =~ /^-L(.+)/ || $_[$i] =~ /^--library-path=(.+)/) { push (@searchPaths, $1); } elsif ($_[$i] eq "-o") { $output = $_[$i + 1] if (defined $_[$i + 1]); $i++; } } # Find out what libraries the executable needs my ($r, $w); my $pid = open2 ($r, $w, 'objdump', '-p', $output); close ($w); foreach (<$r>) { next unless (/^ NEEDED/); s/^ NEEDED[ \t]+//; s/\n//; my $lib = searchLib ($_, \@searchPaths); push (@deps, $lib) if (defined $lib); } close ($r); waitpid ($pid, 0); # Some -l options have no effect. For example, when linking apollon, # -lXinerama is passed, yet the resulting executable doesn't have a # DT_NEEDED entry for libXinerama.so. Remove those options so that # they won't interfere with forceStatic(). foreach (@_) { if ((/^-l(.+)/ || /^--library=(.+)/) && !searchLib("lib$1.a", \@searchPaths) && !(/^-lpng$/) # Special case the libpng mess, bah ) { # "Xinerama" my $arg = $1; # Only add to @argv if $arg is in @deps foreach my $dep (@deps) { # "/usr/X11R6/lib/libXinerama.so.1" -> "Xinerama" my ($soname) = $dep =~ /.*\/lib(.+)\.so/; if ($arg eq $soname) { push (@argv, "-l$arg"); last; } } } else { push (@argv, $_); } } # Find out which symbols the executable needs, and which symbols are provided # by the libraries it's linked to. my %appsyms = extractSymbols ('UBV', $output); my @bogusDeps; foreach my $lib (@deps) { # Never remove libc, libgcc_s and libstdc++ next if ($lib =~ /^\/lib\/lib(c|gcc_s|stdc\+\+)\.so/); my %libsyms = extractSymbols ('TBVRDSWG', $lib); my $bogus = 1; foreach my $sym (keys %libsyms) { if (defined $appsyms{$sym}) { debug ("Real dependancy $lib: $sym (lib: $libsyms{$sym} - app: $appsyms{$sym})\n"); $bogus = 0; last; } } if ($bogus) { my ($soname) = $lib =~ /.*\/lib(.+)\.so/; push (@bogusDeps, $soname); } } FINAL: { # Don't strip dependancies that are explicitly marked as not bogus my %notBogus; if (!empty($ENV{APBUILD_NOT_BOGUS})) { foreach (split / +/, $ENV{APBUILD_NOT_BOGUS}) { $notBogus{$_} = 1; } } my @tmp; foreach (@bogusDeps) { push @tmp, $_ if (!$notBogus{$_}); } @bogusDeps = @tmp; my @options = removeLibraries(\@bogusDeps, @argv); @options = forceStatic(@options); if ("@options" ne "@argv") { my @command = ($gcc->command, @include, @linking, @options); debug("Bogus dependancies: @bogusDeps\n") if (@bogusDeps); debug("Relinking: @command\n"); my $status = run(@command); exit($status) if ($status != 0); } } } sub extractSymbols { my $types = shift; my %symbols = (); my ($r, $w); my $pid = open2 ($r, $w, 'nm', '-D', @_); close ($w); foreach (<$r>) { if (/^.{9}[$types]/) { s/\n//; s/^.{9}//; my ($type, $name) = split (/ /, $_, 2); $symbols{$name} = $type; } } close ($r); waitpid ($pid, 0); return %symbols; } # Force static linking against libraries in $APBUILD_STATIC and certain X libraries. sub forceStatic { my (%xlibs, %staticList, $X11linked, $linkedToStaticX); my (@args, @searchPaths); # Create a list of libraries that we want to statically link $gcc->getSearchPaths(\@_, \@searchPaths); if (defined $ENV{'APBUILD_NO_STATIC_X'}) { warn "WARNING: APBUILD_NO_STATIC_X is no longer used because it became the default behaviour.\n"; } if (defined $ENV{'APBUILD_STATIC_X'} && $ENV{'APBUILD_STATIC_X'} eq "1") { foreach (qw(Xrender Xcursor Xfixes Xi Xinerama Xrandr Xv Xxf86dga Xxf86misc Xxf86vm)) { my $file = searchLib("lib$_.a", \@searchPaths); $staticList{$_} = $file if (defined $file); $xlibs{$_} = 1; } } my @static_deps; if (!empty($ENV{APBUILD_STATIC})) { foreach (split / +/, $ENV{APBUILD_STATIC}) { my ($lib, $file) = split /=/, $_, 2; $file = searchLib("lib$lib.a", \@searchPaths) if (!defined $file); $staticList{$lib} = $file if (defined $file); if (defined $file) { # find the DT_NEEDED entries that this library-to-be-made-static # has so that final linking works (the deps need to be added after the static lib so the bogus stripper doesn't remove them) my ($r, $w); my $abslib = searchLib("lib$lib.so", \@searchPaths); if (!defined $abslib) { warn "WARNING: Failed to find lib$lib.so in " . join(":", @searchPaths) . ".\n"; next; } my $pid = open2 ($r, $w, 'objdump', '-p', $abslib); close ($w); foreach (<$r>) { next unless (/^ NEEDED\s+lib(.+?)\.so/); s/^ NEEDED\s+lib(.+?)\.so(.*)/$1/; s/\n//; push(@static_deps, "-l$_"); } close ($r); waitpid ($pid, 0); } } } push (@_, @static_deps); # Modify linker options for static linking my $callback = sub { my $type = shift; my $libname; if ($type eq "param" && $gcc->isLibrary($_[0], \$libname)) { $X11linked = 1 if (!$X11linked && $libname eq 'X11'); if ($staticList{$libname}) { # This parameter is a library and is in the list of libraries # to statically link; replace parameter by a filename to the # static library push @args, $staticList{$libname}; $linkedToStaticX = 1 if ($xlibs{$libname}); if ($libname eq "Xcursor") { # With some versions of X11 (on Slack 11, # xorg 6.9.0, anyway), Xcursor links against Xfixes. # If we switch Xcursor to being linked statically, # force linking of Xfixes too so we don't get # undefined symbol errors from Xcursor. push @args, $staticList{"Xfixes"}; } } else { push @args, @_; } } else { push @args, @_; } }; $gcc->foreach(\@_, $callback); # The app must be linked to libX11 if it has statically linked any the static X libraries push @args, "-lX11" if ($linkedToStaticX && !$X11linked); return @args; } ## # compileSource(source, argss, extension, extra...) # source: the source filename. # args: the full GCC arguments (may include other source files) used for compilation. # extension: if not undef, $extension will be appended to the output object file's filename. # extra: extra parameters to pass to the compiler. sub compileSource { my ($source, $args, $ext) = @_; my (@command, @tmp, @params, @sourceParams, @otherParams); $gcc->splitParams($args, undef, \@tmp); push @tmp, $source; if (defined $ext) { # Change the output file's extension. $gcc->setOutputFile(\@tmp, $gcc->getOutputFile(\@tmp) . $ext); } $gcc->splitParams(\@tmp, undef, \@params); my $callback = sub { my $type = shift; if ($type eq 'param' && $gcc->sourceOrderIsImportant($_[0])) { push @sourceParams, @_; } else { push @otherParams, @_; } }; $gcc->foreach(\@params, $callback); if ($source =~ /\.($srcTypes)$/) { @command = ($gcc->command, @include, @sourceParams, $source, @otherParams); } else { @command = ($gcc->command, @sourceParams, $source, @otherParams); } debug "@command\n"; my $status = run(@command); exit($status) if ($status != 0); } # Checks whether there should be an ABI2 version of a certain static library. sub checkStaticLibrary { my ($lib, $ext) = @_; my ($pid, $r, $w, @objects, $doubleCompile); my (undef, $libdir, $libname) = File::Spec->splitpath($lib); my $newlib = "$lib$ext"; # Return the ABI2 version if already exists. return $newlib if (-f $newlib); # Check the content of the archive. Check whether # there are ABI2 versions of the object files inside. $pid = open2($r, $w, 'ar', 't', $lib); close($w); while ((my $file = <$r>)) { $file =~ s/\n//g; if (-f "$libdir/$file$ext") { push @objects, "$file$ext"; $doubleCompile = 1; } else { push @objects, $file; } } close($r); waitpid ($pid, 0); if ($doubleCompile) { my $oldDir = getcwd(); $newlib = "$libname$ext"; debug "Creating static library $newlib\n"; chdir($libdir); my @command = ("ar", "cru", $newlib, @objects); debug(@command); my $ret = run(@command); exit($ret) if ($ret != 0); @command = ("ranlib", $newlib); debug(@command); $ret = run(@command); exit($ret) if ($ret != 0); chdir($oldDir); return $newlib; } else { return undef; } } sub linkObjects { my ($args, $ext) = @_; my @options = modifyLinkerOptions(@{$args}); if (defined $ext) { $gcc->setOutputFile(\@options, $gcc->getOutputFile(\@options) . $ext); # Check whether this object links to any static libraries. # If it does, check whether there should be an ABI2 version of that # static library, and attempt to create it. my @options2; my $callback = sub { my $type = shift; if ($type eq 'param' && $gcc->isStaticLib($_[0])) { my $newlib = checkStaticLibrary($_[0], $ext); if (defined $newlib) { push @options2, $newlib; } else { push @options2, @_; } } else { push @options2, @_; } }; $gcc->foreach(\@options, $callback); @options = @options2; } my @command = ($gcc->command, @linking, @options); debug "@command\n"; my $status = run(@command); exit ($status) if ($status != 0); my (@files, @options2); $gcc->splitParams(\@options, \@files, \@options2); manipulateDeps(@files, @options2); } ## # beginDoubleCompiling() # # Prepare the environment for double compiling. sub beginDoubleCompiling { # Since g++ will be executed another time, we don't want it to # print output to stdout/stderr, because it can potentially # screw up some build systems such as libtool. # stderr will go to the console (/dev/tty), stdout will be # lost (/dev/null). our $stdout_fd = fileno(STDOUT); our $stderr_fd = fileno(STDERR); our $stdout_saved = POSIX::dup($stdout_fd); our $stderr_saved = POSIX::dup($stderr_fd); my $fd1 = POSIX::open("/dev/null", O_CREAT | O_WRONLY, 0644); my $fd2 = POSIX::open("/dev/tty", O_CREAT | O_WRONLY, 0644); POSIX::dup2($fd1, $stdout_fd); POSIX::dup2($fd2, $stderr_fd); POSIX::close($fd1); POSIX::close($fd2); } ## # endDoubleCompiling() # # Unprepare the environment for double compiling. sub endDoubleCompiling { our ($stdout_fd, $stderr_fd, $stdout_saved, $stderr_saved); POSIX::dup2($stdout_saved, $stdout_fd); POSIX::dup2($stderr_saved, $stderr_fd); POSIX::close($stdout_saved); POSIX::close($stderr_saved); }