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 DistutilsPlatformError 13from distutils.ccompiler import CCompiler 14from distutils import log 15 16_can_read_reg = False 17try: 18 import winreg 19 20 _can_read_reg = True 21 hkey_mod = winreg 22 23 RegOpenKeyEx = winreg.OpenKeyEx 24 RegEnumKey = winreg.EnumKey 25 RegEnumValue = winreg.EnumValue 26 RegError = winreg.error 27 28except ImportError: 29 try: 30 import win32api 31 import win32con 32 _can_read_reg = True 33 hkey_mod = win32con 34 35 RegOpenKeyEx = win32api.RegOpenKeyEx 36 RegEnumKey = win32api.RegEnumKey 37 RegEnumValue = win32api.RegEnumValue 38 RegError = win32api.error 39 except ImportError: 40 log.info("Warning: Can't read registry to find the " 41 "necessary compiler setting\n" 42 "Make sure that Python modules winreg, " 43 "win32api or win32con are installed.") 44 45if _can_read_reg: 46 HKEYS = (hkey_mod.HKEY_USERS, 47 hkey_mod.HKEY_CURRENT_USER, 48 hkey_mod.HKEY_LOCAL_MACHINE, 49 hkey_mod.HKEY_CLASSES_ROOT) 50 51def read_keys(base, key): 52 """Return list of registry keys.""" 53 try: 54 handle = RegOpenKeyEx(base, key) 55 except RegError: 56 return None 57 L = [] 58 i = 0 59 while True: 60 try: 61 k = RegEnumKey(handle, i) 62 except RegError: 63 break 64 L.append(k) 65 i += 1 66 return L 67 68def read_values(base, key): 69 """Return dict of registry keys and values. 70 71 All names are converted to lowercase. 72 """ 73 try: 74 handle = RegOpenKeyEx(base, key) 75 except RegError: 76 return None 77 d = {} 78 i = 0 79 while True: 80 try: 81 name, value, type = RegEnumValue(handle, i) 82 except RegError: 83 break 84 name = name.lower() 85 d[convert_mbcs(name)] = convert_mbcs(value) 86 i += 1 87 return d 88 89def convert_mbcs(s): 90 dec = getattr(s, "decode", None) 91 if dec is not None: 92 try: 93 s = dec("mbcs") 94 except UnicodeError: 95 pass 96 return s 97 98class MacroExpander: 99 def __init__(self, version): 100 self.macros = {} 101 self.load_macros(version) 102 103 def set_macro(self, macro, path, key): 104 for base in HKEYS: 105 d = read_values(base, path) 106 if d: 107 self.macros["$(%s)" % macro] = d[key] 108 break 109 110 def load_macros(self, version): 111 vsbase = r"Software\Microsoft\VisualStudio\%0.1f" % version 112 self.set_macro("VCInstallDir", vsbase + r"\Setup\VC", "productdir") 113 self.set_macro("VSInstallDir", vsbase + r"\Setup\VS", "productdir") 114 net = r"Software\Microsoft\.NETFramework" 115 self.set_macro("FrameworkDir", net, "installroot") 116 try: 117 if version > 7.0: 118 self.set_macro("FrameworkSDKDir", net, "sdkinstallrootv1.1") 119 else: 120 self.set_macro("FrameworkSDKDir", net, "sdkinstallroot") 121 except KeyError as exc: # 122 raise DistutilsPlatformError( 123 """Python was built with Visual Studio 2003; 124extensions must be built with a compiler than can generate compatible binaries. 125Visual Studio 2003 was not found on this system. If you have Cygwin installed, 126you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""") 127 128 p = r"Software\Microsoft\NET Framework Setup\Product" 129 for base in HKEYS: 130 try: 131 h = RegOpenKeyEx(base, p) 132 except RegError: 133 continue 134 key = RegEnumKey(h, 0) 135 d = read_values(base, r"%s\%s" % (p, key)) 136 self.macros["$(FrameworkVersion)"] = d["version"] 137 138 def sub(self, s): 139 for k, v in self.macros.items(): 140 s = s.replace(k, v) 141 return s 142 143def get_build_version(): 144 """Return the version of MSVC that was used to build Python. 145 146 For Python 2.3 and up, the version number is included in 147 sys.version. For earlier versions, assume the compiler is MSVC 6. 148 """ 149 prefix = "MSC v." 150 i = sys.version.find(prefix) 151 if i == -1: 152 return 6 153 i = i + len(prefix) 154 s, rest = sys.version[i:].split(" ", 1) 155 majorVersion = int(s[:-2]) - 6 156 if majorVersion >= 13: 157 # v13 was skipped and should be v14 158 majorVersion += 1 159 minorVersion = int(s[2:3]) / 10.0 160 # I don't think paths are affected by minor version in version 6 161 if majorVersion == 6: 162 minorVersion = 0 163 if majorVersion >= 6: 164 return majorVersion + minorVersion 165 # else we don't know what version of the compiler this is 166 return None 167 168def get_build_architecture(): 169 """Return the processor architecture. 170 171 Possible results are "Intel" or "AMD64". 172 """ 173 174 prefix = " bit (" 175 i = sys.version.find(prefix) 176 if i == -1: 177 return "Intel" 178 j = sys.version.find(")", i) 179 return sys.version[i+len(prefix):j] 180 181def normalize_and_reduce_paths(paths): 182 """Return a list of normalized paths with duplicates removed. 183 184 The current order of paths is maintained. 185 """ 186 # Paths are normalized so things like: /a and /a/ aren't both preserved. 187 reduced_paths = [] 188 for p in paths: 189 np = os.path.normpath(p) 190 # XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set. 191 if np not in reduced_paths: 192 reduced_paths.append(np) 193 return reduced_paths 194 195 196class MSVCCompiler(CCompiler) : 197 """Concrete class that implements an interface to Microsoft Visual C++, 198 as defined by the CCompiler abstract class.""" 199 200 compiler_type = 'msvc' 201 202 # Just set this so CCompiler's constructor doesn't barf. We currently 203 # don't use the 'set_executables()' bureaucracy provided by CCompiler, 204 # as it really isn't necessary for this sort of single-compiler class. 205 # Would be nice to have a consistent interface with UnixCCompiler, 206 # though, so it's worth thinking about. 207 executables = {} 208 209 # Private class data (need to distinguish C from C++ source for compiler) 210 _c_extensions = ['.c'] 211 _cpp_extensions = ['.cc', '.cpp', '.cxx'] 212 _rc_extensions = ['.rc'] 213 _mc_extensions = ['.mc'] 214 215 # Needed for the filename generation methods provided by the 216 # base class, CCompiler. 217 src_extensions = (_c_extensions + _cpp_extensions + 218 _rc_extensions + _mc_extensions) 219 res_extension = '.res' 220 obj_extension = '.obj' 221 static_lib_extension = '.lib' 222 shared_lib_extension = '.dll' 223 static_lib_format = shared_lib_format = '%s%s' 224 exe_extension = '.exe' 225 226 def __init__(self, verbose=0, dry_run=0, force=0): 227 CCompiler.__init__ (self, verbose, dry_run, force) 228 self.__version = get_build_version() 229 self.__arch = get_build_architecture() 230 if self.__arch == "Intel": 231 # x86 232 if self.__version >= 7: 233 self.__root = r"Software\Microsoft\VisualStudio" 234 self.__macros = MacroExpander(self.__version) 235 else: 236 self.__root = r"Software\Microsoft\Devstudio" 237 self.__product = "Visual Studio version %s" % self.__version 238 else: 239 # Win64. Assume this was built with the platform SDK 240 self.__product = "Microsoft SDK compiler %s" % (self.__version + 6) 241 242 self.initialized = False 243 244 245 # -- Miscellaneous methods ----------------------------------------- 246 247 # Helper methods for using the MSVC registry settings 248 249 def find_exe(self, exe): 250 """Return path to an MSVC executable program. 251 252 Tries to find the program in several places: first, one of the 253 MSVC program search paths from the registry; next, the directories 254 in the PATH environment variable. If any of those work, return an 255 absolute path that is known to exist. If none of them work, just 256 return the original program name, 'exe'. 257 """ 258 for p in self.__paths: 259 fn = os.path.join(os.path.abspath(p), exe) 260 if os.path.isfile(fn): 261 return fn 262 263 # didn't find it; try existing path 264 for p in os.environ['Path'].split(';'): 265 fn = os.path.join(os.path.abspath(p),exe) 266 if os.path.isfile(fn): 267 return fn 268 269 return exe 270 271 def get_msvc_paths(self, path, platform='x86'): 272 """Get a list of devstudio directories (include, lib or path). 273 274 Return a list of strings. The list will be empty if unable to 275 access the registry or appropriate registry keys not found. 276 """ 277 if not _can_read_reg: 278 return [] 279 280 path = path + " dirs" 281 if self.__version >= 7: 282 key = (r"%s\%0.1f\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories" 283 % (self.__root, self.__version)) 284 else: 285 key = (r"%s\6.0\Build System\Components\Platforms" 286 r"\Win32 (%s)\Directories" % (self.__root, platform)) 287 288 for base in HKEYS: 289 d = read_values(base, key) 290 if d: 291 if self.__version >= 7: 292 return self.__macros.sub(d[path]).split(";") 293 else: 294 return d[path].split(";") 295 # MSVC 6 seems to create the registry entries we need only when 296 # the GUI is run. 297 if self.__version == 6: 298 for base in HKEYS: 299 if read_values(base, r"%s\6.0" % self.__root) is not None: 300 self.warn("It seems you have Visual Studio 6 installed, " 301 "but the expected registry settings are not present.\n" 302 "You must at least run the Visual Studio GUI once " 303 "so that these entries are created.") 304 break 305 return [] 306 307 def set_path_env_var(self, name): 308 """Set environment variable 'name' to an MSVC path type value. 309 310 This is equivalent to a SET command prior to execution of spawned 311 commands. 312 """ 313 314 if name == "lib": 315 p = self.get_msvc_paths("library") 316 else: 317 p = self.get_msvc_paths(name) 318 if p: 319 os.environ[name] = ';'.join(p) 320 321 322if get_build_version() >= 8.0: 323 log.debug("Importing new compiler from distutils.msvc9compiler") 324 OldMSVCCompiler = MSVCCompiler 325 from distutils.msvc9compiler import MSVCCompiler 326 # get_build_architecture not really relevant now we support cross-compile 327 from distutils.msvc9compiler import MacroExpander 328