gcc   [plain text]


#!/usr/bin/perl
#
# Copyright (c) 2007, 2008 Apple Inc. All rights reserved.
#
# @APPLE_LICENSE_HEADER_START@
# 
# This file contains Original Code and/or Modifications of Original Code
# as defined in and that are subject to the Apple Public Source License
# Version 2.0 (the 'License'). You may not use this file except in
# compliance with the License. Please obtain a copy of the License at
# http://www.opensource.apple.com/apsl/ and read it before using this
# file.
# 
# The Original Code and all software distributed under the License are
# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
# Please see the License for the specific language governing rights and
# limitations under the License.
#-----------------------------------------------------------------------
# 
# This program is a wrapper for gcc and other GNU compilers:
#
# 1) When given a -no64 argument, all 64-bit -arch arguments are stripped.
#
# 2) When SDKROOT and _TC_SPLIT_ARCHS are both defined, and we are building
# multiple architectures, split between the SDKROOT architectures (defined
# by tconf --archs) and the regular architectures.  Then we compile those
# SDKROOT architectures with the appropriate SKDROOT flags, and the regular
# architectures without those flags.  Finally, the two binaries are lipo-ed
# together.
#
# 3) If _TC_SPLIT_ARCHS is not defined, we will always add the SDKROOT flags
# if SDKROOT is defined.

use strict;
use DirHandle;
use File::Basename ();
use File::chdir;
use File::Spec;
use File::Temp ();

my $MyName = File::Basename::basename($0);

my $sdkroot = $ENV{SDKROOT};
my $splitarchs = $ENV{_TC_SPLIT_ARCHS};

my %SDKArchs;
%SDKArchs = map {($_ => 1)} split(' ', `tconf --archs`)
    if defined($sdkroot) && defined($splitarchs);

my $compile = 0;
my $doprint = 0;
my $got_arch = 0;
my $got_o = 0;
my $no64 = 0;
my $output;
my(%sdkarchhash, %sdk64archhash, %regulararchhash, %regular64archhash, @otherargs);
for(@ARGV) {
    if($got_arch) {
	if($SDKArchs{$_}) {
	    if(/64$/) {
		$sdk64archhash{$_} = 1;
	    } else {
		$sdkarchhash{$_} = 1;
	    }
	} elsif(/64$/) {
	    $regular64archhash{$_} = 1;
	} else {
	    $regulararchhash{$_} = 1;
	}
	$got_arch = 0;
	next;
    }
    if($got_o) {
	$output = $_;
	$got_o = 0;
	next;
    }
    if($_ eq '-arch') {
	$got_arch++;
	next;
    }
    if($_ eq '-no64') {
	$no64++;
	next;
    }
    if($_ eq '-o') {
	$got_o++;
	next;
    }
    $compile++ if $_ eq '-c';
    push(@otherargs, $_);
}
my @sdkarchlist = map {('-arch', $_)} keys(%sdkarchhash);
my @sdk64archlist = map {('-arch', $_)} keys(%sdk64archhash);
my @regulararchlist = map {('-arch', $_)} keys(%regulararchhash);
my @regular64archlist = map {('-arch', $_)} keys(%regular64archhash);
if(scalar(@sdk64archlist) > 0) {
    if($no64) {
	# ignore @sdk64archlist
	$doprint++;
    } else {
	# otherwise, append them to @sdkarchlist
	push(@sdkarchlist, @sdk64archlist);
    }
}
if(scalar(@regular64archlist) > 0) {
    if($no64) {
	# ignore @regular64archlist
	$doprint++;
    } else {
	# otherwise, append them to @regulararchlist
	push(@regulararchlist, @regular64archlist);
    }
}

if(!defined($splitarchs)) {
    my @args = ("/usr/bin/$MyName", @regulararchlist);
    if(defined($sdkroot)) {
	push(@args, $compile ? ('-isysroot', $sdkroot) :
	    "-Wl,-syslibroot,$sdkroot");
	$doprint = 1;
    }
    push(@args, '-o', $output) if defined($output);
    push(@args, @otherargs);
    print STDERR "===>@args\n" if $doprint;
    exec @args;
    exit 1; # exec failed
}

if(scalar(@regulararchlist) > 0 && scalar(@sdkarchlist) > 0) {
    my @args;
    # We create a temp directory where the temp files are created (and any
    # other cruft); this directory is automatically removed when this
    # program exits.
    my $tmpdir = File::Temp::tempdir('compilertempXXXXXXXX', CLEANUP => 1, TMPDIR => 1);
    if(defined($output)) {
	# The -o option was specified, so we just use -o for each set of
	# architectures, and lipo the results together.
	my $sdkfile = File::Spec->join($tmpdir, "sdkfile");
	if($compile) {
	    @args = ("/usr/bin/$MyName", @sdkarchlist, '-isysroot', $sdkroot, '-o', $sdkfile, @otherargs);
	} else {
	    @args = ("/usr/bin/$MyName", @sdkarchlist, "-Wl,-syslibroot,$sdkroot", '-o', $sdkfile, @otherargs);
	}
	print STDERR "===>@args\n";
	system(@args) == 0 or exit 1;
	my $regularfile = File::Spec->join($tmpdir, "regularfile");
	@args = ("/usr/bin/$MyName", @regulararchlist, '-o', $regularfile, @otherargs);
	print STDERR "===>@args\n";
	system(@args) == 0 or exit 1;
	@args = ('lipo', '-create', $sdkfile, $regularfile, '-output', $output);
	print STDERR "===>@args\n";
	system(@args) == 0 or exit 1;
    } else {
	# We don't have -o, so we create two subdirectories, chdir into each
	# to do the compile, chdir back and lipo the files in the two sub-
	# directories together.  But first, we need make symlinks in each
	# of these directories pointing to each files/directories in the
	# current directory.
	my $sdkdir = File::Spec->join($tmpdir, "sdkdir");
	mkdir $sdkdir or die "Can't mkdir($sdkdir): $!\n";
	my $regulardir = File::Spec->join($tmpdir, "regulardir");
	mkdir $regulardir or die "Can't mkdir($regulardir): $!\n";
	my $d = DirHandle->new('.') or die "Can't DirHandle->new('.'): $!\n";
	while(defined($_ = $d->read)) {
	    next if $_ eq '.' || $_ eq '..';
	    my $oldname = File::Spec->join($CWD, $_);
	    my $newname = File::Spec->join($sdkdir, $_);
	    symlink($oldname, $newname)
		or die "Can't symlink($oldname, $newname): $!\n";
	    $newname = File::Spec->join($regulardir, $_);
	    symlink($oldname, $newname)
		or die "Can't symlink($oldname, $newname): $!\n";
	}
	undef $d;
	if($compile) {
	    @args = ("/usr/bin/$MyName", @sdkarchlist,
		'-isysroot', $sdkroot, @otherargs);
	} else {
	    @args = ("/usr/bin/$MyName", @sdkarchlist,
		"-Wl,-syslibroot,$sdkroot", @otherargs);
	}
	print STDERR "===>@args\n";
	my @filelist;
	{
	    local $CWD = $sdkdir; # chdir to $sdkdir within this block
	    system(@args) == 0 or exit 1;
	    my $d = DirHandle->new('.')
		or die "Can't DirHandle->new('.'): $!\n";
	    while(defined($_ = $d->read)) {
		push(@filelist, $_) if !-l $_ && -f _;
	    }
	}
	@args = ("/usr/bin/$MyName", @regulararchlist, @otherargs);
	print STDERR "===>@args\n";
	{
	    local $CWD = $regulardir; # chdir to $regulardir within this block
	    system(@args) == 0 or exit 1;
	}
	for(@filelist) {
	    @args = (
		'lipo',
		'-create',
		File::Spec->join($sdkdir, $_),
		File::Spec->join($regulardir, $_),
		'-output',
		$_
	    );
	    print STDERR "===>@args\n";
	    system(@args) == 0 or exit 1;
	}
    }
    exit 0;
} else {
    push(@regulararchlist, @sdkarchlist);
}

if(scalar(@regulararchlist) == 0) { # no -arch flag was specified
    chomp(my $nativearch = `arch`);
    # If $nativearch is in %SDKArchs, we push it into @sdkarchlist.  This
    # will cause the appropriate SDKROOT flag to be added, even though no
    # -arch flag will be used.
    push(@sdkarchlist, $nativearch) if $SDKArchs{$nativearch};
}

my @args = ("/usr/bin/$MyName", @regulararchlist);
if(scalar(@sdkarchlist)) {
    push(@args, $compile ? ('-isysroot', $sdkroot) :
	"-Wl,-syslibroot,$sdkroot");
    $doprint = 1;
}
push(@args, '-o', $output) if defined($output);
push(@args, @otherargs);
print STDERR "===>@args\n" if $doprint;
exec @args;
exit 1; # exec failed