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.