【问题标题】:Different CMAKE_BUILD_TYPE per target每个目标不同的 CMAKE_BUILD_TYPE
【发布时间】:2020-06-23 10:38:47
【问题描述】:

我正在处理一个非常大的项目,我正在从使用自定义 Makefile 转向使用 cmake,但我仍然缺少使用 Makefile 实现的功能。

项目有很多子目录,每个子目录都编译成静态库,然后链接到最终的可执行文件中。

这是一个小例子

src/
  lib1/
  lib2/
  lib3/
  main.cpp
  CMakeLists.txt

在 CMakeLists.txt 中可能是这样的:

add_subdirectory(lib1)
add_subdirectory(lib2)
add_subdirectory(lib3)
add_executable(test main.cpp)
target_link_libraries(test PUBLIC lib1 lib2 lib3)

我想调试最终的可执行文件,但是我不想构建所有带有调试符号且没有优化的静态库,因为那样调试会变得太慢。

所以我想用CMAKE_BUILD_TYPE=Releaselib1lib3CMAKE_BUILD_TYPE=Debug 构建lib2

请记住,实际上有大约 10 个库而不是三个库,我希望能够为每个库执行此操作,并同时为其中多个库执行此操作。

有没有办法从主要的CMakeLists.txt 做到这一点?

我更希望能通过命令行实现这一点:

cmake -DDEBUG_LIBS={lib1,lib3} /path/to/src
cmake --build .

【问题讨论】:

  • CMake 3.17 将在一个构建中引入多种构建类型。不确定这是否足以满足您的需求。
  • 引用那个?
  • 这将使用相同的配置构建所有目标,它只允许您构建两种不同的配置。这不是我要问的。
  • 是的,您必须运行构建两次才能获得所需的混合配置。
  • 不要将CMAKE_BUILD_TYPE 设置为空白或自定义一个,您可以在其中准确设置您想要的基本标志。然后根据下面的答案和 cmets 为您想要的库添加额外的调试和优化。我建议创建一个每个库目标调用的函数,检查它是否显示在DEBUG_LIBS 中,然后使用正确的值调用target_compile_options。但是您应该将其设置为 -DDEBUG_LIBS=lib1;lib3 以便列表处理有效。

标签: c++ cmake


【解决方案1】:

不要设置CMAKE_BUILD_TYPE 将其保留为空白或进行自定义设置,以准确设置您想要的基本标志。然后为所需的库添加额外的调试和优化。我建议创建每个库目标调用的函数(或宏),检查它是否显示在DEBUG_LIBS 中,然后使用正确的值调用 target_compile_options。但是您应该将其设置为-DDEBUG_LIBS=lib1;lib3,以便列表处理工作。

function(check_debug libname)
  if(${libname} IN_LIST DEBUG_LIBS)
    target_compile_options(${libname} PRIVATE -g -O0)
  endif()
end_function()

【讨论】:

    【解决方案2】:

    您可以使用ExternalProject 而不是add_subdirectory 作为您的依赖项,告诉它安装到${CMAKE_CURRENT_BINARY_DIR} 的子目录中,然后使用find_package 在那里找到它。这使您可以使用所需的任何构建选项组合。

    当你第一次构建它时,这确实需要一个两阶段的过程:运行cmake,然后运行make 来构建依赖关系,然后再次运行cmake 来检测依赖关系,然后再次运行make 来构建你自己的目标。如果你愿意,也有ways around that

    例如,这是我将 OpenCV 构建为项目的一部分的方式(简化):

    # Build OpenCV from source.
    include(ExternalProject)
    ExternalProject_Add(opencv
        SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/deps/opencv
        CMAKE_ARGS
            -DBUILD_LIST=core,imgproc
            -DBUILD_SHARED_LIBS=OFF
            -DCMAKE_BUILD_TYPE=Release
            -DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR}/deps/opencv
    )
    
    # Find it in the directory where we just built it.
    find_package(OpenCV
        PATHS ${CMAKE_CURRENT_BINARY_DIR}/deps/opencv
        NO_DEFAULT_PATH
    )
    
    # Abort if we weren't able to find it.
    if(NOT OpenCV_FOUND)
        message(STATUS "OpenCV was not found. This means it still needs to be built from source. To build it, run:")
        message(STATUS "")
        message(STATUS "    cmake --build ${CMAKE_CURRENT_BINARY_DIR} --target all")
        message(STATUS "")
        message(STATUS "Then re-run cmake to detect the built library.")
        return()
    endif()
    
    # Consume it in the usual way.
    add_binary(my_binary ...)
    target_link_libraries(my_binary ${OpenCV_LIBS})
    

    不知道这是否是“最佳实践”,但它对我有用。

    【讨论】:

      【解决方案3】:

      调试符号通常不会影响速度。您可能可以为所有目标保留它。如果符号有问题,它们可以是之后来自可执行文件/库的stripped。这种方法的好处是您仍然可以调试您认为不需要调试的库(尽管如果对其进行了优化,它可能不一定是理想的体验)。

      您可以使用 target_compile_options 启用或禁用单个目标的优化,以便始终或从不优化。


      指定不同的 CMAKE_BUILD_TYPE 可能会出现问题,因为如果您执行基于 NDEBUG 宏添加成员之类的操作,它会影响可能影响程序 ABI 的宏。如果你混合构建类型,那么你必须确保调试宏没有在头文件中使用。

      实现这一点的最简单方法是在单独的项目中构建库并将构建的库导入依赖于库的最终程序中。

      【讨论】:

      • 调试符号会影响在 gdb 中加载可执行文件所需的时间,并且它们会使可执行文件的大小变大。至于target_compile_options,有没有办法遍历-DDEBUG_LIBS传递的所有库,只为它们添加编译选项?
      • @ChrisG 如果当前目标在DEBUG_LIBS 列表中,您可以使用IN_LIST generator expression 有条件地添加编译选项:target_compile_options(lib3 PRIVATE $<$<IN_LIST:lib3,${DEBUG_LIBS}>:-O0>)
      • 我和@ChrisG 在同一条船上,希望有一个好的解决方案。在我的例子中,来自所有库的带有调试符号的可执行文件重 170 MB;在 gdb 之外链接甚至启动都需要相当长的时间。发布版本只有 8 MB。
      猜你喜欢
      • 2016-09-22
      • 1970-01-01
      • 2016-06-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多