Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
extension.py259 linesDownload Raw Back to _distutils
1"""distutils.extension2 3Provides the Extension class, used to describe C/C++ extension4modules in setup scripts."""5 6from __future__ import annotations7 8import os9import warnings10from collections.abc import Iterable11 12# This class is really only used by the "build_ext" command, so it might13# make sense to put it in distutils.command.build_ext.  However, that14# module is already big enough, and I want to make this class a bit more15# complex to simplify some common cases ("foo" module in "foo.c") and do16# better error-checking ("foo.c" actually exists).17#18# Also, putting this in build_ext.py means every setup script would have to19# import that large-ish module (indirectly, through distutils.core) in20# order to do anything.21 22 23class Extension:24    """Just a collection of attributes that describes an extension25    module and everything needed to build it (hopefully in a portable26    way, but there are hooks that let you be as unportable as you need).27 28    Instance attributes:29      name : string30        the full name of the extension, including any packages -- ie.31        *not* a filename or pathname, but Python dotted name32      sources : Iterable[string | os.PathLike]33        iterable of source filenames (except strings, which could be misinterpreted34        as a single filename), relative to the distribution root (where the setup35        script lives), in Unix form (slash-separated) for portability. Can be any36        non-string iterable (list, tuple, set, etc.) containing strings or37        PathLike objects. Source files may be C, C++, SWIG (.i), platform-specific38        resource files, or whatever else is recognized by the "build_ext" command39        as source for a Python extension.40      include_dirs : [string]41        list of directories to search for C/C++ header files (in Unix42        form for portability)43      define_macros : [(name : string, value : string|None)]44        list of macros to define; each macro is defined using a 2-tuple,45        where 'value' is either the string to define it to or None to46        define it without a particular value (equivalent of "#define47        FOO" in source or -DFOO on Unix C compiler command line)48      undef_macros : [string]49        list of macros to undefine explicitly50      library_dirs : [string]51        list of directories to search for C/C++ libraries at link time52      libraries : [string]53        list of library names (not filenames or paths) to link against54      runtime_library_dirs : [string]55        list of directories to search for C/C++ libraries at run time56        (for shared extensions, this is when the extension is loaded)57      extra_objects : [string]58        list of extra files to link with (eg. object files not implied59        by 'sources', static library that must be explicitly specified,60        binary resource files, etc.)61      extra_compile_args : [string]62        any extra platform- and compiler-specific information to use63        when compiling the source files in 'sources'.  For platforms and64        compilers where "command line" makes sense, this is typically a65        list of command-line arguments, but for other platforms it could66        be anything.67      extra_link_args : [string]68        any extra platform- and compiler-specific information to use69        when linking object files together to create the extension (or70        to create a new static Python interpreter).  Similar71        interpretation as for 'extra_compile_args'.72      export_symbols : [string]73        list of symbols to be exported from a shared extension.  Not74        used on all platforms, and not generally necessary for Python75        extensions, which typically export exactly one symbol: "init" +76        extension_name.77      swig_opts : [string]78        any extra options to pass to SWIG if a source file has the .i79        extension.80      depends : [string]81        list of files that the extension depends on82      language : string83        extension language (i.e. "c", "c++", "objc"). Will be detected84        from the source extensions if not provided.85      optional : boolean86        specifies that a build failure in the extension should not abort the87        build process, but simply not install the failing extension.88    """89 90    # When adding arguments to this constructor, be sure to update91    # setup_keywords in core.py.92    def __init__(93        self,94        name: str,95        sources: Iterable[str | os.PathLike[str]],96        include_dirs: list[str] | None = None,97        define_macros: list[tuple[str, str | None]] | None = None,98        undef_macros: list[str] | None = None,99        library_dirs: list[str] | None = None,100        libraries: list[str] | None = None,101        runtime_library_dirs: list[str] | None = None,102        extra_objects: list[str] | None = None,103        extra_compile_args: list[str] | None = None,104        extra_link_args: list[str] | None = None,105        export_symbols: list[str] | None = None,106        swig_opts: list[str] | None = None,107        depends: list[str] | None = None,108        language: str | None = None,109        optional: bool | None = None,110        **kw,  # To catch unknown keywords111    ):112        if not isinstance(name, str):113            raise TypeError("'name' must be a string")114 115        # handle the string case first; since strings are iterable, disallow them116        if isinstance(sources, str):117            raise TypeError(118                "'sources' must be an iterable of strings or PathLike objects, not a string"119            )120 121        # now we check if it's iterable and contains valid types122        try:123            self.sources = list(map(os.fspath, sources))124        except TypeError:125            raise TypeError(126                "'sources' must be an iterable of strings or PathLike objects"127            )128 129        self.name = name130        self.include_dirs = include_dirs or []131        self.define_macros = define_macros or []132        self.undef_macros = undef_macros or []133        self.library_dirs = library_dirs or []134        self.libraries = libraries or []135        self.runtime_library_dirs = runtime_library_dirs or []136        self.extra_objects = extra_objects or []137        self.extra_compile_args = extra_compile_args or []138        self.extra_link_args = extra_link_args or []139        self.export_symbols = export_symbols or []140        self.swig_opts = swig_opts or []141        self.depends = depends or []142        self.language = language143        self.optional = optional144 145        # If there are unknown keyword options, warn about them146        if len(kw) > 0:147            options = [repr(option) for option in kw]148            options = ', '.join(sorted(options))149            msg = f"Unknown Extension options: {options}"150            warnings.warn(msg)151 152    def __repr__(self):153        return f'<{self.__class__.__module__}.{self.__class__.__qualname__}({self.name!r}) at {id(self):#x}>'154 155 156def read_setup_file(filename):  # noqa: C901157    """Reads a Setup file and returns Extension instances."""158    from distutils.sysconfig import _variable_rx, expand_makefile_vars, parse_makefile159    from distutils.text_file import TextFile160    from distutils.util import split_quoted161 162    # First pass over the file to gather "VAR = VALUE" assignments.163    vars = parse_makefile(filename)164 165    # Second pass to gobble up the real content: lines of the form166    #   <module> ... [<sourcefile> ...] [<cpparg> ...] [<library> ...]167    file = TextFile(168        filename,169        strip_comments=True,170        skip_blanks=True,171        join_lines=True,172        lstrip_ws=True,173        rstrip_ws=True,174    )175    try:176        extensions = []177 178        while True:179            line = file.readline()180            if line is None:  # eof181                break182            if _variable_rx.match(line):  # VAR=VALUE, handled in first pass183                continue184 185            if line[0] == line[-1] == "*":186                file.warn(f"'{line}' lines not handled yet")187                continue188 189            line = expand_makefile_vars(line, vars)190            words = split_quoted(line)191 192            # NB. this parses a slightly different syntax than the old193            # makesetup script: here, there must be exactly one extension per194            # line, and it must be the first word of the line.  I have no idea195            # why the old syntax supported multiple extensions per line, as196            # they all wind up being the same.197 198            module = words[0]199            ext = Extension(module, [])200            append_next_word = None201 202            for word in words[1:]:203                if append_next_word is not None:204                    append_next_word.append(word)205                    append_next_word = None206                    continue207 208                suffix = os.path.splitext(word)[1]209                switch = word[0:2]210                value = word[2:]211 212                if suffix in (".c", ".cc", ".cpp", ".cxx", ".c++", ".m", ".mm"):213                    # hmm, should we do something about C vs. C++ sources?214                    # or leave it up to the CCompiler implementation to215                    # worry about?216                    ext.sources.append(word)217                elif switch == "-I":218                    ext.include_dirs.append(value)219                elif switch == "-D":220                    equals = value.find("=")221                    if equals == -1:  # bare "-DFOO" -- no value222                        ext.define_macros.append((value, None))223                    else:  # "-DFOO=blah"224                        ext.define_macros.append((value[0:equals], value[equals + 2 :]))225                elif switch == "-U":226                    ext.undef_macros.append(value)227                elif switch == "-C":  # only here 'cause makesetup has it!228                    ext.extra_compile_args.append(word)229                elif switch == "-l":230                    ext.libraries.append(value)231                elif switch == "-L":232                    ext.library_dirs.append(value)233                elif switch == "-R":234                    ext.runtime_library_dirs.append(value)235                elif word == "-rpath":236                    append_next_word = ext.runtime_library_dirs237                elif word == "-Xlinker":238                    append_next_word = ext.extra_link_args239                elif word == "-Xcompiler":240                    append_next_word = ext.extra_compile_args241                elif switch == "-u":242                    ext.extra_link_args.append(word)243                    if not value:244                        append_next_word = ext.extra_link_args245                elif suffix in (".a", ".so", ".sl", ".o", ".dylib"):246                    # NB. a really faithful emulation of makesetup would247                    # append a .o file to extra_objects only if it248                    # had a slash in it; otherwise, it would s/.o/.c/249                    # and append it to sources.  Hmmmm.250                    ext.extra_objects.append(word)251                else:252                    file.warn(f"unrecognized argument '{word}'")253 254            extensions.append(ext)255    finally:256        file.close()257 258    return extensions259 
codekingpro/portable-devtools · Team Ai