From b82796e58d1b63dfd57561179199cdc06c6f1b24 Mon Sep 17 00:00:00 2001 From: Monty Montgomery Date: Sat, 14 May 2022 23:57:10 -0400 Subject: [PATCH] CMake enhancements for automated external package building Add explicit defaults where missing for clarity. Support passing in NETGEN_GIT_VERSION for use when building, eg, RPM packages using rpkg, which requires building from an untagged source bundle, that is, not directly from a git checkout. Add option to choose build against internal or external Pybind11; add cmake module to find and configure against system Pybind. re-add versioning of shared library files. Add section for GCC-specific options setting (right now, to turn off harmless warnings). Warn if pybind11_stubgen is not found. Adjust stubgen paths to work when build is not in-place in the source tree. --- CMakeLists.txt | 89 +++++--- cmake/cmake_modules/FindPythonLibsNew.cmake | 202 +++++++++++++++++ cmake/generate_version_file.cmake | 5 +- cmake/pybind11Tools.cmake | 227 ++++++++++++++++++++ libsrc/core/CMakeLists.txt | 1 + ng/CMakeLists.txt | 9 +- python/CMakeLists.txt | 22 +- 7 files changed, 510 insertions(+), 45 deletions(-) create mode 100644 cmake/cmake_modules/FindPythonLibsNew.cmake create mode 100644 cmake/pybind11Tools.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index b7662035..0a27f793 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,32 +7,33 @@ cmake_policy(VERSION 3.13) option( USE_NATIVE_ARCH "build for native cpu architecture" ON) -option( USE_GUI "build with GUI" ON ) -option( USE_PYTHON "build with python interface" ON ) -option( USE_MPI "enable mpi parallelization" OFF ) -option( USE_MPI4PY "enable mpi4py interface" ON ) -option( USE_OCC "build with OpenCascade geometry kernel interface" OFF) -option( USE_STLGEOM "build with STL geometry support" ON) -option( USE_CSG "build with CSG kernel" ON) -option( USE_INTERFACE "build nginterface" ON) -option( USE_GEOM2D "build 2d geometry kernels" ON) -option( USE_JPEG "enable snapshots using library libjpeg" OFF ) -option( USE_MPEG "enable video recording with FFmpeg, uses libavcodec" OFF ) -option( USE_CGNS "enable CGNS file read/write support" OFF ) -option( USE_NUMA "compile with NUMA-aware code") -option( INTEL_MIC "cross compile for intel xeon phi") +option( USE_GUI "build with GUI" ON ) +option( USE_PYTHON "build with python interface" ON ) +option( USE_MPI "enable mpi parallelization" OFF ) +option( USE_MPI4PY "enable mpi4py interface" ON ) +option( USE_OCC "build with OpenCascade geometry kernel interface" OFF) +option( USE_STLGEOM "build with STL geometry support" ON) +option( USE_CSG "build with CSG kernel" ON) +option( USE_INTERFACE "build nginterface" ON) +option( USE_GEOM2D "build 2d geometry kernels" ON) +option( USE_JPEG "enable snapshots using library libjpeg" OFF ) +option( USE_MPEG "enable video recording with FFmpeg, uses libavcodec" OFF ) +option( USE_CGNS "enable CGNS file read/write support" OFF ) +option( USE_NUMA "compile with NUMA-aware code" OFF ) +option( INTEL_MIC "cross compile for intel xeon phi" OFF ) option( INSTALL_PROFILES "install environment variable settings to /etc/profile.d" OFF ) -option( USE_CCACHE "use ccache") +option( USE_CCACHE "use ccache" OFF ) option( USE_INTERNAL_TCL "Compile tcl files into the code and don't install them" ON) -option( ENABLE_UNIT_TESTS "Enable Catch unit tests") +option( USE_INTERNAL_PYBIND11 "Use internally bundled version of Pybind11" ON) +option( ENABLE_UNIT_TESTS "Enable Catch unit tests" OFF) option( ENABLE_CPP_CORE_GUIDELINES_CHECK "Enable cpp core guideline checks on ngcore" OFF) -option( USE_SPDLOG "Enable spd log logging" OFF) -option( DEBUG_LOG "Enable more debug output (may increase computation time) - only works with USE_SPDLOG=ON" OFF) -option( CHECK_RANGE "Check array range access, automatically enabled if built in debug mode" OFF) +option( USE_SPDLOG "Enable spd log logging" OFF) +option( DEBUG_LOG "Enable more debug output (may increase computation time) - only works with USE_SPDLOG=ON" OFF) +option( CHECK_RANGE "Check array range access, automatically enabled if built in debug mode" OFF) option( BUILD_STUB_FILES "Build stub files for better autocompletion" ON) -option( BUILD_FOR_CONDA "Link python libraries only to executables" OFF) +option( BUILD_FOR_CONDA "Link python libraries only to executables" OFF) -option( USE_SUPERBUILD "build dependencies automatically" ON) +option( USE_SUPERBUILD "build dependencies automatically" ON) option( TRACE_MEMORY "Enable memory tracing" OFF) set(NG_COMPILE_FLAGS "" CACHE STRING "Additional compile flags") @@ -76,19 +77,20 @@ if (USE_SUPERBUILD) # execute the superbuild (this script will be invoked again without the # USE_SUPERBUILD option this time) include (cmake/SuperBuild.cmake) - return() # stop processing this file further -else() - project(Netgen) - if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) - set(CMAKE_INSTALL_PREFIX "${INSTALL_DIR_DEFAULT}" CACHE PATH "Install directory" FORCE) - endif() + return() # stop processing this file further endif() +# Versioning variables come in from the configuration include, need them now. set(CMAKE_EXPORT_COMPILE_COMMANDS ON) include (${CMAKE_CURRENT_LIST_DIR}/cmake/generate_version_file.cmake) set(CPACK_PACKAGE_VERSION "${NETGEN_VERSION}") +set(PACKAGE_VERSION "${NETGEN_VERSION}") +project(Netgen VERSION "${NETGEN_VERSION_SHORT}") +if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${INSTALL_DIR_DEFAULT}" CACHE PATH "Install directory" FORCE) +endif() ####################################################################### if(USE_CCACHE) @@ -230,11 +232,20 @@ endif(WIN32) if(APPLE) set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -undefined dynamic_lookup") endif(APPLE) +if (CMAKE_CXX_COMPILER_ID MATCHES "GNU") + add_compile_options(-Wno-deprecated -Wno-sign-compare -Wno-unused-variable -Wno-unused-but-set-variable) + add_compile_options(-Wno-cpp -Wno-unused -Wno-parentheses) + # Uncomment below to run sanitizers + #add_compile_options(-g3 -O0 -fno-inline -fno-omit-frame-pointer -fsanitize=undefined -fsanitize=address -fno-common) + #add_link_options(-g3 -O0 -fno-inline -fno-omit-frame-pointer -fsanitize=undefined -fsanitize=address -fno-common) +endif() ####################################################################### add_library(nglib ${NGLIB_LIBRARY_TYPE}) +set_target_properties(nglib PROPERTIES VERSION "${NETGEN_LIBRARY_VERSION}") if(USE_GUI) add_library(nggui ${NGGUI_LIBRARY_TYPE}) + set_target_properties(nggui PROPERTIES VERSION "${NETGEN_LIBRARY_VERSION}") if(WIN32) set_target_properties( nggui PROPERTIES OUTPUT_NAME "libnggui") endif(WIN32) @@ -302,7 +313,13 @@ else() endif() if (USE_PYTHON) - add_subdirectory(external_dependencies/pybind11) + if (USE_INTERNAL_PYBIND11) + message(STATUS "Checking for bundled Pybind11...") + add_subdirectory(external_dependencies/pybind11) + else() + message(STATUS "Looking for external Pybind11...") + include(cmake/pybind11Tools.cmake) + endif() find_path(PYBIND_INCLUDE_DIR pybind11/pybind11.h HINTS ${PYTHON_INCLUDE_DIR}) if( PYBIND_INCLUDE_DIR ) message(STATUS "Found Pybind11: ${PYBIND_INCLUDE_DIR}") @@ -427,11 +444,23 @@ if (USE_MPEG) endif (USE_MPEG) ####################################################################### -add_custom_target(ng_generate_version_file - ${CMAKE_COMMAND} +# When not building directly from git source, version will need to be +# set explicitly--- but still get it from git! +if (NETGEN_VERSION_GIT) + add_custom_target(ng_generate_version_file + ${CMAKE_COMMAND} + -DNETGEN_VERSION_GIT=${NETGEN_VERSION_GIT} -DBDIR=${CMAKE_CURRENT_BINARY_DIR} -P ${CMAKE_CURRENT_LIST_DIR}/cmake/generate_version_file.cmake ) +else() + add_custom_target(ng_generate_version_file + ${CMAKE_COMMAND} + -DBDIR=${CMAKE_CURRENT_BINARY_DIR} + -P ${CMAKE_CURRENT_LIST_DIR}/cmake/generate_version_file.cmake + ) +endif() + ####################################################################### if(INSTALL_PROFILES) file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/netgen.sh "#!/bin/sh\n") diff --git a/cmake/cmake_modules/FindPythonLibsNew.cmake b/cmake/cmake_modules/FindPythonLibsNew.cmake new file mode 100644 index 00000000..e660c5f3 --- /dev/null +++ b/cmake/cmake_modules/FindPythonLibsNew.cmake @@ -0,0 +1,202 @@ +# - Find python libraries +# This module finds the libraries corresponding to the Python interpreter +# FindPythonInterp provides. +# This code sets the following variables: +# +# PYTHONLIBS_FOUND - have the Python libs been found +# PYTHON_PREFIX - path to the Python installation +# PYTHON_LIBRARIES - path to the python library +# PYTHON_INCLUDE_DIRS - path to where Python.h is found +# PYTHON_MODULE_EXTENSION - lib extension, e.g. '.so' or '.pyd' +# PYTHON_MODULE_PREFIX - lib name prefix: usually an empty string +# PYTHON_SITE_PACKAGES - path to installation site-packages +# PYTHON_IS_DEBUG - whether the Python interpreter is a debug build +# +# Thanks to talljimbo for the patch adding the 'LDVERSION' config +# variable usage. + +#============================================================================= +# Copyright 2001-2009 Kitware, Inc. +# Copyright 2012 Continuum Analytics, Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# * Neither the names of Kitware, Inc., the Insight Software Consortium, +# nor the names of their contributors may be used to endorse or promote +# products derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#============================================================================= + +# Checking for the extension makes sure that `LibsNew` was found and not just `Libs`. +if(PYTHONLIBS_FOUND AND PYTHON_MODULE_EXTENSION) + return() +endif() + +# Use the Python interpreter to find the libs. +if(PythonLibsNew_FIND_REQUIRED) + find_package(PythonInterp ${PythonLibsNew_FIND_VERSION} REQUIRED) +else() + find_package(PythonInterp ${PythonLibsNew_FIND_VERSION}) +endif() + +if(NOT PYTHONINTERP_FOUND) + set(PYTHONLIBS_FOUND FALSE) + set(PythonLibsNew_FOUND FALSE) + return() +endif() + +# According to http://stackoverflow.com/questions/646518/python-how-to-detect-debug-interpreter +# testing whether sys has the gettotalrefcount function is a reliable, cross-platform +# way to detect a CPython debug interpreter. +# +# The library suffix is from the config var LDVERSION sometimes, otherwise +# VERSION. VERSION will typically be like "2.7" on unix, and "27" on windows. +execute_process(COMMAND "${PYTHON_EXECUTABLE}" "-c" + "from distutils import sysconfig as s;import sys;import struct; +print('.'.join(str(v) for v in sys.version_info)); +print(sys.prefix); +print(s.get_python_inc(plat_specific=True)); +print(s.get_python_lib(plat_specific=True)); +print(s.get_config_var('SO')); +print(hasattr(sys, 'gettotalrefcount')+0); +print(struct.calcsize('@P')); +print(s.get_config_var('LDVERSION') or s.get_config_var('VERSION')); +print(s.get_config_var('LIBDIR') or ''); +print(s.get_config_var('MULTIARCH') or ''); +" + RESULT_VARIABLE _PYTHON_SUCCESS + OUTPUT_VARIABLE _PYTHON_VALUES + ERROR_VARIABLE _PYTHON_ERROR_VALUE) + +if(NOT _PYTHON_SUCCESS MATCHES 0) + if(PythonLibsNew_FIND_REQUIRED) + message(FATAL_ERROR + "Python config failure:\n${_PYTHON_ERROR_VALUE}") + endif() + set(PYTHONLIBS_FOUND FALSE) + set(PythonLibsNew_FOUND FALSE) + return() +endif() + +# Convert the process output into a list +if(WIN32) + string(REGEX REPLACE "\\\\" "/" _PYTHON_VALUES ${_PYTHON_VALUES}) +endif() +string(REGEX REPLACE ";" "\\\\;" _PYTHON_VALUES ${_PYTHON_VALUES}) +string(REGEX REPLACE "\n" ";" _PYTHON_VALUES ${_PYTHON_VALUES}) +list(GET _PYTHON_VALUES 0 _PYTHON_VERSION_LIST) +list(GET _PYTHON_VALUES 1 PYTHON_PREFIX) +list(GET _PYTHON_VALUES 2 PYTHON_INCLUDE_DIR) +list(GET _PYTHON_VALUES 3 PYTHON_SITE_PACKAGES) +list(GET _PYTHON_VALUES 4 PYTHON_MODULE_EXTENSION) +list(GET _PYTHON_VALUES 5 PYTHON_IS_DEBUG) +list(GET _PYTHON_VALUES 6 PYTHON_SIZEOF_VOID_P) +list(GET _PYTHON_VALUES 7 PYTHON_LIBRARY_SUFFIX) +list(GET _PYTHON_VALUES 8 PYTHON_LIBDIR) +list(GET _PYTHON_VALUES 9 PYTHON_MULTIARCH) + +# Make sure the Python has the same pointer-size as the chosen compiler +# Skip if CMAKE_SIZEOF_VOID_P is not defined +if(CMAKE_SIZEOF_VOID_P AND (NOT "${PYTHON_SIZEOF_VOID_P}" STREQUAL "${CMAKE_SIZEOF_VOID_P}")) + if(PythonLibsNew_FIND_REQUIRED) + math(EXPR _PYTHON_BITS "${PYTHON_SIZEOF_VOID_P} * 8") + math(EXPR _CMAKE_BITS "${CMAKE_SIZEOF_VOID_P} * 8") + message(FATAL_ERROR + "Python config failure: Python is ${_PYTHON_BITS}-bit, " + "chosen compiler is ${_CMAKE_BITS}-bit") + endif() + set(PYTHONLIBS_FOUND FALSE) + set(PythonLibsNew_FOUND FALSE) + return() +endif() + +# The built-in FindPython didn't always give the version numbers +string(REGEX REPLACE "\\." ";" _PYTHON_VERSION_LIST ${_PYTHON_VERSION_LIST}) +list(GET _PYTHON_VERSION_LIST 0 PYTHON_VERSION_MAJOR) +list(GET _PYTHON_VERSION_LIST 1 PYTHON_VERSION_MINOR) +list(GET _PYTHON_VERSION_LIST 2 PYTHON_VERSION_PATCH) + +# Make sure all directory separators are '/' +string(REGEX REPLACE "\\\\" "/" PYTHON_PREFIX ${PYTHON_PREFIX}) +string(REGEX REPLACE "\\\\" "/" PYTHON_INCLUDE_DIR ${PYTHON_INCLUDE_DIR}) +string(REGEX REPLACE "\\\\" "/" PYTHON_SITE_PACKAGES ${PYTHON_SITE_PACKAGES}) + +if(CMAKE_HOST_WIN32 AND NOT (MSYS OR MINGW)) + set(PYTHON_LIBRARY + "${PYTHON_PREFIX}/libs/Python${PYTHON_LIBRARY_SUFFIX}.lib") + + # when run in a venv, PYTHON_PREFIX points to it. But the libraries remain in the + # original python installation. They may be found relative to PYTHON_INCLUDE_DIR. + if(NOT EXISTS "${PYTHON_LIBRARY}") + get_filename_component(_PYTHON_ROOT ${PYTHON_INCLUDE_DIR} DIRECTORY) + set(PYTHON_LIBRARY + "${_PYTHON_ROOT}/libs/Python${PYTHON_LIBRARY_SUFFIX}.lib") + endif() + + # raise an error if the python libs are still not found. + if(NOT EXISTS "${PYTHON_LIBRARY}") + message(FATAL_ERROR "Python libraries not found") + endif() + +else() + if(PYTHON_MULTIARCH) + set(_PYTHON_LIBS_SEARCH "${PYTHON_LIBDIR}/${PYTHON_MULTIARCH}" "${PYTHON_LIBDIR}") + else() + set(_PYTHON_LIBS_SEARCH "${PYTHON_LIBDIR}") + endif() + #message(STATUS "Searching for Python libs in ${_PYTHON_LIBS_SEARCH}") + # Probably this needs to be more involved. It would be nice if the config + # information the python interpreter itself gave us were more complete. + find_library(PYTHON_LIBRARY + NAMES "python${PYTHON_LIBRARY_SUFFIX}" + PATHS ${_PYTHON_LIBS_SEARCH} + NO_DEFAULT_PATH) + + # If all else fails, just set the name/version and let the linker figure out the path. + if(NOT PYTHON_LIBRARY) + set(PYTHON_LIBRARY python${PYTHON_LIBRARY_SUFFIX}) + endif() +endif() + +MARK_AS_ADVANCED( + PYTHON_LIBRARY + PYTHON_INCLUDE_DIR +) + +# We use PYTHON_INCLUDE_DIR, PYTHON_LIBRARY and PYTHON_DEBUG_LIBRARY for the +# cache entries because they are meant to specify the location of a single +# library. We now set the variables listed by the documentation for this +# module. +SET(PYTHON_INCLUDE_DIRS "${PYTHON_INCLUDE_DIR}") +SET(PYTHON_LIBRARIES "${PYTHON_LIBRARY}") +SET(PYTHON_DEBUG_LIBRARIES "${PYTHON_DEBUG_LIBRARY}") + +find_package_message(PYTHON + "Found PythonLibs: ${PYTHON_LIBRARY}" + "${PYTHON_EXECUTABLE}${PYTHON_VERSION}") + +set(PYTHONLIBS_FOUND TRUE) +set(PythonLibsNew_FOUND TRUE) diff --git a/cmake/generate_version_file.cmake b/cmake/generate_version_file.cmake index c4a579d1..e8f092ea 100644 --- a/cmake/generate_version_file.cmake +++ b/cmake/generate_version_file.cmake @@ -24,8 +24,8 @@ if(status AND NOT status EQUAL 0) string(REGEX REPLACE "^netgen(.*)" "\\1" git_version_string "${git_version_string}") endif() else() - MESSAGE(WARNING "Could not determine git-version from source code - assuming 6.2.0.0") - set(git_version_string "v6.2.0.0") + MESSAGE(WARNING "Could not determine git-version from source code - assuming 6.99.2200-0-0") + set(git_version_string "v6.99.2200-0-0") endif() endif() string(STRIP ${git_version_string} git_version_string) @@ -57,6 +57,7 @@ if(NOT NETGEN_VERSION_PYTHON) set(NETGEN_VERSION_PYTHON ${NETGEN_VERSION_TWEAK}) endif() +set(NETGEN_LIBRARY_VERSION "${NETGEN_VERSION_SHORT}") set(version_file ${BDIR}/netgen_version.hpp) set(new_version_file_string "\ diff --git a/cmake/pybind11Tools.cmake b/cmake/pybind11Tools.cmake new file mode 100644 index 00000000..c7156c02 --- /dev/null +++ b/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.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() diff --git a/libsrc/core/CMakeLists.txt b/libsrc/core/CMakeLists.txt index ee89b4f6..7c550fb9 100644 --- a/libsrc/core/CMakeLists.txt +++ b/libsrc/core/CMakeLists.txt @@ -13,6 +13,7 @@ add_library(ngcore ${NGCORE_LIBRARY_TYPE} utils.cpp version.cpp ) +set_target_properties(ngcore PROPERTIES VERSION "${NETGEN_LIBRARY_VERSION}") target_compile_options(ngcore PUBLIC "${NG_COMPILE_FLAGS}") diff --git a/ng/CMakeLists.txt b/ng/CMakeLists.txt index 3ad3d8be..9320d2f3 100644 --- a/ng/CMakeLists.txt +++ b/ng/CMakeLists.txt @@ -20,6 +20,7 @@ if(USE_GUI) if(NOT BUILD_FOR_CONDA) add_executable(netgen ngappinit.cpp) + set_target_properties(netgen PROPERTIES OUTPUT_NAME netgen-mesher) if(WIN32) target_sources(netgen PRIVATE ../windows/netgen.rc) endif(WIN32) @@ -28,7 +29,7 @@ if(USE_GUI) if(APPLE) set_target_properties(netgen PROPERTIES OUTPUT_NAME netgen) endif(APPLE) - target_link_libraries( netgen ${PYTHON_LIBRARIES} ${TCL_LIBRARY} ${TK_LIBRARY}) + target_link_libraries( netgen ${PYTHON_LIBRARIES} ${TCL_LIBRARY} ${TK_LIBRARY} ${JPEG_LIBRARIES}) endif(NOT BUILD_FOR_CONDA) install(TARGETS nggui ${NG_INSTALL_DIR}) @@ -36,6 +37,7 @@ endif(USE_GUI) if(USE_PYTHON) add_library(ngpy SHARED netgenpy.cpp) + set_target_properties(ngpy PROPERTIES VERSION "${NETGEN_LIBRARY_VERSION}") target_link_libraries( ngpy PUBLIC nglib PRIVATE "$" ) if(APPLE) set_target_properties( ngpy PROPERTIES SUFFIX ".so") @@ -44,10 +46,11 @@ if(USE_PYTHON) set_target_properties( ngpy PROPERTIES OUTPUT_NAME "libngpy") endif() set_target_properties(ngpy PROPERTIES INSTALL_RPATH "${NG_RPATH_TOKEN}/../${NETGEN_PYTHON_RPATH}") - install(TARGETS ngpy DESTINATION ${NG_INSTALL_DIR_PYTHON}/${NG_INSTALL_SUFFIX} COMPONENT netgen) + install(TARGETS ngpy DESTINATION ${NG_INSTALL_DIR_PYTHON}/netgen COMPONENT netgen) if(USE_GUI) add_library(ngguipy SHARED ngguipy.cpp) + set_target_properties(ngguipy PROPERTIES VERSION "${NETGEN_LIBRARY_VERSION}") target_link_libraries( ngguipy PUBLIC nglib nggui PRIVATE "$" $) if(APPLE) set_target_properties( ngguipy PROPERTIES SUFFIX ".so") @@ -56,7 +59,7 @@ if(USE_PYTHON) set_target_properties( ngguipy PROPERTIES OUTPUT_NAME "libngguipy") endif() set_target_properties(ngguipy PROPERTIES INSTALL_RPATH "${NG_RPATH_TOKEN}/../${NETGEN_PYTHON_RPATH}") - install(TARGETS ngguipy DESTINATION ${NG_INSTALL_DIR_PYTHON}/${NG_INSTALL_SUFFIX} COMPONENT netgen) + install(TARGETS ngguipy DESTINATION ${NG_INSTALL_DIR_PYTHON}/netgen COMPONENT netgen) endif(USE_GUI) endif(USE_PYTHON) diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index b4f46866..6a2fd84e 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -14,7 +14,7 @@ install(FILES meshing.py csg.py geom2d.py stl.py gui.py NgOCC.py occ.py read_gmsh.py read_meshio.py webgui.py - DESTINATION ${NG_INSTALL_DIR_PYTHON}/${NG_INSTALL_SUFFIX} + DESTINATION ${NG_INSTALL_DIR_PYTHON}/netgen COMPONENT netgen ) @@ -26,13 +26,15 @@ install(FILES # build stub files for pybind11 packages if(BUILD_STUB_FILES) -execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "import pybind11_stubgen; print(pybind11_stubgen.__file__)" OUTPUT_VARIABLE stubgen_path RESULT_VARIABLE pybind11_stubgen) -if(pybind11_stubgen AND NOT ${pybind11_stubgen} EQUAL 0) - message(WARNING "pybind11-stubgen not found, if you want to create stub files -for better autocompletion support install it with pip.") -else() - message("-- Found pybind11-stubgen: ${stubgen_path}") - install(CODE "execute_process(COMMAND ${PYTHON_EXECUTABLE} -m pybind11_stubgen --no-setup-py netgen)") - install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../stubs/netgen-stubs/ DESTINATION ${NG_INSTALL_DIR_PYTHON}/netgen/ COMPONENT netgen) -endif() + message(STATUS "Building Python stub files...") + execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "import pybind11_stubgen; print(pybind11_stubgen.__file__)" OUTPUT_VARIABLE stubgen_path RESULT_VARIABLE pybind11_stubgen) + if(pybind11_stubgen AND NOT ${pybind11_stubgen} EQUAL 0) + message(WARNING "pybind11-stubgen not found. If you want to create stub files for better autocompletion support, + install pybind11-stubgen with pip.") + else() + message("-- Found pybind11-stubgen: ${stubgen_path}") + set(env{PYTHONPATH} ".:${CMAKE_INSTALL_PREFIX}/${PYTHON_PACKAGES_INSTALL_DIR}") + install(CODE "execute_process(COMMAND ${PYTHON_EXECUTABLE} -m pybind11_stubgen -o ${CMAKE_BINARY_DIR}/stubs --no-setup-py netgen)") + install(DIRECTORY ${CMAKE_BINARY_DIR}/stubs/netgen-stubs/ DESTINATION ${NG_INSTALL_DIR_PYTHON}/netgen COMPONENT netgen) + endif() endif(BUILD_STUB_FILES)