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 an import library for its dll 14# - create a def-file for python??.dll 15# - create an 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 48import os 49import sys 50import copy 51from subprocess import Popen, PIPE, check_output 52import re 53 54from distutils.unixccompiler import UnixCCompiler 55from distutils.file_util import write_file 56from distutils.errors import (DistutilsExecError, CCompilerError, 57 CompileError, UnknownFileError) 58from distutils.version import LooseVersion 59from distutils.spawn import find_executable 60 61def get_msvcr(): 62 """Include the appropriate MSVC runtime library if Python was built 63 with MSVC 7.0 or later. 64 """ 65 msc_pos = sys.version.find('MSC v.') 66 if msc_pos != -1: 67 msc_ver = sys.version[msc_pos+6:msc_pos+10] 68 if msc_ver == '1300': 69 # MSVC 7.0 70 return ['msvcr70'] 71 elif msc_ver == '1310': 72 # MSVC 7.1 73 return ['msvcr71'] 74 elif msc_ver == '1400': 75 # VS2005 / MSVC 8.0 76 return ['msvcr80'] 77 elif msc_ver == '1500': 78 # VS2008 / MSVC 9.0 79 return ['msvcr90'] 80 elif msc_ver == '1600': 81 # VS2010 / MSVC 10.0 82 return ['msvcr100'] 83 else: 84 raise ValueError("Unknown MS Compiler version %s " % msc_ver) 85 86 87class CygwinCCompiler(UnixCCompiler): 88 """ Handles the Cygwin port of the GNU C compiler to Windows. 89 """ 90 compiler_type = 'cygwin' 91 obj_extension = ".o" 92 static_lib_extension = ".a" 93 shared_lib_extension = ".dll" 94 static_lib_format = "lib%s%s" 95 shared_lib_format = "%s%s" 96 exe_extension = ".exe" 97 98 def __init__(self, verbose=0, dry_run=0, force=0): 99 100 UnixCCompiler.__init__(self, verbose, dry_run, force) 101 102 status, details = check_config_h() 103 self.debug_print("Python's GCC status: %s (details: %s)" % 104 (status, details)) 105 if status is not CONFIG_H_OK: 106 self.warn( 107 "Python's pyconfig.h doesn't seem to support your compiler. " 108 "Reason: %s. " 109 "Compiling may fail because of undefined preprocessor macros." 110 % details) 111 112 self.gcc_version, self.ld_version, self.dllwrap_version = \ 113 get_versions() 114 self.debug_print(self.compiler_type + ": gcc %s, ld %s, dllwrap %s\n" % 115 (self.gcc_version, 116 self.ld_version, 117 self.dllwrap_version) ) 118 119 # ld_version >= "2.10.90" and < "2.13" should also be able to use 120 # gcc -mdll instead of dllwrap 121 # Older dllwraps had own version numbers, newer ones use the 122 # same as the rest of binutils ( also ld ) 123 # dllwrap 2.10.90 is buggy 124 if self.ld_version >= "2.10.90": 125 self.linker_dll = "gcc" 126 else: 127 self.linker_dll = "dllwrap" 128 129 # ld_version >= "2.13" support -shared so use it instead of 130 # -mdll -static 131 if self.ld_version >= "2.13": 132 shared_option = "-shared" 133 else: 134 shared_option = "-mdll -static" 135 136 # Hard-code GCC because that's what this is all about. 137 # XXX optimization, warnings etc. should be customizable. 138 self.set_executables(compiler='gcc -mcygwin -O -Wall', 139 compiler_so='gcc -mcygwin -mdll -O -Wall', 140 compiler_cxx='g++ -mcygwin -O -Wall', 141 linker_exe='gcc -mcygwin', 142 linker_so=('%s -mcygwin %s' % 143 (self.linker_dll, shared_option))) 144 145 # cygwin and mingw32 need different sets of libraries 146 if self.gcc_version == "2.91.57": 147 # cygwin shouldn't need msvcrt, but without the dlls will crash 148 # (gcc version 2.91.57) -- perhaps something about initialization 149 self.dll_libraries=["msvcrt"] 150 self.warn( 151 "Consider upgrading to a newer version of gcc") 152 else: 153 # Include the appropriate MSVC runtime library if Python was built 154 # with MSVC 7.0 or later. 155 self.dll_libraries = get_msvcr() 156 157 def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): 158 """Compiles the source by spawning GCC and windres if needed.""" 159 if ext == '.rc' or ext == '.res': 160 # gcc needs '.res' and '.rc' compiled to object files !!! 161 try: 162 self.spawn(["windres", "-i", src, "-o", obj]) 163 except DistutilsExecError as msg: 164 raise CompileError(msg) 165 else: # for other files use the C-compiler 166 try: 167 self.spawn(self.compiler_so + cc_args + [src, '-o', obj] + 168 extra_postargs) 169 except DistutilsExecError as msg: 170 raise CompileError(msg) 171 172 def link(self, target_desc, objects, output_filename, output_dir=None, 173 libraries=None, library_dirs=None, runtime_library_dirs=None, 174 export_symbols=None, debug=0, extra_preargs=None, 175 extra_postargs=None, build_temp=None, target_lang=None): 176 """Link the objects.""" 177 # use separate copies, so we can modify the lists 178 extra_preargs = copy.copy(extra_preargs or []) 179 libraries = copy.copy(libraries or []) 180 objects = copy.copy(objects or []) 181 182 # Additional libraries 183 libraries.extend(self.dll_libraries) 184 185 # handle export symbols by creating a def-file 186 # with executables this only works with gcc/ld as linker 187 if ((export_symbols is not None) and 188 (target_desc != self.EXECUTABLE or self.linker_dll == "gcc")): 189 # (The linker doesn't do anything if output is up-to-date. 190 # So it would probably better to check if we really need this, 191 # but for this we had to insert some unchanged parts of 192 # UnixCCompiler, and this is not what we want.) 193 194 # we want to put some files in the same directory as the 195 # object files are, build_temp doesn't help much 196 # where are the object files 197 temp_dir = os.path.dirname(objects[0]) 198 # name of dll to give the helper files the same base name 199 (dll_name, dll_extension) = os.path.splitext( 200 os.path.basename(output_filename)) 201 202 # generate the filenames for these files 203 def_file = os.path.join(temp_dir, dll_name + ".def") 204 lib_file = os.path.join(temp_dir, 'lib' + dll_name + ".a") 205 206 # Generate .def file 207 contents = [ 208 "LIBRARY %s" % os.path.basename(output_filename), 209 "EXPORTS"] 210 for sym in export_symbols: 211 contents.append(sym) 212 self.execute(write_file, (def_file, contents), 213 "writing %s" % def_file) 214 215 # next add options for def-file and to creating import libraries 216 217 # dllwrap uses different options than gcc/ld 218 if self.linker_dll == "dllwrap": 219 extra_preargs.extend(["--output-lib", lib_file]) 220 # for dllwrap we have to use a special option 221 extra_preargs.extend(["--def", def_file]) 222 # we use gcc/ld here and can be sure ld is >= 2.9.10 223 else: 224 # doesn't work: bfd_close build\...\libfoo.a: Invalid operation 225 #extra_preargs.extend(["-Wl,--out-implib,%s" % lib_file]) 226 # for gcc/ld the def-file is specified as any object files 227 objects.append(def_file) 228 229 #end: if ((export_symbols is not None) and 230 # (target_desc != self.EXECUTABLE or self.linker_dll == "gcc")): 231 232 # who wants symbols and a many times larger output file 233 # should explicitly switch the debug mode on 234 # otherwise we let dllwrap/ld strip the output file 235 # (On my machine: 10KiB < stripped_file < ??100KiB 236 # unstripped_file = stripped_file + XXX KiB 237 # ( XXX=254 for a typical python extension)) 238 if not debug: 239 extra_preargs.append("-s") 240 241 UnixCCompiler.link(self, target_desc, objects, output_filename, 242 output_dir, libraries, library_dirs, 243 runtime_library_dirs, 244 None, # export_symbols, we do this in our def-file 245 debug, extra_preargs, extra_postargs, build_temp, 246 target_lang) 247 248 # -- Miscellaneous methods ----------------------------------------- 249 250 def object_filenames(self, source_filenames, strip_dir=0, output_dir=''): 251 """Adds supports for rc and res files.""" 252 if output_dir is None: 253 output_dir = '' 254 obj_names = [] 255 for src_name in source_filenames: 256 # use normcase to make sure '.rc' is really '.rc' and not '.RC' 257 base, ext = os.path.splitext(os.path.normcase(src_name)) 258 if ext not in (self.src_extensions + ['.rc','.res']): 259 raise UnknownFileError("unknown file type '%s' (from '%s')" % \ 260 (ext, src_name)) 261 if strip_dir: 262 base = os.path.basename (base) 263 if ext in ('.res', '.rc'): 264 # these need to be compiled to object files 265 obj_names.append (os.path.join(output_dir, 266 base + ext + self.obj_extension)) 267 else: 268 obj_names.append (os.path.join(output_dir, 269 base + self.obj_extension)) 270 return obj_names 271 272# the same as cygwin plus some additional parameters 273class Mingw32CCompiler(CygwinCCompiler): 274 """ Handles the Mingw32 port of the GNU C compiler to Windows. 275 """ 276 compiler_type = 'mingw32' 277 278 def __init__(self, verbose=0, dry_run=0, force=0): 279 280 CygwinCCompiler.__init__ (self, verbose, dry_run, force) 281 282 # ld_version >= "2.13" support -shared so use it instead of 283 # -mdll -static 284 if self.ld_version >= "2.13": 285 shared_option = "-shared" 286 else: 287 shared_option = "-mdll -static" 288 289 # A real mingw32 doesn't need to specify a different entry point, 290 # but cygwin 2.91.57 in no-cygwin-mode needs it. 291 if self.gcc_version <= "2.91.57": 292 entry_point = '--entry _DllMain@12' 293 else: 294 entry_point = '' 295 296 if is_cygwingcc(): 297 raise CCompilerError( 298 'Cygwin gcc cannot be used with --compiler=mingw32') 299 300 self.set_executables(compiler='gcc -O -Wall', 301 compiler_so='gcc -mdll -O -Wall', 302 compiler_cxx='g++ -O -Wall', 303 linker_exe='gcc', 304 linker_so='%s %s %s' 305 % (self.linker_dll, shared_option, 306 entry_point)) 307 # Maybe we should also append -mthreads, but then the finished 308 # dlls need another dll (mingwm10.dll see Mingw32 docs) 309 # (-mthreads: Support thread-safe exception handling on `Mingw32') 310 311 # no additional libraries needed 312 self.dll_libraries=[] 313 314 # Include the appropriate MSVC runtime library if Python was built 315 # with MSVC 7.0 or later. 316 self.dll_libraries = get_msvcr() 317 318# Because these compilers aren't configured in Python's pyconfig.h file by 319# default, we should at least warn the user if he is using an unmodified 320# version. 321 322CONFIG_H_OK = "ok" 323CONFIG_H_NOTOK = "not ok" 324CONFIG_H_UNCERTAIN = "uncertain" 325 326def check_config_h(): 327 """Check if the current Python installation appears amenable to building 328 extensions with GCC. 329 330 Returns a tuple (status, details), where 'status' is one of the following 331 constants: 332 333 - CONFIG_H_OK: all is well, go ahead and compile 334 - CONFIG_H_NOTOK: doesn't look good 335 - CONFIG_H_UNCERTAIN: not sure -- unable to read pyconfig.h 336 337 'details' is a human-readable string explaining the situation. 338 339 Note there are two ways to conclude "OK": either 'sys.version' contains 340 the string "GCC" (implying that this Python was built with GCC), or the 341 installed "pyconfig.h" contains the string "__GNUC__". 342 """ 343 344 # XXX since this function also checks sys.version, it's not strictly a 345 # "pyconfig.h" check -- should probably be renamed... 346 347 from distutils import sysconfig 348 349 # if sys.version contains GCC then python was compiled with GCC, and the 350 # pyconfig.h file should be OK 351 if "GCC" in sys.version: 352 return CONFIG_H_OK, "sys.version mentions 'GCC'" 353 354 # let's see if __GNUC__ is mentioned in python.h 355 fn = sysconfig.get_config_h_filename() 356 try: 357 config_h = open(fn) 358 try: 359 if "__GNUC__" in config_h.read(): 360 return CONFIG_H_OK, "'%s' mentions '__GNUC__'" % fn 361 else: 362 return CONFIG_H_NOTOK, "'%s' does not mention '__GNUC__'" % fn 363 finally: 364 config_h.close() 365 except OSError as exc: 366 return (CONFIG_H_UNCERTAIN, 367 "couldn't read '%s': %s" % (fn, exc.strerror)) 368 369RE_VERSION = re.compile(br'(\d+\.\d+(\.\d+)*)') 370 371def _find_exe_version(cmd): 372 """Find the version of an executable by running `cmd` in the shell. 373 374 If the command is not found, or the output does not match 375 `RE_VERSION`, returns None. 376 """ 377 executable = cmd.split()[0] 378 if find_executable(executable) is None: 379 return None 380 out = Popen(cmd, shell=True, stdout=PIPE).stdout 381 try: 382 out_string = out.read() 383 finally: 384 out.close() 385 result = RE_VERSION.search(out_string) 386 if result is None: 387 return None 388 # LooseVersion works with strings 389 # so we need to decode our bytes 390 return LooseVersion(result.group(1).decode()) 391 392def get_versions(): 393 """ Try to find out the versions of gcc, ld and dllwrap. 394 395 If not possible it returns None for it. 396 """ 397 commands = ['gcc -dumpversion', 'ld -v', 'dllwrap --version'] 398 return tuple([_find_exe_version(cmd) for cmd in commands]) 399 400def is_cygwingcc(): 401 '''Try to determine if the gcc that would be used is from cygwin.''' 402 out_string = check_output(['gcc', '-dumpmachine']) 403 return out_string.strip().endswith(b'cygwin') 404