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