From be023897aeef0fc340191df7a618c00fce41e9b7 Mon Sep 17 00:00:00 2001 From: Gene Hoffman Date: Mon, 18 May 2020 02:12:03 -0700 Subject: [PATCH 1/3] Try manifest.in to get .git included --- MANIFEST.in | 1 + setup.py | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 MANIFEST.in diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 00000000..db1ee9a4 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1 @@ +graft .git diff --git a/setup.py b/setup.py index 78922eea..f6b6fe52 100644 --- a/setup.py +++ b/setup.py @@ -102,7 +102,11 @@ def run(self): Work around pybind11's need to be on the filesystem """ if os.path.exists('.gitmodules'): - out = subprocess.run(['git', 'submodule', 'update', '--init', '--recursive']) + try: + subprocess.run(['git', 'submodule', 'update', '--init', '--recursive']) + except OSError: + raise RuntimeError("git is not available" + + ", ".join(e.name for e in self.extensions)) if platform.system() == "Windows": cmake_version = LooseVersion( From 73c916a7afec0b735e351dc1267901c7aec96fbb Mon Sep 17 00:00:00 2001 From: Gene Hoffman <30377676+hoffmang9@users.noreply.github.com> Date: Mon, 18 May 2020 13:49:33 -0700 Subject: [PATCH 2/3] pip install pybind (#17) * pip install pybind, flake8/mypy setup.py * Remove MANIFEST.in *Update README for pip install *mypy is wrong about 'build' so setup.py not added yet --- .github/workflows/build.yml | 12 +- MANIFEST.in | 1 - README.md | 15 ++- setup.py | 59 ++++----- src/cmake/pybind11Tools.cmake | 227 ++++++++++++++++++++++++++++++++++ 5 files changed, 269 insertions(+), 45 deletions(-) delete mode 100644 MANIFEST.in create mode 100644 src/cmake/pybind11Tools.cmake diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 95843e90..7aea5cfb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,21 +17,24 @@ jobs: fetch-depth: 0 # we need fetch-depth 0 so setuptools_scm can resolve tags + - name: Checkout submodules + uses: snickerbockers/submodules-init@v4 + - uses: actions/setup-python@v1 name: Install Python with: python-version: '3.7' - - name: Install cibuildwheel + - name: Install cibuildwheel and pybind11 run: | python -m pip install --upgrade pip - pip install wheel + pip install wheel pybind11==2.5.0 pip install cibuildwheel==1.3.0 - name: Lint source with flake8 run: | pip install flake8 - flake8 src + flake8 src setup.py - name: Lint source with mypy run: | @@ -60,9 +63,11 @@ jobs: && rm -f /usr/bin/cmake && yum -y install boost-devel gmp-devel && python -m pip install --upgrade pip + && pip install pybind11==2.5.0 CIBW_BEFORE_BUILD_MACOS: > brew install boost && python -m pip install --upgrade pip + && pip install pybind11==2.5.0 CIBW_ENVIRONMENT_MACOS: MACOSX_DEPLOYMENT_TARGET=10.14 CIBW_BUILD_VERBOSITY_MACOS: 0 CIBW_TEST_REQUIRES: pytest @@ -74,6 +79,7 @@ jobs: && cp {wheel} {dest_dir} CIBW_BEFORE_BUILD_WINDOWS: > python -m pip install --upgrade pip + && pip install pybind11==2.5.0 && git clone https://github.com/Chia-Network/mpir_gc_x64.git CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: > ls -l mpir_gc_x64 && pip uninstall -y delocate diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index db1ee9a4..00000000 --- a/MANIFEST.in +++ /dev/null @@ -1 +0,0 @@ -graft .git diff --git a/README.md b/README.md index 01309292..53bc7c82 100644 --- a/README.md +++ b/README.md @@ -8,18 +8,21 @@ [![Language grade: Python](https://img.shields.io/lgtm/grade/python/g/Chia-Network/chiavdf.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/Chia-Network/chiavdf/context:python) [![Language grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/Chia-Network/chiavdf.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/Chia-Network/chiavdf/context:cpp) -## Building -This component requires cmake, boost and GMP. +## Building a wheel +Compiling chiavdf requires cmake, boost and GMP. ``` -pip install wheel setuptools_scm +python3 -m venv venv +source venv/bin/activate + +pip install wheel setuptools_scm pybind11 pip wheel . ``` The primary build process for this repository is to use GitHub Actions to build binary wheels for MacOS, Linux, and Windows and publish them with a source wheel on PyPi. See `.github/workflows/build.yml`. setup.py adds -a dependency on [pybind11](https://github.com/pybind/pybind11) by invoking git -to check out the pybind submodules. Building is then managed by +a dependency on [pybind11](https://github.com/pybind/pybind11) using +`pip install pybind11`. Building is then managed by [cibuildwheel](https://github.com/joerick/cibuildwheel). Further installation is then available via `pip install chiavdf` e.g. @@ -37,7 +40,7 @@ To build vdf_client set the environment variable BUILD_VDF_CLIENT to "Y". Similarly, to build vdf_bench set the environment variable BUILD_VDF_BENCH to "Y". `export BUILD_VDF_BENCH=Y`. -This is currently automated in the +This is currently automated via pip in the [install-timelord.sh](https://github.com/Chia-Network/chia-blockchain/blob/master/install-timelord.sh) script in the [chia-blockchain repository](https://github.com/Chia-Network/chia-blockchain) diff --git a/setup.py b/setup.py index f6b6fe52..a1985935 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,15 @@ -from distutils.command.install import install -from distutils.command.build import build +import os +import re +import shutil +import sys +import platform +import subprocess -from setuptools import Command +from distutils.command.build import build +from distutils.command.install import install +from distutils.version import LooseVersion +from setuptools.command.build_ext import build_ext +from setuptools import setup, setuptools, Extension, Command BUILD_HOOKS = [] @@ -46,18 +54,6 @@ class install_hook(HookCommand): ############################################ -import os -import re -import shutil -import sys -import platform -import subprocess - -from setuptools import setup, Extension -from setuptools.command.build_ext import build_ext -from distutils.version import LooseVersion - - class CMakeExtension(Extension): def __init__(self, name, sourcedir=''): Extension.__init__(self, name, sources=['./']) @@ -95,18 +91,9 @@ def run(self): try: out = subprocess.check_output(['cmake', '--version']) except OSError: - raise RuntimeError("CMake must be installed to build" + - " the following extensions: " + - ", ".join(e.name for e in self.extensions)) - """ - Work around pybind11's need to be on the filesystem - """ - if os.path.exists('.gitmodules'): - try: - subprocess.run(['git', 'submodule', 'update', '--init', '--recursive']) - except OSError: - raise RuntimeError("git is not available" - + ", ".join(e.name for e in self.extensions)) + raise RuntimeError("CMake must be installed to build" + + " the following extensions: " + + ", ".join(e.name for e in self.extensions)) if platform.system() == "Windows": cmake_version = LooseVersion( @@ -137,8 +124,9 @@ def build_extension(self, ext): env = os.environ.copy() env['CXXFLAGS'] = '{} -DVERSION_INFO=\\"{}\\"'.format( - env.get('CXXFLAGS', ''), - self.distribution.get_version()) + env.get('CXXFLAGS', ''), + self.distribution.get_version() + ) subprocess.check_call(['cmake', ext.sourcedir] + cmake_args, env=env) subprocess.check_call(['cmake', '--build', '.'] + build_args) @@ -161,7 +149,7 @@ def __str__(self): ext_modules = [ Extension( 'chiavdf', - sorted ( + sorted( [ "src/python_bindings/fastvdf.cpp", "src/refcode/lzcnt.c", @@ -204,7 +192,8 @@ def cpp_flag(compiler): flags = ['-std=c++17', '-std=c++14', '-std=c++11'] for flag in flags: - if has_flag(compiler, flag): return flag + if has_flag(compiler, flag): + return flag raise RuntimeError('Unsupported compiler -- at least C++11 support ' 'is needed!') @@ -213,12 +202,12 @@ def cpp_flag(compiler): class BuildExt(build_ext): """A custom build extension for adding compiler-specific options.""" c_opts = { - 'msvc': ['/EHsc','/std:c++17'], - 'unix': [], + 'msvc': ['/EHsc', '/std:c++17'], + 'unix': [""], } l_opts = { - 'msvc': [], - 'unix': [], + 'msvc': [""], + 'unix': [""], } if sys.platform == 'darwin': diff --git a/src/cmake/pybind11Tools.cmake b/src/cmake/pybind11Tools.cmake new file mode 100644 index 00000000..508e4742 --- /dev/null +++ b/src/cmake/pybind11Tools.cmake @@ -0,0 +1,227 @@ +# tools/pybind11Tools.cmake -- Build system for the pybind11 modules +# +# Copyright (c) 2015 Wenzel Jakob +# +# All rights reserved. Use of this source code is governed by a +# BSD-style license that can be found in the LICENSE file. + +cmake_minimum_required(VERSION 2.8.12) + +# Add a CMake parameter for choosing a desired Python version +if(NOT PYBIND11_PYTHON_VERSION) + set(PYBIND11_PYTHON_VERSION "" CACHE STRING "Python version to use for compiling modules") +endif() + +set(Python_ADDITIONAL_VERSIONS 3.9 3.8 3.7 3.6 3.5 3.4) +find_package(PythonLibsNew ${PYBIND11_PYTHON_VERSION} REQUIRED) + +include(CheckCXXCompilerFlag) +include(CMakeParseArguments) + +if(NOT PYBIND11_CPP_STANDARD AND NOT CMAKE_CXX_STANDARD) + if(NOT MSVC) + check_cxx_compiler_flag("-std=c++14" HAS_CPP14_FLAG) + + if (HAS_CPP14_FLAG) + set(PYBIND11_CPP_STANDARD -std=c++14) + else() + check_cxx_compiler_flag("-std=c++11" HAS_CPP11_FLAG) + if (HAS_CPP11_FLAG) + set(PYBIND11_CPP_STANDARD -std=c++11) + else() + message(FATAL_ERROR "Unsupported compiler -- pybind11 requires C++11 support!") + endif() + endif() + elseif(MSVC) + set(PYBIND11_CPP_STANDARD /std:c++14) + endif() + + set(PYBIND11_CPP_STANDARD ${PYBIND11_CPP_STANDARD} CACHE STRING + "C++ standard flag, e.g. -std=c++11, -std=c++14, /std:c++14. Defaults to C++14 mode." FORCE) +endif() + +# Checks whether the given CXX/linker flags can compile and link a cxx file. cxxflags and +# linkerflags are lists of flags to use. The result variable is a unique variable name for each set +# of flags: the compilation result will be cached base on the result variable. If the flags work, +# sets them in cxxflags_out/linkerflags_out internal cache variables (in addition to ${result}). +function(_pybind11_return_if_cxx_and_linker_flags_work result cxxflags linkerflags cxxflags_out linkerflags_out) + set(CMAKE_REQUIRED_LIBRARIES ${linkerflags}) + check_cxx_compiler_flag("${cxxflags}" ${result}) + if (${result}) + set(${cxxflags_out} "${cxxflags}" CACHE INTERNAL "" FORCE) + set(${linkerflags_out} "${linkerflags}" CACHE INTERNAL "" FORCE) + endif() +endfunction() + +# Internal: find the appropriate link time optimization flags for this compiler +function(_pybind11_add_lto_flags target_name prefer_thin_lto) + if (NOT DEFINED PYBIND11_LTO_CXX_FLAGS) + set(PYBIND11_LTO_CXX_FLAGS "" CACHE INTERNAL "") + set(PYBIND11_LTO_LINKER_FLAGS "" CACHE INTERNAL "") + + if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") + set(cxx_append "") + set(linker_append "") + if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT APPLE) + # Clang Gold plugin does not support -Os; append -O3 to MinSizeRel builds to override it + set(linker_append ";$<$:-O3>") + elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU") + set(cxx_append ";-fno-fat-lto-objects") + endif() + + if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND prefer_thin_lto) + _pybind11_return_if_cxx_and_linker_flags_work(HAS_FLTO_THIN + "-flto=thin${cxx_append}" "-flto=thin${linker_append}" + PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS) + endif() + + if (NOT HAS_FLTO_THIN) + _pybind11_return_if_cxx_and_linker_flags_work(HAS_FLTO + "-flto${cxx_append}" "-flto${linker_append}" + PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS) + endif() + elseif (CMAKE_CXX_COMPILER_ID MATCHES "Intel") + # Intel equivalent to LTO is called IPO + _pybind11_return_if_cxx_and_linker_flags_work(HAS_INTEL_IPO + "-ipo" "-ipo" PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS) + elseif(MSVC) + # cmake only interprets libraries as linker flags when they start with a - (otherwise it + # converts /LTCG to \LTCG as if it was a Windows path). Luckily MSVC supports passing flags + # with - instead of /, even if it is a bit non-standard: + _pybind11_return_if_cxx_and_linker_flags_work(HAS_MSVC_GL_LTCG + "/GL" "-LTCG" PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS) + endif() + + if (PYBIND11_LTO_CXX_FLAGS) + message(STATUS "LTO enabled") + else() + message(STATUS "LTO disabled (not supported by the compiler and/or linker)") + endif() + endif() + + # Enable LTO flags if found, except for Debug builds + if (PYBIND11_LTO_CXX_FLAGS) + target_compile_options(${target_name} PRIVATE "$<$>:${PYBIND11_LTO_CXX_FLAGS}>") + endif() + if (PYBIND11_LTO_LINKER_FLAGS) + target_link_libraries(${target_name} PRIVATE "$<$>:${PYBIND11_LTO_LINKER_FLAGS}>") + endif() +endfunction() + +# Build a Python extension module: +# pybind11_add_module( [MODULE | SHARED] [EXCLUDE_FROM_ALL] +# [NO_EXTRAS] [SYSTEM] [THIN_LTO] source1 [source2 ...]) +# +function(pybind11_add_module target_name) + set(options MODULE SHARED EXCLUDE_FROM_ALL NO_EXTRAS SYSTEM THIN_LTO) + cmake_parse_arguments(ARG "${options}" "" "" ${ARGN}) + + if(ARG_MODULE AND ARG_SHARED) + message(FATAL_ERROR "Can't be both MODULE and SHARED") + elseif(ARG_SHARED) + set(lib_type SHARED) + else() + set(lib_type MODULE) + endif() + + if(ARG_EXCLUDE_FROM_ALL) + set(exclude_from_all EXCLUDE_FROM_ALL) + endif() + + add_library(${target_name} ${lib_type} ${exclude_from_all} ${ARG_UNPARSED_ARGUMENTS}) + + if(ARG_SYSTEM) + set(inc_isystem SYSTEM) + endif() + + target_include_directories(${target_name} ${inc_isystem} + PRIVATE ${PYBIND11_INCLUDE_DIR} # from project CMakeLists.txt + PRIVATE ${pybind11_INCLUDE_DIR} # from pybind11Config + PRIVATE ${PYTHON_INCLUDE_DIRS}) + + # Python debug libraries expose slightly different objects + # https://docs.python.org/3.6/c-api/intro.html#debugging-builds + # https://stackoverflow.com/questions/39161202/how-to-work-around-missing-pymodule-create2-in-amd64-win-python35-d-lib + if(PYTHON_IS_DEBUG) + target_compile_definitions(${target_name} PRIVATE Py_DEBUG) + endif() + + # The prefix and extension are provided by FindPythonLibsNew.cmake + set_target_properties(${target_name} PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}") + set_target_properties(${target_name} PROPERTIES SUFFIX "${PYTHON_MODULE_EXTENSION}") + + # -fvisibility=hidden is required to allow multiple modules compiled against + # different pybind versions to work properly, and for some features (e.g. + # py::module_local). We force it on everything inside the `pybind11` + # namespace; also turning it on for a pybind module compilation here avoids + # potential warnings or issues from having mixed hidden/non-hidden types. + set_target_properties(${target_name} PROPERTIES CXX_VISIBILITY_PRESET "hidden") + set_target_properties(${target_name} PROPERTIES CUDA_VISIBILITY_PRESET "hidden") + + if(WIN32 OR CYGWIN) + # Link against the Python shared library on Windows + target_link_libraries(${target_name} PRIVATE ${PYTHON_LIBRARIES}) + elseif(APPLE) + # It's quite common to have multiple copies of the same Python version + # installed on one's system. E.g.: one copy from the OS and another copy + # that's statically linked into an application like Blender or Maya. + # If we link our plugin library against the OS Python here and import it + # into Blender or Maya later on, this will cause segfaults when multiple + # conflicting Python instances are active at the same time (even when they + # are of the same version). + + # Windows is not affected by this issue since it handles DLL imports + # differently. The solution for Linux and Mac OS is simple: we just don't + # link against the Python library. The resulting shared library will have + # missing symbols, but that's perfectly fine -- they will be resolved at + # import time. + + target_link_libraries(${target_name} PRIVATE "-undefined dynamic_lookup") + + if(ARG_SHARED) + # Suppress CMake >= 3.0 warning for shared libraries + set_target_properties(${target_name} PROPERTIES MACOSX_RPATH ON) + endif() + endif() + + # Make sure C++11/14 are enabled + if(CMAKE_VERSION VERSION_LESS 3.3) + target_compile_options(${target_name} PUBLIC ${PYBIND11_CPP_STANDARD}) + else() + target_compile_options(${target_name} PUBLIC $<$:${PYBIND11_CPP_STANDARD}>) + endif() + + if(ARG_NO_EXTRAS) + return() + endif() + + _pybind11_add_lto_flags(${target_name} ${ARG_THIN_LTO}) + + if (NOT MSVC AND NOT ${CMAKE_BUILD_TYPE} MATCHES Debug|RelWithDebInfo) + # Strip unnecessary sections of the binary on Linux/Mac OS + if(CMAKE_STRIP) + if(APPLE) + add_custom_command(TARGET ${target_name} POST_BUILD + COMMAND ${CMAKE_STRIP} -x $) + else() + add_custom_command(TARGET ${target_name} POST_BUILD + COMMAND ${CMAKE_STRIP} $) + endif() + endif() + endif() + + if(MSVC) + # /MP enables multithreaded builds (relevant when there are many files), /bigobj is + # needed for bigger binding projects due to the limit to 64k addressable sections + target_compile_options(${target_name} PRIVATE /bigobj) + if(CMAKE_VERSION VERSION_LESS 3.11) + target_compile_options(${target_name} PRIVATE $<$>:/MP>) + else() + # Only set these options for C++ files. This is important so that, for + # instance, projects that include other types of source files like CUDA + # .cu files don't get these options propagated to nvcc since that would + # cause the build to fail. + target_compile_options(${target_name} PRIVATE $<$>:$<$:/MP>>) + endif() + endif() +endfunction() From d56a7b6876bf93a8a439a0adcfee896d38e106e7 Mon Sep 17 00:00:00 2001 From: Gene Hoffman Date: Mon, 18 May 2020 15:05:01 -0700 Subject: [PATCH 3/3] Use FetchContent to download pybind11 --- .github/workflows/build.yml | 9 +- .gitignore | 1 + README.md | 7 +- src/CMakeLists.txt | 12 +- src/cmake/pybind11Tools.cmake | 227 ---------------------------------- 5 files changed, 16 insertions(+), 240 deletions(-) delete mode 100644 src/cmake/pybind11Tools.cmake diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7aea5cfb..82bf9596 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,18 +17,14 @@ jobs: fetch-depth: 0 # we need fetch-depth 0 so setuptools_scm can resolve tags - - name: Checkout submodules - uses: snickerbockers/submodules-init@v4 - - uses: actions/setup-python@v1 name: Install Python with: python-version: '3.7' - - name: Install cibuildwheel and pybind11 + - name: Install cibuildwheel run: | python -m pip install --upgrade pip - pip install wheel pybind11==2.5.0 pip install cibuildwheel==1.3.0 - name: Lint source with flake8 @@ -63,11 +59,9 @@ jobs: && rm -f /usr/bin/cmake && yum -y install boost-devel gmp-devel && python -m pip install --upgrade pip - && pip install pybind11==2.5.0 CIBW_BEFORE_BUILD_MACOS: > brew install boost && python -m pip install --upgrade pip - && pip install pybind11==2.5.0 CIBW_ENVIRONMENT_MACOS: MACOSX_DEPLOYMENT_TARGET=10.14 CIBW_BUILD_VERBOSITY_MACOS: 0 CIBW_TEST_REQUIRES: pytest @@ -79,7 +73,6 @@ jobs: && cp {wheel} {dest_dir} CIBW_BEFORE_BUILD_WINDOWS: > python -m pip install --upgrade pip - && pip install pybind11==2.5.0 && git clone https://github.com/Chia-Network/mpir_gc_x64.git CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: > ls -l mpir_gc_x64 && pip uninstall -y delocate diff --git a/.gitignore b/.gitignore index 6904e154..6ff94676 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ CMakeFiles/ Makefile CMakeCache.txt cmake_install.cmake +_deps/ # Binary files src/compile_asm diff --git a/README.md b/README.md index 53bc7c82..bcf0c063 100644 --- a/README.md +++ b/README.md @@ -20,9 +20,10 @@ pip wheel . The primary build process for this repository is to use GitHub Actions to build binary wheels for MacOS, Linux, and Windows and publish them with -a source wheel on PyPi. See `.github/workflows/build.yml`. setup.py adds -a dependency on [pybind11](https://github.com/pybind/pybind11) using -`pip install pybind11`. Building is then managed by +a source wheel on PyPi. See `.github/workflows/build.yml`. CMake uses +[FetchContent](https://cmake.org/cmake/help/latest/module/FetchContent.html) +to download [pybind11](https://github.com/pybind/pybind11). +Building is then managed by [cibuildwheel](https://github.com/joerick/cibuildwheel). Further installation is then available via `pip install chiavdf` e.g. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8c861d34..7897fe3b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -26,8 +26,16 @@ include_directories( set (CMAKE_CXX_FLAGS "-std=c++1z") -add_subdirectory(lib/pybind11) -pybind11_add_module(chiavdf +include(FetchContent) + +FetchContent_Declare( + pybind11-src + GIT_REPOSITORY https://github.com/pybind/pybind11.git + GIT_TAG v2.5.0 +) +FetchContent_MakeAvailable(pybind11-src) + +pybind11_add_module(chiavdf ${CMAKE_CURRENT_SOURCE_DIR}/python_bindings/fastvdf.cpp ${CMAKE_CURRENT_SOURCE_DIR}/refcode/lzcnt.c ) diff --git a/src/cmake/pybind11Tools.cmake b/src/cmake/pybind11Tools.cmake deleted file mode 100644 index 508e4742..00000000 --- a/src/cmake/pybind11Tools.cmake +++ /dev/null @@ -1,227 +0,0 @@ -# tools/pybind11Tools.cmake -- Build system for the pybind11 modules -# -# Copyright (c) 2015 Wenzel Jakob -# -# All rights reserved. Use of this source code is governed by a -# BSD-style license that can be found in the LICENSE file. - -cmake_minimum_required(VERSION 2.8.12) - -# Add a CMake parameter for choosing a desired Python version -if(NOT PYBIND11_PYTHON_VERSION) - set(PYBIND11_PYTHON_VERSION "" CACHE STRING "Python version to use for compiling modules") -endif() - -set(Python_ADDITIONAL_VERSIONS 3.9 3.8 3.7 3.6 3.5 3.4) -find_package(PythonLibsNew ${PYBIND11_PYTHON_VERSION} REQUIRED) - -include(CheckCXXCompilerFlag) -include(CMakeParseArguments) - -if(NOT PYBIND11_CPP_STANDARD AND NOT CMAKE_CXX_STANDARD) - if(NOT MSVC) - check_cxx_compiler_flag("-std=c++14" HAS_CPP14_FLAG) - - if (HAS_CPP14_FLAG) - set(PYBIND11_CPP_STANDARD -std=c++14) - else() - check_cxx_compiler_flag("-std=c++11" HAS_CPP11_FLAG) - if (HAS_CPP11_FLAG) - set(PYBIND11_CPP_STANDARD -std=c++11) - else() - message(FATAL_ERROR "Unsupported compiler -- pybind11 requires C++11 support!") - endif() - endif() - elseif(MSVC) - set(PYBIND11_CPP_STANDARD /std:c++14) - endif() - - set(PYBIND11_CPP_STANDARD ${PYBIND11_CPP_STANDARD} CACHE STRING - "C++ standard flag, e.g. -std=c++11, -std=c++14, /std:c++14. Defaults to C++14 mode." FORCE) -endif() - -# Checks whether the given CXX/linker flags can compile and link a cxx file. cxxflags and -# linkerflags are lists of flags to use. The result variable is a unique variable name for each set -# of flags: the compilation result will be cached base on the result variable. If the flags work, -# sets them in cxxflags_out/linkerflags_out internal cache variables (in addition to ${result}). -function(_pybind11_return_if_cxx_and_linker_flags_work result cxxflags linkerflags cxxflags_out linkerflags_out) - set(CMAKE_REQUIRED_LIBRARIES ${linkerflags}) - check_cxx_compiler_flag("${cxxflags}" ${result}) - if (${result}) - set(${cxxflags_out} "${cxxflags}" CACHE INTERNAL "" FORCE) - set(${linkerflags_out} "${linkerflags}" CACHE INTERNAL "" FORCE) - endif() -endfunction() - -# Internal: find the appropriate link time optimization flags for this compiler -function(_pybind11_add_lto_flags target_name prefer_thin_lto) - if (NOT DEFINED PYBIND11_LTO_CXX_FLAGS) - set(PYBIND11_LTO_CXX_FLAGS "" CACHE INTERNAL "") - set(PYBIND11_LTO_LINKER_FLAGS "" CACHE INTERNAL "") - - if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") - set(cxx_append "") - set(linker_append "") - if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT APPLE) - # Clang Gold plugin does not support -Os; append -O3 to MinSizeRel builds to override it - set(linker_append ";$<$:-O3>") - elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU") - set(cxx_append ";-fno-fat-lto-objects") - endif() - - if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND prefer_thin_lto) - _pybind11_return_if_cxx_and_linker_flags_work(HAS_FLTO_THIN - "-flto=thin${cxx_append}" "-flto=thin${linker_append}" - PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS) - endif() - - if (NOT HAS_FLTO_THIN) - _pybind11_return_if_cxx_and_linker_flags_work(HAS_FLTO - "-flto${cxx_append}" "-flto${linker_append}" - PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS) - endif() - elseif (CMAKE_CXX_COMPILER_ID MATCHES "Intel") - # Intel equivalent to LTO is called IPO - _pybind11_return_if_cxx_and_linker_flags_work(HAS_INTEL_IPO - "-ipo" "-ipo" PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS) - elseif(MSVC) - # cmake only interprets libraries as linker flags when they start with a - (otherwise it - # converts /LTCG to \LTCG as if it was a Windows path). Luckily MSVC supports passing flags - # with - instead of /, even if it is a bit non-standard: - _pybind11_return_if_cxx_and_linker_flags_work(HAS_MSVC_GL_LTCG - "/GL" "-LTCG" PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS) - endif() - - if (PYBIND11_LTO_CXX_FLAGS) - message(STATUS "LTO enabled") - else() - message(STATUS "LTO disabled (not supported by the compiler and/or linker)") - endif() - endif() - - # Enable LTO flags if found, except for Debug builds - if (PYBIND11_LTO_CXX_FLAGS) - target_compile_options(${target_name} PRIVATE "$<$>:${PYBIND11_LTO_CXX_FLAGS}>") - endif() - if (PYBIND11_LTO_LINKER_FLAGS) - target_link_libraries(${target_name} PRIVATE "$<$>:${PYBIND11_LTO_LINKER_FLAGS}>") - endif() -endfunction() - -# Build a Python extension module: -# pybind11_add_module( [MODULE | SHARED] [EXCLUDE_FROM_ALL] -# [NO_EXTRAS] [SYSTEM] [THIN_LTO] source1 [source2 ...]) -# -function(pybind11_add_module target_name) - set(options MODULE SHARED EXCLUDE_FROM_ALL NO_EXTRAS SYSTEM THIN_LTO) - cmake_parse_arguments(ARG "${options}" "" "" ${ARGN}) - - if(ARG_MODULE AND ARG_SHARED) - message(FATAL_ERROR "Can't be both MODULE and SHARED") - elseif(ARG_SHARED) - set(lib_type SHARED) - else() - set(lib_type MODULE) - endif() - - if(ARG_EXCLUDE_FROM_ALL) - set(exclude_from_all EXCLUDE_FROM_ALL) - endif() - - add_library(${target_name} ${lib_type} ${exclude_from_all} ${ARG_UNPARSED_ARGUMENTS}) - - if(ARG_SYSTEM) - set(inc_isystem SYSTEM) - endif() - - target_include_directories(${target_name} ${inc_isystem} - PRIVATE ${PYBIND11_INCLUDE_DIR} # from project CMakeLists.txt - PRIVATE ${pybind11_INCLUDE_DIR} # from pybind11Config - PRIVATE ${PYTHON_INCLUDE_DIRS}) - - # Python debug libraries expose slightly different objects - # https://docs.python.org/3.6/c-api/intro.html#debugging-builds - # https://stackoverflow.com/questions/39161202/how-to-work-around-missing-pymodule-create2-in-amd64-win-python35-d-lib - if(PYTHON_IS_DEBUG) - target_compile_definitions(${target_name} PRIVATE Py_DEBUG) - endif() - - # The prefix and extension are provided by FindPythonLibsNew.cmake - set_target_properties(${target_name} PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}") - set_target_properties(${target_name} PROPERTIES SUFFIX "${PYTHON_MODULE_EXTENSION}") - - # -fvisibility=hidden is required to allow multiple modules compiled against - # different pybind versions to work properly, and for some features (e.g. - # py::module_local). We force it on everything inside the `pybind11` - # namespace; also turning it on for a pybind module compilation here avoids - # potential warnings or issues from having mixed hidden/non-hidden types. - set_target_properties(${target_name} PROPERTIES CXX_VISIBILITY_PRESET "hidden") - set_target_properties(${target_name} PROPERTIES CUDA_VISIBILITY_PRESET "hidden") - - if(WIN32 OR CYGWIN) - # Link against the Python shared library on Windows - target_link_libraries(${target_name} PRIVATE ${PYTHON_LIBRARIES}) - elseif(APPLE) - # It's quite common to have multiple copies of the same Python version - # installed on one's system. E.g.: one copy from the OS and another copy - # that's statically linked into an application like Blender or Maya. - # If we link our plugin library against the OS Python here and import it - # into Blender or Maya later on, this will cause segfaults when multiple - # conflicting Python instances are active at the same time (even when they - # are of the same version). - - # Windows is not affected by this issue since it handles DLL imports - # differently. The solution for Linux and Mac OS is simple: we just don't - # link against the Python library. The resulting shared library will have - # missing symbols, but that's perfectly fine -- they will be resolved at - # import time. - - target_link_libraries(${target_name} PRIVATE "-undefined dynamic_lookup") - - if(ARG_SHARED) - # Suppress CMake >= 3.0 warning for shared libraries - set_target_properties(${target_name} PROPERTIES MACOSX_RPATH ON) - endif() - endif() - - # Make sure C++11/14 are enabled - if(CMAKE_VERSION VERSION_LESS 3.3) - target_compile_options(${target_name} PUBLIC ${PYBIND11_CPP_STANDARD}) - else() - target_compile_options(${target_name} PUBLIC $<$:${PYBIND11_CPP_STANDARD}>) - endif() - - if(ARG_NO_EXTRAS) - return() - endif() - - _pybind11_add_lto_flags(${target_name} ${ARG_THIN_LTO}) - - if (NOT MSVC AND NOT ${CMAKE_BUILD_TYPE} MATCHES Debug|RelWithDebInfo) - # Strip unnecessary sections of the binary on Linux/Mac OS - if(CMAKE_STRIP) - if(APPLE) - add_custom_command(TARGET ${target_name} POST_BUILD - COMMAND ${CMAKE_STRIP} -x $) - else() - add_custom_command(TARGET ${target_name} POST_BUILD - COMMAND ${CMAKE_STRIP} $) - endif() - endif() - endif() - - if(MSVC) - # /MP enables multithreaded builds (relevant when there are many files), /bigobj is - # needed for bigger binding projects due to the limit to 64k addressable sections - target_compile_options(${target_name} PRIVATE /bigobj) - if(CMAKE_VERSION VERSION_LESS 3.11) - target_compile_options(${target_name} PRIVATE $<$>:/MP>) - else() - # Only set these options for C++ files. This is important so that, for - # instance, projects that include other types of source files like CUDA - # .cu files don't get these options propagated to nvcc since that would - # cause the build to fail. - target_compile_options(${target_name} PRIVATE $<$>:$<$:/MP>>) - endif() - endif() -endfunction()