Skip to content
Snippets Groups Projects
  • Christophe Guillon's avatar
    5a68721a
    [Build] Enable build of C++ unit tests with python bindings · 5a68721a
    Christophe Guillon authored and Olivier BICHLER's avatar Olivier BICHLER committed
    
    Adjust dependencies of core archive for allowing builds
    for python modules and build for executables when -DPYBIND=ON.
    Build can now be performed with -DPYBIND=ON and -DTEST=ON.
    Build can be done in case where the python libraries are not
    present (cibuildwheel), though in this case it is not possible
    to build standalone executables.
    Install pybind11 headers as part of interface in order to
    provide complete dependencies.
    Add AIDGE_BUILD_TEST envvar to setup.py to -DTEST=ON.
    Update required cmake version which should be actually >=3.18.
    Update and fix README.
    
    Co-authored-by: default avatarGrégoire Kubler <gregoire.kubler@proton.me>
    5a68721a
    History
    [Build] Enable build of C++ unit tests with python bindings
    Christophe Guillon authored and Olivier BICHLER's avatar Olivier BICHLER committed
    
    Adjust dependencies of core archive for allowing builds
    for python modules and build for executables when -DPYBIND=ON.
    Build can now be performed with -DPYBIND=ON and -DTEST=ON.
    Build can be done in case where the python libraries are not
    present (cibuildwheel), though in this case it is not possible
    to build standalone executables.
    Install pybind11 headers as part of interface in order to
    provide complete dependencies.
    Add AIDGE_BUILD_TEST envvar to setup.py to -DTEST=ON.
    Update required cmake version which should be actually >=3.18.
    Update and fix README.
    
    Co-authored-by: default avatarGrégoire Kubler <gregoire.kubler@proton.me>
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
CMakeLists.txt 7.88 KiB
cmake_minimum_required(VERSION 3.18)
set(CXX_STANDARD 14)

file(STRINGS "${CMAKE_SOURCE_DIR}/version.txt" version)

project(aidge_core
        VERSION ${version}
        DESCRIPTION "Core algorithms for operators and graph of the AIDGE framework" 
        LANGUAGES CXX)
message(STATUS "Project name: ${CMAKE_PROJECT_NAME}")
message(STATUS "Project version: ${version}")
add_definitions(-DPROJECT_VERSION="${version}")

message(STATUS "Project name: ${CMAKE_PROJECT_NAME}")
message(STATUS "Project version: ${version}")

# Note : project name is {project} and python module name is also {project}
set(module_name _${CMAKE_PROJECT_NAME}) # target name
set(pybind_module_name ${CMAKE_PROJECT_NAME}) # name of submodule for python bindings

##############################################
# Define options
option(PYBIND "python binding" OFF)
option(WERROR "Warning as error" OFF)
option(TEST "Enable tests" ON)
option(COVERAGE "Enable coverage" OFF)
option(ENABLE_ASAN "Enable ASan (AddressSanitizer) for runtime analysis of memory use (over/underflow, memory leak, ...)" OFF)

##############################################
# Import utils CMakeLists
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")

if(CMAKE_COMPILER_IS_GNUCXX AND COVERAGE)
    Include(CodeCoverage)
endif()

##############################################
# Find system dependencies
Include(FetchContent)

set(FMT_VERSION 10.2.1)
message(STATUS "Retrieving fmt ${FMT_VERSION} from git")
FetchContent_Declare(
    fmt
    GIT_REPOSITORY https://github.com/fmtlib/fmt.git
    GIT_TAG        ${FMT_VERSION} # or a later release
)

set(FMT_SYSTEM_HEADERS ON)
FetchContent_MakeAvailable(fmt)
set_property(TARGET fmt PROPERTY POSITION_INDEPENDENT_CODE ON)

find_package(Threads REQUIRED)

##############################################
# Create target and set properties

file(GLOB_RECURSE src_files "src/*.cpp")
file(GLOB_RECURSE inc_files "include/*.hpp")

add_library(${module_name} ${src_files} ${inc_files})

#Set target properties
set_property(TARGET ${module_name} PROPERTY POSITION_INDEPENDENT_CODE ON)
target_include_directories(${module_name}
    PUBLIC
        $<INSTALL_INTERFACE:include>
        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
    PRIVATE
        ${CMAKE_CURRENT_SOURCE_DIR}/src
)

if( ${ENABLE_ASAN} )
    message("Building ${module_name} with ASAN.")
    set(SANITIZE_FLAGS -fsanitize=address -fno-omit-frame-pointer)
    target_link_libraries(${module_name}
        PUBLIC
            -fsanitize=address
    )
    target_compile_options(${module_name}
        PRIVATE
            ${SANITIZE_FLAGS}
    )
endif()

# PYTHON BINDING
set(AIDGE_REQUIRES_PYTHON FALSE) # Will be set if aidge_core lib depends upon python interpreter
set(AIDGE_PYTHON_HAS_EMBED FALSE)  # Will be set if python interpreter is found on the system
if (PYBIND)
    # Python binding lib is by default installed in <prefix>/python_packages/<package>/
    # When installed from python, setup.py should set it to the python package dir
    set(PYBIND_INSTALL_PREFIX python_packages/${pybind_module_name} CACHE PATH "Python package install prefix")

    include(PybindModuleCreation)
    generate_python_binding(${pybind_module_name} ${module_name})

    ##
    # As of now, when PYBIND is set, the core archive itself depends upon pybind/python,
    # we define -DPYBIND and the dependencies on pybind/python runtime where necessary.

    # Add -DPYBIND to compilation and interface
    target_compile_definitions(${module_name} PUBLIC PYBIND)

    # Add dependencies on pybind/python. See details in add_pybind_dependency()
    include(PybindDependency)
    add_pybind_dependency(${module_name})
    ##
endif()

target_link_libraries(${module_name} PUBLIC Threads::Threads fmt::fmt)
target_compile_features(${module_name} PRIVATE cxx_std_14)

if (DOSANITIZE STREQUAL "ON")
set(SANITIZE_FLAGS -fsanitize=address,leak,undefined,float-divide-by-zero -fno-omit-frame-pointer)
#TODO sanitizer seems buggy in some situations with msvc, leading to linker errors, temporarily inactivating it
#set(SANITIZE_MSVC_FLAGS)
set(SANITIZE_MSVC_FLAGS /fsanitize=address)
target_compile_definitions(${module_name} PUBLIC _DISABLE_VECTOR_ANNOTATION)
else()
set(SANITIZE_FLAGS)
set(SANITIZE_MSVC_FLAGS)
endif()

set(STRICT_ALIASING_FLAGS -fstrict-aliasing -Wstrict-aliasing=2)

# -fvisibility=hidden required by pybind11
target_compile_options(${module_name} PUBLIC
    $<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:AppleClang>,$<CXX_COMPILER_ID:GNU>>:
    -fvisibility=hidden>)
target_compile_options(${module_name} PRIVATE
$<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:AppleClang>,$<CXX_COMPILER_ID:GNU>>:
-Wall -Wextra -Wold-style-cast -Winline -pedantic -Werror=narrowing -Wshadow $<$<BOOL:${WERROR}>:-Werror> ${SANITIZE_FLAGS}>)
target_compile_options(${module_name} PRIVATE
$<$<CXX_COMPILER_ID:GNU>:${STRICT_ALIASING_FLAGS}>)
target_compile_options(${module_name} PRIVATE
$<$<CXX_COMPILER_ID:MSVC>:
/W4 /wd4477 /DWIN32 /D_WINDOWS /GR /EHsc /MP /Zc:__cplusplus /Zc:preprocessor /permissive- ${SANITIZE_MSVC_FLAGS}>)
if (DOSANITIZE STREQUAL "ON")
    target_compile_options(${module_name} PRIVATE $<$<CXX_COMPILER_ID:MSVC>:/MDd>)
endif()
# TODO FIXME: I'm not sure it's a good idea to propagate this option but, at this point, it was the only way that worked to silence C4477
target_compile_options(${module_name} PUBLIC $<$<CXX_COMPILER_ID:MSVC>: /wd4477>)

target_link_options(${module_name} PUBLIC $<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:AppleClang>,$<CXX_COMPILER_ID:GNU>>:${SANITIZE_FLAGS}>)
#target_link_options(${module_name} PUBLIC $<$<CXX_COMPILER_ID:MSVC>:${SANITIZE_MSVC_FLAGS}>)

if(CMAKE_COMPILER_IS_GNUCXX AND COVERAGE)
    append_coverage_compiler_flags()
endif()

##############################################
# Installation instructions
if(NOT $ENV{AIDGE_INSTALL} STREQUAL "")
    set(CMAKE_INSTALL_PREFIX $ENV{AIDGE_INSTALL})
    message(WARNING "CMAKE_INSTALL_PREFIX set to env variable AIDGE_INSTALL by default = ${CMAKE_INSTALL_PREFIX}")
endif()

include(GNUInstallDirs)
set(INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/cmake/${CMAKE_PROJECT_NAME})

install(TARGETS ${module_name} EXPORT ${CMAKE_PROJECT_NAME}-targets
  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
  INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)

install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})

if (PYBIND)
    install(TARGETS ${pybind_module_name}
        DESTINATION ${PYBIND_INSTALL_PREFIX}
    )
endif()

#Export the targets to a script

install(EXPORT ${CMAKE_PROJECT_NAME}-targets
 FILE "${CMAKE_PROJECT_NAME}-targets.cmake"
 DESTINATION ${INSTALL_CONFIGDIR}
#  COMPONENT ${module_name}
)

#Create a ConfigVersion.cmake file
include(CMakePackageConfigHelpers)

write_basic_package_version_file(
    "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_PROJECT_NAME}-config-version.cmake"
    VERSION ${version}
    COMPATIBILITY AnyNewerVersion
)

configure_package_config_file("${CMAKE_PROJECT_NAME}-config.cmake.in"
    "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_PROJECT_NAME}-config.cmake"
    INSTALL_DESTINATION ${INSTALL_CONFIGDIR}
)

#Install the config, configversion and custom find modules
install(FILES
    "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_PROJECT_NAME}-config.cmake"
    "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_PROJECT_NAME}-config-version.cmake"
    DESTINATION ${INSTALL_CONFIGDIR}
)

##############################################
## Exporting from the build tree
message(STATUS "Exporting created targets to use them in another build")
export(EXPORT ${CMAKE_PROJECT_NAME}-targets
    FILE "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_PROJECT_NAME}-targets.cmake")

##############################################
## Add test
if(TEST)
    if (AIDGE_REQUIRES_PYTHON AND NOT AIDGE_PYTHON_HAS_EMBED)
        message(WARNING "Skipping compilation of tests: missing Python embedded interpreter")
    else()
        enable_testing()
        add_subdirectory(unit_tests)
    endif()
endif()