Skip to content

Commit

Permalink
Add Conan packaging support, support system deps, refactor CMake
Browse files Browse the repository at this point in the history
  • Loading branch information
valgur committed Sep 30, 2024
1 parent 16459a5 commit 297684f
Show file tree
Hide file tree
Showing 13 changed files with 377 additions and 182 deletions.
118 changes: 23 additions & 95 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,84 +1,30 @@
cmake_minimum_required(VERSION 3.15)
include(cmake/ThirdParty.cmake)
project(novatel_edie VERSION 3.3.7 LANGUAGES CXX)

option(BUILD_TESTS "Build tests" ON)
option(BUILD_EXAMPLES "Build examples" ON)
option(CMAKE_POSITION_INDEPENDENT_CODE "Set -fPIC" ON)
option(WARNINGS_AS_ERRORS "Treat warnings as errors" OFF)
option(COVERAGE "Coverage" OFF)
option(USE_STATIC_LIBS "Statically link dependencies" OFF)

if(USE_STATIC_LIBS)
# Set Conan statically link dependencies
unset(CONAN_INSTALL_ARGS CACHE)
set(CONAN_OPTIONS -o spdlog/*:shared=False -o fmt/*:shared=False)
else()
# Set Conan dynamically link dependencies
unset(CONAN_INSTALL_ARGS CACHE)
set(CONAN_OPTIONS -o spdlog/*:shared=True -o fmt/*:shared=True)
endif()

include(cmake/third_party.cmake)
project(novatel-edie VERSION 3.3.7 LANGUAGES CXX)

if(NOT DEFINED CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()

# Check platforms
if(CMAKE_HOST_WIN32)
set(WINDOWS 1)
cmake_host_system_information(RESULT OS_NAME QUERY OS_NAME)
set(DISTRIB_NAME ${OS_NAME})
elseif(CMAKE_HOST_UNIX)
set(LINUX 1)
cmake_host_system_information(RESULT DISTRIB_NAME QUERY DISTRIB_NAME)
cmake_host_system_information(RESULT DISTRIB_VERSION_ID QUERY DISTRIB_VERSION_ID)
set(DISTRIB_NAME "${DISTRIB_NAME}-${DISTRIB_VERSION_ID}")
else()
message(WARNING "Unable to identify OS. Update script to support distribution or OS")
if(NOT DEFINED CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 17)
endif()

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

if(MSVC)
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
if(NOT DEFINED CMAKE_MSVC_RUNTIME_LIBRARY)
# shared spdlog requires MultiThreadedDLL
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
endif()
add_definitions(-DWIN32 -D_WINDOWS)
add_compile_options(/W4 /GR /EHsc /utf-8 /wd4244 /wd4996)
add_compile_options("$<$<CONFIG:Release>:/Ox;/Ob2>")
if(WARNINGS_AS_ERRORS)
add_compile_options(/WX)
endif()
elseif(CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang|AppleClang)$")
add_compile_options(-Wall -Wextra -pedantic)
add_compile_options("$<$<CONFIG:Release>:-O3>")
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)

if(WINDOWS)
# TODO: we shouldn't have to do this, something is bloating an object file
add_compile_options($<$<CONFIG:Debug>:-O1>)
endif()

if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
add_compile_options(-Wno-format-truncation)
endif()

if(WARNINGS_AS_ERRORS)
add_compile_options(-Werror)
endif()

if(COVERAGE)
message("Coverage is On")
add_compile_options(--coverage)
add_link_options(--coverage)
endif()
else()
message(WARNING "Unable to identify compiler.")
endif()
include(GNUInstallDirs)
include(cmake/CompilerOptions.cmake)
include(cmake/Utils.cmake)
# For custom Find*.cmake modules
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/modules")

# Output all binaries in the same directory for easier testing
string(TOUPPER ${CMAKE_BUILD_TYPE} BUILD_TYPE)
Expand All @@ -92,25 +38,16 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${BUILD_TYPE} "${OUTPUT_DIR}")
# Look for shared libs in the same directory as the executable when running tests
set(CMAKE_BUILD_RPATH "\$ORIGIN")

# Find Git package, if not need to install manually or through .yml file
find_package(Git)
if(NOT Git_FOUND)
message(FATAL_ERROR "Git was not found. Install Git and make sure it is in your PATH.")
endif()

find_package(Git REQUIRED)
if(NOT DEFINED GIT_BRANCH)
set(GIT_BRANCH "main")
endif()

# Build version of EDIE through cmake
if(GIT_EXECUTABLE)
execute_process(COMMAND ${CMAKE_COMMAND}
-D SRC=${CMAKE_CURRENT_SOURCE_DIR}/src/version.h.in
-D DST=${CMAKE_CURRENT_SOURCE_DIR}/include/novatel_edie/version.h
-D GIT_EXECUTABLE=${GIT_EXECUTABLE}
-D GIT_BRANCH=${GIT_BRANCH}
-P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/GenerateVersionHeader.cmake)
endif()
execute_process(COMMAND ${CMAKE_COMMAND}
-D SRC=${CMAKE_CURRENT_SOURCE_DIR}/src/version.h.in
-D DST=${CMAKE_CURRENT_SOURCE_DIR}/include/novatel_edie/version.h
-D GIT_EXECUTABLE=${GIT_EXECUTABLE}
-D GIT_BRANCH=${GIT_BRANCH}
-P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/GenerateVersionHeader.cmake)

# Build EDIE components
add_subdirectory(src/common)
Expand Down Expand Up @@ -140,20 +77,11 @@ if(BUILD_EXAMPLES)
add_subdirectory(examples)
endif()

# Copy third-party shared libs to the build directory for tests
# Copy shared libs to the build output directory for tests
copy_cpp_runtime_dlls("${OUTPUT_DIR}")
copy_third_party_shared_libs("${OUTPUT_DIR}")

# Copy the C++ runtime DLL for non-MSVC compilers on Windows
if(WIN32 AND NOT MSVC)
if(NOT EXISTS CMAKE_CXX_COMPILER)
find_program(CXX_COMPILER_PATH NAMES "${CMAKE_CXX_COMPILER}")
else()
set(CXX_COMPILER_PATH "${CMAKE_CXX_COMPILER}")
endif()
get_filename_component(COMPILER_BIN_DIR "${CXX_COMPILER_PATH}" DIRECTORY)
foreach(stdcpp_library libstdc++-6.dll libc++.dll)
if(EXISTS "${COMPILER_BIN_DIR}/${stdcpp_library}")
file(COPY "${COMPILER_BIN_DIR}/${stdcpp_library}" DESTINATION "${OUTPUT_DIR}")
endif()
endforeach()
endif()
# Install
install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(FILES database/messages_public.json DESTINATION ${CMAKE_INSTALL_DATADIR}/novatel_edie)
install_novatel_edie_cmake_config()
40 changes: 40 additions & 0 deletions cmake/CompilerOptions.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
if(MSVC)
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
if(NOT DEFINED CMAKE_MSVC_RUNTIME_LIBRARY)
if(BUILD_SHARED_LIBS)
# shared spdlog requires MultiThreadedDLL
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
else()
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
endif()
endif()
add_definitions(-DWIN32 -D_WINDOWS)
add_compile_options(/W4 /GR /EHsc /utf-8 /wd4244 /wd4996)
add_compile_options("$<$<CONFIG:Release>:/Ox;/Ob2>")
if(WARNINGS_AS_ERRORS)
add_compile_options(/WX)
endif()
elseif(CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang|AppleClang)$")
add_compile_options(-Wall -Wextra -pedantic)
add_compile_options("$<$<CONFIG:Release>:-O3>")

if(CMAKE_HOST_WIN32)
add_compile_options($<$<CONFIG:Debug>:-Wa,-mbig-obj>)
endif()

if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
add_compile_options(-Wno-format-truncation)
endif()

if(WARNINGS_AS_ERRORS)
add_compile_options(-Werror)
endif()

if(COVERAGE)
message("Coverage is On")
add_compile_options(--coverage)
add_link_options(--coverage)
endif()
else()
message(WARNING "Unable to identify compiler.")
endif()
70 changes: 70 additions & 0 deletions cmake/ThirdParty.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
include(CMakeDependentOption)

if(CMAKE_TOOLCHAIN_FILE MATCHES "conan_toolchain.cmake")
set(CONAN_ALREADY_ACTIVE 1)
endif()

cmake_dependent_option(USE_CONAN "Use Conan to automatically manage dependencies" ON
"NOT DEFINED VCPKG_TOOLCHAIN AND NOT CONAN_ALREADY_ACTIVE" OFF)

if(USE_CONAN)
if(CMAKE_VERSION VERSION_LESS 3.24)
message(FATAL_ERROR "Automatic Conan integration requires CMake 3.24 or later.")
endif()
if(NOT DEFINED CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
if(NOT DEFINED CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 17)
endif()
list(APPEND CMAKE_PROJECT_TOP_LEVEL_INCLUDES ${CMAKE_CURRENT_LIST_DIR}/conan_provider.cmake)
endif()

# Copy third-party shared libs to the build directory for tests
function(copy_third_party_shared_libs target_dir)
if(USE_CONAN)
get_property(conan_generators_folder GLOBAL PROPERTY CONAN_GENERATORS_FOLDER)
include("${conan_generators_folder}/conan_runtime_paths.cmake")
endif()
if(NOT DEFINED CONAN_RUNTIME_LIB_DIRS)
if(USE_CONAN)
message(FATAL_ERROR "Failed to load CONAN_RUNTIME_LIB_DIRS")
endif()
# TODO: Add support for vcpkg
message(STATUS "Not using Conan, skipping copying third-party shared libraries.")
return()
endif()

message(STATUS "Copying third-party shared libraries to ${target_dir}...")

if(WIN32)
set(pattern "*.dll")
elseif(APPLE)
set(pattern "*.dylib")
else()
set(pattern "*.so*")
endif()

set(copied_files)
foreach(path ${CONAN_RUNTIME_LIB_DIRS})
message(STATUS "Copying shared libraries from ${path}")
file(GLOB libs "${path}/${pattern}")
file(COPY ${libs} DESTINATION "${target_dir}")
list(APPEND copied_files ${libs})
endforeach()

# Set RPATH to $ORIGIN for the copied libraries
if(NOT WIN32)
find_program(PATCHELF_EXECUTABLE patchelf REQUIRED)
foreach(lib ${copied_files})
get_filename_component(lib_name ${lib} NAME)
if(APPLE)
execute_process(COMMAND install_name_tool -add_rpath @loader_path "${target_dir}/${lib_name}"
COMMAND_ERROR_IS_FATAL ANY)
else()
execute_process(COMMAND "${PATCHELF_EXECUTABLE}" --set-rpath \$ORIGIN "${target_dir}/${lib_name}"
COMMAND_ERROR_IS_FATAL ANY)
endif()
endforeach()
endif()
endfunction()
44 changes: 44 additions & 0 deletions cmake/Utils.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Copy the C++ runtime DLL for non-MSVC compilers on Windows
function(copy_cpp_runtime_dlls target_dir)
if(WIN32 AND NOT MSVC)
if(NOT EXISTS CMAKE_CXX_COMPILER)
find_program(CXX_COMPILER_PATH NAMES "${CMAKE_CXX_COMPILER}")
else()
set(CXX_COMPILER_PATH "${CMAKE_CXX_COMPILER}")
endif()
get_filename_component(COMPILER_BIN_DIR "${CXX_COMPILER_PATH}" DIRECTORY)
foreach(stdcpp_library libstdc++-6.dll libc++.dll)
if(EXISTS "${COMPILER_BIN_DIR}/${stdcpp_library}")
file(COPY "${COMPILER_BIN_DIR}/${stdcpp_library}" DESTINATION "${target_dir}")
endif()
endforeach()
endif()
endfunction()

# Generate and install nova_edie-config.cmake
function(install_novatel_edie_cmake_config)
include(GNUInstallDirs)
set(CMAKE_CONFIG_INSTALL_DIR lib/cmake/novatel_edie)

install(TARGETS novatel_edie EXPORT novatel_edie-targets)
install(EXPORT novatel_edie-targets
NAMESPACE novatel_edie::
FILE novatel_edie-targets.cmake
DESTINATION ${CMAKE_CONFIG_INSTALL_DIR}
COMPONENT novatel_edie)

include(CMakePackageConfigHelpers)
configure_package_config_file(cmake/novatel_edie-config.cmake.in
"${CMAKE_CURRENT_BINARY_DIR}/novatel_edie-config.cmake"
INSTALL_DESTINATION ${CMAKE_CONFIG_INSTALL_DIR}
PATH_VARS CMAKE_INSTALL_INCLUDEDIR CMAKE_INSTALL_DATADIR)

write_basic_package_version_file(novatel_edie-config-version.cmake
VERSION ${novatel_edie_VERSION}
COMPATIBILITY SameMajorVersion)

install(FILES "${CMAKE_CURRENT_BINARY_DIR}/novatel_edie-config.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/novatel_edie-config-version.cmake"
DESTINATION ${CMAKE_CONFIG_INSTALL_DIR}
COMPONENT novatel_edie)
endfunction()
49 changes: 49 additions & 0 deletions cmake/modules/Findspdlog_setup.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Findspdlog_setup.cmake
#
# This module tries to locate the spdlog_setup library using find_package(spdlog_setup CONFIG).
# If not found, it falls back to downloading and configuring it from an official release.

find_package(spdlog_setup CONFIG QUIET)

if(NOT spdlog_setup_FOUND)
set(spdlog_setup_VERSION "1.1.0")
set(spdlog_setup_URL "https://github.com/gegles/spdlog_setup/archive/refs/tags/v${spdlog_setup_VERSION}.tar.gz")
set(spdlog_setup_SHA256 "80a37463a1cd2735f6f7af0b0dfb01a1ecc0a271b33fb29966564b9758f7c309")
set(spdlog_setup_SOURCE_DIR "${CMAKE_BINARY_DIR}/third_party/spdlog_setup")
set(spdlog_setup_INCLUDE_DIR "${spdlog_setup_SOURCE_DIR}/include")

find_package(cpptoml REQUIRED CONFIG)

if(POLICY CMP0135)
cmake_policy(SET CMP0135 NEW)
endif()
include(FetchContent)
FetchContent_Declare(
spdlog_setup
URL "${spdlog_setup_URL}"
URL_HASH SHA256=${spdlog_setup_SHA256}
SOURCE_DIR "${spdlog_setup_SOURCE_DIR}"
EXCLUDE_FROM_ALL
)
FetchContent_GetProperties(spdlog_setup)
if(NOT EXISTS "${spdlog_setup_INCLUDE_DIR}")
message(STATUS "spdlog_setup not found. Fetching it from ${spdlog_setup_URL} instead...")
FetchContent_MakeAvailable(spdlog_setup)
endif()

add_library(spdlog_setup::spdlog_setup INTERFACE IMPORTED)
target_include_directories(spdlog_setup::spdlog_setup INTERFACE
"$<BUILD_INTERFACE:${spdlog_setup_INCLUDE_DIR}>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/novatel_edie/third_party>"
)
target_link_libraries(spdlog_setup::spdlog_setup INTERFACE cpptoml)

install(DIRECTORY "${spdlog_setup_SOURCE_DIR}/include/"
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/novatel_edie/third_party
)

set(spdlog_setup_FOUND TRUE CACHE INTERNAL "")
set(spdlog_setup_VENDORED TRUE CACHE INTERNAL "")
else()
set(spdlog_setup_VENDORED FALSE CACHE INTERNAL "")
endif()
21 changes: 21 additions & 0 deletions cmake/novatel_edie-config.cmake.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
@PACKAGE_INIT@

include(CMakeFindDependencyMacro)

include("${CMAKE_CURRENT_LIST_DIR}/novatel_edie-targets.cmake")

set_and_check(novatel_edie_INCLUDE_DIRS "@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@")
set_and_check(novatel_edie_DATA_DIR "@PACKAGE_CMAKE_INSTALL_DATADIR@")
set(novatel_edie_LIBRARIES novatel_edie::novatel_edie)
check_required_components(novatel_edie)

find_dependency(nlohmann_json)
find_dependency(spdlog)

if(@spdlog_setup_VENDORED@)
# spdlog_setup has been vendored, find its dependencies
find_dependency(cpptoml)
else()
# use external spdlog_setup
find_dependency(spdlog_setup)
endif()
Loading

0 comments on commit 297684f

Please sign in to comment.