]>
jfr.im git - dlqueue.git/blob - venv/lib/python3.11/site-packages/setuptools/command/build_ext.py
4 from importlib
.machinery
import EXTENSION_SUFFIXES
5 from importlib
.util
import cache_from_source
as _compiled_file_name
6 from typing
import Dict
, Iterator
, List
, Tuple
7 from pathlib
import Path
9 from distutils
.command
.build_ext
import build_ext
as _du_build_ext
10 from distutils
.ccompiler
import new_compiler
11 from distutils
.sysconfig
import customize_compiler
, get_config_var
12 from distutils
import log
14 from setuptools
.errors
import BaseError
15 from setuptools
.extension
import Extension
, Library
18 # Attempt to use Cython for building extensions, if available
19 from Cython
.Distutils
.build_ext
import build_ext
as _build_ext
21 # Additionally, assert that the compiler module will load
23 __import__('Cython.Compiler.Main')
25 _build_ext
= _du_build_ext
27 # make sure _config_vars is initialized
28 get_config_var("LDSHARED")
29 from distutils
.sysconfig
import _config_vars
as _CONFIG_VARS
# noqa
32 def _customize_compiler_for_shlib(compiler
):
33 if sys
.platform
== "darwin":
34 # building .dylib requires additional compiler flags on OSX; here we
35 # temporarily substitute the pyconfig.h variables so that distutils'
36 # 'customize_compiler' uses them before we build the shared libraries.
37 tmp
= _CONFIG_VARS
.copy()
39 # XXX Help! I don't have any idea whether these are right...
42 ] = "gcc -Wl,-x -dynamiclib -undefined dynamic_lookup"
43 _CONFIG_VARS
['CCSHARED'] = " -dynamiclib"
44 _CONFIG_VARS
['SO'] = ".dylib"
45 customize_compiler(compiler
)
48 _CONFIG_VARS
.update(tmp
)
50 customize_compiler(compiler
)
57 if sys
.platform
== "darwin":
63 use_stubs
= have_rtld
= hasattr(dl
, 'RTLD_NOW')
69 return s
if have_rtld
else ''
72 def get_abi3_suffix():
73 """Return the file extension for an abi3-compliant Extension()"""
74 for suffix
in EXTENSION_SUFFIXES
:
75 if '.abi3' in suffix
: # Unix
77 elif suffix
== '.pyd': # Windows
81 class build_ext(_build_ext
):
82 editable_mode
: bool = False
86 """Build extensions in build directory, then copy if --inplace"""
87 old_inplace
, self
.inplace
= self
.inplace
, 0
89 self
.inplace
= old_inplace
91 self
.copy_extensions_to_source()
93 def _get_inplace_equivalent(self
, build_py
, ext
: Extension
) -> Tuple
[str, str]:
94 fullname
= self
.get_ext_fullname(ext
.name
)
95 filename
= self
.get_ext_filename(fullname
)
96 modpath
= fullname
.split('.')
97 package
= '.'.join(modpath
[:-1])
98 package_dir
= build_py
.get_package_dir(package
)
99 inplace_file
= os
.path
.join(package_dir
, os
.path
.basename(filename
))
100 regular_file
= os
.path
.join(self
.build_lib
, filename
)
101 return (inplace_file
, regular_file
)
103 def copy_extensions_to_source(self
):
104 build_py
= self
.get_finalized_command('build_py')
105 for ext
in self
.extensions
:
106 inplace_file
, regular_file
= self
._get
_inplace
_equivalent
(build_py
, ext
)
108 # Always copy, even if source is older than destination, to ensure
109 # that the right extensions for the current Python/platform are
111 if os
.path
.exists(regular_file
) or not ext
.optional
:
112 self
.copy_file(regular_file
, inplace_file
, level
=self
.verbose
)
115 inplace_stub
= self
._get
_equivalent
_stub
(ext
, inplace_file
)
116 self
._write
_stub
_file
(inplace_stub
, ext
, compile=True)
117 # Always compile stub and remove the original (leave the cache behind)
118 # (this behaviour was observed in previous iterations of the code)
120 def _get_equivalent_stub(self
, ext
: Extension
, output_file
: str) -> str:
121 dir_
= os
.path
.dirname(output_file
)
122 _
, _
, name
= ext
.name
.rpartition(".")
123 return f
"{os.path.join(dir_, name)}.py"
125 def _get_output_mapping(self
) -> Iterator
[Tuple
[str, str]]:
129 build_py
= self
.get_finalized_command('build_py')
130 opt
= self
.get_finalized_command('install_lib').optimize
or ""
132 for ext
in self
.extensions
:
133 inplace_file
, regular_file
= self
._get
_inplace
_equivalent
(build_py
, ext
)
134 yield (regular_file
, inplace_file
)
137 # This version of `build_ext` always builds artifacts in another dir,
138 # when "inplace=True" is given it just copies them back.
139 # This is done in the `copy_extensions_to_source` function, which
140 # always compile stub files via `_compile_and_remove_stub`.
141 # At the end of the process, a `.pyc` stub file is created without the
142 # corresponding `.py`.
144 inplace_stub
= self
._get
_equivalent
_stub
(ext
, inplace_file
)
145 regular_stub
= self
._get
_equivalent
_stub
(ext
, regular_file
)
146 inplace_cache
= _compiled_file_name(inplace_stub
, optimization
=opt
)
147 output_cache
= _compiled_file_name(regular_stub
, optimization
=opt
)
148 yield (output_cache
, inplace_cache
)
150 def get_ext_filename(self
, fullname
):
151 so_ext
= os
.getenv('SETUPTOOLS_EXT_SUFFIX')
153 filename
= os
.path
.join(*fullname
.split('.')) + so_ext
155 filename
= _build_ext
.get_ext_filename(self
, fullname
)
156 so_ext
= get_config_var('EXT_SUFFIX')
158 if fullname
in self
.ext_map
:
159 ext
= self
.ext_map
[fullname
]
160 use_abi3
= getattr(ext
, 'py_limited_api') and get_abi3_suffix()
162 filename
= filename
[: -len(so_ext
)]
163 so_ext
= get_abi3_suffix()
164 filename
= filename
+ so_ext
165 if isinstance(ext
, Library
):
166 fn
, ext
= os
.path
.splitext(filename
)
167 return self
.shlib_compiler
.library_filename(fn
, libtype
)
168 elif use_stubs
and ext
._links
_to
_dynamic
:
169 d
, fn
= os
.path
.split(filename
)
170 return os
.path
.join(d
, 'dl-' + fn
)
173 def initialize_options(self
):
174 _build_ext
.initialize_options(self
)
175 self
.shlib_compiler
= None
178 self
.editable_mode
= False
180 def finalize_options(self
):
181 _build_ext
.finalize_options(self
)
182 self
.extensions
= self
.extensions
or []
183 self
.check_extensions_list(self
.extensions
)
184 self
.shlibs
= [ext
for ext
in self
.extensions
if isinstance(ext
, Library
)]
186 self
.setup_shlib_compiler()
187 for ext
in self
.extensions
:
188 ext
._full
_name
= self
.get_ext_fullname(ext
.name
)
189 for ext
in self
.extensions
:
190 fullname
= ext
._full
_name
191 self
.ext_map
[fullname
] = ext
193 # distutils 3.1 will also ask for module names
194 # XXX what to do with conflicts?
195 self
.ext_map
[fullname
.split('.')[-1]] = ext
197 ltd
= self
.shlibs
and self
.links_to_dynamic(ext
) or False
198 ns
= ltd
and use_stubs
and not isinstance(ext
, Library
)
199 ext
._links
_to
_dynamic
= ltd
201 filename
= ext
._file
_name
= self
.get_ext_filename(fullname
)
202 libdir
= os
.path
.dirname(os
.path
.join(self
.build_lib
, filename
))
203 if ltd
and libdir
not in ext
.library_dirs
:
204 ext
.library_dirs
.append(libdir
)
205 if ltd
and use_stubs
and os
.curdir
not in ext
.runtime_library_dirs
:
206 ext
.runtime_library_dirs
.append(os
.curdir
)
208 if self
.editable_mode
:
211 def setup_shlib_compiler(self
):
212 compiler
= self
.shlib_compiler
= new_compiler(
213 compiler
=self
.compiler
, dry_run
=self
.dry_run
, force
=self
.force
215 _customize_compiler_for_shlib(compiler
)
217 if self
.include_dirs
is not None:
218 compiler
.set_include_dirs(self
.include_dirs
)
219 if self
.define
is not None:
220 # 'define' option is a list of (name,value) tuples
221 for name
, value
in self
.define
:
222 compiler
.define_macro(name
, value
)
223 if self
.undef
is not None:
224 for macro
in self
.undef
:
225 compiler
.undefine_macro(macro
)
226 if self
.libraries
is not None:
227 compiler
.set_libraries(self
.libraries
)
228 if self
.library_dirs
is not None:
229 compiler
.set_library_dirs(self
.library_dirs
)
230 if self
.rpath
is not None:
231 compiler
.set_runtime_library_dirs(self
.rpath
)
232 if self
.link_objects
is not None:
233 compiler
.set_link_objects(self
.link_objects
)
235 # hack so distutils' build_extension() builds a library instead
236 compiler
.link_shared_object
= link_shared_object
.__get
__(compiler
)
238 def get_export_symbols(self
, ext
):
239 if isinstance(ext
, Library
):
240 return ext
.export_symbols
241 return _build_ext
.get_export_symbols(self
, ext
)
243 def build_extension(self
, ext
):
244 ext
._convert
_pyx
_sources
_to
_lang
()
245 _compiler
= self
.compiler
247 if isinstance(ext
, Library
):
248 self
.compiler
= self
.shlib_compiler
249 _build_ext
.build_extension(self
, ext
)
251 build_lib
= self
.get_finalized_command('build_py').build_lib
252 self
.write_stub(build_lib
, ext
)
254 self
.compiler
= _compiler
256 def links_to_dynamic(self
, ext
):
257 """Return true if 'ext' links to a dynamic lib in the same package"""
258 # XXX this should check to ensure the lib is actually being built
259 # XXX as dynamic, and not just using a locally-found version or a
260 # XXX static-compiled version
261 libnames
= dict.fromkeys([lib
._full
_name
for lib
in self
.shlibs
])
262 pkg
= '.'.join(ext
._full
_name
.split('.')[:-1] + [''])
263 return any(pkg
+ libname
in libnames
for libname
in ext
.libraries
)
265 def get_source_files(self
) -> List
[str]:
266 return [*_build_ext
.get_source_files(self
), *self
._get
_internal
_depends
()]
268 def _get_internal_depends(self
) -> Iterator
[str]:
269 """Yield ``ext.depends`` that are contained by the project directory"""
270 project_root
= Path(self
.distribution
.src_root
or os
.curdir
).resolve()
271 depends
= (dep
for ext
in self
.extensions
for dep
in ext
.depends
)
273 def skip(orig_path
: str, reason
: str) -> None:
275 "dependency %s won't be automatically "
276 "included in the manifest: the path %s",
284 if path
.is_absolute():
285 skip(dep
, "must be relative")
288 if ".." in path
.parts
:
289 skip(dep
, "can't have `..` segments")
293 resolved
= (project_root
/ path
).resolve(strict
=True)
295 skip(dep
, "doesn't exist")
299 resolved
.relative_to(project_root
)
301 skip(dep
, "must be inside the project root")
304 yield path
.as_posix()
306 def get_outputs(self
) -> List
[str]:
308 return list(self
.get_output_mapping().keys())
309 return sorted(_build_ext
.get_outputs(self
) + self
.__get
_stubs
_outputs
())
311 def get_output_mapping(self
) -> Dict
[str, str]:
312 """See :class:`setuptools.commands.build.SubCommand`"""
313 mapping
= self
._get
_output
_mapping
()
314 return dict(sorted(mapping
, key
=lambda x
: x
[0]))
316 def __get_stubs_outputs(self
):
317 # assemble the base name for each extension that needs a stub
319 os
.path
.join(self
.build_lib
, *ext
._full
_name
.split('.'))
320 for ext
in self
.extensions
323 # pair each base with the extension
324 pairs
= itertools
.product(ns_ext_bases
, self
.__get
_output
_extensions
())
325 return list(base
+ fnext
for base
, fnext
in pairs
)
327 def __get_output_extensions(self
):
330 if self
.get_finalized_command('build_py').optimize
:
333 def write_stub(self
, output_dir
, ext
, compile=False):
334 stub_file
= os
.path
.join(output_dir
, *ext
._full
_name
.split('.')) + '.py'
335 self
._write
_stub
_file
(stub_file
, ext
, compile)
337 def _write_stub_file(self
, stub_file
: str, ext
: Extension
, compile=False):
338 log
.info("writing stub loader for %s to %s", ext
._full
_name
, stub_file
)
339 if compile and os
.path
.exists(stub_file
):
340 raise BaseError(stub_file
+ " already exists! Please delete.")
342 f
= open(stub_file
, 'w')
346 "def __bootstrap__():",
347 " global __bootstrap__, __file__, __loader__",
348 " import sys, os, pkg_resources, importlib.util"
350 " __file__ = pkg_resources.resource_filename"
351 "(__name__,%r)" % os
.path
.basename(ext
._file
_name
),
352 " del __bootstrap__",
353 " if '__loader__' in globals():",
355 if_dl(" old_flags = sys.getdlopenflags()"),
356 " old_dir = os.getcwd()",
358 " os.chdir(os.path.dirname(__file__))",
359 if_dl(" sys.setdlopenflags(dl.RTLD_NOW)"),
360 " spec = importlib.util.spec_from_file_location(",
361 " __name__, __file__)",
362 " mod = importlib.util.module_from_spec(spec)",
363 " spec.loader.exec_module(mod)",
365 if_dl(" sys.setdlopenflags(old_flags)"),
366 " os.chdir(old_dir)",
374 self
._compile
_and
_remove
_stub
(stub_file
)
376 def _compile_and_remove_stub(self
, stub_file
: str):
377 from distutils
.util
import byte_compile
379 byte_compile([stub_file
], optimize
=0, force
=True, dry_run
=self
.dry_run
)
380 optimize
= self
.get_finalized_command('install_lib').optimize
383 [stub_file
], optimize
=optimize
, force
=True, dry_run
=self
.dry_run
385 if os
.path
.exists(stub_file
) and not self
.dry_run
:
389 if use_stubs
or os
.name
== 'nt':
390 # Build shared libraries
392 def link_shared_object(
399 runtime_library_dirs
=None,
414 runtime_library_dirs
,
424 # Build static libraries everywhere else
427 def link_shared_object(
434 runtime_library_dirs
=None,
442 # XXX we need to either disallow these attrs on Library instances,
443 # or warn/abort here if set, or something...
444 # libraries=None, library_dirs=None, runtime_library_dirs=None,
445 # export_symbols=None, extra_preargs=None, extra_postargs=None,
448 assert output_dir
is None # distutils build_ext doesn't pass this
449 output_dir
, filename
= os
.path
.split(output_libname
)
450 basename
, ext
= os
.path
.splitext(filename
)
451 if self
.library_filename("x").startswith('lib'):
452 # strip 'lib' prefix; this is kludgy if some platform uses
454 basename
= basename
[3:]
456 self
.create_static_lib(objects
, basename
, output_dir
, debug
, target_lang
)