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