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