"""distutils.msvccompiler
Contains MSVCCompiler, an implementation of the abstract CCompiler class
for the Microsoft Visual Studio.
# hacked by Robin Becker and Thomas Heller to do a better job of
# finding DevStudio (through the registry)
# This module should be kept compatible with Python 2.1.
__revision__
= "$Id: msvccompiler.py,v 1.64.2.4 2005/08/07 20:50:37 loewis Exp $"
from distutils
.errors
import \
DistutilsExecError
, DistutilsPlatformError
, \
CompileError
, LibError
, LinkError
from distutils
.ccompiler
import \
CCompiler
, gen_preprocess_options
, gen_lib_options
from distutils
import log
RegOpenKeyEx
= _winreg
.OpenKeyEx
RegEnumKey
= _winreg
.EnumKey
RegEnumValue
= _winreg
.EnumValue
RegOpenKeyEx
= win32api
.RegOpenKeyEx
RegEnumKey
= win32api
.RegEnumKey
RegEnumValue
= win32api
.RegEnumValue
RegError
= win32api
.error
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.")
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."""
handle
= RegOpenKeyEx(base
, key
)
k
= RegEnumKey(handle
, i
)
def read_values(base
, key
):
"""Return dict of registry keys and values.
All names are converted to lowercase.
handle
= RegOpenKeyEx(base
, key
)
name
, value
, type = RegEnumValue(handle
, i
)
d
[convert_mbcs(name
)] = convert_mbcs(value
)
enc
= getattr(s
, "encode", None)
def __init__(self
, version
):
self
.load_macros(version
)
def set_macro(self
, macro
, path
, key
):
d
= read_values(base
, path
)
self
.macros
["$(%s)" % macro
] = d
[key
]
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")
self
.set_macro("FrameworkSDKDir", net
, "sdkinstallrootv1.1")
self
.set_macro("FrameworkSDKDir", net
, "sdkinstallroot")
raise DistutilsPlatformError
, \
("The .NET Framework SDK needs to be installed before "
"building extensions for Python.")
p
= r
"Software\Microsoft\NET Framework Setup\Product"
h
= RegOpenKeyEx(base
, p
)
d
= read_values(base
, r
"%s\%s" % (p
, key
))
self
.macros
["$(FrameworkVersion)"] = d
["version"]
for k
, v
in self
.macros
.items():
s
= string
.replace(s
, k
, v
)
"""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.
i
= string
.find(sys
.version
, 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
return majorVersion
+ minorVersion
# else we don't know what version of the compiler this is
class MSVCCompiler (CCompiler
) :
"""Concrete class that implements an interface to Microsoft Visual C++,
as defined by the CCompiler abstract class."""
# Just set this so CCompiler's constructor doesn't barf. We currently
# don't use the 'set_executables()' bureaucracy provided by CCompiler,
# as it really isn't necessary for this sort of single-compiler class.
# Would be nice to have a consistent interface with UnixCCompiler,
# though, so it's worth thinking about.
# Private class data (need to distinguish C from C++ source for compiler)
_cpp_extensions
= ['.cc', '.cpp', '.cxx']
# Needed for the filename generation methods provided by the
src_extensions
= (_c_extensions
+ _cpp_extensions
+
_rc_extensions
+ _mc_extensions
)
static_lib_extension
= '.lib'
shared_lib_extension
= '.dll'
static_lib_format
= shared_lib_format
= '%s%s'
def __init__ (self
, verbose
=0, dry_run
=0, force
=0):
CCompiler
.__init
__ (self
, verbose
, dry_run
, force
)
self
.__version
= get_build_version()
self
.__root
= r
"Software\Microsoft\VisualStudio"
self
.__macros
= MacroExpander(self
.__version
)
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
for p
in string
.split(os
.environ
['path'], ';'):
os
.environ
['path'] = string
.join(self
.__paths
, ';')
self
.preprocess_options
= None
self
.compile_options
= [ '/nologo', '/Ox', '/MD', '/W3', '/GX' ,
self
.compile_options_debug
= ['/nologo', '/Od', '/MDd', '/W3', '/GX',
self
.ldflags_shared
= ['/DLL', '/nologo', '/INCREMENTAL:NO']
self
.ldflags_shared_debug
= [
'/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG'
self
.ldflags_shared_debug
= [
'/DLL', '/nologo', '/INCREMENTAL:no', '/pdb:None', '/DEBUG'
self
.ldflags_static
= [ '/nologo']
# -- Worker methods ------------------------------------------------
def object_filenames (self
,
# Copied from ccompiler.py, extended to return .res as 'object'-file
if output_dir
is None: output_dir
= ''
for src_name
in source_filenames
:
(base
, ext
) = os
.path
.splitext (src_name
)
base
= os
.path
.splitdrive(base
)[1] # Chop off the drive
base
= base
[os
.path
.isabs(base
):] # If abs, chop off leading /
if ext
not in self
.src_extensions
:
# Better to raise an exception instead of silently continuing
# and later complain about sources and targets having
raise CompileError ("Don't know how to compile %s" % src_name
)
base
= os
.path
.basename (base
)
if ext
in self
._rc
_extensions
:
obj_names
.append (os
.path
.join (output_dir
,
base
+ self
.res_extension
))
elif ext
in self
._mc
_extensions
:
obj_names
.append (os
.path
.join (output_dir
,
base
+ self
.res_extension
))
obj_names
.append (os
.path
.join (output_dir
,
base
+ self
.obj_extension
))
def compile(self
, sources
,
output_dir
=None, macros
=None, include_dirs
=None, debug
=0,
extra_preargs
=None, extra_postargs
=None, depends
=None):
if not self
.initialized
: self
.initialize()
macros
, objects
, extra_postargs
, pp_opts
, build
= \
self
._setup
_compile
(output_dir
, macros
, include_dirs
, sources
,
compile_opts
= extra_preargs
or []
compile_opts
.append ('/c')
compile_opts
.extend(self
.compile_options_debug
)
compile_opts
.extend(self
.compile_options
)
# pass the full pathname to MSVC in debug mode,
# this allows the debugger to find the source file
# without asking the user to browse for it
src
= os
.path
.abspath(src
)
if ext
in self
._c
_extensions
:
elif ext
in self
._cpp
_extensions
:
elif ext
in self
._rc
_extensions
:
# compile .RC to .RES file
self
.spawn ([self
.rc
] + pp_opts
+
[output_opt
] + [input_opt
])
except DistutilsExecError
, msg
:
elif ext
in self
._mc
_extensions
:
# Compile .MC to .RC file to .RES file.
# * '-h dir' specifies the directory for the
# * '-r dir' specifies the target directory of the
# generated RC file and the binary message resource
# For now (since there are no options to change this),
# we use the source-directory for the include file and
# the build directory for the RC file and message
# resources. This works at least for win32all.
h_dir
= os
.path
.dirname (src
)
rc_dir
= os
.path
.dirname (obj
)
# first compile .MC to .RC and .H file
['-h', h_dir
, '-r', rc_dir
] + [src
])
base
, _
= os
.path
.splitext (os
.path
.basename (src
))
rc_file
= os
.path
.join (rc_dir
, base
+ '.rc')
# then compile .RC to .RES file
["/fo" + obj
] + [rc_file
])
except DistutilsExecError
, msg
:
# how to handle this file?
"Don't know how to compile %s to %s" % \
self
.spawn ([self
.cc
] + compile_opts
+ pp_opts
+
[input_opt
, output_opt
] +
except DistutilsExecError
, msg
:
def create_static_lib (self
,
if not self
.initialized
: self
.initialize()
(objects
, output_dir
) = self
._fix
_object
_args
(objects
, output_dir
)
self
.library_filename (output_libname
, output_dir
=output_dir
)
if self
._need
_link
(objects
, output_filename
):
lib_args
= objects
+ ['/OUT:' + output_filename
]
pass # XXX what goes here?
self
.spawn ([self
.lib
] + lib_args
)
except DistutilsExecError
, msg
:
log
.debug("skipping %s (up-to-date)", output_filename
)
runtime_library_dirs
=None,
if not self
.initialized
: self
.initialize()
(objects
, output_dir
) = self
._fix
_object
_args
(objects
, output_dir
)
(libraries
, library_dirs
, runtime_library_dirs
) = \
self
._fix
_lib
_args
(libraries
, library_dirs
, runtime_library_dirs
)
self
.warn ("I don't know what to do with 'runtime_library_dirs': "
+ str (runtime_library_dirs
))
lib_opts
= gen_lib_options (self
,
library_dirs
, runtime_library_dirs
,
if output_dir
is not None:
output_filename
= os
.path
.join (output_dir
, output_filename
)
if self
._need
_link
(objects
, output_filename
):
if target_desc
== CCompiler
.EXECUTABLE
:
ldflags
= self
.ldflags_shared_debug
[1:]
ldflags
= self
.ldflags_shared
[1:]
ldflags
= self
.ldflags_shared_debug
ldflags
= self
.ldflags_shared
for sym
in (export_symbols
or []):
export_opts
.append("/EXPORT:" + sym
)
ld_args
= (ldflags
+ lib_opts
+ export_opts
+
objects
+ ['/OUT:' + output_filename
])
# The MSVC linker generates .lib and .exp files, which cannot be
# suppressed by any linker switches. The .lib files may even be
# needed! Make sure they are generated in the temporary build
# directory. Since they have different names for debug and release
# builds, they can go into the same directory.
if export_symbols
is not None:
(dll_name
, dll_ext
) = os
.path
.splitext(
os
.path
.basename(output_filename
))
implib_file
= os
.path
.join(
os
.path
.dirname(objects
[0]),
self
.library_filename(dll_name
))
ld_args
.append ('/IMPLIB:' + implib_file
)
ld_args
[:0] = extra_preargs
ld_args
.extend(extra_postargs
)
self
.mkpath (os
.path
.dirname (output_filename
))
self
.spawn ([self
.linker
] + ld_args
)
except DistutilsExecError
, msg
:
log
.debug("skipping %s (up-to-date)", output_filename
)
# -- Miscellaneous methods -----------------------------------------
# These are all used by the 'gen_lib_options() function, in
def library_dir_option (self
, dir):
def runtime_library_dir_option (self
, dir):
raise DistutilsPlatformError
, \
"don't know how to set runtime library search path for MSVC++"
def library_option (self
, lib
):
return self
.library_filename (lib
)
def find_library_file (self
, dirs
, lib
, debug
=0):
# Prefer a debugging library if found (and requested), but deal
# with it if we don't have one.
try_names
= [lib
+ "_d", lib
]
libfile
= os
.path
.join(dir, self
.library_filename (name
))
if os
.path
.exists(libfile
):
# Oops, didn't find it in *any* of 'dirs'
# Helper methods for using the MSVC registry settings
"""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'.
fn
= os
.path
.join(os
.path
.abspath(p
), exe
)
# didn't find it; try existing path
for p
in string
.split(os
.environ
['Path'],';'):
fn
= os
.path
.join(os
.path
.abspath(p
),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.
key
= (r
"%s\%0.1f\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories"
% (self
.__root
, self
.__version
))
key
= (r
"%s\6.0\Build System\Components\Platforms"
r
"\Win32 (%s)\Directories" % (self
.__root
, platform
))
d
= read_values(base
, key
)
return string
.split(self
.__macros
.sub(d
[path
]), ";")
return string
.split(d
[path
], ";")
# MSVC 6 seems to create the registry entries we need only when
if read_values(base
, r
"%s\6.0" % self
.__root
) is not None:
self
.warn("It seems you have Visual Studio 6 installed, "
"but the expected registry settings are not present.\n"
"You must at least run the Visual Studio GUI once "
"so that these entries are created.")
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
p
= self
.get_msvc_paths("library")
p
= self
.get_msvc_paths(name
)
os
.environ
[name
] = string
.join(p
, ';')