summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorGreg Ward <gward@python.net>1999-08-14 23:57:49 (GMT)
committerGreg Ward <gward@python.net>1999-08-14 23:57:49 (GMT)
commite393ddb2e05836be79f4fc34ed26ff217074c967 (patch)
treefb259d59676e59a1890393bb5dffab8ab0e40348 /Lib
parentb4dbfb318ceda8be952417984f330b2c8e51ffac (diff)
downloadcpython-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.py192
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