setup_helpers.py
Go to the documentation of this file.
1 # -*- coding: utf-8 -*-
2 
3 """
4 This module provides helpers for C++11+ projects using pybind11.
5 
6 LICENSE:
7 
8 Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>, All rights reserved.
9 
10 Redistribution and use in source and binary forms, with or without
11 modification, are permitted provided that the following conditions are met:
12 
13 1. Redistributions of source code must retain the above copyright notice, this
14  list of conditions and the following disclaimer.
15 
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.
19 
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.
23 
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.
34 """
35 
36 import contextlib
37 import os
38 import shutil
39 import sys
40 import tempfile
41 import threading
42 import warnings
43 
44 try:
45  from setuptools.command.build_ext import build_ext as _build_ext
46  from setuptools import Extension as _Extension
47 except ImportError:
48  from distutils.command.build_ext import build_ext as _build_ext
49  from distutils.extension import Extension as _Extension
50 
51 import distutils.errors
52 
53 
54 WIN = sys.platform.startswith("win32")
55 PY2 = sys.version_info[0] < 3
56 MACOS = sys.platform.startswith("darwin")
57 STD_TMPL = "/std:c++{}" if WIN else "-std=c++{}"
58 
59 
60 # It is recommended to use PEP 518 builds if using this module. However, this
61 # file explicitly supports being copied into a user's project directory
62 # standalone, and pulling pybind11 with the deprecated setup_requires feature.
63 # If you copy the file, remember to add it to your MANIFEST.in, and add the current
64 # directory into your path if it sits beside your setup.py.
65 
66 
67 class Pybind11Extension(_Extension):
68  """
69  Build a C++11+ Extension module with pybind11. This automatically adds the
70  recommended flags when you init the extension and assumes C++ sources - you
71  can further modify the options yourself.
72 
73  The customizations are:
74 
75  * ``/EHsc`` and ``/bigobj`` on Windows
76  * ``stdlib=libc++`` on macOS
77  * ``visibility=hidden`` and ``-g0`` on Unix
78 
79  Finally, you can set ``cxx_std`` via constructor or afterwords to enable
80  flags for C++ std, and a few extra helper flags related to the C++ standard
81  level. It is _highly_ recommended you either set this, or use the provided
82  ``build_ext``, which will search for the highest supported extension for
83  you if the ``cxx_std`` property is not set. Do not set the ``cxx_std``
84  property more than once, as flags are added when you set it. Set the
85  property to None to disable the addition of C++ standard flags.
86 
87  If you want to add pybind11 headers manually, for example for an exact
88  git checkout, then set ``include_pybind11=False``.
89 
90  Warning: do not use property-based access to the instance on Python 2 -
91  this is an ugly old-style class due to Distutils.
92  """
93 
94  def _add_cflags(self, *flags):
95  for flag in flags:
96  if flag not in self.extra_compile_args:
97  self.extra_compile_args.append(flag)
98 
99  def _add_lflags(self, *flags):
100  for flag in flags:
101  if flag not in self.extra_compile_args:
102  self.extra_link_args.append(flag)
103 
104  def __init__(self, *args, **kwargs):
105 
106  self._cxx_level = 0
107  cxx_std = kwargs.pop("cxx_std", 0)
108 
109  if "language" not in kwargs:
110  kwargs["language"] = "c++"
111 
112  include_pybind11 = kwargs.pop("include_pybind11", True)
113 
114  # Can't use super here because distutils has old-style classes in
115  # Python 2!
116  _Extension.__init__(self, *args, **kwargs)
117 
118  # Include the installed package pybind11 headers
119  if include_pybind11:
120  # If using setup_requires, this fails the first time - that's okay
121  try:
122  import pybind11
123 
124  pyinc = pybind11.get_include()
125 
126  if pyinc not in self.include_dirs:
127  self.include_dirs.append(pyinc)
128  except ImportError:
129  pass
130 
131  # Have to use the accessor manually to support Python 2 distutils
132  Pybind11Extension.cxx_std.__set__(self, cxx_std)
133 
134  if WIN:
135  self._add_cflags("/EHsc", "/bigobj")
136  else:
137  self._add_cflags("-fvisibility=hidden", "-g0")
138  if MACOS:
139  self._add_cflags("-stdlib=libc++")
140  self._add_lflags("-stdlib=libc++")
141 
142  @property
143  def cxx_std(self):
144  """
145  The CXX standard level. If set, will add the required flags. If left
146  at 0, it will trigger an automatic search when pybind11's build_ext
147  is used. If None, will have no effect. Besides just the flags, this
148  may add a register warning/error fix for Python 2 or macos-min 10.9
149  or 10.14.
150  """
151  return self._cxx_level
152 
153  @cxx_std.setter
154  def cxx_std(self, level):
155 
156  if self._cxx_level:
157  warnings.warn("You cannot safely change the cxx_level after setting it!")
158 
159  # MSVC 2015 Update 3 and later only have 14 (and later 17) modes
160  if WIN and level == 11:
161  level = 14
162 
163  self._cxx_level = level
164 
165  if not level:
166  return
167 
168  self.extra_compile_args.append(STD_TMPL.format(level))
169 
170  if MACOS and "MACOSX_DEPLOYMENT_TARGET" not in os.environ:
171  # C++17 requires a higher min version of macOS
172  macosx_min = "-mmacosx-version-min=" + ("10.9" if level < 17 else "10.14")
173  self.extra_compile_args.append(macosx_min)
174  self.extra_link_args.append(macosx_min)
175 
176  if PY2:
177  if level >= 17:
178  self.extra_compile_args.append("/wd503" if WIN else "-Wno-register")
179  elif not WIN and level >= 14:
180  self.extra_compile_args.append("-Wno-deprecated-register")
181 
182 
183 # Just in case someone clever tries to multithread
184 tmp_chdir_lock = threading.Lock()
185 cpp_cache_lock = threading.Lock()
186 
187 
188 @contextlib.contextmanager
189 def tmp_chdir():
190  "Prepare and enter a temporary directory, cleanup when done"
191 
192  # Threadsafe
193  with tmp_chdir_lock:
194  olddir = os.getcwd()
195  try:
196  tmpdir = tempfile.mkdtemp()
197  os.chdir(tmpdir)
198  yield tmpdir
199  finally:
200  os.chdir(olddir)
201  shutil.rmtree(tmpdir)
202 
203 
204 # cf http://bugs.python.org/issue26689
205 def has_flag(compiler, flag):
206  """
207  Return the flag if a flag name is supported on the
208  specified compiler, otherwise None (can be used as a boolean).
209  If multiple flags are passed, return the first that matches.
210  """
211 
212  with tmp_chdir():
213  fname = "flagcheck.cpp"
214  with open(fname, "w") as f:
215  f.write("int main (int argc, char **argv) { return 0; }")
216 
217  try:
218  compiler.compile([fname], extra_postargs=[flag])
219  except distutils.errors.CompileError:
220  return False
221  return True
222 
223 
224 # Every call will cache the result
225 cpp_flag_cache = None
226 
227 
228 def auto_cpp_level(compiler):
229  """
230  Return the max supported C++ std level (17, 14, or 11).
231  """
232 
233  global cpp_flag_cache
234 
235  # If this has been previously calculated with the same args, return that
236  with cpp_cache_lock:
237  if cpp_flag_cache:
238  return cpp_flag_cache
239 
240  levels = [17, 14] + ([] if WIN else [11])
241 
242  for level in levels:
243  if has_flag(compiler, STD_TMPL.format(level)):
244  with cpp_cache_lock:
245  cpp_flag_cache = level
246  return level
247 
248  msg = "Unsupported compiler -- at least C++11 support is needed!"
249  raise RuntimeError(msg)
250 
251 
252 class build_ext(_build_ext): # noqa: N801
253  """
254  Customized build_ext that allows an auto-search for the highest supported
255  C++ level for Pybind11Extension.
256  """
257 
258  def build_extensions(self):
259  """
260  Build extensions, injecting C++ std for Pybind11Extension if needed.
261  """
262 
263  for ext in self.extensions:
264  if hasattr(ext, "_cxx_level") and ext._cxx_level == 0:
265  # Python 2 syntax - old-style distutils class
266  ext.__class__.cxx_std.__set__(ext, auto_cpp_level(self.compiler))
267 
268  # Python 2 doesn't allow super here, since distutils uses old-style
269  # classes!
270  _build_ext.build_extensions(self)
bool hasattr(handle obj, handle name)
Definition: pytypes.h:403
def auto_cpp_level(compiler)
def has_flag(compiler, flag)


gtsam
Author(s):
autogenerated on Sat May 8 2021 02:44:01