Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
build_clib.py200 linesDownload Raw Back to command
1"""distutils.command.build_clib2 3Implements the Distutils 'build_clib' command, to build a C/C++ library4that is included in the module distribution and needed by an extension5module."""6 7# XXX this module has *lots* of code ripped-off quite transparently from8# build_ext.py -- not surprisingly really, as the work required to build9# a static library from a collection of C source files is not really all10# that different from what's required to build a shared object file from11# a collection of C source files.  Nevertheless, I haven't done the12# necessary refactoring to account for the overlap in code between the13# two modules, mainly because a number of subtle details changed in the14# cut 'n paste.  Sigh.15from __future__ import annotations16 17import os18from collections.abc import Callable19from distutils._log import log20from typing import ClassVar21 22from ..ccompiler import new_compiler, show_compilers23from ..core import Command24from ..errors import DistutilsSetupError25from ..sysconfig import customize_compiler26 27 28class build_clib(Command):29    description = "build C/C++ libraries used by Python extensions"30 31    user_options: ClassVar[list[tuple[str, str, str]]] = [32        ('build-clib=', 'b', "directory to build C/C++ libraries to"),33        ('build-temp=', 't', "directory to put temporary build by-products"),34        ('debug', 'g', "compile with debugging information"),35        ('force', 'f', "forcibly build everything (ignore file timestamps)"),36        ('compiler=', 'c', "specify the compiler type"),37    ]38 39    boolean_options: ClassVar[list[str]] = ['debug', 'force']40 41    help_options: ClassVar[list[tuple[str, str | None, str, Callable[[], object]]]] = [42        ('help-compiler', None, "list available compilers", show_compilers),43    ]44 45    def initialize_options(self):46        self.build_clib = None47        self.build_temp = None48 49        # List of libraries to build50        self.libraries = None51 52        # Compilation options for all libraries53        self.include_dirs = None54        self.define = None55        self.undef = None56        self.debug = None57        self.force = False58        self.compiler = None59 60    def finalize_options(self) -> None:61        # This might be confusing: both build-clib and build-temp default62        # to build-temp as defined by the "build" command.  This is because63        # I think that C libraries are really just temporary build64        # by-products, at least from the point of view of building Python65        # extensions -- but I want to keep my options open.66        self.set_undefined_options(67            'build',68            ('build_temp', 'build_clib'),69            ('build_temp', 'build_temp'),70            ('compiler', 'compiler'),71            ('debug', 'debug'),72            ('force', 'force'),73        )74 75        self.libraries = self.distribution.libraries76        if self.libraries:77            self.check_library_list(self.libraries)78 79        if self.include_dirs is None:80            self.include_dirs = self.distribution.include_dirs or []81        if isinstance(self.include_dirs, str):82            self.include_dirs = self.include_dirs.split(os.pathsep)83 84        # XXX same as for build_ext -- what about 'self.define' and85        # 'self.undef' ?86 87    def run(self) -> None:88        if not self.libraries:89            return90 91        self.compiler = new_compiler(compiler=self.compiler, force=self.force)92        customize_compiler(self.compiler)93 94        if self.include_dirs is not None:95            self.compiler.set_include_dirs(self.include_dirs)96        if self.define is not None:97            # 'define' option is a list of (name,value) tuples98            for name, value in self.define:99                self.compiler.define_macro(name, value)100        if self.undef is not None:101            for macro in self.undef:102                self.compiler.undefine_macro(macro)103 104        self.build_libraries(self.libraries)105 106    def check_library_list(self, libraries) -> None:107        """Ensure that the list of libraries is valid.108 109        `library` is presumably provided as a command option 'libraries'.110        This method checks that it is a list of 2-tuples, where the tuples111        are (library_name, build_info_dict).112 113        Raise DistutilsSetupError if the structure is invalid anywhere;114        just returns otherwise.115        """116        if not isinstance(libraries, list):117            raise DistutilsSetupError("'libraries' option must be a list of tuples")118 119        for lib in libraries:120            if not isinstance(lib, tuple) and len(lib) != 2:121                raise DistutilsSetupError("each element of 'libraries' must a 2-tuple")122 123            name, build_info = lib124 125            if not isinstance(name, str):126                raise DistutilsSetupError(127                    "first element of each tuple in 'libraries' "128                    "must be a string (the library name)"129                )130 131            if '/' in name or (os.sep != '/' and os.sep in name):132                raise DistutilsSetupError(133                    f"bad library name '{lib[0]}': may not contain directory separators"134                )135 136            if not isinstance(build_info, dict):137                raise DistutilsSetupError(138                    "second element of each tuple in 'libraries' "139                    "must be a dictionary (build info)"140                )141 142    def get_library_names(self):143        # Assume the library list is valid -- 'check_library_list()' is144        # called from 'finalize_options()', so it should be!145        if not self.libraries:146            return None147 148        lib_names = []149        for lib_name, _build_info in self.libraries:150            lib_names.append(lib_name)151        return lib_names152 153    def get_source_files(self):154        self.check_library_list(self.libraries)155        filenames = []156        for lib_name, build_info in self.libraries:157            sources = build_info.get('sources')158            if sources is None or not isinstance(sources, (list, tuple)):159                raise DistutilsSetupError(160                    f"in 'libraries' option (library '{lib_name}'), "161                    "'sources' must be present and must be "162                    "a list of source filenames"163                )164 165            filenames.extend(sources)166        return filenames167 168    def build_libraries(self, libraries) -> None:169        for lib_name, build_info in libraries:170            sources = build_info.get('sources')171            if sources is None or not isinstance(sources, (list, tuple)):172                raise DistutilsSetupError(173                    f"in 'libraries' option (library '{lib_name}'), "174                    "'sources' must be present and must be "175                    "a list of source filenames"176                )177            sources = list(sources)178 179            log.info("building '%s' library", lib_name)180 181            # First, compile the source code to object files in the library182            # directory.  (This should probably change to putting object183            # files in a temporary build directory.)184            macros = build_info.get('macros')185            include_dirs = build_info.get('include_dirs')186            objects = self.compiler.compile(187                sources,188                output_dir=self.build_temp,189                macros=macros,190                include_dirs=include_dirs,191                debug=self.debug,192            )193 194            # Now "link" the object files together into a static library.195            # (On Unix at least, this isn't really linking -- it just196            # builds an archive.  Whatever.)197            self.compiler.create_static_lib(198                objects, lib_name, output_dir=self.build_clib, debug=self.debug199            )200 
codekingpro/portable-devtools · Team Ai