1 """distutils.cygwinccompiler
3 Provides the CygwinCCompiler class, a subclass of UnixCCompiler that
4 handles the Cygwin port of the GNU C compiler to Windows. It also contains
5 the Mingw32CCompiler class which handles the mingw32 port of GCC (same as
6 cygwin in no-cygwin mode).
15 from subprocess import check_output
17 from .unixccompiler import UnixCCompiler
18 from .file_util import write_file
21 DistutilsPlatformError,
25 from .version import LooseVersion, suppress_known_deprecation
26 from ._collections import RangeMap
29 _msvcr_lookup = RangeMap.left(
46 1900: ['ucrt', 'vcruntime140'],
47 2000: RangeMap.undefined_value,
53 """Include the appropriate MSVC runtime library if Python was built
54 with MSVC 7.0 or later.
56 match = re.search(r'MSC v\.(\d{4})', sys.version)
58 msc_ver = int(match.group(1))
59 except AttributeError:
62 return _msvcr_lookup[msc_ver]
64 raise ValueError("Unknown MS Compiler version %s " % msc_ver)
67 _runtime_library_dirs_msg = (
68 "Unable to set runtime library search path on Windows, "
69 "usually indicated by `runtime_library_dirs` parameter to Extension"
73 class CygwinCCompiler(UnixCCompiler):
74 """Handles the Cygwin port of the GNU C compiler to Windows."""
76 compiler_type = 'cygwin'
78 static_lib_extension = ".a"
79 shared_lib_extension = ".dll.a"
80 dylib_lib_extension = ".dll"
81 static_lib_format = "lib%s%s"
82 shared_lib_format = "lib%s%s"
83 dylib_lib_format = "cyg%s%s"
84 exe_extension = ".exe"
86 def __init__(self, verbose=0, dry_run=0, force=0):
88 super().__init__(verbose, dry_run, force)
90 status, details = check_config_h()
92 "Python's GCC status: {} (details: {})".format(status, details)
94 if status is not CONFIG_H_OK:
96 "Python's pyconfig.h doesn't seem to support your compiler. "
98 "Compiling may fail because of undefined preprocessor macros." % details
101 self.cc = os.environ.get('CC', 'gcc')
102 self.cxx = os.environ.get('CXX', 'g++')
104 self.linker_dll = self.cc
105 shared_option = "-shared"
107 self.set_executables(
108 compiler='%s -mcygwin -O -Wall' % self.cc,
109 compiler_so='%s -mcygwin -mdll -O -Wall' % self.cc,
110 compiler_cxx='%s -mcygwin -O -Wall' % self.cxx,
111 linker_exe='%s -mcygwin' % self.cc,
112 linker_so=('{} -mcygwin {}'.format(self.linker_dll, shared_option)),
115 # Include the appropriate MSVC runtime library if Python was built
116 # with MSVC 7.0 or later.
117 self.dll_libraries = get_msvcr()
120 def gcc_version(self):
121 # Older numpy dependend on this existing to check for ancient
122 # gcc versions. This doesn't make much sense with clang etc so
123 # just hardcode to something recent.
124 # https://github.com/numpy/numpy/pull/20333
126 "gcc_version attribute of CygwinCCompiler is deprecated. "
127 "Instead of returning actual gcc version a fixed value 11.2.0 is returned.",
131 with suppress_known_deprecation():
132 return LooseVersion("11.2.0")
134 def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
135 """Compiles the source by spawning GCC and windres if needed."""
136 if ext == '.rc' or ext == '.res':
137 # gcc needs '.res' and '.rc' compiled to object files !!!
139 self.spawn(["windres", "-i", src, "-o", obj])
140 except DistutilsExecError as msg:
141 raise CompileError(msg)
142 else: # for other files use the C-compiler
145 self.compiler_so + cc_args + [src, '-o', obj] + extra_postargs
147 except DistutilsExecError as msg:
148 raise CompileError(msg)
158 runtime_library_dirs=None,
166 """Link the objects."""
167 # use separate copies, so we can modify the lists
168 extra_preargs = copy.copy(extra_preargs or [])
169 libraries = copy.copy(libraries or [])
170 objects = copy.copy(objects or [])
172 if runtime_library_dirs:
173 self.warn(_runtime_library_dirs_msg)
175 # Additional libraries
176 libraries.extend(self.dll_libraries)
178 # handle export symbols by creating a def-file
179 # with executables this only works with gcc/ld as linker
180 if (export_symbols is not None) and (
181 target_desc != self.EXECUTABLE or self.linker_dll == "gcc"
183 # (The linker doesn't do anything if output is up-to-date.
184 # So it would probably better to check if we really need this,
185 # but for this we had to insert some unchanged parts of
186 # UnixCCompiler, and this is not what we want.)
188 # we want to put some files in the same directory as the
189 # object files are, build_temp doesn't help much
190 # where are the object files
191 temp_dir = os.path.dirname(objects[0])
192 # name of dll to give the helper files the same base name
193 (dll_name, dll_extension) = os.path.splitext(
194 os.path.basename(output_filename)
197 # generate the filenames for these files
198 def_file = os.path.join(temp_dir, dll_name + ".def")
201 contents = ["LIBRARY %s" % os.path.basename(output_filename), "EXPORTS"]
202 for sym in export_symbols:
204 self.execute(write_file, (def_file, contents), "writing %s" % def_file)
206 # next add options for def-file
208 # for gcc/ld the def-file is specified as any object files
209 objects.append(def_file)
211 # end: if ((export_symbols is not None) and
212 # (target_desc != self.EXECUTABLE or self.linker_dll == "gcc")):
214 # who wants symbols and a many times larger output file
215 # should explicitly switch the debug mode on
216 # otherwise we let ld strip the output file
217 # (On my machine: 10KiB < stripped_file < ??100KiB
218 # unstripped_file = stripped_file + XXX KiB
219 # ( XXX=254 for a typical python extension))
221 extra_preargs.append("-s")
231 runtime_library_dirs,
232 None, # export_symbols, we do this in our def-file
240 def runtime_library_dir_option(self, dir):
241 # cygwin doesn't support rpath. While in theory we could error
242 # out like MSVC does, code might expect it to work like on Unix, so
243 # just warn and hope for the best.
244 self.warn(_runtime_library_dirs_msg)
247 # -- Miscellaneous methods -----------------------------------------
249 def _make_out_path(self, output_dir, strip_dir, src_name):
250 # use normcase to make sure '.rc' is really '.rc' and not '.RC'
251 norm_src_name = os.path.normcase(src_name)
252 return super()._make_out_path(output_dir, strip_dir, norm_src_name)
255 def out_extensions(self):
257 Add support for rc and res files.
260 **super().out_extensions,
261 **{ext: ext + self.obj_extension for ext in ('.res', '.rc')},
265 # the same as cygwin plus some additional parameters
266 class Mingw32CCompiler(CygwinCCompiler):
267 """Handles the Mingw32 port of the GNU C compiler to Windows."""
269 compiler_type = 'mingw32'
271 def __init__(self, verbose=0, dry_run=0, force=0):
273 super().__init__(verbose, dry_run, force)
275 shared_option = "-shared"
277 if is_cygwincc(self.cc):
278 raise CCompilerError('Cygwin gcc cannot be used with --compiler=mingw32')
280 self.set_executables(
281 compiler='%s -O -Wall' % self.cc,
282 compiler_so='%s -mdll -O -Wall' % self.cc,
283 compiler_cxx='%s -O -Wall' % self.cxx,
284 linker_exe='%s' % self.cc,
285 linker_so='{} {}'.format(self.linker_dll, shared_option),
288 def runtime_library_dir_option(self, dir):
289 raise DistutilsPlatformError(_runtime_library_dirs_msg)
292 # Because these compilers aren't configured in Python's pyconfig.h file by
293 # default, we should at least warn the user if he is using an unmodified
297 CONFIG_H_NOTOK = "not ok"
298 CONFIG_H_UNCERTAIN = "uncertain"
301 def check_config_h():
302 """Check if the current Python installation appears amenable to building
305 Returns a tuple (status, details), where 'status' is one of the following
308 - CONFIG_H_OK: all is well, go ahead and compile
309 - CONFIG_H_NOTOK: doesn't look good
310 - CONFIG_H_UNCERTAIN: not sure -- unable to read pyconfig.h
312 'details' is a human-readable string explaining the situation.
314 Note there are two ways to conclude "OK": either 'sys.version' contains
315 the string "GCC" (implying that this Python was built with GCC), or the
316 installed "pyconfig.h" contains the string "__GNUC__".
319 # XXX since this function also checks sys.version, it's not strictly a
320 # "pyconfig.h" check -- should probably be renamed...
322 from distutils import sysconfig
324 # if sys.version contains GCC then python was compiled with GCC, and the
325 # pyconfig.h file should be OK
326 if "GCC" in sys.version:
327 return CONFIG_H_OK, "sys.version mentions 'GCC'"
329 # Clang would also work
330 if "Clang" in sys.version:
331 return CONFIG_H_OK, "sys.version mentions 'Clang'"
333 # let's see if __GNUC__ is mentioned in python.h
334 fn = sysconfig.get_config_h_filename()
338 if "__GNUC__" in config_h.read():
339 return CONFIG_H_OK, "'%s' mentions '__GNUC__'" % fn
341 return CONFIG_H_NOTOK, "'%s' does not mention '__GNUC__'" % fn
344 except OSError as exc:
345 return (CONFIG_H_UNCERTAIN, "couldn't read '{}': {}".format(fn, exc.strerror))
349 '''Try to determine if the compiler that would be used is from cygwin.'''
350 out_string = check_output(shlex.split(cc) + ['-dumpmachine'])
351 return out_string.strip().endswith(b'cygwin')
356 A stand-in for the previous get_versions() function to prevent failures
357 when monkeypatched. See pypa/setuptools#2969.