cmake_minimum_required(VERSION 3.24...3.31) project(BOOST_HISTOGRAM LANGUAGES CXX) # Version is added later include(FetchContent) # Boost histogram requires C++14 set(CMAKE_CXX_STANDARD 14 CACHE STRING "The C++ standard to compile with, 14+") set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) # Adding folders to keep the structure a bit nicer in IDE's set_property(GLOBAL PROPERTY USE_FOLDERS ON) # This will force color output at build time if this env variable is set _at # configure time_. This is useful for CI. if($ENV{FORCE_COLOR}) if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") add_compile_options(-fdiagnostics-color=always) elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") add_compile_options(-fcolor-diagnostics) endif() endif() # This is a standard recipe for setting a default build type set(_default_build_type "Debug") if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) message(STATUS "Setting build type to '${_default_build_type}' as none was specified.") set(CMAKE_BUILD_TYPE "${_default_build_type}" CACHE STRING "Choose the type of build." FORCE) # Set the possible values of build type for cmake-gui set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") endif() # If no Python venv set, and .venv exists, use it. if(NOT DEFINED ENV{VIRTUALENV} AND NOT DEFINED Python_ROOT_DIR AND NOT DEFINED Python_EXECUTABLE AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.venv") set(Python_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/.venv") endif() # Adding pybind11 and setting up Python # Will display pybind11 version set(PYBIND11_FINDPYTHON TRUE) set(Python_ARTIFACTS_INTERACTIVE TRUE) FetchContent_Declare( pybind11 GIT_REPOSITORY https://github.com/pybind/pybind11.git GIT_TAG bd67643652d3800837f1f41549a2a5adbaa3fafe # v2.13.3 FIND_PACKAGE_ARGS NAMES pybind11) FetchContent_MakeAvailable(pybind11) # Display versions message(STATUS "CMake ${CMAKE_VERSION}") message(STATUS "Python ${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}") if(DEFINED pybind11_VERSION) message(STATUS "pybind11 ${pybind11_VERSION}") endif() # This is completely optional and just adds hints to IDEs - no affect on build at all. file(GLOB_RECURSE BOOST_HIST_FILES "extern/histogram/include/*.hpp") file(GLOB_RECURSE BOOST_HIST_PY_HEADERS "include/bh_python/*.hpp") # List the source files for the Python extension # On some backends (like make), this will regenerate if a file was added/removed file(GLOB BOOST_HIST_PY_SRC CONFIGURE_DEPENDS src/*.cpp) # This is the Python module pybind11_add_module(_core MODULE ${BOOST_HIST_PY_HEADERS} ${BOOST_HIST_PY_SRC} ${BOOST_HIST_FILES}) # Add the include directory for boost/histogram/python target_include_directories(_core PRIVATE include) # These are the Boost header-only libraries required by Boost::Histogram target_include_directories( _core SYSTEM PUBLIC extern/assert/include extern/config/include extern/core/include extern/histogram/include extern/mp11/include extern/throw_exception/include extern/variant2/include) # Support older Windows from newer MSVC target_compile_options(_core PRIVATE $<$:/d2FH4->) # Better error messages target_compile_definitions(_core PRIVATE PYBIND11_DETAILED_ERROR_MESSAGES) # Atomic is required for armv7l if(CMAKE_SYSTEM_PROCESSOR MATCHES "armv7l") target_link_libraries(_core PRIVATE atomic) endif() # This makes IDE's like XCode mimic the Boost Histogram structure source_group( TREE ${CMAKE_CURRENT_SOURCE_DIR}/extern/histogram/include PREFIX "Header Files" FILES ${BOOST_HIST_FILES}) source_group( TREE ${CMAKE_CURRENT_SOURCE_DIR}/include PREFIX "Header Files" FILES ${BOOST_HIST_PY_HEADERS}) source_group( TREE ${CMAKE_CURRENT_SOURCE_DIR}/src PREFIX "Source Files" FILES ${BOOST_HIST_PY_SRC}) # Cause warnings to be errors (not recommended for MSVC, since pybind11 might cause a few there) option(BOOST_HISTOGRAM_ERRORS "Make warnings errors (for CI mostly)") # Adding warnings # Boost.Histogram doesn't pass sign -Wsign-conversion if("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") target_compile_options( _core PRIVATE -Wall -Wextra -pedantic-errors -Wconversion -Wsign-compare -Wno-unused-value -Wno-sign-conversion) if(BOOST_HISTOGRAM_ERRORS) target_compile_options(_core PRIVATE -Werror) endif() elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "MSVC") target_compile_options(_core PRIVATE /W4) if(BOOST_HISTOGRAM_ERRORS) target_compile_options(_core PRIVATE /WE) endif() endif() # This allows setting the maximum number of axes in a histogram set(BOOST_HISTOGRAM_DETAIL_AXES_LIMIT "" CACHE STRING "Set the maximum number of axis in a histogram (affects compile time and size)") if(NOT "${BOOST_HISTOGRAM_DETAIL_AXES_LIMIT}" STREQUAL "") target_compile_definitions( _core PRIVATE BOOST_HISTOGRAM_DETAIL_AXES_LIMIT=${BOOST_HISTOGRAM_DETAIL_AXES_LIMIT}) endif() # Make the output library be in boost_histogram/_core... # This is a generator expression to avoid Debug/Release subdirectories in IDEs, # which confuses Python. set_property(TARGET _core PROPERTY LIBRARY_OUTPUT_DIRECTORY "$<1:boost_histogram>") # Collect all the python files and symlink them into the build directory # Protects from in-source builds (don't do this, please) if(NOT "${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}" AND NOT DEFINED SKBUILD) file( GLOB_RECURSE BOOST_HIST_PY_FILES LIST_DIRECTORIES false RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/src" CONFIGURE_DEPENDS "src/boost_histogram/*.py") foreach(F IN LISTS BOOST_HIST_PY_FILES) file(REMOVE "${CMAKE_CURRENT_BINARY_DIR}/${F}") file(CREATE_LINK "${CMAKE_CURRENT_SOURCE_DIR}/src/${F}" "${CMAKE_CURRENT_BINARY_DIR}/${F}" COPY_ON_ERROR SYMBOLIC) endforeach() endif() # Support installing install(TARGETS _core DESTINATION "boost_histogram") if(NOT DEFINED SKBUILD) install(DIRECTORY "src/boost_histogram" DESTINATION ".") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/boost_histogram/version.py" DESTINATION "boost_histogram") # Tests (Requires pytest to be available to run) include(CTest) endif() if(DEFINED SKBUILD) # Don't worry about the version elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/boost_histogram/version.py") set(VERSION_REGEX [=[version[ \t]*=[ \t]*["']([0-9]+\.[0-9]+\.[0-9]+)]=]) # Read in the line containing the version file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/src/boost_histogram/version.py" VERSION_STRING REGEX [=[version[ \t]*=]=]) # Pick out just the version string(REGEX MATCH [=[[0-9]+\.[0-9]+\.[0-9]+]=] VERSION_STRING "${VERSION_STRING}") else() pybind11_find_import(setuptools_scm REQUIRED) execute_process( COMMAND ${Python_EXECUTABLE} -c "from setuptools_scm import get_version; print(get_version())" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" OUTPUT_VARIABLE VERSION_FULL_STRING OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND_ERROR_IS_FATAL ANY) string(REGEX MATCH [=[^[0-9]+\.[0-9]+\.[0-9]+]=] VERSION_STRING "${VERSION_FULL_STRING}") string(REPLACE "-" "." VERSION_STRING "${VERSION_STRING}") file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/boost_histogram/version.py" "version = '${VERSION_FULL_STRING}'") message(STATUS "Full version output: ${VERSION_FULL_STRING}") endif() if(NOT DEFINED SKBUILD) project( BOOST_HISTOGRAM LANGUAGES CXX VERSION ${VERSION_STRING}) message(STATUS "boost-histogram ${BOOST_HISTOGRAM_VERSION}") if(BUILD_TESTING) add_subdirectory(tests) endif() endif()