Variant of SF patch 614770: MSVC 7 support
distutils now looks for the compiler version in sys.version, falling back to MSVC 6 if the version isn't listed (Python 2.2 and lower). Add helper routines for reading the registry. Refactor many module functions into methods of the compiler to avoid passing lots of state as arguments.
This commit is contained in:
parent
bc06feae9c
commit
9ddf6c3803
|
@ -1,7 +1,8 @@
|
|||
"""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
|
||||
|
@ -12,7 +13,6 @@ for the Microsoft Visual Studio."""
|
|||
__revision__ = "$Id$"
|
||||
|
||||
import sys, os, string
|
||||
from types import *
|
||||
from distutils.errors import \
|
||||
DistutilsExecError, DistutilsPlatformError, \
|
||||
CompileError, LibError, LinkError
|
||||
|
@ -48,126 +48,116 @@ except ImportError:
|
|||
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 = []
|
||||
for base in (HKEY_CLASSES_ROOT,
|
||||
HKEY_LOCAL_MACHINE,
|
||||
HKEY_CURRENT_USER,
|
||||
HKEY_USERS):
|
||||
i = 0
|
||||
while 1:
|
||||
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:
|
||||
pass
|
||||
L.sort()
|
||||
L.reverse()
|
||||
break
|
||||
L.append(k)
|
||||
i = i + 1
|
||||
return L
|
||||
|
||||
# get_devstudio_versions ()
|
||||
def read_values(base, key):
|
||||
"""Return dict of registry keys and values.
|
||||
|
||||
|
||||
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 []
|
||||
|
||||
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:
|
||||
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:
|
||||
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
|
||||
return L
|
||||
return s
|
||||
|
||||
# get_msvc_paths()
|
||||
class MacroExpander:
|
||||
|
||||
def __init__(self, version):
|
||||
self.macros = {}
|
||||
self.load_macros(version)
|
||||
|
||||
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'."""
|
||||
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\%s.0" % 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")
|
||||
self.set_macro("FrameworkSDKDir", net, "sdkinstallroot")
|
||||
|
||||
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
|
||||
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"]
|
||||
|
||||
# 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
|
||||
def sub(self, s):
|
||||
for k, v in self.macros.items():
|
||||
s = string.replace(s, k, v)
|
||||
return s
|
||||
|
||||
return exe # last desperate hope
|
||||
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.
|
||||
"""
|
||||
|
||||
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,';')
|
||||
|
||||
prefix = "MSC v."
|
||||
i = string.find(sys.version, prefix)
|
||||
if i == -1:
|
||||
return 6
|
||||
i += len(prefix)
|
||||
s, rest = sys.version[i:].split(" ", 1)
|
||||
n = int(s[:-2])
|
||||
if n == 12:
|
||||
return 6
|
||||
elif n == 13:
|
||||
return 7
|
||||
# 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++,
|
||||
|
@ -199,39 +189,31 @@ class MSVCCompiler (CCompiler) :
|
|||
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)
|
||||
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:
|
||||
# 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")
|
||||
|
||||
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' ,
|
||||
|
@ -500,4 +482,68 @@ class MSVCCompiler (CCompiler) :
|
|||
|
||||
# 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\7.0\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories"
|
||||
% (self.__root,))
|
||||
else:
|
||||
key = (r"%s\6.0\Build System\Components\Platforms"
|
||||
|
||||
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, ';')
|
||||
|
||||
|
|
Loading…
Reference in New Issue