mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			317 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			317 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| """distutils.ccompiler
 | |
| 
 | |
| Contains MSVCCompiler, an implementation of the abstract CCompiler class
 | |
| for the Microsoft Visual Studio """
 | |
| 
 | |
| 
 | |
| # created 1999/08/19, Perry Stoll
 | |
| # 
 | |
| __rcsid__ = "$Id$"
 | |
| 
 | |
| import os
 | |
| import sys
 | |
| from distutils.errors import *
 | |
| from distutils.ccompiler import CCompiler
 | |
| 
 | |
| 
 | |
| class MSVCCompiler ( CCompiler) :
 | |
|     """Abstract base class to define the interface that must be implemented
 | |
|        by real compiler abstraction classes.  Might have some use as a
 | |
|        place for shared code, but it's not yet clear what code can be
 | |
|        shared between compiler abstraction models for different platforms.
 | |
| 
 | |
|        The basic idea behind a compiler abstraction class is that each
 | |
|        instance can be used for all the compile/link steps in building
 | |
|        a single project.  Thus, attributes common to all of those compile
 | |
|        and link steps -- include directories, macros to define, libraries
 | |
|        to link against, etc. -- are attributes of the compiler instance.
 | |
|        To allow for variability in how individual files are treated,
 | |
|        most (all?) of those attributes may be varied on a per-compilation
 | |
|        or per-link basis."""
 | |
| 
 | |
|     def __init__ (self,
 | |
|                   verbose=0,
 | |
|                   dry_run=0):
 | |
| 
 | |
|         CCompiler.__init__ (self, verbose, dry_run)
 | |
| 
 | |
|         
 | |
|         # XXX This is a nasty dependency to add on something otherwise
 | |
|         #       pretty clean.  move it to build_ext under an nt
 | |
|         #       specific part.
 | |
|         #       shared libraries need to link against python15.lib
 | |
|         self.add_library ( "python" + sys.version[0] + sys.version[2] )
 | |
|         self.add_library_dir( os.path.join( sys.exec_prefix, 'libs' ) )
 | |
|         
 | |
|         self.cc   = "cl.exe"
 | |
|         self.link = "link.exe"
 | |
|         self.preprocess_options = None
 | |
|         self.compile_options = [ '/nologo' ]
 | |
| 
 | |
|         self.ldflags_shared = ['/DLL', '/nologo']
 | |
|         self.ldflags_static = [ '/nologo']
 | |
| 
 | |
|     # XXX things not handled by this compiler abstraction model:
 | |
|     #   * client can't provide additional options for a compiler,
 | |
|     #     e.g. warning, optimization, debugging flags.  Perhaps this
 | |
|     #     should be the domain of concrete compiler abstraction classes
 | |
|     #     (UnixCCompiler, MSVCCompiler, etc.) -- or perhaps the base
 | |
|     #     class should have methods for the common ones.
 | |
|     #   * can't put output files (object files, libraries, whatever)
 | |
|     #     into a separate directory from their inputs.  Should this be
 | |
|     #     handled by an 'output_dir' attribute of the whole object, or a
 | |
|     #     parameter to the compile/link_* methods, or both?
 | |
|     #   * can't completely override the include or library searchg
 | |
|     #     path, ie. no "cc -I -Idir1 -Idir2" or "cc -L -Ldir1 -Ldir2".
 | |
|     #     I'm not sure how widely supported this is even by Unix
 | |
|     #     compilers, much less on other platforms.  And I'm even less
 | |
|     #     sure how useful it is; maybe for cross-compiling, but
 | |
|     #     support for that is a ways off.  (And anyways, cross
 | |
|     #     compilers probably have a dedicated binary with the
 | |
|     #     right paths compiled in.  I hope.)
 | |
|     #   * can't do really freaky things with the library list/library
 | |
|     #     dirs, e.g. "-Ldir1 -lfoo -Ldir2 -lfoo" to link against
 | |
|     #     different versions of libfoo.a in different locations.  I
 | |
|     #     think this is useless without the ability to null out the
 | |
|     #     library search path anyways.
 | |
|     
 | |
| 
 | |
|     # -- Worker methods ------------------------------------------------
 | |
|     # (must be implemented by subclasses)
 | |
| 
 | |
|     _c_extensions = [ '.c' ]
 | |
|     _cpp_extensions = [ '.cc', 'cpp' ]
 | |
| 
 | |
|     _obj_ext = '.obj'
 | |
|     _exe_ext = 'exe'
 | |
|     _shared_lib_ext = '.dll'
 | |
|     _static_lib_ext = '.lib'
 | |
|     
 | |
|     def compile (self,
 | |
|                  sources,
 | |
|                  macros=None,
 | |
|                  includes=None):
 | |
|         """Compile one or more C/C++ source files.  'sources' must be
 | |
|            a list of strings, each one the name of a C/C++ source
 | |
|            file.  Return a list of the object filenames generated
 | |
|            (one for each source filename in 'sources').
 | |
| 
 | |
|            'macros', if given, must be a list of macro definitions.  A
 | |
|            macro definition is either a (name, value) 2-tuple or a (name,)
 | |
|            1-tuple.  The former defines a macro; if the value is None, the
 | |
|            macro is defined without an explicit value.  The 1-tuple case
 | |
|            undefines a macro.  Later definitions/redefinitions/
 | |
|            undefinitions take precedence.
 | |
| 
 | |
|            'includes', if given, must be a list of strings, the directories
 | |
|            to add to the default include file search path for this
 | |
|            compilation only."""
 | |
| 
 | |
|         if macros is None:
 | |
|             macros = []
 | |
|         if includes is None:
 | |
|             includes = []
 | |
| 
 | |
|         objectFiles = []
 | |
| 
 | |
|         base_pp_opts = _gen_preprocess_options (self.macros + macros,
 | |
|                                                 self.include_dirs + includes)
 | |
| 
 | |
|         base_pp_opts.append('/c')
 | |
|         
 | |
|         for srcFile in sources:
 | |
|             base,ext = os.path.splitext(srcFile)
 | |
|             objFile = base + ".obj"
 | |
| 
 | |
|             if ext in self._c_extensions:
 | |
|                 fileOpt = "/Tc"
 | |
|             elif ext in self._cpp_extensions:
 | |
|                 fileOpt = "/Tp"
 | |
| 
 | |
|             inputOpt  = fileOpt + srcFile
 | |
|             outputOpt = "/Fo"   + objFile
 | |
| 
 | |
|             pp_opts = base_pp_opts + [ outputOpt, inputOpt ]
 | |
| 
 | |
|             returnCode = self.spawn( [ self.cc ] + self.compile_options + pp_opts )
 | |
|             # XXX check for valid return code
 | |
| 
 | |
|             objectFiles.append( objFile )
 | |
| 
 | |
| 
 | |
|         return objectFiles
 | |
|     
 | |
|     # XXX this is kind of useless without 'link_binary()' or
 | |
|     # 'link_executable()' or something -- or maybe 'link_static_lib()'
 | |
|     # should not exist at all, and we just have 'link_binary()'?
 | |
|     def link_static_lib (self,
 | |
|                          objects,
 | |
|                          output_libname,
 | |
|                          libraries=None,
 | |
|                          library_dirs=None):
 | |
|         """Link a bunch of stuff together to create a static library
 | |
|            file.  The "bunch of stuff" consists of the list of object
 | |
|            files supplied as 'objects', the extra object files supplied
 | |
|            to 'add_link_object()' and/or 'set_link_objects()', the
 | |
|            libraries supplied to 'add_library()' and/or
 | |
|            'set_libraries()', and the libraries supplied as 'libraries'
 | |
|            (if any).
 | |
| 
 | |
|            'output_libname' should be a library name, not a filename;
 | |
|            the filename will be inferred from the library name.
 | |
| 
 | |
|            'library_dirs', if supplied, should be a list of additional
 | |
|            directories to search on top of the system default and those
 | |
|            supplied to 'add_library_dir()' and/or 'set_library_dirs()'."""
 | |
|         
 | |
|         if libraries is None:
 | |
|             libraries = []
 | |
|         if library_dirs is None:
 | |
|             library_dirs = []
 | |
|         if build_info is None:
 | |
|             build_info = {}
 | |
|         
 | |
|         lib_opts = _gen_lib_options (self.libraries + libraries,
 | |
|                                      self.library_dirs + library_dirs)
 | |
| 
 | |
|         if build_info.has_key('def_file') :
 | |
|             lib_opts.append('/DEF:' + build_info['def_file'] )
 | |
|                             
 | |
|         ld_args = self.ldflags_static + lib_opts + \
 | |
|                   objects + ['/OUT:' + output_filename]
 | |
| 
 | |
|         self.spawn ( [ self.link ] + ld_args )
 | |
|     
 | |
| 
 | |
|     def link_shared_lib (self,
 | |
|                          objects,
 | |
|                          output_libname,
 | |
|                          libraries=None,
 | |
|                          library_dirs=None,
 | |
|                          build_info=None):
 | |
|         """Link a bunch of stuff together to create a shared library
 | |
|            file.  Has the same effect as 'link_static_lib()' except
 | |
|            that the filename inferred from 'output_libname' will most
 | |
|            likely be different, and the type of file generated will
 | |
|            almost certainly be different."""
 | |
|         # XXX should we sanity check the library name? (eg. no
 | |
|         # slashes)
 | |
|         self.link_shared_object (objects, self.shared_library_name(output_libname),
 | |
|                                  build_info=build_info )
 | |
|     
 | |
|     def link_shared_object (self,
 | |
|                             objects,
 | |
|                             output_filename,
 | |
|                             libraries=None,
 | |
|                             library_dirs=None,
 | |
|                             build_info=None):
 | |
|         """Link a bunch of stuff together to create a shared object
 | |
|            file.  Much like 'link_shared_lib()', except the output
 | |
|            filename is explicitly supplied as 'output_filename'."""
 | |
|         if libraries is None:
 | |
|             libraries = []
 | |
|         if library_dirs is None:
 | |
|             library_dirs = []
 | |
|         if build_info is None:
 | |
|             build_info = {}
 | |
|         
 | |
|         lib_opts = _gen_lib_options (self.libraries + libraries,
 | |
|                                      self.library_dirs + library_dirs)
 | |
| 
 | |
|         if build_info.has_key('def_file') :
 | |
|             lib_opts.append('/DEF:' + build_info['def_file'] )
 | |
|                             
 | |
|         ld_args = self.ldflags_shared + lib_opts + \
 | |
|                   objects + ['/OUT:' + output_filename]
 | |
| 
 | |
|         self.spawn ( [ self.link ] + ld_args )
 | |
| 
 | |
| 
 | |
|     # -- Filename mangling methods -------------------------------------
 | |
| 
 | |
|     def _change_extensions( self, filenames, newExtension ):
 | |
|         object_filenames = []
 | |
| 
 | |
|         for srcFile in filenames:
 | |
|             base,ext = os.path.splitext( srcFile )
 | |
|             # XXX should we strip off any existing path?
 | |
|             object_filenames.append( base + newExtension )
 | |
| 
 | |
|         return object_filenames
 | |
| 
 | |
|     def object_filenames (self, source_filenames):
 | |
|         """Return the list of object filenames corresponding to each
 | |
|            specified source filename."""
 | |
|         return self._change_extensions( source_filenames, self._obj_ext )
 | |
| 
 | |
|     def shared_object_filename (self, source_filename):
 | |
|         """Return the shared object filename corresponding to a
 | |
|            specified source filename."""
 | |
|         return self._change_extensions( source_filenames, self._shared_lib_ext )
 | |
| 
 | |
|     def library_filename (self, libname):
 | |
|         """Return the static library filename corresponding to the
 | |
|            specified library name."""
 | |
|         return "lib%s%s" %( libname, self._static_lib_ext )
 | |
| 
 | |
|     def shared_library_filename (self, libname):
 | |
|         """Return the shared library filename corresponding to the
 | |
|            specified library name."""
 | |
|         return "lib%s%s" %( libname, self._shared_lib_ext )
 | |
| 
 | |
| # class MSVCCompiler
 | |
| 
 | |
| def _gen_preprocess_options (macros, includes):
 | |
| 
 | |
|     # XXX it would be nice (mainly aesthetic, and so we don't generate
 | |
|     # stupid-looking command lines) to go over 'macros' and eliminate
 | |
|     # redundant definitions/undefinitions (ie. ensure that only the
 | |
|     # latest mention of a particular macro winds up on the command
 | |
|     # line).  I don't think it's essential, though, since most (all?)
 | |
|     # Unix C compilers only pay attention to the latest -D or -U
 | |
|     # mention of a macro on their command line.  Similar situation for
 | |
|     # 'includes'.  I'm punting on both for now.  Anyways, weeding out
 | |
|     # redundancies like this should probably be the province of
 | |
|     # CCompiler, since the data structures used are inherited from it
 | |
|     # and therefore common to all CCompiler classes.
 | |
| 
 | |
| 
 | |
|     pp_opts = []
 | |
|     for macro in macros:
 | |
|         if len (macro) == 1:        # undefine this macro
 | |
|             pp_opts.append ("-U%s" % macro[0])
 | |
|         elif len (macro) == 2:
 | |
|             if macro[1] is None:    # define with no explicit value
 | |
|                 pp_opts.append ("-D%s" % macro[0])
 | |
|             else:
 | |
|                 # XXX *don't* need to be clever about quoting the
 | |
|                 # macro value here, because we're going to avoid the
 | |
|                 # shell at all costs when we spawn the command!
 | |
|                 pp_opts.append ("-D%s=%s" % macro)
 | |
| 
 | |
|     for dir in includes:
 | |
|         pp_opts.append ("-I%s" % dir)
 | |
| 
 | |
|     return pp_opts
 | |
| 
 | |
| def _gen_lib_options (libraries, library_dirs):
 | |
| 
 | |
|     lib_opts = []
 | |
| 
 | |
|     for dir in library_dirs:
 | |
|         lib_opts.append ("/LIBPATH:%s" % dir)
 | |
| 
 | |
|     # XXX it's important that we *not* remove redundant library mentions!
 | |
|     # sometimes you really do have to say "-lfoo -lbar -lfoo" in order to
 | |
|     # resolve all symbols.  I just hope we never have to say "-lfoo obj.o
 | |
|     # -lbar" to get things to work -- that's certainly a possibility, but a
 | |
|     # pretty nasty way to arrange your C code.
 | |
| 
 | |
|     for lib in libraries:
 | |
|         lib_opts.append ("%s.lib" % lib) # import libraries end in .lib
 | |
| 
 | |
|     return lib_opts
 | |
| 
 | |
| # _gen_lib_options ()
 | |
| 
 | |
|     
 | 
