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