blob: 39a10b277c0504e393e5082ced19e4a90638dd4b [file] [log] [blame]
Tarek Ziade1231a4e2011-05-19 13:07:25 +02001"""CCompiler implementation for old Microsoft Visual Studio compilers.
2
3For a compiler compatible with VS 2005 and 2008, use msvc9compiler.
4"""
5
6# Written by Perry Stoll
7# hacked by Robin Becker and Thomas Heller to do a better job of
8# finding DevStudio (through the registry)
9
10
11import sys
12import os
13
14from packaging.errors import (PackagingExecError, PackagingPlatformError,
15 CompileError, LibError, LinkError)
16from packaging.compiler.ccompiler import CCompiler
17from packaging.compiler import gen_lib_options
18from packaging import logger
19
20_can_read_reg = False
21try:
22 import winreg
23
24 _can_read_reg = True
25 hkey_mod = winreg
26
27 RegOpenKeyEx = winreg.OpenKeyEx
28 RegEnumKey = winreg.EnumKey
29 RegEnumValue = winreg.EnumValue
30 RegError = winreg.error
31
32except ImportError:
33 try:
34 import win32api
35 import win32con
36 _can_read_reg = True
37 hkey_mod = win32con
38
39 RegOpenKeyEx = win32api.RegOpenKeyEx
40 RegEnumKey = win32api.RegEnumKey
41 RegEnumValue = win32api.RegEnumValue
42 RegError = win32api.error
43
44 except ImportError:
45 logger.warning(
46 "can't read registry to find the necessary compiler setting;\n"
47 "make sure that Python modules _winreg, win32api or win32con "
48 "are installed.")
49
50if _can_read_reg:
51 HKEYS = (hkey_mod.HKEY_USERS,
52 hkey_mod.HKEY_CURRENT_USER,
53 hkey_mod.HKEY_LOCAL_MACHINE,
54 hkey_mod.HKEY_CLASSES_ROOT)
55
56
57def read_keys(base, key):
58 """Return list of registry keys."""
59
60 try:
61 handle = RegOpenKeyEx(base, key)
62 except RegError:
63 return None
64 L = []
65 i = 0
66 while True:
67 try:
68 k = RegEnumKey(handle, i)
69 except RegError:
70 break
71 L.append(k)
72 i = i + 1
73 return L
74
75
76def read_values(base, key):
77 """Return dict of registry keys and values.
78
79 All names are converted to lowercase.
80 """
81 try:
82 handle = RegOpenKeyEx(base, key)
83 except RegError:
84 return None
85 d = {}
86 i = 0
87 while True:
88 try:
89 name, value, type = RegEnumValue(handle, i)
90 except RegError:
91 break
92 name = name.lower()
93 d[convert_mbcs(name)] = convert_mbcs(value)
94 i = i + 1
95 return d
96
97
98def convert_mbcs(s):
99 enc = getattr(s, "encode", None)
100 if enc is not None:
101 try:
102 s = enc("mbcs")
103 except UnicodeError:
104 pass
105 return s
106
107
108class MacroExpander:
109
110 def __init__(self, version):
111 self.macros = {}
112 self.load_macros(version)
113
114 def set_macro(self, macro, path, key):
115 for base in HKEYS:
116 d = read_values(base, path)
117 if d:
118 self.macros["$(%s)" % macro] = d[key]
119 break
120
121 def load_macros(self, version):
122 vsbase = r"Software\Microsoft\VisualStudio\%0.1f" % version
123 self.set_macro("VCInstallDir", vsbase + r"\Setup\VC", "productdir")
124 self.set_macro("VSInstallDir", vsbase + r"\Setup\VS", "productdir")
125 net = r"Software\Microsoft\.NETFramework"
126 self.set_macro("FrameworkDir", net, "installroot")
127 try:
128 if version > 7.0:
129 self.set_macro("FrameworkSDKDir", net, "sdkinstallrootv1.1")
130 else:
131 self.set_macro("FrameworkSDKDir", net, "sdkinstallroot")
132 except KeyError:
133 raise PackagingPlatformError(
134"""Python was built with Visual Studio 2003; extensions must be built with
135a compiler than can generate compatible binaries. Visual Studio 2003 was
136not found on this system. If you have Cygwin installed, you can try
Éric Araujob6be20c2011-06-16 23:34:55 +0200137compiling with MingW32, by passing "-c mingw32" to pysetup.""")
Tarek Ziade1231a4e2011-05-19 13:07:25 +0200138
139 p = r"Software\Microsoft\NET Framework Setup\Product"
140 for base in HKEYS:
141 try:
142 h = RegOpenKeyEx(base, p)
143 except RegError:
144 continue
145 key = RegEnumKey(h, 0)
146 d = read_values(base, r"%s\%s" % (p, key))
147 self.macros["$(FrameworkVersion)"] = d["version"]
148
149 def sub(self, s):
150 for k, v in self.macros.items():
151 s = s.replace(k, v)
152 return s
153
154
155def get_build_version():
156 """Return the version of MSVC that was used to build Python.
157
158 For Python 2.3 and up, the version number is included in
159 sys.version. For earlier versions, assume the compiler is MSVC 6.
160 """
161
162 prefix = "MSC v."
163 i = sys.version.find(prefix)
164 if i == -1:
165 return 6
166 i = i + len(prefix)
167 s, rest = sys.version[i:].split(" ", 1)
168 majorVersion = int(s[:-2]) - 6
169 minorVersion = int(s[2:3]) / 10.0
170 # I don't think paths are affected by minor version in version 6
171 if majorVersion == 6:
172 minorVersion = 0
173 if majorVersion >= 6:
174 return majorVersion + minorVersion
175 # else we don't know what version of the compiler this is
176 return None
177
178
179def get_build_architecture():
180 """Return the processor architecture.
181
182 Possible results are "Intel", "Itanium", or "AMD64".
183 """
184
185 prefix = " bit ("
186 i = sys.version.find(prefix)
187 if i == -1:
188 return "Intel"
189 j = sys.version.find(")", i)
190 return sys.version[i+len(prefix):j]
191
192
193def normalize_and_reduce_paths(paths):
194 """Return a list of normalized paths with duplicates removed.
195
196 The current order of paths is maintained.
197 """
198 # Paths are normalized so things like: /a and /a/ aren't both preserved.
199 reduced_paths = []
200 for p in paths:
201 np = os.path.normpath(p)
202 # XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set.
203 if np not in reduced_paths:
204 reduced_paths.append(np)
205 return reduced_paths
206
207
208class MSVCCompiler(CCompiler):
209 """Concrete class that implements an interface to Microsoft Visual C++,
210 as defined by the CCompiler abstract class."""
211
212 name = 'msvc'
213 description = "Microsoft Visual C++"
214
215 # Just set this so CCompiler's constructor doesn't barf. We currently
216 # don't use the 'set_executables()' bureaucracy provided by CCompiler,
217 # as it really isn't necessary for this sort of single-compiler class.
218 # Would be nice to have a consistent interface with UnixCCompiler,
219 # though, so it's worth thinking about.
220 executables = {}
221
222 # Private class data (need to distinguish C from C++ source for compiler)
223 _c_extensions = ['.c']
224 _cpp_extensions = ['.cc', '.cpp', '.cxx']
225 _rc_extensions = ['.rc']
226 _mc_extensions = ['.mc']
227
228 # Needed for the filename generation methods provided by the
229 # base class, CCompiler.
230 src_extensions = (_c_extensions + _cpp_extensions +
231 _rc_extensions + _mc_extensions)
232 res_extension = '.res'
233 obj_extension = '.obj'
234 static_lib_extension = '.lib'
235 shared_lib_extension = '.dll'
236 static_lib_format = shared_lib_format = '%s%s'
237 exe_extension = '.exe'
238
Éric Araujo4d155462011-11-15 11:43:20 +0100239 def __init__(self, dry_run=False, force=False):
240 super(MSVCCompiler, self).__init__(dry_run, force)
Tarek Ziade1231a4e2011-05-19 13:07:25 +0200241 self.__version = get_build_version()
242 self.__arch = get_build_architecture()
243 if self.__arch == "Intel":
244 # x86
245 if self.__version >= 7:
246 self.__root = r"Software\Microsoft\VisualStudio"
247 self.__macros = MacroExpander(self.__version)
248 else:
249 self.__root = r"Software\Microsoft\Devstudio"
250 self.__product = "Visual Studio version %s" % self.__version
251 else:
252 # Win64. Assume this was built with the platform SDK
253 self.__product = "Microsoft SDK compiler %s" % (self.__version + 6)
254
255 self.initialized = False
256
257 def initialize(self):
258 self.__paths = []
259 if ("DISTUTILS_USE_SDK" in os.environ and "MSSdk" in os.environ and
260 self.find_exe("cl.exe")):
261 # Assume that the SDK set up everything alright; don't try to be
262 # smarter
263 self.cc = "cl.exe"
264 self.linker = "link.exe"
265 self.lib = "lib.exe"
266 self.rc = "rc.exe"
267 self.mc = "mc.exe"
268 else:
269 self.__paths = self.get_msvc_paths("path")
270
271 if len(self.__paths) == 0:
272 raise PackagingPlatformError("Python was built with %s "
273 "and extensions need to be built with the same "
274 "version of the compiler, but it isn't installed." %
275 self.__product)
276
277 self.cc = self.find_exe("cl.exe")
278 self.linker = self.find_exe("link.exe")
279 self.lib = self.find_exe("lib.exe")
280 self.rc = self.find_exe("rc.exe") # resource compiler
281 self.mc = self.find_exe("mc.exe") # message compiler
282 self.set_path_env_var('lib')
283 self.set_path_env_var('include')
284
285 # extend the MSVC path with the current path
286 try:
287 for p in os.environ['path'].split(';'):
288 self.__paths.append(p)
289 except KeyError:
290 pass
291 self.__paths = normalize_and_reduce_paths(self.__paths)
292 os.environ['path'] = ';'.join(self.__paths)
293
294 self.preprocess_options = None
295 if self.__arch == "Intel":
296 self.compile_options = ['/nologo', '/Ox', '/MD', '/W3', '/GX',
297 '/DNDEBUG']
298 self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GX',
299 '/Z7', '/D_DEBUG']
300 else:
301 # Win64
302 self.compile_options = ['/nologo', '/Ox', '/MD', '/W3', '/GS-',
303 '/DNDEBUG']
304 self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GS-',
305 '/Z7', '/D_DEBUG']
306
307 self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO']
308 if self.__version >= 7:
309 self.ldflags_shared_debug = [
310 '/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG'
311 ]
312 else:
313 self.ldflags_shared_debug = [
314 '/DLL', '/nologo', '/INCREMENTAL:no', '/pdb:None', '/DEBUG'
315 ]
316 self.ldflags_static = [ '/nologo']
317
318 self.initialized = True
319
320 # -- Worker methods ------------------------------------------------
321
322 def object_filenames(self, source_filenames, strip_dir=False, output_dir=''):
323 # Copied from ccompiler.py, extended to return .res as 'object'-file
324 # for .rc input file
325 if output_dir is None:
326 output_dir = ''
327 obj_names = []
328 for src_name in source_filenames:
329 base, ext = os.path.splitext(src_name)
330 base = os.path.splitdrive(base)[1] # Chop off the drive
331 base = base[os.path.isabs(base):] # If abs, chop off leading /
332 if ext not in self.src_extensions:
333 # Better to raise an exception instead of silently continuing
334 # and later complain about sources and targets having
335 # different lengths
336 raise CompileError("Don't know how to compile %s" % src_name)
337 if strip_dir:
338 base = os.path.basename(base)
339 if ext in self._rc_extensions:
340 obj_names.append(os.path.join(output_dir,
341 base + self.res_extension))
342 elif ext in self._mc_extensions:
343 obj_names.append(os.path.join(output_dir,
344 base + self.res_extension))
345 else:
346 obj_names.append(os.path.join(output_dir,
347 base + self.obj_extension))
348 return obj_names
349
350 def compile(self, sources,
351 output_dir=None, macros=None, include_dirs=None, debug=False,
352 extra_preargs=None, extra_postargs=None, depends=None):
353
354 if not self.initialized:
355 self.initialize()
356 macros, objects, extra_postargs, pp_opts, build = \
357 self._setup_compile(output_dir, macros, include_dirs, sources,
358 depends, extra_postargs)
359
360 compile_opts = extra_preargs or []
361 compile_opts.append('/c')
362 if debug:
363 compile_opts.extend(self.compile_options_debug)
364 else:
365 compile_opts.extend(self.compile_options)
366
367 for obj in objects:
368 try:
369 src, ext = build[obj]
370 except KeyError:
371 continue
372 if debug:
373 # pass the full pathname to MSVC in debug mode,
374 # this allows the debugger to find the source file
375 # without asking the user to browse for it
376 src = os.path.abspath(src)
377
378 if ext in self._c_extensions:
379 input_opt = "/Tc" + src
380 elif ext in self._cpp_extensions:
381 input_opt = "/Tp" + src
382 elif ext in self._rc_extensions:
383 # compile .RC to .RES file
384 input_opt = src
385 output_opt = "/fo" + obj
386 try:
387 self.spawn([self.rc] + pp_opts +
388 [output_opt] + [input_opt])
389 except PackagingExecError as msg:
390 raise CompileError(msg)
391 continue
392 elif ext in self._mc_extensions:
393
394 # Compile .MC to .RC file to .RES file.
395 # * '-h dir' specifies the directory for the
396 # generated include file
397 # * '-r dir' specifies the target directory of the
398 # generated RC file and the binary message resource
399 # it includes
400 #
401 # For now (since there are no options to change this),
402 # we use the source-directory for the include file and
403 # the build directory for the RC file and message
404 # resources. This works at least for win32all.
405
406 h_dir = os.path.dirname(src)
407 rc_dir = os.path.dirname(obj)
408 try:
409 # first compile .MC to .RC and .H file
410 self.spawn([self.mc] +
411 ['-h', h_dir, '-r', rc_dir] + [src])
412 base, _ = os.path.splitext(os.path.basename(src))
413 rc_file = os.path.join(rc_dir, base + '.rc')
414 # then compile .RC to .RES file
415 self.spawn([self.rc] +
416 ["/fo" + obj] + [rc_file])
417
418 except PackagingExecError as msg:
419 raise CompileError(msg)
420 continue
421 else:
422 # how to handle this file?
423 raise CompileError(
424 "Don't know how to compile %s to %s" %
425 (src, obj))
426
427 output_opt = "/Fo" + obj
428 try:
429 self.spawn([self.cc] + compile_opts + pp_opts +
430 [input_opt, output_opt] +
431 extra_postargs)
432 except PackagingExecError as msg:
433 raise CompileError(msg)
434
435 return objects
436
437 def create_static_lib(self, objects, output_libname, output_dir=None,
438 debug=False, target_lang=None):
439 if not self.initialized:
440 self.initialize()
441 objects, output_dir = self._fix_object_args(objects, output_dir)
442 output_filename = \
443 self.library_filename(output_libname, output_dir=output_dir)
444
445 if self._need_link(objects, output_filename):
446 lib_args = objects + ['/OUT:' + output_filename]
447 if debug:
448 pass # XXX what goes here?
449 try:
450 self.spawn([self.lib] + lib_args)
451 except PackagingExecError as msg:
452 raise LibError(msg)
453
454 else:
455 logger.debug("skipping %s (up-to-date)", output_filename)
456
457 def link(self, target_desc, objects, output_filename, output_dir=None,
458 libraries=None, library_dirs=None, runtime_library_dirs=None,
459 export_symbols=None, debug=False, extra_preargs=None,
460 extra_postargs=None, build_temp=None, target_lang=None):
461
462 if not self.initialized:
463 self.initialize()
464 objects, output_dir = self._fix_object_args(objects, output_dir)
465 libraries, library_dirs, runtime_library_dirs = \
466 self._fix_lib_args(libraries, library_dirs, runtime_library_dirs)
467
468 if runtime_library_dirs:
469 self.warn("don't know what to do with 'runtime_library_dirs': %s"
470 % (runtime_library_dirs,))
471
472 lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs,
473 libraries)
474 if output_dir is not None:
475 output_filename = os.path.join(output_dir, output_filename)
476
477 if self._need_link(objects, output_filename):
478
479 if target_desc == CCompiler.EXECUTABLE:
480 if debug:
481 ldflags = self.ldflags_shared_debug[1:]
482 else:
483 ldflags = self.ldflags_shared[1:]
484 else:
485 if debug:
486 ldflags = self.ldflags_shared_debug
487 else:
488 ldflags = self.ldflags_shared
489
490 export_opts = []
491 for sym in (export_symbols or []):
492 export_opts.append("/EXPORT:" + sym)
493
494 ld_args = (ldflags + lib_opts + export_opts +
495 objects + ['/OUT:' + output_filename])
496
497 # The MSVC linker generates .lib and .exp files, which cannot be
498 # suppressed by any linker switches. The .lib files may even be
499 # needed! Make sure they are generated in the temporary build
500 # directory. Since they have different names for debug and release
501 # builds, they can go into the same directory.
502 if export_symbols is not None:
503 dll_name, dll_ext = os.path.splitext(
504 os.path.basename(output_filename))
505 implib_file = os.path.join(
506 os.path.dirname(objects[0]),
507 self.library_filename(dll_name))
508 ld_args.append('/IMPLIB:' + implib_file)
509
510 if extra_preargs:
511 ld_args[:0] = extra_preargs
512 if extra_postargs:
513 ld_args.extend(extra_postargs)
514
515 self.mkpath(os.path.dirname(output_filename))
516 try:
517 self.spawn([self.linker] + ld_args)
518 except PackagingExecError as msg:
519 raise LinkError(msg)
520
521 else:
522 logger.debug("skipping %s (up-to-date)", output_filename)
523
524 # -- Miscellaneous methods -----------------------------------------
525 # These are all used by the 'gen_lib_options() function, in
526 # ccompiler.py.
527
528 def library_dir_option(self, dir):
529 return "/LIBPATH:" + dir
530
531 def runtime_library_dir_option(self, dir):
532 raise PackagingPlatformError("don't know how to set runtime library search path for MSVC++")
533
534 def library_option(self, lib):
535 return self.library_filename(lib)
536
537 def find_library_file(self, dirs, lib, debug=False):
538 # Prefer a debugging library if found (and requested), but deal
539 # with it if we don't have one.
540 if debug:
541 try_names = [lib + "_d", lib]
542 else:
543 try_names = [lib]
544 for dir in dirs:
545 for name in try_names:
546 libfile = os.path.join(dir, self.library_filename(name))
547 if os.path.exists(libfile):
548 return libfile
549 else:
550 # Oops, didn't find it in *any* of 'dirs'
551 return None
552
553 # Helper methods for using the MSVC registry settings
554
555 def find_exe(self, exe):
556 """Return path to an MSVC executable program.
557
558 Tries to find the program in several places: first, one of the
559 MSVC program search paths from the registry; next, the directories
560 in the PATH environment variable. If any of those work, return an
561 absolute path that is known to exist. If none of them work, just
562 return the original program name, 'exe'.
563 """
564
565 for p in self.__paths:
566 fn = os.path.join(os.path.abspath(p), exe)
567 if os.path.isfile(fn):
568 return fn
569
570 # didn't find it; try existing path
571 for p in os.environ['Path'].split(';'):
572 fn = os.path.join(os.path.abspath(p), exe)
573 if os.path.isfile(fn):
574 return fn
575
576 return exe
577
578 def get_msvc_paths(self, path, platform='x86'):
579 """Get a list of devstudio directories (include, lib or path).
580
581 Return a list of strings. The list will be empty if unable to
582 access the registry or appropriate registry keys not found.
583 """
584
585 if not _can_read_reg:
586 return []
587
588 path = path + " dirs"
589 if self.__version >= 7:
590 key = (r"%s\%0.1f\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories"
591 % (self.__root, self.__version))
592 else:
593 key = (r"%s\6.0\Build System\Components\Platforms"
594 r"\Win32 (%s)\Directories" % (self.__root, platform))
595
596 for base in HKEYS:
597 d = read_values(base, key)
598 if d:
599 if self.__version >= 7:
600 return self.__macros.sub(d[path]).split(";")
601 else:
602 return d[path].split(";")
603 # MSVC 6 seems to create the registry entries we need only when
604 # the GUI is run.
605 if self.__version == 6:
606 for base in HKEYS:
607 if read_values(base, r"%s\6.0" % self.__root) is not None:
608 self.warn("It seems you have Visual Studio 6 installed, "
609 "but the expected registry settings are not present.\n"
610 "You must at least run the Visual Studio GUI once "
611 "so that these entries are created.")
612 break
613 return []
614
615 def set_path_env_var(self, name):
616 """Set environment variable 'name' to an MSVC path type value.
617
618 This is equivalent to a SET command prior to execution of spawned
619 commands.
620 """
621
622 if name == "lib":
623 p = self.get_msvc_paths("library")
624 else:
625 p = self.get_msvc_paths(name)
626 if p:
627 os.environ[name] = ';'.join(p)
628
629
630if get_build_version() >= 8.0:
631 logger.debug("importing new compiler from distutils.msvc9compiler")
632 OldMSVCCompiler = MSVCCompiler
633 from packaging.compiler.msvc9compiler import MSVCCompiler
634 # get_build_architecture not really relevant now we support cross-compile
635 from packaging.compiler.msvc9compiler import MacroExpander