首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >通用跨平台C++14项目的C++14项目模板

通用跨平台C++14项目的C++14项目模板
EN

Code Review用户
提问于 2018-03-13 14:12:24
回答 1查看 3.6K关注 0票数 23

UPDATE:我不再用我的实际计划的内容更新这个帖子了,因为它们已经发生了很大的变化(单元测试现在是用doctest完成的,代码覆盖率也包括在内,等等)。这篇文章仍然是完整的,应该足以作为新C++14项目的起点。

最近,我决定创建一个C++14对象模板,我计划在我的所有C++项目中使用该模板。我在这里使用的构建系统生成器是CMake。这是我在制定这个计划时的目标:

  1. 该项目应与MSVC、GCC和Clang一起编写。
  2. 我可以在所有平台上测试代码。
  3. 我可以在所有平台上安装目标。
  4. 我可以使用DO2在所有平台上生成文档。

以下是我的项目结构:

代码语言:javascript
复制
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

代码语言:javascript
复制
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 in src

代码语言:javascript
复制
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 in test

代码语言:javascript
复制
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 in third_party/catch (取自官方文档并略有修改):

代码语言:javascript
复制
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 in doc

代码语言:javascript
复制
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. 我的代码适合Clang,GCC和MSVC吗?(我已经用Travis CI和Appveyor进行了测试,但我只需要确保)
  4. 我是否正确地安装了我的所有目标(在正确的目录)?
  5. 如何处理此设置中的资源文件?假设src中的一个文件需要src/resources/中的一个文件。那么这个文件将在哪里安装呢?在bin还是其他地方?(充满疑问)
EN

回答 1

Code Review用户

发布于 2021-09-23 07:47:28

没有什么需要改进的。我希望看到实际的TARGET名称而不是Project-Name,但总的来说,这是一个可读性好、结构良好的CMake项目。最小的CMake版本3.1是从过去的爆炸,但如果没有使用和/或必要的最近的特性,看到适当的最小版本是很好的。这点值得尊敬!

  1. 在设置项目方面,我是否遵循了最佳实践?

是的,除了一个选项,即BUILD_TESTS和测试部分:

代码语言:javascript
复制
if (BUILD_TESTS)
    add_subdirectory(third_party/catch)
    include(CTest)
    enable_testing()
    add_subdirectory(test)
endif (BUILD_TESTS)

CTest模块本身包含一个选项BUILD_TESTING,并调用enable_testing() if BUILD_TESTING=ON。虽然这只记录在CMake 3.15及以上中,但它一直是活动的至少自2005年以来。因此,可以将上述部分简化为

代码语言:javascript
复制
include(CTest)
if(BUILD_TESTING)
    add_subdirectory(third_party/catch)
    add_subdirectory(test)
endif (BUILD_TESTING)

然后,我们也可以摆脱我们自己的option(BUILD_TESTS ...)

  1. 有任何方法可以改进我的CMake语法吗?

不是很多。CMake语法总是受个人偏好的影响。我发现你的配置很容易读懂,也很容易浏览,所以我说它是非常好的。所有其他的“改进”都是我个人的喜好。该偏好的一部分是在短块上去掉endif(...)中的表达式;但这只是我的偏好。

  1. 我的代码适合Clang,GCC和MSVC吗?(我已经用Travis CI和Appveyor进行了测试,但我只需要确保)

是的,但是您的编译器检查比必要的要多一些。CMake提供MSVC检查Microsoft (++)是否处于活动状态。因此,您可以将测试放宽到

代码语言:javascript
复制
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 (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")
票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/189489

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档