1"""distutils.cygwinccompiler 2 3Provides the CygwinCCompiler class, a subclass of UnixCCompiler that 4handles the Cygwin port of the GNU C compiler to Windows. It also contains 5the Mingw32CCompiler class which handles the mingw32 port of GCC (same as 6cygwin in no-cygwin mode). 7""" 8 9# problems: 10# 11# * if you use a msvc compiled python version (1.5.2) 12# 1. you have to insert a __GNUC__ section in its config.h 13# 2. you have to generate a import library for its dll 14# - create a def-file for python??.dll 15# - create a import library using 16# dlltool --dllname python15.dll --def python15.def \ 17# --output-lib libpython15.a 18# 19# see also http://starship.python.net/crew/kernr/mingw32/Notes.html 20# 21# * We put export_symbols in a def-file, and don't use 22# --export-all-symbols because it doesn't worked reliable in some 23# tested configurations. And because other windows compilers also 24# need their symbols specified this no serious problem. 25# 26# tested configurations: 27# 28# * cygwin gcc 2.91.57/ld 2.9.4/dllwrap 0.2.4 works 29# (after patching python's config.h and for C++ some other include files) 30# see also http://starship.python.net/crew/kernr/mingw32/Notes.html 31# * mingw32 gcc 2.95.2/ld 2.9.4/dllwrap 0.2.4 works 32# (ld doesn't support -shared, so we use dllwrap) 33# * cygwin gcc 2.95.2/ld 2.10.90/dllwrap 2.10.90 works now 34# - its dllwrap doesn't work, there is a bug in binutils 2.10.90 35# see also http://sources.redhat.com/ml/cygwin/2000-06/msg01274.html 36# - using gcc -mdll instead dllwrap doesn't work without -static because 37# it tries to link against dlls instead their import libraries. (If 38# it finds the dll first.) 39# By specifying -static we force ld to link against the import libraries, 40# this is windows standard and there are normally not the necessary symbols 41# in the dlls. 42# *** only the version of June 2000 shows these problems 43# * cygwin gcc 3.2/ld 2.13.90 works 44# (ld supports -shared) 45# * mingw gcc 3.2/ld 2.13 works 46# (ld supports -shared) 47 48# This module should be kept compatible with Python 2.1. 49 50__revision__ = "$Id$" 51 52import os,sys,copy 53from distutils.ccompiler import gen_preprocess_options, gen_lib_options 54from distutils.unixccompiler import UnixCCompiler 55from distutils.file_util import write_file 56from distutils.errors import DistutilsExecError, CompileError, UnknownFileError 57from distutils import log 58 59def get_msvcr(): 60 """Include the appropriate MSVC runtime library if Python was built 61 with MSVC 7.0 or later. 62 """ 63 # FIXME: next code is from issue870382 64 # MS C-runtime libraries never support backward compatibility. 65 # Linking to a different library without to specify correct runtime 66 # version for the headers will link renamed functions to msvcrt. 67 # See issue3308: this piece of code is python problem even 68 # with correct w32api headers. 69 # Issue: for MSVC compiler we can get the version and from version 70 # to determine mcvcrt as code below. But what about if python is 71 # build with GCC compiler? 72 # Output of sys.version is information for python build on first 73 # line, on the next line is information for the compiler and the 74 # output lack information for the C-runtime. 75 msc_pos = sys.version.find('MSC v.') 76 if msc_pos != -1: 77 msc_ver = sys.version[msc_pos+6:msc_pos+10] 78 if msc_ver == '1300': 79 # MSVC 7.0 80 return ['msvcr70'] 81 elif msc_ver == '1310': 82 # MSVC 7.1 83 return ['msvcr71'] 84 elif msc_ver == '1400': 85 # VS2005 / MSVC 8.0 86 return ['msvcr80'] 87 elif msc_ver == '1500': 88 # VS2008 / MSVC 9.0 89 return ['msvcr90'] 90 else: 91 raise ValueError("Unknown MS Compiler version %s " % msc_ver) 92 else: 93 return [] 94 95 96class CygwinCCompiler (UnixCCompiler): 97 98 compiler_type = 'cygwin' 99 obj_extension = ".o" 100 static_lib_extension = ".a" 101 shared_lib_extension = ".dll" 102 # FIXME: dylib_... = ".dll.a" is not enought for binutils 103 # loader on win32 platform !!! 104 dylib_lib_extension = ".dll.a" 105 static_lib_format = "lib%s%s" 106 shared_lib_format = "%s%s" 107 exe_extension = ".exe" 108 109 def __init__ (self, verbose=0, dry_run=0, force=0): 110 111 UnixCCompiler.__init__ (self, verbose, dry_run, force) 112 113 (status, details) = check_config_h() 114 self.debug_print("Python's GCC status: %s (details: %s)" % 115 (status, details)) 116 if status is not CONFIG_H_OK: 117 self.warn( 118 "Python's pyconfig.h doesn't seem to support your compiler. " 119 "Reason: %s. " 120 "Compiling may fail because of undefined preprocessor macros." 121 % details) 122 123 # Next line of code is problem for cross-compiled enviroment: 124 # NOTE: GCC cross-compiler is prefixed by the <hostarch>-<hostos>- 125 # and by default binaries are installed in same directory 126 # as native compiler. 127 self.gcc_version, self.ld_version, self.dllwrap_version = \ 128 get_versions() 129 self.debug_print(self.compiler_type + ": gcc %s, ld %s, dllwrap %s\n" % 130 (self.gcc_version, 131 self.ld_version, 132 self.dllwrap_version) ) 133 134 # ld_version >= "2.10.90" and < "2.13" should also be able to use 135 # gcc -mdll instead of dllwrap 136 # Older dllwraps had own version numbers, newer ones use the 137 # same as the rest of binutils ( also ld ) 138 # dllwrap 2.10.90 is buggy 139 if self.ld_version >= "2.10.90": 140 self.linker_dll = "gcc" 141 else: 142 self.linker_dll = "dllwrap" 143 144 # ld_version >= "2.13" support -shared so use it instead of 145 # -mdll -static 146 if self.ld_version >= "2.13": 147 shared_option = "-shared" 148 else: 149 shared_option = "-mdll -static" 150 151 # FIXME: 152 # Hard-code may override unix-compiler settings and isn't 153 # possible to use Makefile variables to pass correct flags ! 154 # Hard-code GCC because that's what this is all about. 155 # XXX optimization, warnings etc. should be customizable. 156 self.set_executables(compiler='gcc -mcygwin -O -Wall', 157 compiler_so='gcc -mcygwin -mdll -O -Wall', 158 compiler_cxx='g++ -mcygwin -O -Wall', 159 linker_exe='gcc -mcygwin', 160 linker_so=('%s -mcygwin %s' % 161 (self.linker_dll, shared_option))) 162 163 # cygwin and mingw32 need different sets of libraries 164 if self.gcc_version == "2.91.57": 165 # cygwin shouldn't need msvcrt, but without the dlls will crash 166 # (gcc version 2.91.57) -- perhaps something about initialization 167 self.dll_libraries=["msvcrt"] 168 self.warn( 169 "Consider upgrading to a newer version of gcc") 170 else: 171 # Include the appropriate MSVC runtime library if Python was built 172 # with MSVC 7.0 or later. 173 self.dll_libraries = get_msvcr() 174 175 # __init__ () 176 177 178 def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): 179 if ext == '.rc' or ext == '.res': 180 # gcc needs '.res' and '.rc' compiled to object files !!! 181 try: 182 self.spawn(["windres", "-i", src, "-o", obj]) 183 except DistutilsExecError, msg: 184 raise CompileError, msg 185 else: # for other files use the C-compiler 186 try: 187 self.spawn(self.compiler_so + cc_args + [src, '-o', obj] + 188 extra_postargs) 189 except DistutilsExecError, msg: 190 raise CompileError, msg 191 192 def link (self, 193 target_desc, 194 objects, 195 output_filename, 196 output_dir=None, 197 libraries=None, 198 library_dirs=None, 199 runtime_library_dirs=None, 200 export_symbols=None, 201 debug=0, 202 extra_preargs=None, 203 extra_postargs=None, 204 build_temp=None, 205 target_lang=None): 206 207 # use separate copies, so we can modify the lists 208 extra_preargs = copy.copy(extra_preargs or []) 209 libraries = copy.copy(libraries or []) 210 objects = copy.copy(objects or []) 211 212 # Additional libraries 213 libraries.extend(self.dll_libraries) 214 215 # handle export symbols by creating a def-file 216 # with executables this only works with gcc/ld as linker 217 if ((export_symbols is not None) and 218 (target_desc != self.EXECUTABLE or self.linker_dll == "gcc")): 219 # (The linker doesn't do anything if output is up-to-date. 220 # So it would probably better to check if we really need this, 221 # but for this we had to insert some unchanged parts of 222 # UnixCCompiler, and this is not what we want.) 223 224 # we want to put some files in the same directory as the 225 # object files are, build_temp doesn't help much 226 # where are the object files 227 temp_dir = os.path.dirname(objects[0]) 228 # name of dll to give the helper files the same base name 229 (dll_name, dll_extension) = os.path.splitext( 230 os.path.basename(output_filename)) 231 232 # generate the filenames for these files 233 def_file = os.path.join(temp_dir, dll_name + ".def") 234 lib_file = os.path.join(temp_dir, 'lib' + dll_name + ".a") 235 236 # Generate .def file 237 contents = [ 238 "LIBRARY %s" % os.path.basename(output_filename), 239 "EXPORTS"] 240 for sym in export_symbols: 241 contents.append(sym) 242 self.execute(write_file, (def_file, contents), 243 "writing %s" % def_file) 244 245 # next add options for def-file and to creating import libraries 246 247 # dllwrap uses different options than gcc/ld 248 if self.linker_dll == "dllwrap": 249 extra_preargs.extend(["--output-lib", lib_file]) 250 # for dllwrap we have to use a special option 251 extra_preargs.extend(["--def", def_file]) 252 # we use gcc/ld here and can be sure ld is >= 2.9.10 253 else: 254 # doesn't work: bfd_close build\...\libfoo.a: Invalid operation 255 #extra_preargs.extend(["-Wl,--out-implib,%s" % lib_file]) 256 # for gcc/ld the def-file is specified as any object files 257 objects.append(def_file) 258 259 #end: if ((export_symbols is not None) and 260 # (target_desc != self.EXECUTABLE or self.linker_dll == "gcc")): 261 262 # who wants symbols and a many times larger output file 263 # should explicitly switch the debug mode on 264 # otherwise we let dllwrap/ld strip the output file 265 # (On my machine: 10KB < stripped_file < ??100KB 266 # unstripped_file = stripped_file + XXX KB 267 # ( XXX=254 for a typical python extension)) 268 if not debug: 269 extra_preargs.append("-s") 270 271 UnixCCompiler.link(self, 272 target_desc, 273 objects, 274 output_filename, 275 output_dir, 276 libraries, 277 library_dirs, 278 runtime_library_dirs, 279 None, # export_symbols, we do this in our def-file 280 debug, 281 extra_preargs, 282 extra_postargs, 283 build_temp, 284 target_lang) 285 286 # link () 287 288 # -- Miscellaneous methods ----------------------------------------- 289 290 # overwrite the one from CCompiler to support rc and res-files 291 def object_filenames (self, 292 source_filenames, 293 strip_dir=0, 294 output_dir=''): 295 if output_dir is None: output_dir = '' 296 obj_names = [] 297 for src_name in source_filenames: 298 # FIXME: "bogus checks for suffix" - as example the commented 299 # by #BOGUS# code break valid assembler suffix ".S" ! 300 #BOGUS## use normcase to make sure '.rc' is really '.rc' and not '.RC' 301 #BOGUS#base, ext = os.path.splitext(os.path.normcase(src_name)) 302 base, ext = os.path.splitext (src_name) 303 ext_normcase = os.path.normcase(ext) 304 if ext_normcase in ['.rc','.res']: 305 ext = ext_normcase 306 if ext not in (self.src_extensions + ['.rc','.res']): 307 raise UnknownFileError, \ 308 "unknown file type '%s' (from '%s')" % \ 309 (ext, src_name) 310 base = os.path.splitdrive(base)[1] # Chop off the drive 311 base = base[os.path.isabs(base):] # If abs, chop off leading / 312 if strip_dir: 313 base = os.path.basename (base) 314 if ext == '.res' or ext == '.rc': 315 # these need to be compiled to object files 316 obj_names.append (os.path.join (output_dir, 317 base + ext + self.obj_extension)) 318 else: 319 obj_names.append (os.path.join (output_dir, 320 base + self.obj_extension)) 321 return obj_names 322 323 # object_filenames () 324 325# class CygwinCCompiler 326 327 328# the same as cygwin plus some additional parameters 329class Mingw32CCompiler (CygwinCCompiler): 330 331 compiler_type = 'mingw32' 332 333 def __init__ (self, 334 verbose=0, 335 dry_run=0, 336 force=0): 337 338 CygwinCCompiler.__init__ (self, verbose, dry_run, force) 339 340 # ld_version >= "2.13" support -shared so use it instead of 341 # -mdll -static 342 if self.ld_version >= "2.13": 343 shared_option = "-shared" 344 else: 345 shared_option = "-mdll -static" 346 347 # A real mingw32 doesn't need to specify a different entry point, 348 # but cygwin 2.91.57 in no-cygwin-mode needs it. 349 if self.gcc_version <= "2.91.57": 350 entry_point = '--entry _DllMain@12' 351 else: 352 entry_point = '' 353 354 self.set_executables(compiler='gcc -mno-cygwin -O -Wall', 355 compiler_so='gcc -mno-cygwin -mdll -O -Wall', 356 compiler_cxx='g++ -mno-cygwin -O -Wall', 357 linker_exe='gcc -mno-cygwin', 358 linker_so='%s -mno-cygwin %s %s' 359 % (self.linker_dll, shared_option, 360 entry_point)) 361 # Maybe we should also append -mthreads, but then the finished 362 # dlls need another dll (mingwm10.dll see Mingw32 docs) 363 # (-mthreads: Support thread-safe exception handling on `Mingw32') 364 365 # no additional libraries needed 366 self.dll_libraries=[] 367 368 # Include the appropriate MSVC runtime library if Python was built 369 # with MSVC 7.0 or later. 370 self.dll_libraries = get_msvcr() 371 372 # __init__ () 373 374# class Mingw32CCompiler 375 376# Because these compilers aren't configured in Python's pyconfig.h file by 377# default, we should at least warn the user if he is using a unmodified 378# version. 379 380CONFIG_H_OK = "ok" 381CONFIG_H_NOTOK = "not ok" 382CONFIG_H_UNCERTAIN = "uncertain" 383 384def check_config_h(): 385 386 """Check if the current Python installation (specifically, pyconfig.h) 387 appears amenable to building extensions with GCC. Returns a tuple 388 (status, details), where 'status' is one of the following constants: 389 CONFIG_H_OK 390 all is well, go ahead and compile 391 CONFIG_H_NOTOK 392 doesn't look good 393 CONFIG_H_UNCERTAIN 394 not sure -- unable to read pyconfig.h 395 'details' is a human-readable string explaining the situation. 396 397 Note there are two ways to conclude "OK": either 'sys.version' contains 398 the string "GCC" (implying that this Python was built with GCC), or the 399 installed "pyconfig.h" contains the string "__GNUC__". 400 """ 401 402 # XXX since this function also checks sys.version, it's not strictly a 403 # "pyconfig.h" check -- should probably be renamed... 404 405 from distutils import sysconfig 406 import string 407 # if sys.version contains GCC then python was compiled with 408 # GCC, and the pyconfig.h file should be OK 409 if string.find(sys.version,"GCC") >= 0: 410 return (CONFIG_H_OK, "sys.version mentions 'GCC'") 411 412 fn = sysconfig.get_config_h_filename() 413 try: 414 # It would probably better to read single lines to search. 415 # But we do this only once, and it is fast enough 416 f = open(fn) 417 try: 418 s = f.read() 419 finally: 420 f.close() 421 422 except IOError, exc: 423 # if we can't read this file, we cannot say it is wrong 424 # the compiler will complain later about this file as missing 425 return (CONFIG_H_UNCERTAIN, 426 "couldn't read '%s': %s" % (fn, exc.strerror)) 427 428 else: 429 # "pyconfig.h" contains an "#ifdef __GNUC__" or something similar 430 if string.find(s,"__GNUC__") >= 0: 431 return (CONFIG_H_OK, "'%s' mentions '__GNUC__'" % fn) 432 else: 433 return (CONFIG_H_NOTOK, "'%s' does not mention '__GNUC__'" % fn) 434 435 436 437def get_versions(): 438 """ Try to find out the versions of gcc, ld and dllwrap. 439 If not possible it returns None for it. 440 """ 441 from distutils.version import LooseVersion 442 from distutils.spawn import find_executable 443 import re 444 445 gcc_exe = os.environ.get('CC') or find_executable('gcc') 446 ld_exe = os.environ.get('LD') or find_executable('ld') 447 if gcc_exe: 448 out = os.popen(gcc_exe + ' -dumpversion','r') 449 out_string = out.read() 450 out.close() 451 result = re.search('(\d+\.\d+(\.\d+)*)',out_string) 452 if result: 453 gcc_version = LooseVersion(result.group(1)) 454 else: 455 gcc_version = None 456 out = os.popen(gcc_exe + ' --print-prog-name ld','r') 457 ld_exe = out.read().decode('ascii').split()[0] 458 out.close() 459 else: 460 gcc_version = None 461 if ld_exe: 462 out = os.popen(ld_exe + ' -v','r') 463 out_string = out.read() 464 out.close() 465 result = re.search('(\d+\.\d+(\.\d+)*)',out_string) 466 if result: 467 ld_version = LooseVersion(result.group(1)) 468 else: 469 ld_version = None 470 else: 471 ld_version = None 472 dllwrap_exe = os.environ.get('DLLWRAP') or find_executable('dllwrap') 473 if dllwrap_exe: 474 out = os.popen(dllwrap_exe + ' --version','r') 475 out_string = out.read() 476 out.close() 477 result = re.search(' (\d+\.\d+(\.\d+)*)',out_string) 478 if result: 479 dllwrap_version = LooseVersion(result.group(1)) 480 else: 481 dllwrap_version = None 482 else: 483 dllwrap_version = None 484 return (gcc_version, ld_version, dllwrap_version) 485