Шаблон проекта CMake для общего кросс-платформенный с++14 проект


Обновление: я больше не обновлять этот пост с содержанием моего проекта , так как они существенно изменились (модульное тестирование теперь осуществляется с doctest, покрытие кода включен и т. д.). Этот пост до сих пор не завершена, как это и должно быть достаточно, чтобы служить в качестве отправной точки для нового C++14 проектов.


Недавно я решил создать на C++14 шаблон oroject, что я планирую использовать для все мои проекты c++. Сборка генератора системы я использую здесь с CMake. Здесь были мои цели, когда я установить это:

  1. Проект должен быть скомпилирован с индекса MSVC, GCC и Clang.
  2. Я могу проверить код на всех платформах.
  3. Я могу установить цели на всех платформах.
  4. Я могу создать документацию на всех платформах с помощью Doxygen.

Вот моя структура проекта:

project-name/
├── CMakeLists.txt
├── cmake
│   └── Modules
│       └── ParseAndAddCatchTests.cmake
├── doc
│   ├── CMakeLists.txt
│   ├── Doxyfile.in
│   └── main_page.md
├── include
│   └── project-abbr
│       ├── config.hpp <-- Contains project versioning
│       └── factorial.hpp
├── src
│   ├── CMakeLists.txt
│   ├── factorial.cpp
│   └── main.cpp
├── test
│   ├── CMakeLists.txt
│   ├── factorial_test.cpp
│   └── test_runner.cpp
└── third_party
    └── catch
        └── CMakeLists.txt

Вот мой топ-уровня CMakeLists.txt:

cmake_minimum_required(VERSION 3.1)

if (POLICY CMP0048)
    cmake_policy(SET CMP0048 NEW)
endif (POLICY CMP0048)

project(Project-Name VERSION 0.1.0)

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

# Options
option(BUILD_TESTS "Build test executable" OFF)
option(GEN_DOCS "Generate documentation" OFF)

set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")

if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
    message(STATUS "Setting build type to 'Release' as no build type was specified")
    set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the build type (Debug/Release)" FORCE)
endif (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)

if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Werror")
    set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g")
    set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2")
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /WX")
    set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /O2")
endif(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU")

include_directories(include)
add_subdirectory(src)

if (BUILD_TESTS)
    add_subdirectory(third_party/catch)
    include(CTest)
    enable_testing()
    add_subdirectory(test)
endif (BUILD_TESTS)

if (GEN_DOCS)
    add_subdirectory(doc)
endif (GEN_DOCS)

# Install the project header files into the appropriate directory
# Other installs are in src/CMakeLists.txt
install(DIRECTORY include/ DESTINATION include)

В CMakeLists.txt в src:

add_executable(Project-Name main.cpp) # The main executable
add_library(Project-Name-lib hello_world.cpp factorial.cpp) # A library for tests

SET_TARGET_PROPERTIES(Project-Name-lib PROPERTIES PREFIX "") # Remove the lib prefix

target_link_libraries(Project-Name Project-Name-lib) # Link our sources to the executable

install(TARGETS Project-Name DESTINATION bin)
install(TARGETS Project-Name-lib
    ARCHIVE DESTINATION lib
    LIBRARY DESTINATION lib
    RUNTIME DESTINATION bin
)

В CMakeLists.txt в test:

set(TEST_SOURCES factorial_test.cpp hello_world_test.cpp)

add_executable(test_runner test_runner.cpp ${TEST_SOURCES})
target_link_libraries(test_runner Project-Name-lib)
target_include_directories(test_runner PRIVATE ${CATCH_INCLUDE_DIR} ${COMMON_INCLUDES})

include(ParseAndAddCatchTests)
ParseAndAddCatchTests(test_runner)

В CMakeLists.txt в third_party/catch (взято из официальной документации и немного изменен):

include(ExternalProject)
find_package(Git REQUIRED)

ExternalProject_Add(
    catch
    PREFIX ${CMAKE_BINARY_DIR}/catch
    GIT_REPOSITORY https://github.com/catchorg/Catch2.git
    TIMEOUT 10
    UPDATE_COMMAND ${GIT_EXECUTABLE} pull
    CONFIGURE_COMMAND ""
    BUILD_COMMAND ""
    INSTALL_COMMAND ""
    LOG_DOWNLOAD ON
)

# Expose required variable (CATCH_INCLUDE_DIR) to parent scope
ExternalProject_Get_Property(catch source_dir)
set(CATCH_INCLUDE_DIR ${source_dir}/single_include CACHE INTERNAL "Path to include folder for Catch")

В CMakeLists.txt в doc:

find_package(Doxygen)

if (DOXYGEN_FOUND)
    set(DOXYGEN_IN Doxyfile.in)
    set(DOXYGEN_OUT ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile)

    configure_file(${DOXYGEN_IN} ${DOXYGEN_OUT} @ONLY)

    add_custom_target(doc
        COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_OUT}
        WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
        VERBATIM
    )
else (DOXYGEN_FOUND)
    message(FATAL_ERROR "Doxygen needs to be installed to generate documentation!")
endif (DOXYGEN_FOUND)

Вот эти вопросы я хочу получить ответы из этого обзора:

  1. Я после лучшие практики с точки зрения создания моего проекта?
  2. Есть ли способ мой синтаксис CMake, например, может быть улучшено?
  3. Мой код подходит для лязгом, и GCC, и MSVC? (Я испытал на Travis CI и Appveyor, но я просто хочу убедиться)
  4. Я должен установить все мои цели правильно (в каталогах)?
  5. Как бы я обрабатывать файлы ресурсов в этой установке? Предположим, файл в src нужен файл в src/resources/. Тогда где этот файл будет установлен? В bin или где-то еще? (Полный такой вопрос)


1528
15
задан 13 марта 2018 в 02:03 Источник Поделиться
Комментарии