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 __revision__ = "$Id$"
     12 
     13 import sys
     14 import os
     15 import string
     16 
     17 from distutils.errors import (DistutilsExecError, DistutilsPlatformError,
     18                               CompileError, LibError, LinkError)
     19 from distutils.ccompiler import CCompiler, gen_lib_options
     20 from distutils import log
     21 
     22 _can_read_reg = 0
     23 try:
     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 
     34 except 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 
     53 if _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 
     59 def 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 
     77 def 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 
     98 def 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 
    107 class 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;
    134 extensions must be built with a compiler than can generate compatible binaries.
    135 Visual Studio 2003 was not found on this system. If you have Cygwin installed,
    136 you 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 
    153 def 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 
    176 def 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 
    189 def 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 
    204 class 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 
    654 if 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