]> git.saurik.com Git - wxWidgets.git/blobdiff - wxPython/distutils/msvccompiler.py
Don't accidentally instal the MSVCRT.dll from XP as it wont' work on
[wxWidgets.git] / wxPython / distutils / msvccompiler.py
index e07a6d5a0ca77942ce1814ce9c7e003936d36333..5f3d8de87232c5a3c75b0776fe2aebcd4751a19c 100644 (file)
@@ -1,7 +1,8 @@
 """distutils.msvccompiler
 
 Contains MSVCCompiler, an implementation of the abstract CCompiler class
 """distutils.msvccompiler
 
 Contains MSVCCompiler, an implementation of the abstract CCompiler class
-for the Microsoft Visual Studio."""
+for the Microsoft Visual Studio.
+"""
 
 # Written by Perry Stoll
 # hacked by Robin Becker and Thomas Heller to do a better job of
 
 # Written by Perry Stoll
 # hacked by Robin Becker and Thomas Heller to do a better job of
@@ -12,7 +13,6 @@ for the Microsoft Visual Studio."""
 __revision__ = "$Id$"
 
 import sys, os, string
 __revision__ = "$Id$"
 
 import sys, os, string
-from types import *
 from distutils.errors import \
      DistutilsExecError, DistutilsPlatformError, \
      CompileError, LibError, LinkError
 from distutils.errors import \
      DistutilsExecError, DistutilsPlatformError, \
      CompileError, LibError, LinkError
@@ -45,129 +45,128 @@ except ImportError:
         RegError = win32api.error
 
     except ImportError:
         RegError = win32api.error
 
     except ImportError:
+        log.info("Warning: Can't read registry to find the "
+                 "necessary compiler setting\n"
+                 "Make sure that Python modules _winreg, "
+                 "win32api or win32con are installed.")
         pass
 
 if _can_read_reg:
         pass
 
 if _can_read_reg:
-    HKEY_CLASSES_ROOT = hkey_mod.HKEY_CLASSES_ROOT
-    HKEY_LOCAL_MACHINE = hkey_mod.HKEY_LOCAL_MACHINE
-    HKEY_CURRENT_USER = hkey_mod.HKEY_CURRENT_USER
-    HKEY_USERS = hkey_mod.HKEY_USERS
+    HKEYS = (hkey_mod.HKEY_USERS,
+             hkey_mod.HKEY_CURRENT_USER,
+             hkey_mod.HKEY_LOCAL_MACHINE,
+             hkey_mod.HKEY_CLASSES_ROOT)
 
 
+def read_keys(base, key):
+    """Return list of registry keys."""
 
 
-
-def get_devstudio_versions ():
-    """Get list of devstudio versions from the Windows registry.  Return a
-       list of strings containing version numbers; the list will be
-       empty if we were unable to access the registry (eg. couldn't import
-       a registry-access module) or the appropriate registry keys weren't
-       found."""
-
-    if not _can_read_reg:
-        return []
-
-    K = 'Software\\Microsoft\\Devstudio'
+    try:
+        handle = RegOpenKeyEx(base, key)
+    except RegError:
+        return None
     L = []
     L = []
-    for base in (HKEY_CLASSES_ROOT,
-                 HKEY_LOCAL_MACHINE,
-                 HKEY_CURRENT_USER,
-                 HKEY_USERS):
+    i = 0
+    while 1:
         try:
         try:
-            k = RegOpenKeyEx(base,K)
-            i = 0
-            while 1:
-                try:
-                    p = RegEnumKey(k,i)
-                    if p[0] in '123456789' and p not in L:
-                        L.append(p)
-                except RegError:
-                    break
-                i = i + 1
+            k = RegEnumKey(handle, i)
         except RegError:
         except RegError:
-            pass
-    L.sort()
-    L.reverse()
+            break
+        L.append(k)
+        i = i + 1
     return L
 
     return L
 
-# get_devstudio_versions ()
-
-
-def get_msvc_paths (path, version='6.0', platform='x86'):
-    """Get a list of devstudio directories (include, lib or path).  Return
-       a list of strings; will be empty list if unable to access the
-       registry or appropriate registry keys not found."""
-
-    if not _can_read_reg:
-        return []
+def read_values(base, key):
+    """Return dict of registry keys and values.
 
 
-    L = []
-    if path=='lib':
-        path= 'Library'
-    path = string.upper(path + ' Dirs')
-    K = ('Software\\Microsoft\\Devstudio\\%s\\' +
-         'Build System\\Components\\Platforms\\Win32 (%s)\\Directories') % \
-        (version,platform)
-    for base in (HKEY_CLASSES_ROOT,
-                 HKEY_LOCAL_MACHINE,
-                 HKEY_CURRENT_USER,
-                 HKEY_USERS):
+    All names are converted to lowercase.
+    """
+    try:
+        handle = RegOpenKeyEx(base, key)
+    except RegError:
+        return None
+    d = {}
+    i = 0
+    while 1:
         try:
         try:
-            k = RegOpenKeyEx(base,K)
-            i = 0
-            while 1:
-                try:
-                    (p,v,t) = RegEnumValue(k,i)
-                    if string.upper(p) == path:
-                        V = string.split(v,';')
-                        for v in V:
-                            if hasattr(v, "encode"):
-                                try:
-                                    v = v.encode("mbcs")
-                                except UnicodeError:
-                                    pass
-                            if v == '' or v in L: continue
-                            L.append(v)
-                        break
-                    i = i + 1
-                except RegError:
-                    break
+            name, value, type = RegEnumValue(handle, i)
         except RegError:
         except RegError:
+            break
+        name = name.lower()
+        d[convert_mbcs(name)] = convert_mbcs(value)
+        i = i + 1
+    return d
+
+def convert_mbcs(s):
+    enc = getattr(s, "encode", None)
+    if enc is not None:
+        try:
+            s = enc("mbcs")
+        except UnicodeError:
             pass
             pass
-    return L
-
-# get_msvc_paths()
-
-
-def find_exe (exe, version_number):
-    """Try to find an MSVC executable program 'exe' (from version
-       'version_number' of MSVC) in several places: first, one of the MSVC
-       program search paths from the registry; next, the directories in the
-       PATH environment variable.  If any of those work, return an absolute
-       path that is known to exist.  If none of them work, just return the
-       original program name, 'exe'."""
-
-    for p in get_msvc_paths ('path', version_number):
-        fn = os.path.join (os.path.abspath(p), exe)
-        if os.path.isfile(fn):
-            return fn
-
-    # didn't find it; try existing path
-    for p in string.split (os.environ['Path'],';'):
-        fn = os.path.join(os.path.abspath(p),exe)
-        if os.path.isfile(fn):
-            return fn
-
-    return exe                          # last desperate hope
-
-
-def set_path_env_var (name, version_number):
-    """Set environment variable 'name' to an MSVC path type value obtained
-       from 'get_msvc_paths()'.  This is equivalent to a SET command prior
-       to execution of spawned commands."""
-
-    p = get_msvc_paths (name, version_number)
-    if p:
-        os.environ[name] = string.join (p,';')
+    return s
+
+class MacroExpander:
+
+    def __init__(self, version):
+        self.macros = {}
+        self.load_macros(version)
+
+    def set_macro(self, macro, path, key):
+        for base in HKEYS:
+            d = read_values(base, path)
+            if d:
+                self.macros["$(%s)" % macro] = d[key]
+                break
+              
+    def load_macros(self, version):
+        vsbase = r"Software\Microsoft\VisualStudio\%0.1f" % version
+        self.set_macro("VCInstallDir", vsbase + r"\Setup\VC", "productdir")
+        self.set_macro("VSInstallDir", vsbase + r"\Setup\VS", "productdir")
+        net = r"Software\Microsoft\.NETFramework"
+        self.set_macro("FrameworkDir", net, "installroot")
+        if version > 7.0:
+            self.set_macro("FrameworkSDKDir", net, "sdkinstallrootv1.1")
+        else:
+            self.set_macro("FrameworkSDKDir", net, "sdkinstallroot")
 
 
+        p = r"Software\Microsoft\NET Framework Setup\Product"
+        for base in HKEYS:
+            try:
+                h = RegOpenKeyEx(base, p)
+            except RegError:
+                continue
+            key = RegEnumKey(h, 0)
+            d = read_values(base, r"%s\%s" % (p, key))
+            self.macros["$(FrameworkVersion)"] = d["version"]
+
+    def sub(self, s):
+        for k, v in self.macros.items():
+            s = string.replace(s, k, v)
+        return s
+
+def get_build_version():
+    """Return the version of MSVC that was used to build Python.
+
+    For Python 2.3 and up, the version number is included in
+    sys.version.  For earlier versions, assume the compiler is MSVC 6.
+    """
+
+    prefix = "MSC v."
+    i = string.find(sys.version, prefix)
+    if i == -1:
+        return 6
+    i = i + len(prefix)
+    s, rest = sys.version[i:].split(" ", 1)
+    majorVersion = int(s[:-2]) - 6
+    minorVersion = int(s[2:3]) / 10.0
+    # I don't think paths are affected by minor version in version 6
+    if majorVersion == 6:
+        minorVersion = 0
+    if majorVersion >= 6:
+        return majorVersion + minorVersion
+    # else we don't know what version of the compiler this is
+    return None
+    
 
 class MSVCCompiler (CCompiler) :
     """Concrete class that implements an interface to Microsoft Visual C++,
 
 class MSVCCompiler (CCompiler) :
     """Concrete class that implements an interface to Microsoft Visual C++,
@@ -199,39 +198,37 @@ class MSVCCompiler (CCompiler) :
     static_lib_format = shared_lib_format = '%s%s'
     exe_extension = '.exe'
 
     static_lib_format = shared_lib_format = '%s%s'
     exe_extension = '.exe'
 
-
-    def __init__ (self,
-                  verbose=0,
-                  dry_run=0,
-                  force=0):
-
+    def __init__ (self, verbose=0, dry_run=0, force=0):
         CCompiler.__init__ (self, verbose, dry_run, force)
         CCompiler.__init__ (self, verbose, dry_run, force)
-        versions = get_devstudio_versions ()
-
-        if versions:
-            version = versions[0]  # highest version
-
-            self.cc   = find_exe("cl.exe", version)
-            self.linker = find_exe("link.exe", version)
-            self.lib  = find_exe("lib.exe", version)
-            self.rc   = find_exe("rc.exe", version)     # resource compiler
-            self.mc   = find_exe("mc.exe", version)     # message compiler
-            set_path_env_var ('lib', version)
-            set_path_env_var ('include', version)
-            path=get_msvc_paths('path', version)
-            try:
-                for p in string.split(os.environ['path'],';'):
-                    path.append(p)
-            except KeyError:
-                pass
-            os.environ['path'] = string.join(path,';')
+        self.__version = get_build_version()
+        if self.__version >= 7:
+            self.__root = r"Software\Microsoft\VisualStudio"
+            self.__macros = MacroExpander(self.__version)
         else:
         else:
-            # devstudio not found in the registry
-            self.cc = "cl.exe"
-            self.linker = "link.exe"
-            self.lib = "lib.exe"
-            self.rc = "rc.exe"
-            self.mc = "mc.exe"
+            self.__root = r"Software\Microsoft\Devstudio"
+        self.__paths = self.get_msvc_paths("path")
+
+        if len (self.__paths) == 0:
+            raise DistutilsPlatformError, \
+                  ("Python was built with version %s of Visual Studio, "
+                   "and extensions need to be built with the same "
+                   "version of the compiler, but it isn't installed." % self.__version)
+
+        self.cc = self.find_exe("cl.exe")
+        self.linker = self.find_exe("link.exe")
+        self.lib = self.find_exe("lib.exe")
+        self.rc = self.find_exe("rc.exe")   # resource compiler
+        self.mc = self.find_exe("mc.exe")   # message compiler
+        self.set_path_env_var('lib')
+        self.set_path_env_var('include')
+
+        # extend the MSVC path with the current path
+        try:
+            for p in string.split(os.environ['path'], ';'):
+                self.__paths.append(p)
+        except KeyError:
+            pass
+        os.environ['path'] = string.join(self.__paths, ';')
 
         self.preprocess_options = None
         self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GX' ,
 
         self.preprocess_options = None
         self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GX' ,
@@ -500,4 +497,69 @@ class MSVCCompiler (CCompiler) :
 
     # find_library_file ()
 
 
     # find_library_file ()
 
-# class MSVCCompiler
+    # Helper methods for using the MSVC registry settings
+
+    def find_exe(self, exe):
+        """Return path to an MSVC executable program.
+
+        Tries to find the program in several places: first, one of the
+        MSVC program search paths from the registry; next, the directories
+        in the PATH environment variable.  If any of those work, return an
+        absolute path that is known to exist.  If none of them work, just
+        return the original program name, 'exe'.
+        """
+
+        for p in self.__paths:
+            fn = os.path.join(os.path.abspath(p), exe)
+            if os.path.isfile(fn):
+                return fn
+
+        # didn't find it; try existing path
+        for p in string.split(os.environ['Path'],';'):
+            fn = os.path.join(os.path.abspath(p),exe)
+            if os.path.isfile(fn):
+                return fn
+
+        return exe
+    
+    def get_msvc_paths(self, path, platform='x86'):
+        """Get a list of devstudio directories (include, lib or path).
+
+        Return a list of strings.  The list will be empty if unable to
+        access the registry or appropriate registry keys not found.
+        """
+
+        if not _can_read_reg:
+            return []
+
+        path = path + " dirs"
+        if self.__version >= 7:
+            key = (r"%s\%0.1f\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories"
+                   % (self.__root, self.__version))
+        else:
+            key = (r"%s\6.0\Build System\Components\Platforms"
+                   r"\Win32 (%s)\Directories" % (self.__root, platform))
+
+        for base in HKEYS:
+            d = read_values(base, key)
+            if d:
+                if self.__version >= 7:
+                    return string.split(self.__macros.sub(d[path]), ";")
+                else:
+                    return string.split(d[path], ";")
+        return []
+
+    def set_path_env_var(self, name):
+        """Set environment variable 'name' to an MSVC path type value.
+
+        This is equivalent to a SET command prior to execution of spawned
+        commands.
+        """
+
+        if name == "lib":
+            p = self.get_msvc_paths("library")
+        else:
+            p = self.get_msvc_paths(name)
+        if p:
+            os.environ[name] = string.join(p, ';')
+