summaryrefslogtreecommitdiff
path: root/tools/apbuild/apgcc
diff options
context:
space:
mode:
Diffstat (limited to 'tools/apbuild/apgcc')
-rwxr-xr-xtools/apbuild/apgcc1023
1 files changed, 1023 insertions, 0 deletions
diff --git a/tools/apbuild/apgcc b/tools/apbuild/apgcc
new file mode 100755
index 00000000..2aba0071
--- /dev/null
+++ b/tools/apbuild/apgcc
@@ -0,0 +1,1023 @@
+#!/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;
+ }
+}
+
+# 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);
+}