Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
CMakeLists.txt 16.82 KiB
#
# This file is a part of the LinkAhead Project.
#
# Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
# Copyright (C) 2024 Joscha Schmiedt <joscha@schmiedt.dev>
# Copyright (C) 2021-2024 IndiScale GmbH <info@indiscale.com>
#
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#

cmake_minimum_required(VERSION 3.13)

set(liblinkahead_VERSION 0.3.0)
set(liblinkahead_COMPATIBLE_SERVER_VERSION_MAJOR 0)
set(liblinkahead_COMPATIBLE_SERVER_VERSION_MINOR 9)
set(liblinkahead_COMPATIBLE_SERVER_VERSION_PATCH 0)
set(liblinkahead_COMPATIBLE_SERVER_VERSION_PRE_RELEASE "")

project(liblinkahead
    VERSION ${liblinkahead_VERSION}
    DESCRIPTION "C and C++ client libraries for LinkAhead"
    LANGUAGES CXX C)

set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(CMAKE_C_EXTENSIONS OFF)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})

IF (WIN32)
    set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
    add_compile_definitions(_CRT_SECURE_NO_WARNINGS) # suppress warnings for fopen, strcpy, etc.
endif()


IF (BUILD_ACM)
    message(STATUS "BUILD_ACM")
    add_compile_definitions("BUILD_ACM")
ENDIF()

message(STATUS "Build directory ${CMAKE_BINARY_DIR}")

###########################################
### GENERAL SETUP of SOURCES
###########################################
set(PROJECT_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/include")
add_subdirectory(src)
add_subdirectory(include)
add_subdirectory(doc)

####################################################################
### CODE GENERATION (WITH GRPC)
####################################################################

find_package(protobuf CONFIG REQUIRED)

# Protobuf/Grpc source files
set(PROTO_FILES
    ${PROJECT_SOURCE_DIR}/proto/proto/caosdb/info/v1/main.proto
    ${PROJECT_SOURCE_DIR}/proto/proto/caosdb/entity/v1/main.proto
)

IF (BUILD_ACM)
    list(APPEND PROTO_FILES
         ${PROJECT_SOURCE_DIR}/proto/proto/caosdb/acm/v1alpha1/main.proto
         )
ENDIF()

set(PROTO_PATH ${PROJECT_SOURCE_DIR}/proto/proto)

set(_PROTOBUF_PROTOC ${Protobuf_PROTOC_EXECUTABLE})
# compiler binaries
IF (WIN32)
    set(_GRPC_CPP_PLUGIN_EXECUTABLE "${CMAKE_BINARY_DIR}/build_tools/grpc_cpp_plugin.exe")
ELSE()
    set(_GRPC_CPP_PLUGIN_EXECUTABLE "${CMAKE_BINARY_DIR}/build_tools/grpc_cpp_plugin")
ENDIF()

# Generated sources
list(LENGTH PROTO_FILES len_proto_files)
math(EXPR len_proto_files "${len_proto_files} - 1")
foreach(i RANGE "${len_proto_files}")
    list(GET PROTO_FILES ${i} next_proto_file)

    # strip away the prefix path and the ".proto" suffix
    string(REPLACE
        "${PROJECT_SOURCE_DIR}/proto/proto/caosdb/"
        ""
        next_proto_module
        "${next_proto_file}")
    string(REPLACE
        ".proto"
        ""
        next_proto_module
        "${next_proto_module}")
    set(next_proto_src
        "${CMAKE_CURRENT_BINARY_DIR}/include/caosdb/${next_proto_module}.pb.cc")
    set(next_proto_hdr
        "${CMAKE_CURRENT_BINARY_DIR}/include/caosdb/${next_proto_module}.pb.h")
    set(next_grpc_src
        "${CMAKE_CURRENT_BINARY_DIR}/include/caosdb/${next_proto_module}.grpc.pb.cc")
    set(next_grpc_hdr
        "${CMAKE_CURRENT_BINARY_DIR}/include/caosdb/${next_proto_module}.grpc.pb.h")
    list(APPEND GRPC_GENERATED_HEADERS "${next_proto_hdr}" "${next_grpc_hdr}")
    list(APPEND GRPC_GENERATED_SOURCES "${next_proto_src}" "${next_grpc_src}")
endforeach()

set(GRPC_GENERATED
    ${GRPC_GENERATED_SOURCES}
    ${GRPC_GENERATED_HEADERS})
add_custom_command(
      OUTPUT ${GRPC_GENERATED}
      COMMAND ${_PROTOBUF_PROTOC}
      ARGS --grpc_out "${CMAKE_CURRENT_BINARY_DIR}/include"
        --cpp_out "${CMAKE_CURRENT_BINARY_DIR}/include"
        -I "${PROTO_PATH}"
        --plugin=protoc-gen-grpc="${_GRPC_CPP_PLUGIN_EXECUTABLE}"
        ${PROTO_FILES}
      DEPENDS ${PROTO_FILES})

# add generated files to include path
include_directories(${CMAKE_CURRENT_BINARY_DIR}/include)
###############################################################################
### Set up main targets
### * linkahead - The main library including the protobuf and grpc generated files.
### * cxxlinkaheadcli - A C++ test client.
### * clinkahead - A C-Wrapper of the C++ linkahead library.
### * clinkaheadcli - A plain C test client.
###############################################################################

find_package(gRPC CONFIG REQUIRED)

# boost
find_package(Boost REQUIRED COMPONENTS json filesystem log thread system log_setup)
# find_package(Boost REQUIRED COMPONENTS filesystem )
# find_package(Boost REQUIRED COMPONENTS log )
# find_package(Boost REQUIRED COMPONENTS thread )
# find_package(Boost REQUIRED COMPONENTS system )

# Boost settings
set(Boost_USE_STATIC_LIBS        ON)  # only find static libs
set(Boost_USE_DEBUG_LIBS        OFF)  # ignore debug libs and
set(Boost_USE_RELEASE_LIBS       ON)  # only find release libs
set(Boost_USE_MULTITHREADED      ON)
set(Boost_USE_STATIC_RUNTIME     ON)  # link Boost Static libraries


find_package(GTest REQUIRED)

# print include directories for debugging
message(DEBUG "CMAKE_INCLUDE_PATH: ${CMAKE_INCLUDE_PATH}")
message(DEBUG "PROTOBUF_INCLUDE_DIRS: ${PROTOBUF_INCLUDE_DIRS}")
message(DEBUG "gRPC_INCLUDE_DIRS: ${gRPC_INCLUDE_DIRS}")
message(DEBUG "Boost_INCLUDE_DIRS: ${Boost_INCLUDE_DIRS}")
message(DEBUG "GTest_INCLUDE_DIRS: ${GTest_INCLUDE_DIRS}")
message(DEBUG "CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS}")
message(DEBUG "liblinkahead_SOURCE_DIR: ${liblinkahead_SOURCE_DIR}")
message(DEBUG "liblinkahead_BINARY_DIR: ${liblinkahead_BINARY_DIR}")

# libcaosdb_grpc
# --------------
add_library(caosdb_grpc STATIC ${GRPC_GENERATED})
target_link_libraries(caosdb_grpc 
	gRPC::grpc gRPC::grpc++ protobuf::libprotobuf
)

# liblinkahead
# ---------
add_library(linkahead SHARED
     ${liblinkahead_INCL} ${liblinkahead_SRC})
target_link_libraries(linkahead PUBLIC
    caosdb_grpc gRPC::grpc gRPC::grpc++ protobuf::libprotobuf ${Boost_LIBRARIES}
)
set(liblinkahead linkahead)

target_include_directories(linkahead PUBLIC
    $<BUILD_INTERFACE:${liblinkahead_SOURCE_DIR}/include>
    $<BUILD_INTERFACE:${liblinkahead_BINARY_DIR}/include>
    $<BUILD_INTERFACE:${liblinkahead_SOURCE_DIR}/src>
    $<INSTALL_INTERFACE:include>
)

# libclinkahead
# ----------
add_library(clinkahead SHARED src/clinkahead.cpp)
target_link_libraries(clinkahead PUBLIC
    linkahead caosdb_grpc gRPC::grpc gRPC::grpc++ protobuf::libprotobuf ${Boost_LIBRARIES}
)
target_include_directories(clinkahead PUBLIC
    $<BUILD_INTERFACE:${liblinkahead_SOURCE_DIR}/include>
    $<BUILD_INTERFACE:${liblinkahead_BINARY_DIR}/include>
    $<INSTALL_INTERFACE:include>
)


# clinkaheadcli
# ----------
add_executable(clinkaheadcli EXCLUDE_FROM_ALL src/clinkaheadcli.c)
target_include_directories(clinkaheadcli PUBLIC
    $<BUILD_INTERFACE:${liblinkahead_SOURCE_DIR}/include>
    $<BUILD_INTERFACE:${liblinkahead_BINARY_DIR}/include>
    $<INSTALL_INTERFACE:include>
)
target_link_libraries(clinkaheadcli PRIVATE
    caosdb_grpc clinkahead
    gRPC::grpc protobuf::libprotobuf ${Boost_LIBRARIES}    
)

# cxxlinkaheadcli
# ------------
add_executable(cxxlinkaheadcli EXCLUDE_FROM_ALL src/cxxlinkaheadcli.cpp)
target_include_directories(cxxlinkaheadcli PUBLIC
    $<BUILD_INTERFACE:${liblinkahead_SOURCE_DIR}/include>
    $<BUILD_INTERFACE:${liblinkahead_BINARY_DIR}/include>
    $<INSTALL_INTERFACE:include>
)
target_link_libraries(cxxlinkaheadcli PRIVATE
    linkahead caosdb_grpc  gRPC::grpc++ protobuf::libprotobuf ${Boost_LIBRARIES}
)



#######################################################
### LINTING with CLANG-TIDY and INCLUDE-WHAT-YOU-USE
#######################################################

include(CheckCXXCompilerFlag)
include(CheckCCompilerFlag)

function(add_compiler_flag flag)
    string(FIND "${CMAKE_CXX_FLAGS}" "${flag}" cxx_present)
    if(cxx_present EQUAL -1)
        check_cxx_compiler_flag("${flag}" flag_supported)
        if(flag_supported)
            set(PEDANTIC_CMAKE_CXX_FLAGS "${PEDANTIC_CMAKE_CXX_FLAGS} ${flag}" PARENT_SCOPE)
        endif()
        unset(flag_supported CACHE)
    endif()
    unset(cxx_present CACHE)

    string(FIND "${CMAKE_C_FLAGS}" "${flag}" c_present)
    if(c_present EQUAL -1)
        check_cxx_compiler_flag("${flag}" flag_supported)
        if(flag_supported)
            set(PEDANTIC_CMAKE_C_FLAGS "${PEDANTIC_CMAKE_C_FLAGS} ${flag}" PARENT_SCOPE)
        endif()
        unset(flag_supported CACHE)
    endif()
    unset(c_present CACHE)
endfunction()



option(LINTING "Enable linting with clang-tidy and iwyu when in non-Debug build-type" OFF)
if("${CMAKE_BUILD_TYPE}" MATCHES "Debug" OR LINTING)
    set(_LINTING ON)
endif()
option(SKIP_LINTING "Skip linting even when in Debug build-type" OFF)
if("${CMAKE_BUILD_TYPE}" MATCHES "Debug" AND SKIP_LINTING)
    message(WARNING "Skipping linting due to SKIP_LINTING option")
    set(_LINTING OFF)
endif()
if(_LINTING)

    ### set paranoid compiler flags
    add_compiler_flag("-Wall")
    add_compiler_flag("-Wextra")
    add_compiler_flag("-pedantic")
    add_compiler_flag("-Werror")

    message(STATUS "PEDANTIC_CMAKE_CXX_FLAGS: [${PEDANTIC_CMAKE_CXX_FLAGS}]")
    set(TARGET_LINKAHEAD_COMPILE_FLAGS "${TARGET_LINKAHEAD_COMPILE_FLAGS} ${PEDANTIC_CMAKE_CXX_FLAGS}")
    set(TARGET_CLINKAHEAD_COMPILE_FLAGS "${TARGET_CLINKAHEAD_COMPILE_FLAGS} ${PEDANTIC_CMAKE_C_FLAGS}")
    set(TARGET_CXXLINKAHEADCLI_COMPILE_FLAGS "${TARGET_CXXLINKAHEADCLI_COMPILE_FLAGS} ${PEDANTIC_CMAKE_CXX_FLAGS}")
    set(TARGET_CLINKAHEADCLI_COMPILE_FLAGS "${TARGET_CLINKAHEADCLI_COMPILE_FLAGS} ${PEDANTIC_CMAKE_C_FLAGS}")

    set_target_properties(linkahead PROPERTIES
        COMPILE_FLAGS "${TARGET_LINKAHEAD_COMPILE_FLAGS}")
    set_target_properties(clinkahead PROPERTIES
        COMPILE_FLAGS "${TARGET_CLINKAHEAD_COMPILE_FLAGS}")
    set_target_properties(cxxlinkaheadcli PROPERTIES
        COMPILE_FLAGS "${TARGET_CXXLINKAHEADCLI_COMPILE_FLAGS}")
    set_target_properties(clinkaheadcli PROPERTIES
        COMPILE_FLAGS "${TARGET_CLINKAHEADCLI_COMPILE_FLAGS}")

    find_program(iwyu
        NAMES include-what-you-use iwyu
        PATHS ${CMAKE_SOURCE_DIR}/tools/include-what-you-use/${iwyu_os}/bin)
    if(NOT iwyu)
        message(WARNING "include-what-you-use: Not found")
    else()
        message(STATUS "include-what-you-use: ${iwyu}")
        set(_CMAKE_CXX_INCLUDE_WHAT_YOU_USE ${iwyu}
            "-Xiwyu" "--cxx17ns" "-Xiwyu" "--no_fwd_decls")

        set_target_properties(linkahead PROPERTIES
            CXX_INCLUDE_WHAT_YOU_USE "${_CMAKE_CXX_INCLUDE_WHAT_YOU_USE}"
            )
        set_target_properties(cxxlinkaheadcli PROPERTIES
            CXX_INCLUDE_WHAT_YOU_USE "${_CMAKE_CXX_INCLUDE_WHAT_YOU_USE}"
            )
        set_target_properties(clinkaheadcli PROPERTIES
            C_INCLUDE_WHAT_YOU_USE "${_CMAKE_CXX_INCLUDE_WHAT_YOU_USE}"
            )
    endif()

    find_program(clang_tidy NAMES clang-tidy clang-tidy-16)
    if(NOT clang_tidy)
        message(WARNING "clang-tidy: Not found")
    else()
        message(STATUS "clang-tidy: ${clang_tidy}")
        set(_CMAKE_CXX_CLANG_TIDY_CHECKS
            "--checks=*,-fuchsia-*,-llvmlibc-*,-readability-convert-member-functions-to-static,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-hicpp-no-array-decay,-llvm-else-after-return,-readability-else-after-return,-modernize-use-trailing-return-type,-bugprone-branch-clone,-altera-*,-misc-include-cleaner,-readability-identifier-*,-llvm-include-order,-misc-const-correctness")
        set(_CMAKE_C_CLANG_TIDY_CHECKS "${_CMAKE_CXX_CLANG_TIDY_CHECKS}")
        set(_CMAKE_CXX_CLANG_TIDY "${clang_tidy}"
            "--header-filter=caosdb/.*[^\(\.pb\.h\)]$")
            # "--warnings-as-errors=*")
        set(_CMAKE_C_CLANG_TIDY "${_CMAKE_CXX_CLANG_TIDY}")
        option(AUTO_FIX_LINTING "Append --fix option to clang-tidy" OFF)
        if(AUTO_FIX_LINTING)
            set(_CMAKE_CXX_CLANG_TIDY "${_CMAKE_CXX_CLANG_TIDY};--fix")
        endif()
        message(STATUS "Using clang-tidy with
            '${_CMAKE_CXX_CLANG_TIDY};${_CMAKE_CXX_CLANG_TIDY_CHECKS}'")
        set_target_properties(linkahead PROPERTIES
            CXX_CLANG_TIDY "${_CMAKE_CXX_CLANG_TIDY};${_CMAKE_CXX_CLANG_TIDY_CHECKS}"
            )
        set_target_properties(cxxlinkaheadcli PROPERTIES
            CXX_CLANG_TIDY "${_CMAKE_CXX_CLANG_TIDY};${_CMAKE_CXX_CLANG_TIDY_CHECKS}"
            )
        set_target_properties(clinkahead PROPERTIES
            C_CLANG_TIDY "${_CMAKE_C_CLANG_TIDY};${_CMAKE_C_CLANG_TIDY_CHECKS}"
            )
        set_target_properties(clinkaheadcli PROPERTIES
            C_CLANG_TIDY "${_CMAKE_C_CLANG_TIDY};${_CMAKE_C_CLANG_TIDY_CHECKS}"
            )
    endif()
endif()


#######################################################
### UNIT TEST
#######################################################

#if("${CMAKE_BUILD_TYPE}" MATCHES "Debug")
    enable_testing()
    add_subdirectory(test)
#endif()

# ###############################################
# ############ INSTALLATION #####################
# ###############################################

set(liblinkahead_INCLUDE_DEST "include/linkahead")
set(liblinkahead_LIB_DEST "lib")

if (WIN32)
    set(CMAKE_INSTALL_PREFIX "$ENV{LOCALAPPDATA}/linkahead")
else()
    set(CMAKE_INSTALL_PREFIX "$ENV{HOME}/.local/")
endif()

install(
    # targets to install
    TARGETS linkahead clinkahead caosdb_grpc
    # name of the CMake "export group" containing the targets we want to install
    EXPORT linkaheadTargets
    # Dynamic, static library and include destination locations after running
    # "make install"
    LIBRARY DESTINATION ${liblinkahead_LIB_DEST}
    ARCHIVE DESTINATION ${liblinkahead_LIB_DEST}
    INCLUDES DESTINATION ${liblinkahead_INCLUDE_DEST}
)


set(liblinkahead_CMAKE_DEST "${liblinkahead_LIB_DEST}/cmake/linkahead")
install(
    # The export we want to save (matches name defined above containing the
    # install targets)
    EXPORT linkaheadTargets
    # CMake file in which to store the export's information
    FILE  linkaheadTargets.cmake
    # Namespace prepends all targets in the export (when we import later, we
    # will use linkahead::linkahead)
    NAMESPACE linkahead::
    # where to place the resulting file (here, we're putting it with the library)
    DESTINATION ${liblinkahead_CMAKE_DEST}
)

install(FILES ${liblinkahead_INCL} ${PROJECT_SOURCE_DIR}/include/clinkahead.h DESTINATION ${liblinkahead_INCLUDE_DEST})
foreach(i RANGE "${len_proto_files}")
    list(GET PROTO_FILES ${i} next_proto_file)

    # strip away the prefix path and the ".proto" suffix
    string(REPLACE
        "${PROJECT_SOURCE_DIR}/proto/proto/caosdb/"
        ""
        next_proto_module
        "${next_proto_file}")
    string(REPLACE
        "/main.proto"
        ""
        next_proto_module
        "${next_proto_module}")
    set(next_proto_hdr
        "${CMAKE_CURRENT_BINARY_DIR}/include/caosdb/${next_proto_module}/main.pb.h")
    set(next_grpc_hdr
        "${CMAKE_CURRENT_BINARY_DIR}/include/caosdb/${next_proto_module}/main.grpc.pb.h")
    install(FILES ${next_proto_hdr} ${next_grpc_hdr} DESTINATION
        ${liblinkahead_INCLUDE_DEST}/${next_proto_module})
endforeach()

install(FILES ${PROJECT_SOURCE_DIR}/linkaheadConfig.cmake
    DESTINATION ${liblinkahead_CMAKE_DEST})

#set_property(TARGET caosdb PROPERTY VERSION ${liblinkahead_VERSION})
include(CMakePackageConfigHelpers)
write_basic_package_version_file(
    "${PROJECT_SOURCE_DIR}/linkaheadConfigVersion.cmake"
  VERSION ${liblinkahead_VERSION}
  COMPATIBILITY AnyNewerVersion
)
install(FILES ${PROJECT_SOURCE_DIR}/linkaheadConfigVersion.cmake
    DESTINATION ${liblinkahead_CMAKE_DEST})

#######################################################
### code formatting with clang-format
#######################################################
option(AUTOFORMATTING "call clang-format at configure time" ON)
if(AUTOFORMATTING AND NOT SKIP_LINTING)
    find_program(clang_format NAMES clang-format-16 clang-format)
    file(GLOB format_test_sources test/*.cpp test/*.h test/*.h.in)
    execute_process(COMMAND ${clang_format} -i --verbose ${liblinkahead_INCL}
        ${liblinkahead_SRC} ${liblinkahead_TEST_SRC}
        ${PROJECT_SOURCE_DIR}/src/cxxlinkaheadcli.cpp
        ${PROJECT_SOURCE_DIR}/src/clinkaheadcli.c
        ${PROJECT_SOURCE_DIR}/src/clinkahead.cpp
        ${PROJECT_SOURCE_DIR}/include/clinkahead.h
        ${format_test_sources}
        WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
endif()