├── .gitignore ├── LICENSE ├── README.org ├── pybind11_cmake ├── .gitignore ├── FindCatch.cmake ├── FindEigen3.cmake ├── FindPythonLibsNew.cmake ├── __init__.py ├── check-style.sh ├── libsize.py ├── mkdoc.py ├── pybind11Config.cmake └── pybind11Tools.cmake ├── pybind11_new_project └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Jariullah Safi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | This repository contains code from https://github.com/pybind/pybind11 which 24 | has the following LICENSE 25 | 26 | Copyright (c) 2016 Wenzel Jakob , All rights reserved. 27 | 28 | Redistribution and use in source and binary forms, with or without 29 | modification, are permitted provided that the following conditions are met: 30 | 31 | 1. Redistributions of source code must retain the above copyright notice, this 32 | list of conditions and the following disclaimer. 33 | 34 | 2. Redistributions in binary form must reproduce the above copyright notice, 35 | this list of conditions and the following disclaimer in the documentation 36 | and/or other materials provided with the distribution. 37 | 38 | 3. Neither the name of the copyright holder nor the names of its contributors 39 | may be used to endorse or promote products derived from this software 40 | without specific prior written permission. 41 | 42 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 43 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 44 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 45 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 46 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 47 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 48 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 49 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 50 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 51 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 52 | 53 | Please also refer to the file CONTRIBUTING.md, which clarifies licensing of 54 | external contributions to this project including patches, pull requests, etc. 55 | 56 | This repository also contains code from https://github.com/pybind/cmake_example/blob/master/LICENSE 57 | which has the following LICENSE 58 | 59 | Copyright (c) 2016 The Pybind Development Team, All rights reserved. 60 | 61 | Redistribution and use in source and binary forms, with or without 62 | modification, are permitted provided that the following conditions are met: 63 | 64 | 1. Redistributions of source code must retain the above copyright notice, this 65 | list of conditions and the following disclaimer. 66 | 67 | 2. Redistributions in binary form must reproduce the above copyright notice, 68 | this list of conditions and the following disclaimer in the documentation 69 | and/or other materials provided with the distribution. 70 | 71 | 3. Neither the name of the copyright holder nor the names of its contributors 72 | may be used to endorse or promote products derived from this software 73 | without specific prior written permission. 74 | 75 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 76 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 77 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 78 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 79 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 80 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 81 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 82 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 83 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 84 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 85 | 86 | You are under no obligation whatsoever to provide any bug fixes, patches, or 87 | upgrades to the features, functionality or performance of the source code 88 | ("Enhancements") to anyone; however, if you choose to make your Enhancements 89 | available either publicly, or directly to the author of this software, without 90 | imposing a separate written license agreement for such Enhancements, then you 91 | hereby grant the following license: a non-exclusive, royalty-free perpetual 92 | license to install, use, modify, prepare derivative works, incorporate into 93 | other computer software, distribute, and sublicense such enhancements or 94 | derivative works thereof, in binary and source code form. 95 | 96 | (I don't know how lincenses work) 97 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | * pybind11-cmake: Straightforward boilerplate generation for pybind11 + cmake 2 | 3 | ** Introduction 4 | 5 | =pybind11= is great and I use it all the time. In most cases I need to work with a =cmake= file so I start the project off using the [[https://github.com/pybind/cmake_example][cmake example]]. This usually involves copying the =CMakeExtension= / =Builder= class to my setup.py file and adding =pybind11= to my project as a =git submodule=. I could do without =cmake= in simpler projects as shown in the [[https://github.com/pybind/python_example][python_example]] but I find it difficult to use for more complicated projects. 6 | 7 | The purpose of this project is to provide an easy to use interface to generate all the boilerplate for the cmake_example and provide the =CMakeExtension= / =Builder= class. It also finds the =pybind11= headers without needing to make a =git submodule=. I will be using this exclusively from now on for all my =C++= wrapping needs and hope it is useful to others as well. 8 | 9 | ** Installation 10 | Just run (preferably in a =virtualenv=) 11 | #+begin_src 12 | pip install pybind11_cmake 13 | #+end_src 14 | 15 | You can also clone this repo and run =pip install .=. 16 | 17 | ** Usage 18 | 19 | The package exposes a single script: =pybind11_new_project= 20 | 21 | #+begin_src 22 | usage: pybind11_new_project [-h] [-a AUTHOR_NAME] [-e AUTHOR_EMAIL] [-c] [-g] 23 | package-name cmake-project-name module-name 24 | output-directory 25 | 26 | positional arguments: 27 | package-name - 28 | cmake-project-name - 29 | module-name - 30 | output-directory - 31 | 32 | optional arguments: 33 | -h, --help show this help message and exit 34 | -a AUTHOR_NAME, --author_name AUTHOR_NAME 35 | Author name for setup.py reasons. (default: '') 36 | -e AUTHOR_EMAIL, --author_email AUTHOR_EMAIL 37 | Author email for setup.py reasons. (default: '') 38 | -c, --cmake_file_only 39 | Flag for generating only a CMakeLists.txt file. 40 | (default: False) 41 | -g, --generate_example_src 42 | Generate an example c++ file. (default: False) 43 | #+end_src 44 | 45 | A basic example would be to go into the root of your project folder and run: 46 | 47 | #+begin_src 48 | pybind11_new_project test_package test_project test_module . -g 49 | #+end_src 50 | 51 | This will create a =CMakeLists.txt= file with the project set at =test_project= 52 | and a =pybind11= module defined as =test_module= and a corresponding =setup.py= file 53 | set up to use that =CMakeLists.txt= file to build your module. Because the =-g= flag 54 | is used it will also create a =main.cpp= file which contains a basic example of how 55 | to expose a function using =pybind11=. At this point you can just =python setup.py build=, 56 | =python setup.py install= or =pip install .= your python package. For the above command 57 | after installation you can run 58 | 59 | #+begin_src python 60 | from test_module import * 61 | print(add(5, 5)) 62 | print(subtract(20, 5)) 63 | #+end_src 64 | 65 | Which should print 66 | #+begin_src 67 | 5 68 | 15 69 | #+end_src 70 | 71 | ** Contributing 72 | There are three major hacks in this repo that I do not like and could use help with: 73 | 1. I straight up copied all the cmake config information from the pybind11 repo (this is the =tools= directory) 74 | 2. I run a =python= command inside the =pybind11Config.cmake= file to find the headers as installed by the =pybind11= python package and set the relevant =cmake= variables 75 | 3. For the =find_package= command to work with =pybind11= installed from =PyPI= in a =virtualenv=, the user has to set the pybind11_DIR variable in their =CMakeLists.txt=. I curently do this by calling a python command and discovering the location of the =pybind11_cmake= package where the =pybind11Config.cmake= file is. The script generates a =CMakeLists.txt= file that already contains this line. 76 | 77 | If you're a =cmake= / =setup.py= wizard and want to help kindly open a PR with a solution for any of these issues. 78 | 79 | ** Common pybind11 questions and their solutions 80 | 81 | *** I can't use =target_link_libraries= 82 | 83 | You might need to add the =PRIVATE= keyword (e.g. =target_link_libraries(open_karto PRIVATE Boost::thread)= ) 84 | 85 | *** Passing shared pointers between python and C++ 86 | 87 | Normally a pybind11 wrapped C++ object is passed as a =unique_ptr=. You can instead expose it as a =shared_ptr= by doing something like: 88 | #+begin_src c++ 89 | py::class_>(m, "ScanMatcherConfig") 90 | .def(py::init<>()) 91 | .def_readwrite("coarse_angle_resolution", &ScanMatcherConfig::m_pCoarseAngleResolution); 92 | #+end_src 93 | Note the two template arguments for =py::class_=. 94 | -------------------------------------------------------------------------------- /pybind11_cmake/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ -------------------------------------------------------------------------------- /pybind11_cmake/FindCatch.cmake: -------------------------------------------------------------------------------- 1 | # - Find the Catch test framework or download it (single header) 2 | # 3 | # This is a quick module for internal use. It assumes that Catch is 4 | # REQUIRED and that a minimum version is provided (not EXACT). If 5 | # a suitable version isn't found locally, the single header file 6 | # will be downloaded and placed in the build dir: PROJECT_BINARY_DIR. 7 | # 8 | # This code sets the following variables: 9 | # CATCH_INCLUDE_DIR - path to catch.hpp 10 | # CATCH_VERSION - version number 11 | 12 | if(NOT Catch_FIND_VERSION) 13 | message(FATAL_ERROR "A version number must be specified.") 14 | elseif(Catch_FIND_REQUIRED) 15 | message(FATAL_ERROR "This module assumes Catch is not required.") 16 | elseif(Catch_FIND_VERSION_EXACT) 17 | message(FATAL_ERROR "Exact version numbers are not supported, only minimum.") 18 | endif() 19 | 20 | # Extract the version number from catch.hpp 21 | function(_get_catch_version) 22 | file(STRINGS "${CATCH_INCLUDE_DIR}/catch.hpp" version_line REGEX "Catch v.*" LIMIT_COUNT 1) 23 | if(version_line MATCHES "Catch v([0-9]+)\\.([0-9]+)\\.([0-9]+)") 24 | set(CATCH_VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}" PARENT_SCOPE) 25 | endif() 26 | endfunction() 27 | 28 | # Download the single-header version of Catch 29 | function(_download_catch version destination_dir) 30 | message(STATUS "Downloading catch v${version}...") 31 | set(url https://github.com/philsquared/Catch/releases/download/v${version}/catch.hpp) 32 | file(DOWNLOAD ${url} "${destination_dir}/catch.hpp" STATUS status) 33 | list(GET status 0 error) 34 | if(error) 35 | message(FATAL_ERROR "Could not download ${url}") 36 | endif() 37 | set(CATCH_INCLUDE_DIR "${destination_dir}" CACHE INTERNAL "") 38 | endfunction() 39 | 40 | # Look for catch locally 41 | find_path(CATCH_INCLUDE_DIR NAMES catch.hpp PATH_SUFFIXES catch) 42 | if(CATCH_INCLUDE_DIR) 43 | _get_catch_version() 44 | endif() 45 | 46 | # Download the header if it wasn't found or if it's outdated 47 | if(NOT CATCH_VERSION OR CATCH_VERSION VERSION_LESS ${Catch_FIND_VERSION}) 48 | if(DOWNLOAD_CATCH) 49 | _download_catch(${Catch_FIND_VERSION} "${PROJECT_BINARY_DIR}/catch/") 50 | _get_catch_version() 51 | else() 52 | set(CATCH_FOUND FALSE) 53 | return() 54 | endif() 55 | endif() 56 | 57 | set(CATCH_FOUND TRUE) 58 | -------------------------------------------------------------------------------- /pybind11_cmake/FindEigen3.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find Eigen3 lib 2 | # 3 | # This module supports requiring a minimum version, e.g. you can do 4 | # find_package(Eigen3 3.1.2) 5 | # to require version 3.1.2 or newer of Eigen3. 6 | # 7 | # Once done this will define 8 | # 9 | # EIGEN3_FOUND - system has eigen lib with correct version 10 | # EIGEN3_INCLUDE_DIR - the eigen include directory 11 | # EIGEN3_VERSION - eigen version 12 | 13 | # Copyright (c) 2006, 2007 Montel Laurent, 14 | # Copyright (c) 2008, 2009 Gael Guennebaud, 15 | # Copyright (c) 2009 Benoit Jacob 16 | # Redistribution and use is allowed according to the terms of the 2-clause BSD license. 17 | 18 | if(NOT Eigen3_FIND_VERSION) 19 | if(NOT Eigen3_FIND_VERSION_MAJOR) 20 | set(Eigen3_FIND_VERSION_MAJOR 2) 21 | endif(NOT Eigen3_FIND_VERSION_MAJOR) 22 | if(NOT Eigen3_FIND_VERSION_MINOR) 23 | set(Eigen3_FIND_VERSION_MINOR 91) 24 | endif(NOT Eigen3_FIND_VERSION_MINOR) 25 | if(NOT Eigen3_FIND_VERSION_PATCH) 26 | set(Eigen3_FIND_VERSION_PATCH 0) 27 | endif(NOT Eigen3_FIND_VERSION_PATCH) 28 | 29 | set(Eigen3_FIND_VERSION "${Eigen3_FIND_VERSION_MAJOR}.${Eigen3_FIND_VERSION_MINOR}.${Eigen3_FIND_VERSION_PATCH}") 30 | endif(NOT Eigen3_FIND_VERSION) 31 | 32 | macro(_eigen3_check_version) 33 | file(READ "${EIGEN3_INCLUDE_DIR}/Eigen/src/Core/util/Macros.h" _eigen3_version_header) 34 | 35 | string(REGEX MATCH "define[ \t]+EIGEN_WORLD_VERSION[ \t]+([0-9]+)" _eigen3_world_version_match "${_eigen3_version_header}") 36 | set(EIGEN3_WORLD_VERSION "${CMAKE_MATCH_1}") 37 | string(REGEX MATCH "define[ \t]+EIGEN_MAJOR_VERSION[ \t]+([0-9]+)" _eigen3_major_version_match "${_eigen3_version_header}") 38 | set(EIGEN3_MAJOR_VERSION "${CMAKE_MATCH_1}") 39 | string(REGEX MATCH "define[ \t]+EIGEN_MINOR_VERSION[ \t]+([0-9]+)" _eigen3_minor_version_match "${_eigen3_version_header}") 40 | set(EIGEN3_MINOR_VERSION "${CMAKE_MATCH_1}") 41 | 42 | set(EIGEN3_VERSION ${EIGEN3_WORLD_VERSION}.${EIGEN3_MAJOR_VERSION}.${EIGEN3_MINOR_VERSION}) 43 | if(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION}) 44 | set(EIGEN3_VERSION_OK FALSE) 45 | else(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION}) 46 | set(EIGEN3_VERSION_OK TRUE) 47 | endif(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION}) 48 | 49 | if(NOT EIGEN3_VERSION_OK) 50 | 51 | message(STATUS "Eigen3 version ${EIGEN3_VERSION} found in ${EIGEN3_INCLUDE_DIR}, " 52 | "but at least version ${Eigen3_FIND_VERSION} is required") 53 | endif(NOT EIGEN3_VERSION_OK) 54 | endmacro(_eigen3_check_version) 55 | 56 | if (EIGEN3_INCLUDE_DIR) 57 | 58 | # in cache already 59 | _eigen3_check_version() 60 | set(EIGEN3_FOUND ${EIGEN3_VERSION_OK}) 61 | 62 | else (EIGEN3_INCLUDE_DIR) 63 | 64 | find_path(EIGEN3_INCLUDE_DIR NAMES signature_of_eigen3_matrix_library 65 | PATHS 66 | ${CMAKE_INSTALL_PREFIX}/include 67 | ${KDE4_INCLUDE_DIR} 68 | PATH_SUFFIXES eigen3 eigen 69 | ) 70 | 71 | if(EIGEN3_INCLUDE_DIR) 72 | _eigen3_check_version() 73 | endif(EIGEN3_INCLUDE_DIR) 74 | 75 | include(FindPackageHandleStandardArgs) 76 | find_package_handle_standard_args(Eigen3 DEFAULT_MSG EIGEN3_INCLUDE_DIR EIGEN3_VERSION_OK) 77 | 78 | mark_as_advanced(EIGEN3_INCLUDE_DIR) 79 | 80 | endif(EIGEN3_INCLUDE_DIR) 81 | 82 | -------------------------------------------------------------------------------- /pybind11_cmake/FindPythonLibsNew.cmake: -------------------------------------------------------------------------------- 1 | # - Find python libraries 2 | # This module finds the libraries corresponding to the Python interpreter 3 | # FindPythonInterp provides. 4 | # This code sets the following variables: 5 | # 6 | # PYTHONLIBS_FOUND - have the Python libs been found 7 | # PYTHON_PREFIX - path to the Python installation 8 | # PYTHON_LIBRARIES - path to the python library 9 | # PYTHON_INCLUDE_DIRS - path to where Python.h is found 10 | # PYTHON_MODULE_EXTENSION - lib extension, e.g. '.so' or '.pyd' 11 | # PYTHON_MODULE_PREFIX - lib name prefix: usually an empty string 12 | # PYTHON_SITE_PACKAGES - path to installation site-packages 13 | # PYTHON_IS_DEBUG - whether the Python interpreter is a debug build 14 | # 15 | # Thanks to talljimbo for the patch adding the 'LDVERSION' config 16 | # variable usage. 17 | 18 | #============================================================================= 19 | # Copyright 2001-2009 Kitware, Inc. 20 | # Copyright 2012 Continuum Analytics, Inc. 21 | # 22 | # All rights reserved. 23 | # 24 | # Redistribution and use in source and binary forms, with or without 25 | # modification, are permitted provided that the following conditions 26 | # are met: 27 | # 28 | # * Redistributions of source code must retain the above copyright 29 | # notice, this list of conditions and the following disclaimer. 30 | # 31 | # * Redistributions in binary form must reproduce the above copyright 32 | # notice, this list of conditions and the following disclaimer in the 33 | # documentation and/or other materials provided with the distribution. 34 | # 35 | # * Neither the names of Kitware, Inc., the Insight Software Consortium, 36 | # nor the names of their contributors may be used to endorse or promote 37 | # products derived from this software without specific prior written 38 | # permission. 39 | # 40 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 41 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 42 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 43 | # # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 44 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 45 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 46 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 47 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 48 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 49 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 50 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 51 | #============================================================================= 52 | 53 | # Checking for the extension makes sure that `LibsNew` was found and not just `Libs`. 54 | if(PYTHONLIBS_FOUND AND PYTHON_MODULE_EXTENSION) 55 | return() 56 | endif() 57 | 58 | # Use the Python interpreter to find the libs. 59 | if(PythonLibsNew_FIND_REQUIRED) 60 | find_package(PythonInterp ${PythonLibsNew_FIND_VERSION} REQUIRED) 61 | else() 62 | find_package(PythonInterp ${PythonLibsNew_FIND_VERSION}) 63 | endif() 64 | 65 | if(NOT PYTHONINTERP_FOUND) 66 | set(PYTHONLIBS_FOUND FALSE) 67 | set(PythonLibsNew_FOUND FALSE) 68 | return() 69 | endif() 70 | 71 | # According to http://stackoverflow.com/questions/646518/python-how-to-detect-debug-interpreter 72 | # testing whether sys has the gettotalrefcount function is a reliable, cross-platform 73 | # way to detect a CPython debug interpreter. 74 | # 75 | # The library suffix is from the config var LDVERSION sometimes, otherwise 76 | # VERSION. VERSION will typically be like "2.7" on unix, and "27" on windows. 77 | execute_process(COMMAND "${PYTHON_EXECUTABLE}" "-c" 78 | "from distutils import sysconfig as s;import sys;import struct; 79 | print('.'.join(str(v) for v in sys.version_info)); 80 | print(sys.prefix); 81 | print(s.get_python_inc(plat_specific=True)); 82 | print(s.get_python_lib(plat_specific=True)); 83 | print(s.get_config_var('SO')); 84 | print(hasattr(sys, 'gettotalrefcount')+0); 85 | print(struct.calcsize('@P')); 86 | print(s.get_config_var('LDVERSION') or s.get_config_var('VERSION')); 87 | print(s.get_config_var('LIBDIR') or ''); 88 | print(s.get_config_var('MULTIARCH') or ''); 89 | " 90 | RESULT_VARIABLE _PYTHON_SUCCESS 91 | OUTPUT_VARIABLE _PYTHON_VALUES 92 | ERROR_VARIABLE _PYTHON_ERROR_VALUE) 93 | 94 | if(NOT _PYTHON_SUCCESS MATCHES 0) 95 | if(PythonLibsNew_FIND_REQUIRED) 96 | message(FATAL_ERROR 97 | "Python config failure:\n${_PYTHON_ERROR_VALUE}") 98 | endif() 99 | set(PYTHONLIBS_FOUND FALSE) 100 | set(PythonLibsNew_FOUND FALSE) 101 | return() 102 | endif() 103 | 104 | # Convert the process output into a list 105 | if(WIN32) 106 | string(REGEX REPLACE "\\\\" "/" _PYTHON_VALUES ${_PYTHON_VALUES}) 107 | endif() 108 | string(REGEX REPLACE ";" "\\\\;" _PYTHON_VALUES ${_PYTHON_VALUES}) 109 | string(REGEX REPLACE "\n" ";" _PYTHON_VALUES ${_PYTHON_VALUES}) 110 | list(GET _PYTHON_VALUES 0 _PYTHON_VERSION_LIST) 111 | list(GET _PYTHON_VALUES 1 PYTHON_PREFIX) 112 | list(GET _PYTHON_VALUES 2 PYTHON_INCLUDE_DIR) 113 | list(GET _PYTHON_VALUES 3 PYTHON_SITE_PACKAGES) 114 | list(GET _PYTHON_VALUES 4 PYTHON_MODULE_EXTENSION) 115 | list(GET _PYTHON_VALUES 5 PYTHON_IS_DEBUG) 116 | list(GET _PYTHON_VALUES 6 PYTHON_SIZEOF_VOID_P) 117 | list(GET _PYTHON_VALUES 7 PYTHON_LIBRARY_SUFFIX) 118 | list(GET _PYTHON_VALUES 8 PYTHON_LIBDIR) 119 | list(GET _PYTHON_VALUES 9 PYTHON_MULTIARCH) 120 | 121 | # Make sure the Python has the same pointer-size as the chosen compiler 122 | # Skip if CMAKE_SIZEOF_VOID_P is not defined 123 | if(CMAKE_SIZEOF_VOID_P AND (NOT "${PYTHON_SIZEOF_VOID_P}" STREQUAL "${CMAKE_SIZEOF_VOID_P}")) 124 | if(PythonLibsNew_FIND_REQUIRED) 125 | math(EXPR _PYTHON_BITS "${PYTHON_SIZEOF_VOID_P} * 8") 126 | math(EXPR _CMAKE_BITS "${CMAKE_SIZEOF_VOID_P} * 8") 127 | message(FATAL_ERROR 128 | "Python config failure: Python is ${_PYTHON_BITS}-bit, " 129 | "chosen compiler is ${_CMAKE_BITS}-bit") 130 | endif() 131 | set(PYTHONLIBS_FOUND FALSE) 132 | set(PythonLibsNew_FOUND FALSE) 133 | return() 134 | endif() 135 | 136 | # The built-in FindPython didn't always give the version numbers 137 | string(REGEX REPLACE "\\." ";" _PYTHON_VERSION_LIST ${_PYTHON_VERSION_LIST}) 138 | list(GET _PYTHON_VERSION_LIST 0 PYTHON_VERSION_MAJOR) 139 | list(GET _PYTHON_VERSION_LIST 1 PYTHON_VERSION_MINOR) 140 | list(GET _PYTHON_VERSION_LIST 2 PYTHON_VERSION_PATCH) 141 | 142 | # Make sure all directory separators are '/' 143 | string(REGEX REPLACE "\\\\" "/" PYTHON_PREFIX ${PYTHON_PREFIX}) 144 | string(REGEX REPLACE "\\\\" "/" PYTHON_INCLUDE_DIR ${PYTHON_INCLUDE_DIR}) 145 | string(REGEX REPLACE "\\\\" "/" PYTHON_SITE_PACKAGES ${PYTHON_SITE_PACKAGES}) 146 | 147 | if(CMAKE_HOST_WIN32) 148 | set(PYTHON_LIBRARY 149 | "${PYTHON_PREFIX}/libs/Python${PYTHON_LIBRARY_SUFFIX}.lib") 150 | 151 | # when run in a venv, PYTHON_PREFIX points to it. But the libraries remain in the 152 | # original python installation. They may be found relative to PYTHON_INCLUDE_DIR. 153 | if(NOT EXISTS "${PYTHON_LIBRARY}") 154 | get_filename_component(_PYTHON_ROOT ${PYTHON_INCLUDE_DIR} DIRECTORY) 155 | set(PYTHON_LIBRARY 156 | "${_PYTHON_ROOT}/libs/Python${PYTHON_LIBRARY_SUFFIX}.lib") 157 | endif() 158 | 159 | # raise an error if the python libs are still not found. 160 | if(NOT EXISTS "${PYTHON_LIBRARY}") 161 | message(FATAL_ERROR "Python libraries not found") 162 | endif() 163 | 164 | else() 165 | if(PYTHON_MULTIARCH) 166 | set(_PYTHON_LIBS_SEARCH "${PYTHON_LIBDIR}/${PYTHON_MULTIARCH}" "${PYTHON_LIBDIR}") 167 | else() 168 | set(_PYTHON_LIBS_SEARCH "${PYTHON_LIBDIR}") 169 | endif() 170 | #message(STATUS "Searching for Python libs in ${_PYTHON_LIBS_SEARCH}") 171 | # Probably this needs to be more involved. It would be nice if the config 172 | # information the python interpreter itself gave us were more complete. 173 | find_library(PYTHON_LIBRARY 174 | NAMES "python${PYTHON_LIBRARY_SUFFIX}" 175 | PATHS ${_PYTHON_LIBS_SEARCH} 176 | NO_DEFAULT_PATH) 177 | 178 | # If all else fails, just set the name/version and let the linker figure out the path. 179 | if(NOT PYTHON_LIBRARY) 180 | set(PYTHON_LIBRARY python${PYTHON_LIBRARY_SUFFIX}) 181 | endif() 182 | endif() 183 | 184 | MARK_AS_ADVANCED( 185 | PYTHON_LIBRARY 186 | PYTHON_INCLUDE_DIR 187 | ) 188 | 189 | # We use PYTHON_INCLUDE_DIR, PYTHON_LIBRARY and PYTHON_DEBUG_LIBRARY for the 190 | # cache entries because they are meant to specify the location of a single 191 | # library. We now set the variables listed by the documentation for this 192 | # module. 193 | SET(PYTHON_INCLUDE_DIRS "${PYTHON_INCLUDE_DIR}") 194 | SET(PYTHON_LIBRARIES "${PYTHON_LIBRARY}") 195 | SET(PYTHON_DEBUG_LIBRARIES "${PYTHON_DEBUG_LIBRARY}") 196 | 197 | find_package_message(PYTHON 198 | "Found PythonLibs: ${PYTHON_LIBRARY}" 199 | "${PYTHON_EXECUTABLE}${PYTHON_VERSION}") 200 | 201 | set(PYTHONLIBS_FOUND TRUE) 202 | set(PythonLibsNew_FOUND TRUE) 203 | -------------------------------------------------------------------------------- /pybind11_cmake/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | import sys 4 | import platform 5 | import subprocess 6 | 7 | from setuptools import Extension 8 | from setuptools.command.build_ext import build_ext 9 | from distutils.version import LooseVersion 10 | 11 | 12 | class CMakeExtension(Extension): 13 | def __init__(self, name, sourcedir=''): 14 | Extension.__init__(self, name, sources=[]) 15 | self.sourcedir = os.path.abspath(sourcedir) 16 | 17 | 18 | class CMakeBuild(build_ext): 19 | def run(self): 20 | try: 21 | out = subprocess.check_output(['cmake', '--version']) 22 | except OSError: 23 | raise RuntimeError("CMake must be installed to build the following extensions: " + 24 | ", ".join(e.name for e in self.extensions)) 25 | 26 | if platform.system() == "Windows": 27 | cmake_version = LooseVersion(re.search(r'version\s*([\d.]+)', out.decode()).group(1)) 28 | if cmake_version < '3.1.0': 29 | raise RuntimeError("CMake >= 3.1.0 is required on Windows") 30 | 31 | for ext in self.extensions: 32 | self.build_extension(ext) 33 | 34 | def build_extension(self, ext): 35 | extdir = os.path.abspath(os.path.dirname(self.get_ext_fullpath(ext.name))) 36 | cmake_args = ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=' + extdir, 37 | '-DPYTHON_EXECUTABLE=' + sys.executable] 38 | 39 | cfg = 'Debug' if self.debug else 'Release' 40 | build_args = ['--config', cfg] 41 | 42 | if platform.system() == "Windows": 43 | cmake_args += ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{}={}'.format(cfg.upper(), extdir)] 44 | if sys.maxsize > 2**32: 45 | cmake_args += ['-A', 'x64'] 46 | build_args += ['--', '/m'] 47 | else: 48 | cmake_args += ['-DCMAKE_BUILD_TYPE=' + cfg] 49 | build_args += ['--', '-j2'] 50 | 51 | env = os.environ.copy() 52 | env['CXXFLAGS'] = '{} -DVERSION_INFO=\\"{}\\"'.format(env.get('CXXFLAGS', ''), 53 | self.distribution.get_version()) 54 | if not os.path.exists(self.build_temp): 55 | os.makedirs(self.build_temp) 56 | subprocess.check_call(['cmake', ext.sourcedir] + cmake_args, cwd=self.build_temp, env=env) 57 | subprocess.check_call(['cmake', '--build', '.'] + build_args, cwd=self.build_temp) 58 | -------------------------------------------------------------------------------- /pybind11_cmake/check-style.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Script to check include/test code for common pybind11 code style errors. 4 | # 5 | # This script currently checks for 6 | # 7 | # 1. use of tabs instead of spaces 8 | # 2. MSDOS-style CRLF endings 9 | # 3. trailing spaces 10 | # 4. missing space between keyword and parenthesis, e.g.: for(, if(, while( 11 | # 5. Missing space between right parenthesis and brace, e.g. 'for (...){' 12 | # 6. opening brace on its own line. It should always be on the same line as the 13 | # if/while/for/do statement. 14 | # 15 | # Invoke as: tools/check-style.sh 16 | # 17 | 18 | check_style_errors=0 19 | IFS=$'\n' 20 | 21 | found="$( GREP_COLORS='mt=41' GREP_COLOR='41' grep $'\t' include tests/*.{cpp,py,h} docs/*.rst -rn --color=always )" 22 | if [ -n "$found" ]; then 23 | # The mt=41 sets a red background for matched tabs: 24 | echo -e '\033[31;01mError: found tab characters in the following files:\033[0m' 25 | check_style_errors=1 26 | echo "$found" | sed -e 's/^/ /' 27 | fi 28 | 29 | 30 | found="$( grep -IUlr $'\r' include tests/*.{cpp,py,h} docs/*.rst --color=always )" 31 | if [ -n "$found" ]; then 32 | echo -e '\033[31;01mError: found CRLF characters in the following files:\033[0m' 33 | check_style_errors=1 34 | echo "$found" | sed -e 's/^/ /' 35 | fi 36 | 37 | found="$(GREP_COLORS='mt=41' GREP_COLOR='41' grep '[[:blank:]]\+$' include tests/*.{cpp,py,h} docs/*.rst -rn --color=always )" 38 | if [ -n "$found" ]; then 39 | # The mt=41 sets a red background for matched trailing spaces 40 | echo -e '\033[31;01mError: found trailing spaces in the following files:\033[0m' 41 | check_style_errors=1 42 | echo "$found" | sed -e 's/^/ /' 43 | fi 44 | 45 | found="$(grep '\<\(if\|for\|while\|catch\)(\|){' include tests/*.{cpp,h} -rn --color=always)" 46 | if [ -n "$found" ]; then 47 | echo -e '\033[31;01mError: found the following coding style problems:\033[0m' 48 | check_style_errors=1 49 | echo "$found" | sed -e 's/^/ /' 50 | fi 51 | 52 | found="$(awk ' 53 | function prefix(filename, lineno) { 54 | return " \033[35m" filename "\033[36m:\033[32m" lineno "\033[36m:\033[0m" 55 | } 56 | function mark(pattern, string) { sub(pattern, "\033[01;31m&\033[0m", string); return string } 57 | last && /^\s*{/ { 58 | print prefix(FILENAME, FNR-1) mark("\\)\\s*$", last) 59 | print prefix(FILENAME, FNR) mark("^\\s*{", $0) 60 | last="" 61 | } 62 | { last = /(if|for|while|catch|switch)\s*\(.*\)\s*$/ ? $0 : "" } 63 | ' $(find include -type f) tests/*.{cpp,h} docs/*.rst)" 64 | if [ -n "$found" ]; then 65 | check_style_errors=1 66 | echo -e '\033[31;01mError: braces should occur on the same line as the if/while/.. statement. Found issues in the following files:\033[0m' 67 | echo "$found" 68 | fi 69 | 70 | exit $check_style_errors 71 | -------------------------------------------------------------------------------- /pybind11_cmake/libsize.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, division 2 | import os 3 | import sys 4 | 5 | # Internal build script for generating debugging test .so size. 6 | # Usage: 7 | # python libsize.py file.so save.txt -- displays the size of file.so and, if save.txt exists, compares it to the 8 | # size in it, then overwrites save.txt with the new size for future runs. 9 | 10 | if len(sys.argv) != 3: 11 | sys.exit("Invalid arguments: usage: python libsize.py file.so save.txt") 12 | 13 | lib = sys.argv[1] 14 | save = sys.argv[2] 15 | 16 | if not os.path.exists(lib): 17 | sys.exit("Error: requested file ({}) does not exist".format(lib)) 18 | 19 | libsize = os.path.getsize(lib) 20 | 21 | print("------", os.path.basename(lib), "file size:", libsize, end='') 22 | 23 | if os.path.exists(save): 24 | with open(save) as sf: 25 | oldsize = int(sf.readline()) 26 | 27 | if oldsize > 0: 28 | change = libsize - oldsize 29 | if change == 0: 30 | print(" (no change)") 31 | else: 32 | print(" (change of {:+} bytes = {:+.2%})".format(change, change / oldsize)) 33 | else: 34 | print() 35 | 36 | with open(save, 'w') as sf: 37 | sf.write(str(libsize)) 38 | 39 | -------------------------------------------------------------------------------- /pybind11_cmake/mkdoc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Syntax: mkdoc.py [-I ..] [.. a list of header files ..] 4 | # 5 | # Extract documentation from C++ header files to use it in Python bindings 6 | # 7 | 8 | import os 9 | import sys 10 | import platform 11 | import re 12 | import textwrap 13 | 14 | from clang import cindex 15 | from clang.cindex import CursorKind 16 | from collections import OrderedDict 17 | from glob import glob 18 | from threading import Thread, Semaphore 19 | from multiprocessing import cpu_count 20 | 21 | RECURSE_LIST = [ 22 | CursorKind.TRANSLATION_UNIT, 23 | CursorKind.NAMESPACE, 24 | CursorKind.CLASS_DECL, 25 | CursorKind.STRUCT_DECL, 26 | CursorKind.ENUM_DECL, 27 | CursorKind.CLASS_TEMPLATE 28 | ] 29 | 30 | PRINT_LIST = [ 31 | CursorKind.CLASS_DECL, 32 | CursorKind.STRUCT_DECL, 33 | CursorKind.ENUM_DECL, 34 | CursorKind.ENUM_CONSTANT_DECL, 35 | CursorKind.CLASS_TEMPLATE, 36 | CursorKind.FUNCTION_DECL, 37 | CursorKind.FUNCTION_TEMPLATE, 38 | CursorKind.CONVERSION_FUNCTION, 39 | CursorKind.CXX_METHOD, 40 | CursorKind.CONSTRUCTOR, 41 | CursorKind.FIELD_DECL 42 | ] 43 | 44 | PREFIX_BLACKLIST = [ 45 | CursorKind.TRANSLATION_UNIT 46 | ] 47 | 48 | CPP_OPERATORS = { 49 | '<=': 'le', '>=': 'ge', '==': 'eq', '!=': 'ne', '[]': 'array', 50 | '+=': 'iadd', '-=': 'isub', '*=': 'imul', '/=': 'idiv', '%=': 51 | 'imod', '&=': 'iand', '|=': 'ior', '^=': 'ixor', '<<=': 'ilshift', 52 | '>>=': 'irshift', '++': 'inc', '--': 'dec', '<<': 'lshift', '>>': 53 | 'rshift', '&&': 'land', '||': 'lor', '!': 'lnot', '~': 'bnot', 54 | '&': 'band', '|': 'bor', '+': 'add', '-': 'sub', '*': 'mul', '/': 55 | 'div', '%': 'mod', '<': 'lt', '>': 'gt', '=': 'assign', '()': 'call' 56 | } 57 | 58 | CPP_OPERATORS = OrderedDict( 59 | sorted(CPP_OPERATORS.items(), key=lambda t: -len(t[0]))) 60 | 61 | job_count = cpu_count() 62 | job_semaphore = Semaphore(job_count) 63 | 64 | 65 | class NoFilenamesError(ValueError): 66 | pass 67 | 68 | 69 | def d(s): 70 | return s if isinstance(s, str) else s.decode('utf8') 71 | 72 | 73 | def sanitize_name(name): 74 | name = re.sub(r'type-parameter-0-([0-9]+)', r'T\1', name) 75 | for k, v in CPP_OPERATORS.items(): 76 | name = name.replace('operator%s' % k, 'operator_%s' % v) 77 | name = re.sub('<.*>', '', name) 78 | name = ''.join([ch if ch.isalnum() else '_' for ch in name]) 79 | name = re.sub('_$', '', re.sub('_+', '_', name)) 80 | return '__doc_' + name 81 | 82 | 83 | def process_comment(comment): 84 | result = '' 85 | 86 | # Remove C++ comment syntax 87 | leading_spaces = float('inf') 88 | for s in comment.expandtabs(tabsize=4).splitlines(): 89 | s = s.strip() 90 | if s.startswith('/*'): 91 | s = s[2:].lstrip('*') 92 | elif s.endswith('*/'): 93 | s = s[:-2].rstrip('*') 94 | elif s.startswith('///'): 95 | s = s[3:] 96 | if s.startswith('*'): 97 | s = s[1:] 98 | if len(s) > 0: 99 | leading_spaces = min(leading_spaces, len(s) - len(s.lstrip())) 100 | result += s + '\n' 101 | 102 | if leading_spaces != float('inf'): 103 | result2 = "" 104 | for s in result.splitlines(): 105 | result2 += s[leading_spaces:] + '\n' 106 | result = result2 107 | 108 | # Doxygen tags 109 | cpp_group = '([\w:]+)' 110 | param_group = '([\[\w:\]]+)' 111 | 112 | s = result 113 | s = re.sub(r'\\c\s+%s' % cpp_group, r'``\1``', s) 114 | s = re.sub(r'\\a\s+%s' % cpp_group, r'*\1*', s) 115 | s = re.sub(r'\\e\s+%s' % cpp_group, r'*\1*', s) 116 | s = re.sub(r'\\em\s+%s' % cpp_group, r'*\1*', s) 117 | s = re.sub(r'\\b\s+%s' % cpp_group, r'**\1**', s) 118 | s = re.sub(r'\\ingroup\s+%s' % cpp_group, r'', s) 119 | s = re.sub(r'\\param%s?\s+%s' % (param_group, cpp_group), 120 | r'\n\n$Parameter ``\2``:\n\n', s) 121 | s = re.sub(r'\\tparam%s?\s+%s' % (param_group, cpp_group), 122 | r'\n\n$Template parameter ``\2``:\n\n', s) 123 | 124 | for in_, out_ in { 125 | 'return': 'Returns', 126 | 'author': 'Author', 127 | 'authors': 'Authors', 128 | 'copyright': 'Copyright', 129 | 'date': 'Date', 130 | 'remark': 'Remark', 131 | 'sa': 'See also', 132 | 'see': 'See also', 133 | 'extends': 'Extends', 134 | 'throw': 'Throws', 135 | 'throws': 'Throws' 136 | }.items(): 137 | s = re.sub(r'\\%s\s*' % in_, r'\n\n$%s:\n\n' % out_, s) 138 | 139 | s = re.sub(r'\\details\s*', r'\n\n', s) 140 | s = re.sub(r'\\brief\s*', r'', s) 141 | s = re.sub(r'\\short\s*', r'', s) 142 | s = re.sub(r'\\ref\s*', r'', s) 143 | 144 | s = re.sub(r'\\code\s?(.*?)\s?\\endcode', 145 | r"```\n\1\n```\n", s, flags=re.DOTALL) 146 | 147 | # HTML/TeX tags 148 | s = re.sub(r'(.*?)', r'``\1``', s, flags=re.DOTALL) 149 | s = re.sub(r'
(.*?)
', r"```\n\1\n```\n", s, flags=re.DOTALL) 150 | s = re.sub(r'(.*?)', r'*\1*', s, flags=re.DOTALL) 151 | s = re.sub(r'(.*?)', r'**\1**', s, flags=re.DOTALL) 152 | s = re.sub(r'\\f\$(.*?)\\f\$', r'$\1$', s, flags=re.DOTALL) 153 | s = re.sub(r'
  • ', r'\n\n* ', s) 154 | s = re.sub(r'', r'', s) 155 | s = re.sub(r'
  • ', r'\n\n', s) 156 | 157 | s = s.replace('``true``', '``True``') 158 | s = s.replace('``false``', '``False``') 159 | 160 | # Re-flow text 161 | wrapper = textwrap.TextWrapper() 162 | wrapper.expand_tabs = True 163 | wrapper.replace_whitespace = True 164 | wrapper.drop_whitespace = True 165 | wrapper.width = 70 166 | wrapper.initial_indent = wrapper.subsequent_indent = '' 167 | 168 | result = '' 169 | in_code_segment = False 170 | for x in re.split(r'(```)', s): 171 | if x == '```': 172 | if not in_code_segment: 173 | result += '```\n' 174 | else: 175 | result += '\n```\n\n' 176 | in_code_segment = not in_code_segment 177 | elif in_code_segment: 178 | result += x.strip() 179 | else: 180 | for y in re.split(r'(?: *\n *){2,}', x): 181 | wrapped = wrapper.fill(re.sub(r'\s+', ' ', y).strip()) 182 | if len(wrapped) > 0 and wrapped[0] == '$': 183 | result += wrapped[1:] + '\n' 184 | wrapper.initial_indent = \ 185 | wrapper.subsequent_indent = ' ' * 4 186 | else: 187 | if len(wrapped) > 0: 188 | result += wrapped + '\n\n' 189 | wrapper.initial_indent = wrapper.subsequent_indent = '' 190 | return result.rstrip().lstrip('\n') 191 | 192 | 193 | def extract(filename, node, prefix, output): 194 | if not (node.location.file is None or 195 | os.path.samefile(d(node.location.file.name), filename)): 196 | return 0 197 | if node.kind in RECURSE_LIST: 198 | sub_prefix = prefix 199 | if node.kind not in PREFIX_BLACKLIST: 200 | if len(sub_prefix) > 0: 201 | sub_prefix += '_' 202 | sub_prefix += d(node.spelling) 203 | for i in node.get_children(): 204 | extract(filename, i, sub_prefix, output) 205 | if node.kind in PRINT_LIST: 206 | comment = d(node.raw_comment) if node.raw_comment is not None else '' 207 | comment = process_comment(comment) 208 | sub_prefix = prefix 209 | if len(sub_prefix) > 0: 210 | sub_prefix += '_' 211 | if len(node.spelling) > 0: 212 | name = sanitize_name(sub_prefix + d(node.spelling)) 213 | output.append((name, filename, comment)) 214 | 215 | 216 | class ExtractionThread(Thread): 217 | def __init__(self, filename, parameters, output): 218 | Thread.__init__(self) 219 | self.filename = filename 220 | self.parameters = parameters 221 | self.output = output 222 | job_semaphore.acquire() 223 | 224 | def run(self): 225 | print('Processing "%s" ..' % self.filename, file=sys.stderr) 226 | try: 227 | index = cindex.Index( 228 | cindex.conf.lib.clang_createIndex(False, True)) 229 | tu = index.parse(self.filename, self.parameters) 230 | extract(self.filename, tu.cursor, '', self.output) 231 | finally: 232 | job_semaphore.release() 233 | 234 | 235 | def read_args(args): 236 | parameters = [] 237 | filenames = [] 238 | if "-x" not in args: 239 | parameters.extend(['-x', 'c++']) 240 | if not any(it.startswith("-std=") for it in args): 241 | parameters.append('-std=c++11') 242 | 243 | if platform.system() == 'Darwin': 244 | dev_path = '/Applications/Xcode.app/Contents/Developer/' 245 | lib_dir = dev_path + 'Toolchains/XcodeDefault.xctoolchain/usr/lib/' 246 | sdk_dir = dev_path + 'Platforms/MacOSX.platform/Developer/SDKs' 247 | libclang = lib_dir + 'libclang.dylib' 248 | 249 | if os.path.exists(libclang): 250 | cindex.Config.set_library_path(os.path.dirname(libclang)) 251 | 252 | if os.path.exists(sdk_dir): 253 | sysroot_dir = os.path.join(sdk_dir, next(os.walk(sdk_dir))[1][0]) 254 | parameters.append('-isysroot') 255 | parameters.append(sysroot_dir) 256 | elif platform.system() == 'Linux': 257 | # clang doesn't find its own base includes by default on Linux, 258 | # but different distros install them in different paths. 259 | # Try to autodetect, preferring the highest numbered version. 260 | def clang_folder_version(d): 261 | return [int(ver) for ver in re.findall(r'(? 4 | # 5 | # All rights reserved. Use of this source code is governed by a 6 | # BSD-style license that can be found in the LICENSE file. 7 | 8 | cmake_minimum_required(VERSION 2.8.12) 9 | 10 | # Add a CMake parameter for choosing a desired Python version 11 | if(NOT PYBIND11_PYTHON_VERSION) 12 | set(PYBIND11_PYTHON_VERSION "" CACHE STRING "Python version to use for compiling modules") 13 | endif() 14 | 15 | set(Python_ADDITIONAL_VERSIONS 3.7 3.6 3.5 3.4) 16 | find_package(PythonLibsNew ${PYBIND11_PYTHON_VERSION} REQUIRED) 17 | 18 | include(CheckCXXCompilerFlag) 19 | include(CMakeParseArguments) 20 | 21 | if(NOT PYBIND11_CPP_STANDARD AND NOT CMAKE_CXX_STANDARD) 22 | if(NOT MSVC) 23 | check_cxx_compiler_flag("-std=c++14" HAS_CPP14_FLAG) 24 | 25 | if (HAS_CPP14_FLAG) 26 | set(PYBIND11_CPP_STANDARD -std=c++14) 27 | else() 28 | check_cxx_compiler_flag("-std=c++11" HAS_CPP11_FLAG) 29 | if (HAS_CPP11_FLAG) 30 | set(PYBIND11_CPP_STANDARD -std=c++11) 31 | else() 32 | message(FATAL_ERROR "Unsupported compiler -- pybind11 requires C++11 support!") 33 | endif() 34 | endif() 35 | elseif(MSVC) 36 | set(PYBIND11_CPP_STANDARD /std:c++14) 37 | endif() 38 | 39 | set(PYBIND11_CPP_STANDARD ${PYBIND11_CPP_STANDARD} CACHE STRING 40 | "C++ standard flag, e.g. -std=c++11, -std=c++14, /std:c++14. Defaults to C++14 mode." FORCE) 41 | endif() 42 | 43 | # Checks whether the given CXX/linker flags can compile and link a cxx file. cxxflags and 44 | # linkerflags are lists of flags to use. The result variable is a unique variable name for each set 45 | # of flags: the compilation result will be cached base on the result variable. If the flags work, 46 | # sets them in cxxflags_out/linkerflags_out internal cache variables (in addition to ${result}). 47 | function(_pybind11_return_if_cxx_and_linker_flags_work result cxxflags linkerflags cxxflags_out linkerflags_out) 48 | set(CMAKE_REQUIRED_LIBRARIES ${linkerflags}) 49 | check_cxx_compiler_flag("${cxxflags}" ${result}) 50 | if (${result}) 51 | set(${cxxflags_out} "${cxxflags}" CACHE INTERNAL "" FORCE) 52 | set(${linkerflags_out} "${linkerflags}" CACHE INTERNAL "" FORCE) 53 | endif() 54 | endfunction() 55 | 56 | # Internal: find the appropriate link time optimization flags for this compiler 57 | function(_pybind11_add_lto_flags target_name prefer_thin_lto) 58 | if (NOT DEFINED PYBIND11_LTO_CXX_FLAGS) 59 | set(PYBIND11_LTO_CXX_FLAGS "" CACHE INTERNAL "") 60 | set(PYBIND11_LTO_LINKER_FLAGS "" CACHE INTERNAL "") 61 | 62 | if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") 63 | set(cxx_append "") 64 | set(linker_append "") 65 | if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT APPLE) 66 | # Clang Gold plugin does not support -Os; append -O3 to MinSizeRel builds to override it 67 | set(linker_append ";$<$:-O3>") 68 | elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU") 69 | set(cxx_append ";-fno-fat-lto-objects") 70 | endif() 71 | 72 | if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND prefer_thin_lto) 73 | _pybind11_return_if_cxx_and_linker_flags_work(HAS_FLTO_THIN 74 | "-flto=thin${cxx_append}" "-flto=thin${linker_append}" 75 | PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS) 76 | endif() 77 | 78 | if (NOT HAS_FLTO_THIN) 79 | _pybind11_return_if_cxx_and_linker_flags_work(HAS_FLTO 80 | "-flto${cxx_append}" "-flto${linker_append}" 81 | PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS) 82 | endif() 83 | elseif (CMAKE_CXX_COMPILER_ID MATCHES "Intel") 84 | # Intel equivalent to LTO is called IPO 85 | _pybind11_return_if_cxx_and_linker_flags_work(HAS_INTEL_IPO 86 | "-ipo" "-ipo" PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS) 87 | elseif(MSVC) 88 | # cmake only interprets libraries as linker flags when they start with a - (otherwise it 89 | # converts /LTCG to \LTCG as if it was a Windows path). Luckily MSVC supports passing flags 90 | # with - instead of /, even if it is a bit non-standard: 91 | _pybind11_return_if_cxx_and_linker_flags_work(HAS_MSVC_GL_LTCG 92 | "/GL" "-LTCG" PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS) 93 | endif() 94 | 95 | if (PYBIND11_LTO_CXX_FLAGS) 96 | message(STATUS "LTO enabled") 97 | else() 98 | message(STATUS "LTO disabled (not supported by the compiler and/or linker)") 99 | endif() 100 | endif() 101 | 102 | # Enable LTO flags if found, except for Debug builds 103 | if (PYBIND11_LTO_CXX_FLAGS) 104 | target_compile_options(${target_name} PRIVATE "$<$>:${PYBIND11_LTO_CXX_FLAGS}>") 105 | endif() 106 | if (PYBIND11_LTO_LINKER_FLAGS) 107 | target_link_libraries(${target_name} PRIVATE "$<$>:${PYBIND11_LTO_LINKER_FLAGS}>") 108 | endif() 109 | endfunction() 110 | 111 | # Build a Python extension module: 112 | # pybind11_add_module( [MODULE | SHARED] [EXCLUDE_FROM_ALL] 113 | # [NO_EXTRAS] [SYSTEM] [THIN_LTO] source1 [source2 ...]) 114 | # 115 | function(pybind11_add_module target_name) 116 | set(options MODULE SHARED EXCLUDE_FROM_ALL NO_EXTRAS SYSTEM THIN_LTO) 117 | cmake_parse_arguments(ARG "${options}" "" "" ${ARGN}) 118 | 119 | if(ARG_MODULE AND ARG_SHARED) 120 | message(FATAL_ERROR "Can't be both MODULE and SHARED") 121 | elseif(ARG_SHARED) 122 | set(lib_type SHARED) 123 | else() 124 | set(lib_type MODULE) 125 | endif() 126 | 127 | if(ARG_EXCLUDE_FROM_ALL) 128 | set(exclude_from_all EXCLUDE_FROM_ALL) 129 | endif() 130 | 131 | add_library(${target_name} ${lib_type} ${exclude_from_all} ${ARG_UNPARSED_ARGUMENTS}) 132 | 133 | if(ARG_SYSTEM) 134 | set(inc_isystem SYSTEM) 135 | endif() 136 | 137 | target_include_directories(${target_name} ${inc_isystem} 138 | PRIVATE ${PYBIND11_INCLUDE_DIR} # from project CMakeLists.txt 139 | PRIVATE ${pybind11_INCLUDE_DIR} # from pybind11Config 140 | PRIVATE ${PYTHON_INCLUDE_DIRS}) 141 | 142 | # Python debug libraries expose slightly different objects 143 | # https://docs.python.org/3.6/c-api/intro.html#debugging-builds 144 | # https://stackoverflow.com/questions/39161202/how-to-work-around-missing-pymodule-create2-in-amd64-win-python35-d-lib 145 | if(PYTHON_IS_DEBUG) 146 | target_compile_definitions(${target_name} PRIVATE Py_DEBUG) 147 | endif() 148 | 149 | # The prefix and extension are provided by FindPythonLibsNew.cmake 150 | set_target_properties(${target_name} PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}") 151 | set_target_properties(${target_name} PROPERTIES SUFFIX "${PYTHON_MODULE_EXTENSION}") 152 | 153 | # -fvisibility=hidden is required to allow multiple modules compiled against 154 | # different pybind versions to work properly, and for some features (e.g. 155 | # py::module_local). We force it on everything inside the `pybind11` 156 | # namespace; also turning it on for a pybind module compilation here avoids 157 | # potential warnings or issues from having mixed hidden/non-hidden types. 158 | set_target_properties(${target_name} PROPERTIES CXX_VISIBILITY_PRESET "hidden") 159 | set_target_properties(${target_name} PROPERTIES CUDA_VISIBILITY_PRESET "hidden") 160 | 161 | if(WIN32 OR CYGWIN) 162 | # Link against the Python shared library on Windows 163 | target_link_libraries(${target_name} PRIVATE ${PYTHON_LIBRARIES}) 164 | elseif(APPLE) 165 | # It's quite common to have multiple copies of the same Python version 166 | # installed on one's system. E.g.: one copy from the OS and another copy 167 | # that's statically linked into an application like Blender or Maya. 168 | # If we link our plugin library against the OS Python here and import it 169 | # into Blender or Maya later on, this will cause segfaults when multiple 170 | # conflicting Python instances are active at the same time (even when they 171 | # are of the same version). 172 | 173 | # Windows is not affected by this issue since it handles DLL imports 174 | # differently. The solution for Linux and Mac OS is simple: we just don't 175 | # link against the Python library. The resulting shared library will have 176 | # missing symbols, but that's perfectly fine -- they will be resolved at 177 | # import time. 178 | 179 | target_link_libraries(${target_name} PRIVATE "-undefined dynamic_lookup") 180 | 181 | if(ARG_SHARED) 182 | # Suppress CMake >= 3.0 warning for shared libraries 183 | set_target_properties(${target_name} PROPERTIES MACOSX_RPATH ON) 184 | endif() 185 | endif() 186 | 187 | # Make sure C++11/14 are enabled 188 | if(CMAKE_VERSION VERSION_LESS 3.3) 189 | target_compile_options(${target_name} PUBLIC ${PYBIND11_CPP_STANDARD}) 190 | else() 191 | target_compile_options(${target_name} PUBLIC $<$:${PYBIND11_CPP_STANDARD}>) 192 | endif() 193 | 194 | if(ARG_NO_EXTRAS) 195 | return() 196 | endif() 197 | 198 | _pybind11_add_lto_flags(${target_name} ${ARG_THIN_LTO}) 199 | 200 | if (NOT MSVC AND NOT ${CMAKE_BUILD_TYPE} MATCHES Debug) 201 | # Strip unnecessary sections of the binary on Linux/Mac OS 202 | if(CMAKE_STRIP) 203 | if(APPLE) 204 | add_custom_command(TARGET ${target_name} POST_BUILD 205 | COMMAND ${CMAKE_STRIP} -x $) 206 | else() 207 | add_custom_command(TARGET ${target_name} POST_BUILD 208 | COMMAND ${CMAKE_STRIP} $) 209 | endif() 210 | endif() 211 | endif() 212 | 213 | if(MSVC) 214 | # /MP enables multithreaded builds (relevant when there are many files), /bigobj is 215 | # needed for bigger binding projects due to the limit to 64k addressable sections 216 | target_compile_options(${target_name} PRIVATE /bigobj) 217 | if(CMAKE_VERSION VERSION_LESS 3.11) 218 | target_compile_options(${target_name} PRIVATE $<$>:/MP>) 219 | else() 220 | # Only set these options for C++ files. This is important so that, for 221 | # instance, projects that include other types of source files like CUDA 222 | # .cu files don't get these options propagated to nvcc since that would 223 | # cause the build to fail. 224 | target_compile_options(${target_name} PRIVATE $<$>:$<$:/MP>>) 225 | endif() 226 | endif() 227 | endfunction() 228 | -------------------------------------------------------------------------------- /pybind11_new_project: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import argh 3 | from argh import arg 4 | import os 5 | 6 | cmake_file = """cmake_minimum_required(VERSION 2.8.12) 7 | project(PROJECT_NAME) 8 | # Note: The lines below allows a pip install pybind11 to be found by cmake. 9 | # Do not remove. 10 | execute_process(COMMAND python -c 11 | "import pybind11_cmake; print(pybind11_cmake.__path__[0])" 12 | OUTPUT_VARIABLE pybind11_DIR OUTPUT_STRIP_TRAILING_WHITESPACE) 13 | find_package(pybind11 REQUIRED) 14 | pybind11_add_module(MODULE_NAME SOURCES)""" 15 | 16 | setup_file = """from setuptools import setup 17 | import sys 18 | 19 | setup_kw = dict( 20 | name='PACKAGE_NAME', 21 | version='0.0.1', 22 | author='AUTH_NAME', 23 | author_email='AUTH_EMAIL', 24 | description='', 25 | long_description='', 26 | setup_requires=['pybind11_cmake'], 27 | zip_safe=False, 28 | ext_modules=[], 29 | cmdclass={} 30 | ) 31 | if len(sys.argv) > 1 and '--help' not in sys.argv[1:] and sys.argv[1] not in ( 32 | '--help-commands', 'egg_info', 'clean', '--version'): 33 | from pybind11_cmake import CMakeExtension, CMakeBuild 34 | setup_kw['ext_modules'] = [CMakeExtension('MODULE_NAME')] 35 | setup_kw['cmdclass'] = dict(build_ext=CMakeBuild) 36 | 37 | if __name__ == '__main__': 38 | setup(**setup_kw) 39 | """ 40 | 41 | cpp_file="""#include 42 | 43 | int add(int i, int j) { 44 | return i + j; 45 | } 46 | 47 | namespace py = pybind11; 48 | 49 | PYBIND11_MODULE(MODULE_NAME, m) { 50 | m.doc() = R"pbdoc( 51 | pybind11_cmake example 52 | ----------------------- 53 | 54 | .. currentmodule:: MODULE_NAME 55 | 56 | .. autosummary:: 57 | :toctree: _generate 58 | 59 | add 60 | subtract 61 | )pbdoc"; 62 | 63 | // Adding a regular function through reference. 64 | m.def("add", &add, R"pbdoc( 65 | Add two numbers 66 | 67 | Some other explanation about the add function. 68 | )pbdoc"); 69 | 70 | // Adding a function via lambda. 71 | m.def("subtract", [](int i, int j) { return i - j; }, R"pbdoc( 72 | Subtract two numbers 73 | 74 | Some other explanation about the subtract function. 75 | )pbdoc"); 76 | 77 | #ifdef VERSION_INFO 78 | m.attr("__version__") = VERSION_INFO; 79 | #else 80 | m.attr("__version__") = "dev"; 81 | #endif 82 | }""" 83 | 84 | @arg('-g', '--generate_example_src', help='Generate an example c++ file.') 85 | @arg('-a', '--author_name', help='Author name for setup.py reasons.') 86 | @arg('-e', '--author_email', help='Author email for setup.py reasons.') 87 | @arg('-c', '--cmake_file_only', help='Flag for generating only a CMakeLists.txt file.') 88 | def generate_project( 89 | package_name, 90 | cmake_project_name, 91 | module_name, 92 | output_directory, 93 | author_name='', 94 | author_email='', 95 | cmake_file_only=False, 96 | generate_example_src=False, 97 | ): 98 | 99 | dir = os.path.abspath(output_directory) 100 | cmake_path = os.path.join(output_directory, 'CMakeLists.txt') 101 | setup_path = os.path.join(output_directory, 'setup.py') 102 | 103 | with open(cmake_path, 'w') as ff: 104 | to_write = cmake_file.replace('PROJECT_NAME', cmake_project_name).replace( 105 | 'MODULE_NAME', module_name) 106 | if generate_example_src: 107 | to_write = to_write.replace('SOURCES', 'main.cpp') 108 | ff.write(to_write) 109 | 110 | print("Generated CMakeLists.txt") 111 | 112 | if not cmake_file_only: 113 | with open(setup_path, 'w') as ff: 114 | to_write = setup_file.replace('PACKAGE_NAME', package_name).replace( 115 | 'AUTH_NAME', 116 | author_name).replace('AUTH_EMAIL', author_email).replace( 117 | 'MODULE_NAME', module_name) 118 | ff.write(to_write) 119 | print("Generated setup.py") 120 | 121 | if generate_example_src: 122 | with open('main.cpp', 'w') as ff: 123 | ff.write(cpp_file.replace('MODULE_NAME', module_name)) 124 | print("Generated main.cpp") 125 | 126 | print("Your pybind11 project is ready. Enjoy :)") 127 | 128 | if __name__ == '__main__': 129 | argh.dispatch_command(generate_project) 130 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | from glob import glob 3 | 4 | pn = 'pybind11_cmake/' 5 | 6 | files = glob(pn + '*.py') + glob(pn + '*.cmake') + glob(pn + '*.sh') + glob(pn + 'clang/*') 7 | 8 | files = [f[len(pn):] for f in files] 9 | 10 | setup( 11 | name='pybind11_cmake', 12 | version='1.0.0', 13 | author='Jariullah Safi', 14 | author_email='safijari@isu.edu', 15 | description='Straightforward boilerplate generation for pybind11 + cmake', 16 | long_description='https://github.com/safijari/pybind11-cmake', 17 | zip_safe=False, 18 | packages=find_packages(), 19 | scripts=['pybind11_new_project'], 20 | url='https://github.com/safijari/pybind11-cmake', 21 | include_package_data=True, 22 | install_requires=['pybind11>=2,<3', 'argh'], 23 | package_data={'pybind11_cmake': files}, 24 | classifiers=[ 25 | 'Development Status :: 5 - Production/Stable', 26 | 'Intended Audience :: Developers', 27 | 'Topic :: Software Development :: Libraries :: Python Modules', 28 | 'Topic :: Utilities', 29 | 'Programming Language :: C++', 30 | 'Programming Language :: Python :: 2.7', 31 | 'Programming Language :: Python :: 3', 32 | 'Programming Language :: Python :: 3.2', 33 | 'Programming Language :: Python :: 3.3', 34 | 'Programming Language :: Python :: 3.4', 35 | 'Programming Language :: Python :: 3.5', 36 | 'Programming Language :: Python :: 3.6', 37 | 'Programming Language :: Python :: 3.7', 38 | 'License :: OSI Approved :: BSD License' 39 | ], 40 | keywords='C++11, Python bindings, CMake, boilerplate', 41 | ) 42 | --------------------------------------------------------------------------------