Home | History | Annotate | Download | only in distutils
      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 import sys, os
     12 from distutils.errors import \
     13      DistutilsExecError, DistutilsPlatformError, \
     14      CompileError, LibError, LinkError
     15 from distutils.ccompiler import \
     16      CCompiler, gen_preprocess_options, gen_lib_options
     17 from distutils import log
     18 
     19 _can_read_reg = False
     20 try:
     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 
     31 except 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 
     49 if _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 
     55 def 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 
     72 def 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 
     93 def 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 
    102 class 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;
    128 extensions must be built with a compiler than can generate compatible binaries.
    129 Visual Studio 2003 was not found on this system. If you have Cygwin installed,
    130 you 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 
    147 def 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 
    172 def 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 
    185 def 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 
    200 class 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 
    638 if 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