Path: blob/main/test/lib/python3.9/site-packages/setuptools/_distutils/extension.py
4799 views
"""distutils.extension12Provides the Extension class, used to describe C/C++ extension3modules in setup scripts."""45import os6import warnings78# This class is really only used by the "build_ext" command, so it might9# make sense to put it in distutils.command.build_ext. However, that10# module is already big enough, and I want to make this class a bit more11# complex to simplify some common cases ("foo" module in "foo.c") and do12# better error-checking ("foo.c" actually exists).13#14# Also, putting this in build_ext.py means every setup script would have to15# import that large-ish module (indirectly, through distutils.core) in16# order to do anything.1718class Extension:19"""Just a collection of attributes that describes an extension20module and everything needed to build it (hopefully in a portable21way, but there are hooks that let you be as unportable as you need).2223Instance attributes:24name : string25the full name of the extension, including any packages -- ie.26*not* a filename or pathname, but Python dotted name27sources : [string]28list of source filenames, relative to the distribution root29(where the setup script lives), in Unix form (slash-separated)30for portability. Source files may be C, C++, SWIG (.i),31platform-specific resource files, or whatever else is recognized32by the "build_ext" command as source for a Python extension.33include_dirs : [string]34list of directories to search for C/C++ header files (in Unix35form for portability)36define_macros : [(name : string, value : string|None)]37list of macros to define; each macro is defined using a 2-tuple,38where 'value' is either the string to define it to or None to39define it without a particular value (equivalent of "#define40FOO" in source or -DFOO on Unix C compiler command line)41undef_macros : [string]42list of macros to undefine explicitly43library_dirs : [string]44list of directories to search for C/C++ libraries at link time45libraries : [string]46list of library names (not filenames or paths) to link against47runtime_library_dirs : [string]48list of directories to search for C/C++ libraries at run time49(for shared extensions, this is when the extension is loaded)50extra_objects : [string]51list of extra files to link with (eg. object files not implied52by 'sources', static library that must be explicitly specified,53binary resource files, etc.)54extra_compile_args : [string]55any extra platform- and compiler-specific information to use56when compiling the source files in 'sources'. For platforms and57compilers where "command line" makes sense, this is typically a58list of command-line arguments, but for other platforms it could59be anything.60extra_link_args : [string]61any extra platform- and compiler-specific information to use62when linking object files together to create the extension (or63to create a new static Python interpreter). Similar64interpretation as for 'extra_compile_args'.65export_symbols : [string]66list of symbols to be exported from a shared extension. Not67used on all platforms, and not generally necessary for Python68extensions, which typically export exactly one symbol: "init" +69extension_name.70swig_opts : [string]71any extra options to pass to SWIG if a source file has the .i72extension.73depends : [string]74list of files that the extension depends on75language : string76extension language (i.e. "c", "c++", "objc"). Will be detected77from the source extensions if not provided.78optional : boolean79specifies that a build failure in the extension should not abort the80build process, but simply not install the failing extension.81"""8283# When adding arguments to this constructor, be sure to update84# setup_keywords in core.py.85def __init__(self, name, sources,86include_dirs=None,87define_macros=None,88undef_macros=None,89library_dirs=None,90libraries=None,91runtime_library_dirs=None,92extra_objects=None,93extra_compile_args=None,94extra_link_args=None,95export_symbols=None,96swig_opts = None,97depends=None,98language=None,99optional=None,100**kw # To catch unknown keywords101):102if not isinstance(name, str):103raise AssertionError("'name' must be a string")104if not (isinstance(sources, list) and105all(isinstance(v, str) for v in sources)):106raise AssertionError("'sources' must be a list of strings")107108self.name = name109self.sources = sources110self.include_dirs = include_dirs or []111self.define_macros = define_macros or []112self.undef_macros = undef_macros or []113self.library_dirs = library_dirs or []114self.libraries = libraries or []115self.runtime_library_dirs = runtime_library_dirs or []116self.extra_objects = extra_objects or []117self.extra_compile_args = extra_compile_args or []118self.extra_link_args = extra_link_args or []119self.export_symbols = export_symbols or []120self.swig_opts = swig_opts or []121self.depends = depends or []122self.language = language123self.optional = optional124125# If there are unknown keyword options, warn about them126if len(kw) > 0:127options = [repr(option) for option in kw]128options = ', '.join(sorted(options))129msg = "Unknown Extension options: %s" % options130warnings.warn(msg)131132def __repr__(self):133return '<%s.%s(%r) at %#x>' % (134self.__class__.__module__,135self.__class__.__qualname__,136self.name,137id(self))138139140def read_setup_file(filename):141"""Reads a Setup file and returns Extension instances."""142from distutils.sysconfig import (parse_makefile, expand_makefile_vars,143_variable_rx)144145from distutils.text_file import TextFile146from distutils.util import split_quoted147148# First pass over the file to gather "VAR = VALUE" assignments.149vars = parse_makefile(filename)150151# Second pass to gobble up the real content: lines of the form152# <module> ... [<sourcefile> ...] [<cpparg> ...] [<library> ...]153file = TextFile(filename,154strip_comments=1, skip_blanks=1, join_lines=1,155lstrip_ws=1, rstrip_ws=1)156try:157extensions = []158159while True:160line = file.readline()161if line is None: # eof162break163if _variable_rx.match(line): # VAR=VALUE, handled in first pass164continue165166if line[0] == line[-1] == "*":167file.warn("'%s' lines not handled yet" % line)168continue169170line = expand_makefile_vars(line, vars)171words = split_quoted(line)172173# NB. this parses a slightly different syntax than the old174# makesetup script: here, there must be exactly one extension per175# line, and it must be the first word of the line. I have no idea176# why the old syntax supported multiple extensions per line, as177# they all wind up being the same.178179module = words[0]180ext = Extension(module, [])181append_next_word = None182183for word in words[1:]:184if append_next_word is not None:185append_next_word.append(word)186append_next_word = None187continue188189suffix = os.path.splitext(word)[1]190switch = word[0:2] ; value = word[2:]191192if suffix in (".c", ".cc", ".cpp", ".cxx", ".c++", ".m", ".mm"):193# hmm, should we do something about C vs. C++ sources?194# or leave it up to the CCompiler implementation to195# worry about?196ext.sources.append(word)197elif switch == "-I":198ext.include_dirs.append(value)199elif switch == "-D":200equals = value.find("=")201if equals == -1: # bare "-DFOO" -- no value202ext.define_macros.append((value, None))203else: # "-DFOO=blah"204ext.define_macros.append((value[0:equals],205value[equals+2:]))206elif switch == "-U":207ext.undef_macros.append(value)208elif switch == "-C": # only here 'cause makesetup has it!209ext.extra_compile_args.append(word)210elif switch == "-l":211ext.libraries.append(value)212elif switch == "-L":213ext.library_dirs.append(value)214elif switch == "-R":215ext.runtime_library_dirs.append(value)216elif word == "-rpath":217append_next_word = ext.runtime_library_dirs218elif word == "-Xlinker":219append_next_word = ext.extra_link_args220elif word == "-Xcompiler":221append_next_word = ext.extra_compile_args222elif switch == "-u":223ext.extra_link_args.append(word)224if not value:225append_next_word = ext.extra_link_args226elif suffix in (".a", ".so", ".sl", ".o", ".dylib"):227# NB. a really faithful emulation of makesetup would228# append a .o file to extra_objects only if it229# had a slash in it; otherwise, it would s/.o/.c/230# and append it to sources. Hmmmm.231ext.extra_objects.append(word)232else:233file.warn("unrecognized argument '%s'" % word)234235extensions.append(ext)236finally:237file.close()238239return extensions240241242