]>
git.saurik.com Git - wxWidgets.git/blob - wxPython/distutils/msvccompiler.py
   1 """distutils.msvccompiler 
   3 Contains MSVCCompiler, an implementation of the abstract CCompiler class 
   4 for the Microsoft Visual Studio. 
   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) 
  11 # This module should be kept compatible with Python 1.5.2. 
  15 import sys
, os
, string
 
  16 from distutils
.errors 
import \
 
  17      DistutilsExecError
, DistutilsPlatformError
, \
 
  18      CompileError
, LibError
, LinkError
 
  19 from distutils
.ccompiler 
import \
 
  20      CCompiler
, gen_preprocess_options
, gen_lib_options
 
  21 from distutils 
import log
 
  30     RegOpenKeyEx 
= _winreg
.OpenKeyEx
 
  31     RegEnumKey 
= _winreg
.EnumKey
 
  32     RegEnumValue 
= _winreg
.EnumValue
 
  33     RegError 
= _winreg
.error
 
  42         RegOpenKeyEx 
= win32api
.RegOpenKeyEx
 
  43         RegEnumKey 
= win32api
.RegEnumKey
 
  44         RegEnumValue 
= win32api
.RegEnumValue
 
  45         RegError 
= win32api
.error
 
  48         log
.info("Warning: Can't read registry to find the " 
  49                  "necessary compiler setting\n" 
  50                  "Make sure that Python modules _winreg, " 
  51                  "win32api or win32con are installed.") 
  55     HKEYS 
= (hkey_mod
.HKEY_USERS
, 
  56              hkey_mod
.HKEY_CURRENT_USER
, 
  57              hkey_mod
.HKEY_LOCAL_MACHINE
, 
  58              hkey_mod
.HKEY_CLASSES_ROOT
) 
  60 def read_keys(base
, key
): 
  61     """Return list of registry keys.""" 
  64         handle 
= RegOpenKeyEx(base
, key
) 
  71             k 
= RegEnumKey(handle
, i
) 
  78 def read_values(base
, key
): 
  79     """Return dict of registry keys and values. 
  81     All names are converted to lowercase. 
  84         handle 
= RegOpenKeyEx(base
, key
) 
  91             name
, value
, type = RegEnumValue(handle
, i
) 
  95         d
[convert_mbcs(name
)] = convert_mbcs(value
) 
 100     enc 
= getattr(s
, "encode", None) 
 110     def __init__(self
, version
): 
 112         self
.load_macros(version
) 
 114     def set_macro(self
, macro
, path
, key
): 
 116             d 
= read_values(base
, path
) 
 118                 self
.macros
["$(%s)" % macro
] = d
[key
] 
 121     def load_macros(self
, version
): 
 122         vsbase 
= r
"Software\Microsoft\VisualStudio\%0.1f" % version
 
 123         self
.set_macro("VCInstallDir", vsbase 
+ r
"\Setup\VC", "productdir") 
 124         self
.set_macro("VSInstallDir", vsbase 
+ r
"\Setup\VS", "productdir") 
 125         net 
= r
"Software\Microsoft\.NETFramework" 
 126         self
.set_macro("FrameworkDir", net
, "installroot") 
 128             self
.set_macro("FrameworkSDKDir", net
, "sdkinstallrootv1.1") 
 130             self
.set_macro("FrameworkSDKDir", net
, "sdkinstallroot") 
 132         p 
= r
"Software\Microsoft\NET Framework Setup\Product" 
 135                 h 
= RegOpenKeyEx(base
, p
) 
 138             key 
= RegEnumKey(h
, 0) 
 139             d 
= read_values(base
, r
"%s\%s" % (p
, key
)) 
 140             self
.macros
["$(FrameworkVersion)"] = d
["version"] 
 143         for k
, v 
in self
.macros
.items(): 
 144             s 
= string
.replace(s
, k
, v
) 
 147 def get_build_version(): 
 148     """Return the version of MSVC that was used to build Python. 
 150     For Python 2.3 and up, the version number is included in 
 151     sys.version.  For earlier versions, assume the compiler is MSVC 6. 
 155     i 
= string
.find(sys
.version
, prefix
) 
 159     s
, rest 
= sys
.version
[i
:].split(" ", 1) 
 160     majorVersion 
= int(s
[:-2]) - 6 
 161     minorVersion 
= int(s
[2:3]) / 10.0 
 162     # I don't think paths are affected by minor version in version 6 
 163     if majorVersion 
== 6: 
 165     if majorVersion 
>= 6: 
 166         return majorVersion 
+ minorVersion
 
 167     # else we don't know what version of the compiler this is 
 171 class MSVCCompiler (CCompiler
) : 
 172     """Concrete class that implements an interface to Microsoft Visual C++, 
 173        as defined by the CCompiler abstract class.""" 
 175     compiler_type 
= 'msvc' 
 177     # Just set this so CCompiler's constructor doesn't barf.  We currently 
 178     # don't use the 'set_executables()' bureaucracy provided by CCompiler, 
 179     # as it really isn't necessary for this sort of single-compiler class. 
 180     # Would be nice to have a consistent interface with UnixCCompiler, 
 181     # though, so it's worth thinking about. 
 184     # Private class data (need to distinguish C from C++ source for compiler) 
 185     _c_extensions 
= ['.c'] 
 186     _cpp_extensions 
= ['.cc', '.cpp', '.cxx'] 
 187     _rc_extensions 
= ['.rc'] 
 188     _mc_extensions 
= ['.mc'] 
 190     # Needed for the filename generation methods provided by the 
 191     # base class, CCompiler. 
 192     src_extensions 
= (_c_extensions 
+ _cpp_extensions 
+ 
 193                       _rc_extensions 
+ _mc_extensions
) 
 194     res_extension 
= '.res' 
 195     obj_extension 
= '.obj' 
 196     static_lib_extension 
= '.lib' 
 197     shared_lib_extension 
= '.dll' 
 198     static_lib_format 
= shared_lib_format 
= '%s%s' 
 199     exe_extension 
= '.exe' 
 201     def __init__ (self
, verbose
=0, dry_run
=0, force
=0): 
 202         CCompiler
.__init
__ (self
, verbose
, dry_run
, force
) 
 203         self
.__version 
= get_build_version() 
 204         if self
.__version 
>= 7: 
 205             self
.__root 
= r
"Software\Microsoft\VisualStudio" 
 206             self
.__macros 
= MacroExpander(self
.__version
) 
 208             self
.__root 
= r
"Software\Microsoft\Devstudio" 
 209         self
.__paths 
= self
.get_msvc_paths("path") 
 211         if len (self
.__paths
) == 0: 
 212             raise DistutilsPlatformError
, \
 
 213                   ("Python was built with version %s of Visual Studio, " 
 214                    "and extensions need to be built with the same " 
 215                    "version of the compiler, but it isn't installed." % self
.__version
) 
 217         self
.cc 
= self
.find_exe("cl.exe") 
 218         self
.linker 
= self
.find_exe("link.exe") 
 219         self
.lib 
= self
.find_exe("lib.exe") 
 220         self
.rc 
= self
.find_exe("rc.exe")   # resource compiler 
 221         self
.mc 
= self
.find_exe("mc.exe")   # message compiler 
 222         self
.set_path_env_var('lib') 
 223         self
.set_path_env_var('include') 
 225         # extend the MSVC path with the current path 
 227             for p 
in string
.split(os
.environ
['path'], ';'): 
 228                 self
.__paths
.append(p
) 
 231         os
.environ
['path'] = string
.join(self
.__paths
, ';') 
 233         self
.preprocess_options 
= None 
 234         self
.compile_options 
= [ '/nologo', '/Ox', '/MD', '/W3', '/GX' , 
 236         self
.compile_options_debug 
= ['/nologo', '/Od', '/MDd', '/W3', '/GX', 
 239         self
.ldflags_shared 
= ['/DLL', '/nologo', '/INCREMENTAL:NO'] 
 240         self
.ldflags_shared_debug 
= [ 
 241             '/DLL', '/nologo', '/INCREMENTAL:no', '/pdb:None', '/DEBUG' 
 243         self
.ldflags_static 
= [ '/nologo'] 
 246     # -- Worker methods ------------------------------------------------ 
 248     def object_filenames (self
, 
 252         # Copied from ccompiler.py, extended to return .res as 'object'-file 
 254         if output_dir 
is None: output_dir 
= '' 
 256         for src_name 
in source_filenames
: 
 257             (base
, ext
) = os
.path
.splitext (src_name
) 
 258             if ext 
not in self
.src_extensions
: 
 259                 # Better to raise an exception instead of silently continuing 
 260                 # and later complain about sources and targets having 
 262                 raise CompileError ("Don't know how to compile %s" % src_name
) 
 264                 base 
= os
.path
.basename (base
) 
 265             if ext 
in self
._rc
_extensions
: 
 266                 obj_names
.append (os
.path
.join (output_dir
, 
 267                                                 base 
+ self
.res_extension
)) 
 268             elif ext 
in self
._mc
_extensions
: 
 269                 obj_names
.append (os
.path
.join (output_dir
, 
 270                                                 base 
+ self
.res_extension
)) 
 272                 obj_names
.append (os
.path
.join (output_dir
, 
 273                                                 base 
+ self
.obj_extension
)) 
 276     # object_filenames () 
 279     def compile(self
, sources
, 
 280                 output_dir
=None, macros
=None, include_dirs
=None, debug
=0, 
 281                 extra_preargs
=None, extra_postargs
=None, depends
=None): 
 283         macros
, objects
, extra_postargs
, pp_opts
, build 
= \
 
 284                 self
._setup
_compile
(output_dir
, macros
, include_dirs
, sources
, 
 285                                     depends
, extra_postargs
) 
 287         compile_opts 
= extra_preargs 
or [] 
 288         compile_opts
.append ('/c') 
 290             compile_opts
.extend(self
.compile_options_debug
) 
 292             compile_opts
.extend(self
.compile_options
) 
 294         for obj
, (src
, ext
) in build
.items(): 
 296                 # pass the full pathname to MSVC in debug mode, 
 297                 # this allows the debugger to find the source file 
 298                 # without asking the user to browse for it 
 299                 src 
= os
.path
.abspath(src
) 
 301             if ext 
in self
._c
_extensions
: 
 302                 input_opt 
= "/Tc" + src
 
 303             elif ext 
in self
._cpp
_extensions
: 
 304                 input_opt 
= "/Tp" + src
 
 305             elif ext 
in self
._rc
_extensions
: 
 306                 # compile .RC to .RES file 
 308                 output_opt 
= "/fo" + obj
 
 310                     self
.spawn ([self
.rc
] + pp_opts 
+ 
 311                                 [output_opt
] + [input_opt
]) 
 312                 except DistutilsExecError
, msg
: 
 313                     raise CompileError
, msg
 
 315             elif ext 
in self
._mc
_extensions
: 
 317                 # Compile .MC to .RC file to .RES file. 
 318                 #   * '-h dir' specifies the directory for the 
 319                 #     generated include file 
 320                 #   * '-r dir' specifies the target directory of the 
 321                 #     generated RC file and the binary message resource 
 324                 # For now (since there are no options to change this), 
 325                 # we use the source-directory for the include file and 
 326                 # the build directory for the RC file and message 
 327                 # resources. This works at least for win32all. 
 329                 h_dir 
= os
.path
.dirname (src
) 
 330                 rc_dir 
= os
.path
.dirname (obj
) 
 332                     # first compile .MC to .RC and .H file 
 333                     self
.spawn ([self
.mc
] + 
 334                                 ['-h', h_dir
, '-r', rc_dir
] + [src
]) 
 335                     base
, _ 
= os
.path
.splitext (os
.path
.basename (src
)) 
 336                     rc_file 
= os
.path
.join (rc_dir
, base 
+ '.rc') 
 337                     # then compile .RC to .RES file 
 338                     self
.spawn ([self
.rc
] + 
 339                                 ["/fo" + obj
] + [rc_file
]) 
 341                 except DistutilsExecError
, msg
: 
 342                     raise CompileError
, msg
 
 345                 # how to handle this file? 
 347                     "Don't know how to compile %s to %s" % \
 
 350             output_opt 
= "/Fo" + obj
 
 352                 self
.spawn ([self
.cc
] + compile_opts 
+ pp_opts 
+ 
 353                             [input_opt
, output_opt
] + 
 355             except DistutilsExecError
, msg
: 
 356                 raise CompileError
, msg
 
 363     def create_static_lib (self
, 
 370         (objects
, output_dir
) = self
._fix
_object
_args 
(objects
, output_dir
) 
 372             self
.library_filename (output_libname
, output_dir
=output_dir
) 
 374         if self
._need
_link 
(objects
, output_filename
): 
 375             lib_args 
= objects 
+ ['/OUT:' + output_filename
] 
 377                 pass                    # XXX what goes here? 
 379                 self
.spawn ([self
.lib
] + lib_args
) 
 380             except DistutilsExecError
, msg
: 
 384             log
.debug("skipping %s (up-to-date)", output_filename
) 
 386     # create_static_lib () 
 395               runtime_library_dirs
=None, 
 403         (objects
, output_dir
) = self
._fix
_object
_args 
(objects
, output_dir
) 
 404         (libraries
, library_dirs
, runtime_library_dirs
) = \
 
 405             self
._fix
_lib
_args 
(libraries
, library_dirs
, runtime_library_dirs
) 
 407         if runtime_library_dirs
: 
 408             self
.warn ("I don't know what to do with 'runtime_library_dirs': " 
 409                        + str (runtime_library_dirs
)) 
 411         lib_opts 
= gen_lib_options (self
, 
 412                                     library_dirs
, runtime_library_dirs
, 
 414         if output_dir 
is not None: 
 415             output_filename 
= os
.path
.join (output_dir
, output_filename
) 
 417         if self
._need
_link 
(objects
, output_filename
): 
 419             if target_desc 
== CCompiler
.EXECUTABLE
: 
 421                     ldflags 
= self
.ldflags_shared_debug
[1:] 
 423                     ldflags 
= self
.ldflags_shared
[1:] 
 426                     ldflags 
= self
.ldflags_shared_debug
 
 428                     ldflags 
= self
.ldflags_shared
 
 431             for sym 
in (export_symbols 
or []): 
 432                 export_opts
.append("/EXPORT:" + sym
) 
 434             ld_args 
= (ldflags 
+ lib_opts 
+ export_opts 
+ 
 435                        objects 
+ ['/OUT:' + output_filename
]) 
 437             # The MSVC linker generates .lib and .exp files, which cannot be 
 438             # suppressed by any linker switches. The .lib files may even be 
 439             # needed! Make sure they are generated in the temporary build 
 440             # directory. Since they have different names for debug and release 
 441             # builds, they can go into the same directory. 
 442             if export_symbols 
is not None: 
 443                 (dll_name
, dll_ext
) = os
.path
.splitext( 
 444                     os
.path
.basename(output_filename
)) 
 445                 implib_file 
= os
.path
.join( 
 446                     os
.path
.dirname(objects
[0]), 
 447                     self
.library_filename(dll_name
)) 
 448                 ld_args
.append ('/IMPLIB:' + implib_file
) 
 451                 ld_args
[:0] = extra_preargs
 
 453                 ld_args
.extend(extra_postargs
) 
 455             self
.mkpath (os
.path
.dirname (output_filename
)) 
 457                 self
.spawn ([self
.linker
] + ld_args
) 
 458             except DistutilsExecError
, msg
: 
 462             log
.debug("skipping %s (up-to-date)", output_filename
) 
 467     # -- Miscellaneous methods ----------------------------------------- 
 468     # These are all used by the 'gen_lib_options() function, in 
 471     def library_dir_option (self
, dir): 
 472         return "/LIBPATH:" + dir 
 474     def runtime_library_dir_option (self
, dir): 
 475         raise DistutilsPlatformError
, \
 
 476               "don't know how to set runtime library search path for MSVC++" 
 478     def library_option (self
, lib
): 
 479         return self
.library_filename (lib
) 
 482     def find_library_file (self
, dirs
, lib
, debug
=0): 
 483         # Prefer a debugging library if found (and requested), but deal 
 484         # with it if we don't have one. 
 486             try_names 
= [lib 
+ "_d", lib
] 
 490             for name 
in try_names
: 
 491                 libfile 
= os
.path
.join(dir, self
.library_filename (name
)) 
 492                 if os
.path
.exists(libfile
): 
 495             # Oops, didn't find it in *any* of 'dirs' 
 498     # find_library_file () 
 500     # Helper methods for using the MSVC registry settings 
 502     def find_exe(self
, exe
): 
 503         """Return path to an MSVC executable program. 
 505         Tries to find the program in several places: first, one of the 
 506         MSVC program search paths from the registry; next, the directories 
 507         in the PATH environment variable.  If any of those work, return an 
 508         absolute path that is known to exist.  If none of them work, just 
 509         return the original program name, 'exe'. 
 512         for p 
in self
.__paths
: 
 513             fn 
= os
.path
.join(os
.path
.abspath(p
), exe
) 
 514             if os
.path
.isfile(fn
): 
 517         # didn't find it; try existing path 
 518         for p 
in string
.split(os
.environ
['Path'],';'): 
 519             fn 
= os
.path
.join(os
.path
.abspath(p
),exe
) 
 520             if os
.path
.isfile(fn
): 
 525     def get_msvc_paths(self
, path
, platform
='x86'): 
 526         """Get a list of devstudio directories (include, lib or path). 
 528         Return a list of strings.  The list will be empty if unable to 
 529         access the registry or appropriate registry keys not found. 
 532         if not _can_read_reg
: 
 535         path 
= path 
+ " dirs" 
 536         if self
.__version 
>= 7: 
 537             key 
= (r
"%s\%0.1f\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories" 
 538                    % (self
.__root
, self
.__version
)) 
 540             key 
= (r
"%s\6.0\Build System\Components\Platforms" 
 541                    r
"\Win32 (%s)\Directories" % (self
.__root
, platform
)) 
 544             d 
= read_values(base
, key
) 
 546                 if self
.__version 
>= 7: 
 547                     return string
.split(self
.__macros
.sub(d
[path
]), ";") 
 549                     return string
.split(d
[path
], ";") 
 552     def set_path_env_var(self
, name
): 
 553         """Set environment variable 'name' to an MSVC path type value. 
 555         This is equivalent to a SET command prior to execution of spawned 
 560             p 
= self
.get_msvc_paths("library") 
 562             p 
= self
.get_msvc_paths(name
) 
 564             os
.environ
[name
] = string
.join(p
, ';')