4 This module provides helpers for C++11+ projects using pybind11.
8 Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>, All rights reserved.
10 Redistribution and use in source and binary forms, with or without
11 modification, are permitted provided that the following conditions are met:
13 1. Redistributions of source code must retain the above copyright notice, this
14 list of conditions and the following disclaimer.
16 2. Redistributions in binary form must reproduce the above copyright notice,
17 this list of conditions and the following disclaimer in the documentation
18 and/or other materials provided with the distribution.
20 3. Neither the name of the copyright holder nor the names of its contributors
21 may be used to endorse or promote products derived from this software
22 without specific prior written permission.
24 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
25 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
26 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
28 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
52 from setuptools.command.build_ext
import build_ext
as _build_ext
53 from setuptools
import Extension
as _Extension
55 from distutils.command.build_ext
import build_ext
as _build_ext
56 from distutils.extension
import Extension
as _Extension
58 import distutils.errors
59 import distutils.ccompiler
62 WIN = sys.platform.startswith(
"win32")
63 PY2 = sys.version_info[0] < 3
64 MACOS = sys.platform.startswith(
"darwin")
65 STD_TMPL =
"/std:c++{}" if WIN
else "-std=c++{}"
77 Build a C++11+ Extension module with pybind11. This automatically adds the
78 recommended flags when you init the extension and assumes C++ sources - you
79 can further modify the options yourself.
81 The customizations are:
83 * ``/EHsc`` and ``/bigobj`` on Windows
84 * ``stdlib=libc++`` on macOS
85 * ``visibility=hidden`` and ``-g0`` on Unix
87 Finally, you can set ``cxx_std`` via constructor or afterwords to enable
88 flags for C++ std, and a few extra helper flags related to the C++ standard
89 level. It is _highly_ recommended you either set this, or use the provided
90 ``build_ext``, which will search for the highest supported extension for
91 you if the ``cxx_std`` property is not set. Do not set the ``cxx_std``
92 property more than once, as flags are added when you set it. Set the
93 property to None to disable the addition of C++ standard flags.
95 If you want to add pybind11 headers manually, for example for an exact
96 git checkout, then set ``include_pybind11=False``.
98 Warning: do not use property-based access to the instance on Python 2 -
99 this is an ugly old-style class due to Distutils.
105 def _add_cflags(self, flags):
106 self.extra_compile_args[:0] = flags
108 def _add_ldflags(self, flags):
109 self.extra_link_args[:0] = flags
114 cxx_std = kwargs.pop(
"cxx_std", 0)
116 if "language" not in kwargs:
117 kwargs[
"language"] =
"c++"
119 include_pybind11 = kwargs.pop(
"include_pybind11",
True)
123 _Extension.__init__(self, *args, **kwargs)
131 pyinc = pybind11.get_include()
133 if pyinc
not in self.include_dirs:
134 self.include_dirs.append(pyinc)
139 Pybind11Extension.cxx_std.__set__(self, cxx_std)
144 cflags += [
"/EHsc",
"/bigobj"]
146 cflags += [
"-fvisibility=hidden",
"-g0"]
148 cflags += [
"-stdlib=libc++"]
149 ldflags += [
"-stdlib=libc++"]
156 The CXX standard level. If set, will add the required flags. If left
157 at 0, it will trigger an automatic search when pybind11's build_ext
158 is used. If None, will have no effect. Besides just the flags, this
159 may add a register warning/error fix for Python 2 or macos-min 10.9
168 warnings.warn(
"You cannot safely change the cxx_level after setting it!")
172 if WIN
and level == 11:
180 cflags = [STD_TMPL.format(level)]
183 if MACOS
and "MACOSX_DEPLOYMENT_TARGET" not in os.environ:
189 current_macos =
tuple(int(x)
for x
in platform.mac_ver()[0].split(
".")[:2])
190 desired_macos = (10, 9)
if level < 17
else (10, 14)
191 macos_string =
".".join(
str(x)
for x
in min(current_macos, desired_macos))
192 macosx_min =
"-mmacosx-version-min=" + macos_string
193 cflags += [macosx_min]
194 ldflags += [macosx_min]
200 cflags += [
"/wd5033"]
202 cflags += [
"-Wno-register"]
204 cflags += [
"-Wno-deprecated-register"]
211 tmp_chdir_lock = threading.Lock()
212 cpp_cache_lock = threading.Lock()
215 @contextlib.contextmanager
217 "Prepare and enter a temporary directory, cleanup when done"
223 tmpdir = tempfile.mkdtemp()
228 shutil.rmtree(tmpdir)
234 Return the flag if a flag name is supported on the
235 specified compiler, otherwise None (can be used as a boolean).
236 If multiple flags are passed, return the first that matches.
240 fname =
"flagcheck.cpp"
241 with open(fname,
"w")
as f:
243 f.write(
"int main (int, char **) { return 0; }")
246 compiler.compile([fname], extra_postargs=[flag])
247 except distutils.errors.CompileError:
253 cpp_flag_cache =
None
258 Return the max supported C++ std level (17, 14, or 11). Returns latest on Windows.
264 global cpp_flag_cache
269 return cpp_flag_cache
271 levels = [17, 14, 11]
274 if has_flag(compiler, STD_TMPL.format(level)):
276 cpp_flag_cache = level
279 msg =
"Unsupported compiler -- at least C++11 support is needed!"
280 raise RuntimeError(msg)
285 Customized build_ext that allows an auto-search for the highest supported
286 C++ level for Pybind11Extension. This is only needed for the auto-search
287 for now, and is completely optional otherwise.
292 Build extensions, injecting C++ std for Pybind11Extension if needed.
295 for ext
in self.extensions:
296 if hasattr(ext,
"_cxx_level")
and ext._cxx_level == 0:
302 _build_ext.build_extensions(self)
307 This will recompile only if the source file changes. It does not check
308 header files, so a more advanced function or Ccache is better if you have
309 editable header files in your package.
311 return os.stat(obj).st_mtime < os.stat(src).st_mtime
316 This is the safest but slowest choice (and is the default) - will always
329 Make a parallel compile function. Inspired by
330 numpy.distutils.ccompiler.CCompiler_compile and cppimport.
332 This takes several arguments that allow you to customize the compile
336 Set an environment variable to control the compilation threads, like
339 0 will automatically multithread, or 1 will only multithread if the
342 The limit for automatic multithreading if non-zero
344 A function of (obj, src) that returns True when recompile is needed. No
345 effect in isolated mode; use ccache instead, see
346 https://github.com/matplotlib/matplotlib/issues/1507/
350 ParallelCompile("NPY_NUM_BUILD_JOBS").install()
354 with ParallelCompile("NPY_NUM_BUILD_JOBS"):
357 By default, this assumes all files need to be recompiled. A smarter
358 function can be provided via needs_recompile. If the output has not yet
359 been generated, the compile will always run, and this function is not
363 __slots__ = (
"envvar",
"default",
"max",
"_old",
"needs_recompile")
365 def __init__(self, envvar=None, default=0, max=0, needs_recompile=no_recompile):
374 Builds a function object usable as distutils.ccompiler.CCompiler.compile.
377 def compile_function(
390 macros, objects, extra_postargs, pp_opts, build = compiler._setup_compile(
391 output_dir, macros, include_dirs, sources, depends, extra_postargs
393 cc_args = compiler._get_cc_args(pp_opts, debug, extra_preargs)
399 if self.
envvar is not None:
402 def _single_compile(obj):
404 src, ext = build[obj]
409 compiler._compile(obj, src, ext, cc_args, extra_postargs, pp_opts)
412 import multiprocessing
413 from multiprocessing.pool
import ThreadPool
419 threads = multiprocessing.cpu_count()
420 threads = self.
max if self.
max and self.
max < threads
else threads
421 except NotImplementedError:
425 for _
in ThreadPool(threads).imap_unordered(_single_compile, objects):
433 return compile_function
436 distutils.ccompiler.CCompiler.compile = self.
function()
440 self.
_old.append(distutils.ccompiler.CCompiler.compile)
444 distutils.ccompiler.CCompiler.compile = self.
_old.pop()