diff options
author | Greg Ward <gward@python.net> | 1999-08-14 23:57:49 (GMT) |
---|---|---|
committer | Greg Ward <gward@python.net> | 1999-08-14 23:57:49 (GMT) |
commit | e393ddb2e05836be79f4fc34ed26ff217074c967 (patch) | |
tree | fb259d59676e59a1890393bb5dffab8ab0e40348 /Lib | |
parent | b4dbfb318ceda8be952417984f330b2c8e51ffac (diff) | |
download | cpython-e393ddb2e05836be79f4fc34ed26ff217074c967.zip cpython-e393ddb2e05836be79f4fc34ed26ff217074c967.tar.gz cpython-e393ddb2e05836be79f4fc34ed26ff217074c967.tar.bz2 |
Implements the 'build_ext' command for building C/C++ extension modules.
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/distutils/command/build_ext.py | 192 |
1 files changed, 192 insertions, 0 deletions
diff --git a/Lib/distutils/command/build_ext.py b/Lib/distutils/command/build_ext.py new file mode 100644 index 0000000..cb9da68 --- /dev/null +++ b/Lib/distutils/command/build_ext.py @@ -0,0 +1,192 @@ +"""distutils.command.build_ext + +Implements the Distutils 'build_ext' command, for building extension +modules (currently limited to C extensions, should accomodate C++ +extensions ASAP).""" + +# created 1999/08/09, Greg Ward + +__rcsid__ = "$Id$" + +import sys, os, string, re +from types import * +from distutils.core import Command +from distutils.ccompiler import new_compiler +from distutils.sysconfig import INCLUDEPY, SO, exec_prefix +from distutils.errors import * + + +# This is the same as a Python NAME, since we must accept any +# valid module name for the extension name. +extension_name_re = re.compile (r'^[a-zA-Z_][a-zA-Z_0-9]*$') + + +class BuildExt (Command): + + # XXX thoughts on how to deal with complex command-line options like + # these, i.e. how to make it so fancy_getopt can suck them off the + # command line and make it look like setup.py defined the appropriate + # lists of tuples of what-have-you. + # - each command needs a callback to process its command-line options + # - Command.__init__() needs access to its share of the whole + # command line (must ultimately come from + # Distribution.parse_command_line()) + # - it then calls the current command class' option-parsing + # callback to deal with weird options like -D, which have to + # parse the option text and churn out some custom data + # structure + # - that data structure (in this case, a list of 2-tuples) + # will then be present in the command object by the time + # we get to set_final_options() (i.e. the constructor + # takes care of both command-line and client options + # in between set_default_options() and set_final_options()) + + options = [('dir=', 'd', + "directory for compiled extension modules"), + ('include-dirs=', 'I', + "list of directories to search for header files"), + ('define=', 'D', + "C preprocessor macros to define"), + ('undef=', 'U', + "C preprocessor macros to undefine"), + ('libs=', 'l', + "external C libraries to link with"), + ('library-dirs=', 'L', + "directories to search for external C libraries"), + ('rpath=', 'R', + "directories to search for shared C libraries at runtime"), + ('link-objects=', 'O', + "extra explicit link objects to include in the link"), + ] + + + def set_default_options (self): + self.dir = None + self.include_dirs = None + self.define = None + self.undef = None + self.libs = None + self.library_dirs = None + self.rpath = None + self.link_objects = None + + def set_final_options (self): + self.set_undefined_options ('build', ('platdir', 'dir')) + + # Make sure Python's include directories (for Python.h, config.h, + # etc.) are in the include search path. We have to roll our own + # "exec include dir", because the Makefile parsed by sysconfig + # doesn't have it (sigh). + py_include = INCLUDEPY # prefix + "include" + "python" + ver + exec_py_include = os.path.join (exec_prefix, 'include', + 'python' + sys.version[0:3]) + if self.include_dirs is None: + self.include_dirs = [] + self.include_dirs.insert (0, py_include) + if exec_py_include != py_include: + self.include_dirs.insert (0, exec_py_include) + + + def run (self): + + self.set_final_options () + (extensions, package) = \ + self.distribution.get_options ('ext_modules', 'package') + + # 'extensions', as supplied by setup.py, is a list of 2-tuples. + # Each tuple is simple: + # (ext_name, build_info) + # build_info is a dictionary containing everything specific to + # building this extension. (Info pertaining to all extensions + # should be handled by general distutils options passed from + # setup.py down to right here, but that's not taken care of yet.) + + + # First, sanity-check the 'extensions' list + self.check_extensions_list (extensions) + + # Setup the CCompiler object that we'll use to do all the + # compiling and linking + self.compiler = new_compiler (verbose=self.distribution.verbose, + dry_run=self.distribution.dry_run) + if self.include_dirs is not None: + self.compiler.set_include_dirs (self.include_dirs) + if self.define is not None: + # 'define' option is a list of (name,value) tuples + for (name,value) in self.define: + self.compiler.define_macro (name, value) + if self.undef is not None: + for macro in self.undef: + self.compiler.undefine_macro (macro) + if self.libs is not None: + self.compiler.set_libraries (self.libs) + if self.library_dirs is not None: + self.compiler.set_library_dirs (self.library_dirs) + if self.rpath is not None: + self.compiler.set_runtime_library_dirs (self.rpath) + if self.link_objects is not None: + self.compiler.set_link_objects (self.link_objects) + + # Now the real loop over extensions + self.build_extensions (extensions) + + + + def check_extensions_list (self, extensions): + + if type (extensions) is not ListType: + raise DistutilsValueError, \ + "'ext_modules' option must be a list of tuples" + + for ext in extensions: + if type (ext) is not TupleType and len (ext) != 2: + raise DistutilsValueError, \ + "each element of 'ext_modules' option must be a 2-tuple" + + if not (type (ext[0]) is StringType and + extension_name_re.match (ext[0])): + raise DistutilsValueError, \ + "first element of each tuple in 'ext_modules' " + \ + "must be the extension name (a string)" + + if type (ext[1]) is not DictionaryType: + raise DistutilsValueError, \ + "second element of each tuple in 'ext_modules' " + \ + "must be a dictionary" + + # end sanity-check for + + # check_extensions_list () + + + def build_extensions (self, extensions): + + for (extension_name, build_info) in extensions: + sources = build_info.get ('sources') + if sources is None or type (sources) is not ListType: + raise DistutilsValueError, \ + "in ext_modules option, 'sources' must be present " + \ + "and must be a list of source filenames" + + macros = build_info.get ('macros') + include_dirs = build_info.get ('include_dirs') + self.compiler.compile (sources, macros, include_dirs) + + objects = self.compiler.object_filenames (sources) + extra_objects = build_info.get ('extra_objects') + if extra_objects: + objects.extend (extra_objects) + libraries = build_info.get ('libraries') + library_dirs = build_info.get ('library_dirs') + + ext_filename = self.extension_filename (extension_name) + self.compiler.link_shared_object (objects, ext_filename, + libraries, library_dirs) + + # build_extensions () + + + def extension_filename (self, ext_name): + return ext_name + SO + +# class BuildExt |