@ -38,84 +38,36 @@ import shutil
import sys
import tempfile
def _unix_commandfile_spawn ( self , command ) :
""" Wrapper around distutils.util.spawn that attempts to use command files.
def _unix_piecemeal_link (
self , target_desc , objects , output_filename , output_dir = None ,
libraries = None , library_dirs = None , runtime_library_dirs = None ,
export_symbols = None , debug = 0 , extra_preargs = None , extra_postargs = None ,
build_temp = None , target_lang = None ) :
""" `link` externalized method taken almost verbatim from UnixCCompiler.
Meant to replace the CCompiler method ` spawn ` on UnixCCompiler and its
derivatives ( e . g . the MinGW32 compiler ) .
Modifies the link command for unix - like compilers by using a command file so
that long command line argument strings don ' t break the command shell ' s
ARG_MAX character limit .
Some commands like ` gcc ` ( and friends like ` clang ` ) support command files to
work around shell command length limits .
"""
objects , output_dir = self . _fix_object_args ( objects , output_dir )
libraries , library_dirs , runtime_library_dirs = self . _fix_lib_args (
libraries , library_dirs , runtime_library_dirs )
# filter out standard library paths, which are not explicitely needed
# for linking
library_dirs = [ dir for dir in library_dirs
if not dir in ( ' /lib ' , ' /lib64 ' , ' /usr/lib ' , ' /usr/lib64 ' ) ]
runtime_library_dirs = [ dir for dir in runtime_library_dirs
if not dir in ( ' /lib ' , ' /lib64 ' , ' /usr/lib ' , ' /usr/lib64 ' ) ]
lib_opts = ccompiler . gen_lib_options ( self , library_dirs , runtime_library_dirs ,
libraries )
if ( not ( isinstance ( output_dir , str ) or isinstance ( output_dir , bytes ) )
and output_dir is not None ) :
raise TypeError ( " ' output_dir ' must be a string or None " )
if output_dir is not None :
output_filename = os . path . join ( output_dir , output_filename )
if self . _need_link ( objects , output_filename ) :
ld_args = ( objects + self . objects +
lib_opts + [ ' -o ' , output_filename ] )
if debug :
ld_args [ : 0 ] = [ ' -g ' ]
if extra_preargs :
ld_args [ : 0 ] = extra_preargs
if extra_postargs :
ld_args . extend ( extra_postargs )
self . mkpath ( os . path . dirname ( output_filename ) )
try :
if target_desc == ccompiler . CCompiler . EXECUTABLE :
linker = self . linker_exe [ : ]
else :
linker = self . linker_so [ : ]
if target_lang == " c++ " and self . compiler_cxx :
# skip over environment variable settings if /usr/bin/env
# is used to set up the linker's environment.
# This is needed on OSX. Note: this assumes that the
# normal and C++ compiler have the same environment
# settings.
i = 0
if os . path . basename ( linker [ 0 ] ) == " env " :
i = 1
while ' = ' in linker [ i ] :
i = i + 1
linker [ i ] = self . compiler_cxx [ i ]
if sys . platform == ' darwin ' :
import _osx_support
linker = _osx_support . compiler_fixup ( linker , ld_args )
temporary_directory = tempfile . mkdtemp ( )
command_filename = os . path . abspath (
os . path . join ( temporary_directory , ' command ' ) )
with open ( command_filename , ' w ' ) as command_file :
escaped_ld_args = [ arg . replace ( ' \\ ' , ' \\ \\ ' ) for arg in ld_args ]
command_file . write ( ' ' . join ( escaped_ld_args ) )
self . spawn ( linker + [ ' @ {} ' . format ( command_filename ) ] )
except errors . DistutilsExecError :
raise ccompiler . LinkError
command_base = os . path . basename ( command [ 0 ] . strip ( ) )
if command_base == ' ccache ' :
command_base = command [ : 2 ]
command_args = command [ 2 : ]
elif command_base . startswith ( ' ccache ' ) or command_base in [ ' gcc ' , ' clang ' , ' clang++ ' , ' g++ ' ] :
command_base = command [ : 1 ]
command_args = command [ 1 : ]
else :
log . debug ( " skipping %s (up-to-date) " , output_filename )
return ccompiler . CCompiler . spawn ( self , command )
temporary_directory = tempfile . mkdtemp ( )
command_filename = os . path . abspath ( os . path . join ( temporary_directory , ' command ' ) )
with open ( command_filename , ' w ' ) as command_file :
escaped_args = [ arg . replace ( ' \\ ' , ' \\ \\ ' ) for arg in command_args ]
command_file . write ( ' ' . join ( escaped_args ) )
modified_command = command_base + [ ' @ {} ' . format ( command_filename ) ]
result = ccompiler . CCompiler . spawn ( self , modified_command )
shutil . rmtree ( temporary_directory )
return result
# TODO(atash) try replacing this monkeypatch of the compiler harness' link
# operation with a monkeypatch of the distutils `spawn` that applies
# command-argument-file hacks where it can. Might be cleaner.
def monkeypatch_unix_compiler ( ) :
""" Monkeypatching is dumb, but it ' s either that or we become maintainers of
something much , much bigger . """
unixccompiler . UnixCCompiler . link = _unix_piecemeal_link
unixccompiler . UnixCCompiler . spawn = _unix_commandfile_spawn