Path: blob/main/test/lib/python3.9/site-packages/setuptools/_distutils/command/config.py
4804 views
"""distutils.command.config12Implements the Distutils 'config' command, a (mostly) empty command class3that exists mainly to be sub-classed by specific module distributions and4applications. The idea is that while every "config" command is different,5at least they're all named the same, and users always see "config" in the6list of standard commands. Also, this is a good place to put common7configure-like tasks: "try to compile this C code", or "figure out where8this header file lives".9"""1011import os, re1213from distutils.core import Command14from distutils.errors import DistutilsExecError15from distutils.sysconfig import customize_compiler16from distutils import log1718LANG_EXT = {"c": ".c", "c++": ".cxx"}1920class config(Command):2122description = "prepare to build"2324user_options = [25('compiler=', None,26"specify the compiler type"),27('cc=', None,28"specify the compiler executable"),29('include-dirs=', 'I',30"list of directories to search for header files"),31('define=', 'D',32"C preprocessor macros to define"),33('undef=', 'U',34"C preprocessor macros to undefine"),35('libraries=', 'l',36"external C libraries to link with"),37('library-dirs=', 'L',38"directories to search for external C libraries"),3940('noisy', None,41"show every action (compile, link, run, ...) taken"),42('dump-source', None,43"dump generated source files before attempting to compile them"),44]454647# The three standard command methods: since the "config" command48# does nothing by default, these are empty.4950def initialize_options(self):51self.compiler = None52self.cc = None53self.include_dirs = None54self.libraries = None55self.library_dirs = None5657# maximal output for now58self.noisy = 159self.dump_source = 16061# list of temporary files generated along-the-way that we have62# to clean at some point63self.temp_files = []6465def finalize_options(self):66if self.include_dirs is None:67self.include_dirs = self.distribution.include_dirs or []68elif isinstance(self.include_dirs, str):69self.include_dirs = self.include_dirs.split(os.pathsep)7071if self.libraries is None:72self.libraries = []73elif isinstance(self.libraries, str):74self.libraries = [self.libraries]7576if self.library_dirs is None:77self.library_dirs = []78elif isinstance(self.library_dirs, str):79self.library_dirs = self.library_dirs.split(os.pathsep)8081def run(self):82pass8384# Utility methods for actual "config" commands. The interfaces are85# loosely based on Autoconf macros of similar names. Sub-classes86# may use these freely.8788def _check_compiler(self):89"""Check that 'self.compiler' really is a CCompiler object;90if not, make it one.91"""92# We do this late, and only on-demand, because this is an expensive93# import.94from distutils.ccompiler import CCompiler, new_compiler95if not isinstance(self.compiler, CCompiler):96self.compiler = new_compiler(compiler=self.compiler,97dry_run=self.dry_run, force=1)98customize_compiler(self.compiler)99if self.include_dirs:100self.compiler.set_include_dirs(self.include_dirs)101if self.libraries:102self.compiler.set_libraries(self.libraries)103if self.library_dirs:104self.compiler.set_library_dirs(self.library_dirs)105106def _gen_temp_sourcefile(self, body, headers, lang):107filename = "_configtest" + LANG_EXT[lang]108with open(filename, "w") as file:109if headers:110for header in headers:111file.write("#include <%s>\n" % header)112file.write("\n")113file.write(body)114if body[-1] != "\n":115file.write("\n")116return filename117118def _preprocess(self, body, headers, include_dirs, lang):119src = self._gen_temp_sourcefile(body, headers, lang)120out = "_configtest.i"121self.temp_files.extend([src, out])122self.compiler.preprocess(src, out, include_dirs=include_dirs)123return (src, out)124125def _compile(self, body, headers, include_dirs, lang):126src = self._gen_temp_sourcefile(body, headers, lang)127if self.dump_source:128dump_file(src, "compiling '%s':" % src)129(obj,) = self.compiler.object_filenames([src])130self.temp_files.extend([src, obj])131self.compiler.compile([src], include_dirs=include_dirs)132return (src, obj)133134def _link(self, body, headers, include_dirs, libraries, library_dirs,135lang):136(src, obj) = self._compile(body, headers, include_dirs, lang)137prog = os.path.splitext(os.path.basename(src))[0]138self.compiler.link_executable([obj], prog,139libraries=libraries,140library_dirs=library_dirs,141target_lang=lang)142143if self.compiler.exe_extension is not None:144prog = prog + self.compiler.exe_extension145self.temp_files.append(prog)146147return (src, obj, prog)148149def _clean(self, *filenames):150if not filenames:151filenames = self.temp_files152self.temp_files = []153log.info("removing: %s", ' '.join(filenames))154for filename in filenames:155try:156os.remove(filename)157except OSError:158pass159160161# XXX these ignore the dry-run flag: what to do, what to do? even if162# you want a dry-run build, you still need some sort of configuration163# info. My inclination is to make it up to the real config command to164# consult 'dry_run', and assume a default (minimal) configuration if165# true. The problem with trying to do it here is that you'd have to166# return either true or false from all the 'try' methods, neither of167# which is correct.168169# XXX need access to the header search path and maybe default macros.170171def try_cpp(self, body=None, headers=None, include_dirs=None, lang="c"):172"""Construct a source file from 'body' (a string containing lines173of C/C++ code) and 'headers' (a list of header files to include)174and run it through the preprocessor. Return true if the175preprocessor succeeded, false if there were any errors.176('body' probably isn't of much use, but what the heck.)177"""178from distutils.ccompiler import CompileError179self._check_compiler()180ok = True181try:182self._preprocess(body, headers, include_dirs, lang)183except CompileError:184ok = False185186self._clean()187return ok188189def search_cpp(self, pattern, body=None, headers=None, include_dirs=None,190lang="c"):191"""Construct a source file (just like 'try_cpp()'), run it through192the preprocessor, and return true if any line of the output matches193'pattern'. 'pattern' should either be a compiled regex object or a194string containing a regex. If both 'body' and 'headers' are None,195preprocesses an empty file -- which can be useful to determine the196symbols the preprocessor and compiler set by default.197"""198self._check_compiler()199src, out = self._preprocess(body, headers, include_dirs, lang)200201if isinstance(pattern, str):202pattern = re.compile(pattern)203204with open(out) as file:205match = False206while True:207line = file.readline()208if line == '':209break210if pattern.search(line):211match = True212break213214self._clean()215return match216217def try_compile(self, body, headers=None, include_dirs=None, lang="c"):218"""Try to compile a source file built from 'body' and 'headers'.219Return true on success, false otherwise.220"""221from distutils.ccompiler import CompileError222self._check_compiler()223try:224self._compile(body, headers, include_dirs, lang)225ok = True226except CompileError:227ok = False228229log.info(ok and "success!" or "failure.")230self._clean()231return ok232233def try_link(self, body, headers=None, include_dirs=None, libraries=None,234library_dirs=None, lang="c"):235"""Try to compile and link a source file, built from 'body' and236'headers', to executable form. Return true on success, false237otherwise.238"""239from distutils.ccompiler import CompileError, LinkError240self._check_compiler()241try:242self._link(body, headers, include_dirs,243libraries, library_dirs, lang)244ok = True245except (CompileError, LinkError):246ok = False247248log.info(ok and "success!" or "failure.")249self._clean()250return ok251252def try_run(self, body, headers=None, include_dirs=None, libraries=None,253library_dirs=None, lang="c"):254"""Try to compile, link to an executable, and run a program255built from 'body' and 'headers'. Return true on success, false256otherwise.257"""258from distutils.ccompiler import CompileError, LinkError259self._check_compiler()260try:261src, obj, exe = self._link(body, headers, include_dirs,262libraries, library_dirs, lang)263self.spawn([exe])264ok = True265except (CompileError, LinkError, DistutilsExecError):266ok = False267268log.info(ok and "success!" or "failure.")269self._clean()270return ok271272273# -- High-level methods --------------------------------------------274# (these are the ones that are actually likely to be useful275# when implementing a real-world config command!)276277def check_func(self, func, headers=None, include_dirs=None,278libraries=None, library_dirs=None, decl=0, call=0):279"""Determine if function 'func' is available by constructing a280source file that refers to 'func', and compiles and links it.281If everything succeeds, returns true; otherwise returns false.282283The constructed source file starts out by including the header284files listed in 'headers'. If 'decl' is true, it then declares285'func' (as "int func()"); you probably shouldn't supply 'headers'286and set 'decl' true in the same call, or you might get errors about287a conflicting declarations for 'func'. Finally, the constructed288'main()' function either references 'func' or (if 'call' is true)289calls it. 'libraries' and 'library_dirs' are used when290linking.291"""292self._check_compiler()293body = []294if decl:295body.append("int %s ();" % func)296body.append("int main () {")297if call:298body.append(" %s();" % func)299else:300body.append(" %s;" % func)301body.append("}")302body = "\n".join(body) + "\n"303304return self.try_link(body, headers, include_dirs,305libraries, library_dirs)306307def check_lib(self, library, library_dirs=None, headers=None,308include_dirs=None, other_libraries=[]):309"""Determine if 'library' is available to be linked against,310without actually checking that any particular symbols are provided311by it. 'headers' will be used in constructing the source file to312be compiled, but the only effect of this is to check if all the313header files listed are available. Any libraries listed in314'other_libraries' will be included in the link, in case 'library'315has symbols that depend on other libraries.316"""317self._check_compiler()318return self.try_link("int main (void) { }", headers, include_dirs,319[library] + other_libraries, library_dirs)320321def check_header(self, header, include_dirs=None, library_dirs=None,322lang="c"):323"""Determine if the system header file named by 'header_file'324exists and can be found by the preprocessor; return true if so,325false otherwise.326"""327return self.try_cpp(body="/* No body */", headers=[header],328include_dirs=include_dirs)329330def dump_file(filename, head=None):331"""Dumps a file content into log.info.332333If head is not None, will be dumped before the file content.334"""335if head is None:336log.info('%s', filename)337else:338log.info(head)339file = open(filename)340try:341log.info(file.read())342finally:343file.close()344345346