1"""distutils.unixccompiler 2 3Contains the UnixCCompiler class, a subclass of CCompiler that handles 4the "typical" Unix-style command-line C compiler: 5 * macros defined with -Dname[=value] 6 * macros undefined with -Uname 7 * include search directories specified with -Idir 8 * libraries specified with -lllib 9 * library search directories specified with -Ldir 10 * compile handled by 'cc' (or similar) executable with -c option: 11 compiles .c to .o 12 * link static library handled by 'ar' command (possibly with 'ranlib') 13 * link shared library handled by 'cc -shared' 14""" 15 16import os, sys, re 17 18from distutils import sysconfig 19from distutils.dep_util import newer 20from distutils.ccompiler import \ 21 CCompiler, gen_preprocess_options, gen_lib_options 22from distutils.errors import \ 23 DistutilsExecError, CompileError, LibError, LinkError 24from distutils import log 25 26if sys.platform == 'darwin': 27 import _osx_support 28 29# XXX Things not currently handled: 30# * optimization/debug/warning flags; we just use whatever's in Python's 31# Makefile and live with it. Is this adequate? If not, we might 32# have to have a bunch of subclasses GNUCCompiler, SGICCompiler, 33# SunCCompiler, and I suspect down that road lies madness. 34# * even if we don't know a warning flag from an optimization flag, 35# we need some way for outsiders to feed preprocessor/compiler/linker 36# flags in to us -- eg. a sysadmin might want to mandate certain flags 37# via a site config file, or a user might want to set something for 38# compiling this module distribution only via the setup.py command 39# line, whatever. As long as these options come from something on the 40# current system, they can be as system-dependent as they like, and we 41# should just happily stuff them into the preprocessor/compiler/linker 42# options and carry on. 43 44 45class UnixCCompiler(CCompiler): 46 47 compiler_type = 'unix' 48 49 # These are used by CCompiler in two places: the constructor sets 50 # instance attributes 'preprocessor', 'compiler', etc. from them, and 51 # 'set_executable()' allows any of these to be set. The defaults here 52 # are pretty generic; they will probably have to be set by an outsider 53 # (eg. using information discovered by the sysconfig about building 54 # Python extensions). 55 executables = {'preprocessor' : None, 56 'compiler' : ["cc"], 57 'compiler_so' : ["cc"], 58 'compiler_cxx' : ["cc"], 59 'linker_so' : ["cc", "-shared"], 60 'linker_exe' : ["cc"], 61 'archiver' : ["ar", "-cr"], 62 'ranlib' : None, 63 } 64 65 if sys.platform[:6] == "darwin": 66 executables['ranlib'] = ["ranlib"] 67 68 # Needed for the filename generation methods provided by the base 69 # class, CCompiler. NB. whoever instantiates/uses a particular 70 # UnixCCompiler instance should set 'shared_lib_ext' -- we set a 71 # reasonable common default here, but it's not necessarily used on all 72 # Unices! 73 74 src_extensions = [".c",".C",".cc",".cxx",".cpp",".m"] 75 obj_extension = ".o" 76 static_lib_extension = ".a" 77 shared_lib_extension = ".so" 78 dylib_lib_extension = ".dylib" 79 xcode_stub_lib_extension = ".tbd" 80 static_lib_format = shared_lib_format = dylib_lib_format = "lib%s%s" 81 xcode_stub_lib_format = dylib_lib_format 82 if sys.platform == "cygwin": 83 exe_extension = ".exe" 84 85 def preprocess(self, source, output_file=None, macros=None, 86 include_dirs=None, extra_preargs=None, extra_postargs=None): 87 fixed_args = self._fix_compile_args(None, macros, include_dirs) 88 ignore, macros, include_dirs = fixed_args 89 pp_opts = gen_preprocess_options(macros, include_dirs) 90 pp_args = self.preprocessor + pp_opts 91 if output_file: 92 pp_args.extend(['-o', output_file]) 93 if extra_preargs: 94 pp_args[:0] = extra_preargs 95 if extra_postargs: 96 pp_args.extend(extra_postargs) 97 pp_args.append(source) 98 99 # We need to preprocess: either we're being forced to, or we're 100 # generating output to stdout, or there's a target output file and 101 # the source file is newer than the target (or the target doesn't 102 # exist). 103 if self.force or output_file is None or newer(source, output_file): 104 if output_file: 105 self.mkpath(os.path.dirname(output_file)) 106 try: 107 self.spawn(pp_args) 108 except DistutilsExecError as msg: 109 raise CompileError(msg) 110 111 def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): 112 compiler_so = self.compiler_so 113 if sys.platform == 'darwin': 114 compiler_so = _osx_support.compiler_fixup(compiler_so, 115 cc_args + extra_postargs) 116 try: 117 self.spawn(compiler_so + cc_args + [src, '-o', obj] + 118 extra_postargs) 119 except DistutilsExecError as msg: 120 raise CompileError(msg) 121 122 def create_static_lib(self, objects, output_libname, 123 output_dir=None, debug=0, target_lang=None): 124 objects, output_dir = self._fix_object_args(objects, output_dir) 125 126 output_filename = \ 127 self.library_filename(output_libname, output_dir=output_dir) 128 129 if self._need_link(objects, output_filename): 130 self.mkpath(os.path.dirname(output_filename)) 131 self.spawn(self.archiver + 132 [output_filename] + 133 objects + self.objects) 134 135 # Not many Unices required ranlib anymore -- SunOS 4.x is, I 136 # think the only major Unix that does. Maybe we need some 137 # platform intelligence here to skip ranlib if it's not 138 # needed -- or maybe Python's configure script took care of 139 # it for us, hence the check for leading colon. 140 if self.ranlib: 141 try: 142 self.spawn(self.ranlib + [output_filename]) 143 except DistutilsExecError as msg: 144 raise LibError(msg) 145 else: 146 log.debug("skipping %s (up-to-date)", output_filename) 147 148 def link(self, target_desc, objects, 149 output_filename, output_dir=None, libraries=None, 150 library_dirs=None, runtime_library_dirs=None, 151 export_symbols=None, debug=0, extra_preargs=None, 152 extra_postargs=None, build_temp=None, target_lang=None): 153 objects, output_dir = self._fix_object_args(objects, output_dir) 154 fixed_args = self._fix_lib_args(libraries, library_dirs, 155 runtime_library_dirs) 156 libraries, library_dirs, runtime_library_dirs = fixed_args 157 158 lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs, 159 libraries) 160 if not isinstance(output_dir, (str, type(None))): 161 raise TypeError("'output_dir' must be a string or None") 162 if output_dir is not None: 163 output_filename = os.path.join(output_dir, output_filename) 164 165 if self._need_link(objects, output_filename): 166 ld_args = (objects + self.objects + 167 lib_opts + ['-o', output_filename]) 168 if debug: 169 ld_args[:0] = ['-g'] 170 if extra_preargs: 171 ld_args[:0] = extra_preargs 172 if extra_postargs: 173 ld_args.extend(extra_postargs) 174 self.mkpath(os.path.dirname(output_filename)) 175 try: 176 if target_desc == CCompiler.EXECUTABLE: 177 linker = self.linker_exe[:] 178 else: 179 linker = self.linker_so[:] 180 if target_lang == "c++" and self.compiler_cxx: 181 # skip over environment variable settings if /usr/bin/env 182 # is used to set up the linker's environment. 183 # This is needed on OSX. Note: this assumes that the 184 # normal and C++ compiler have the same environment 185 # settings. 186 i = 0 187 if os.path.basename(linker[0]) == "env": 188 i = 1 189 while '=' in linker[i]: 190 i += 1 191 192 if os.path.basename(linker[i]) == 'ld_so_aix': 193 # AIX platforms prefix the compiler with the ld_so_aix 194 # script, so we need to adjust our linker index 195 offset = 1 196 else: 197 offset = 0 198 199 linker[i+offset] = self.compiler_cxx[i] 200 201 if sys.platform == 'darwin': 202 linker = _osx_support.compiler_fixup(linker, ld_args) 203 204 self.spawn(linker + ld_args) 205 except DistutilsExecError as msg: 206 raise LinkError(msg) 207 else: 208 log.debug("skipping %s (up-to-date)", output_filename) 209 210 # -- Miscellaneous methods ----------------------------------------- 211 # These are all used by the 'gen_lib_options() function, in 212 # ccompiler.py. 213 214 def library_dir_option(self, dir): 215 return "-L" + dir 216 217 def _is_gcc(self, compiler_name): 218 return "gcc" in compiler_name or "g++" in compiler_name 219 220 def runtime_library_dir_option(self, dir): 221 # XXX Hackish, at the very least. See Python bug #445902: 222 # http://sourceforge.net/tracker/index.php 223 # ?func=detail&aid=445902&group_id=5470&atid=105470 224 # Linkers on different platforms need different options to 225 # specify that directories need to be added to the list of 226 # directories searched for dependencies when a dynamic library 227 # is sought. GCC on GNU systems (Linux, FreeBSD, ...) has to 228 # be told to pass the -R option through to the linker, whereas 229 # other compilers and gcc on other systems just know this. 230 # Other compilers may need something slightly different. At 231 # this time, there's no way to determine this information from 232 # the configuration data stored in the Python installation, so 233 # we use this hack. 234 compiler = os.path.basename(sysconfig.get_config_var("CC")) 235 if sys.platform[:6] == "darwin": 236 # MacOSX's linker doesn't understand the -R flag at all 237 return "-L" + dir 238 elif sys.platform[:7] == "freebsd": 239 return "-Wl,-rpath=" + dir 240 elif sys.platform[:5] == "hp-ux": 241 if self._is_gcc(compiler): 242 return ["-Wl,+s", "-L" + dir] 243 return ["+s", "-L" + dir] 244 else: 245 if self._is_gcc(compiler): 246 # gcc on non-GNU systems does not need -Wl, but can 247 # use it anyway. Since distutils has always passed in 248 # -Wl whenever gcc was used in the past it is probably 249 # safest to keep doing so. 250 if sysconfig.get_config_var("GNULD") == "yes": 251 # GNU ld needs an extra option to get a RUNPATH 252 # instead of just an RPATH. 253 return "-Wl,--enable-new-dtags,-R" + dir 254 else: 255 return "-Wl,-R" + dir 256 else: 257 # No idea how --enable-new-dtags would be passed on to 258 # ld if this system was using GNU ld. Don't know if a 259 # system like this even exists. 260 return "-R" + dir 261 262 def library_option(self, lib): 263 return "-l" + lib 264 265 def find_library_file(self, dirs, lib, debug=0): 266 shared_f = self.library_filename(lib, lib_type='shared') 267 dylib_f = self.library_filename(lib, lib_type='dylib') 268 xcode_stub_f = self.library_filename(lib, lib_type='xcode_stub') 269 static_f = self.library_filename(lib, lib_type='static') 270 271 if sys.platform == 'darwin': 272 # On OSX users can specify an alternate SDK using 273 # '-isysroot', calculate the SDK root if it is specified 274 # (and use it further on) 275 # 276 # Note that, as of Xcode 7, Apple SDKs may contain textual stub 277 # libraries with .tbd extensions rather than the normal .dylib 278 # shared libraries installed in /. The Apple compiler tool 279 # chain handles this transparently but it can cause problems 280 # for programs that are being built with an SDK and searching 281 # for specific libraries. Callers of find_library_file need to 282 # keep in mind that the base filename of the returned SDK library 283 # file might have a different extension from that of the library 284 # file installed on the running system, for example: 285 # /Applications/Xcode.app/Contents/Developer/Platforms/ 286 # MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/ 287 # usr/lib/libedit.tbd 288 # vs 289 # /usr/lib/libedit.dylib 290 cflags = sysconfig.get_config_var('CFLAGS') 291 m = re.search(r'-isysroot\s+(\S+)', cflags) 292 if m is None: 293 sysroot = '/' 294 else: 295 sysroot = m.group(1) 296 297 298 299 for dir in dirs: 300 shared = os.path.join(dir, shared_f) 301 dylib = os.path.join(dir, dylib_f) 302 static = os.path.join(dir, static_f) 303 xcode_stub = os.path.join(dir, xcode_stub_f) 304 305 if sys.platform == 'darwin' and ( 306 dir.startswith('/System/') or ( 307 dir.startswith('/usr/') and not dir.startswith('/usr/local/'))): 308 309 shared = os.path.join(sysroot, dir[1:], shared_f) 310 dylib = os.path.join(sysroot, dir[1:], dylib_f) 311 static = os.path.join(sysroot, dir[1:], static_f) 312 xcode_stub = os.path.join(sysroot, dir[1:], xcode_stub_f) 313 314 # We're second-guessing the linker here, with not much hard 315 # data to go on: GCC seems to prefer the shared library, so I'm 316 # assuming that *all* Unix C compilers do. And of course I'm 317 # ignoring even GCC's "-static" option. So sue me. 318 if os.path.exists(dylib): 319 return dylib 320 elif os.path.exists(xcode_stub): 321 return xcode_stub 322 elif os.path.exists(shared): 323 return shared 324 elif os.path.exists(static): 325 return static 326 327 # Oops, didn't find it in *any* of 'dirs' 328 return None 329