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