CMake 使用设置编译依赖项

CMake compile dependencies with settings

本文关键字:依赖 编译 设置 CMake      更新时间:2023-10-16

我想知道如何在为这些依赖项启用特定设置的同时为我的项目编译依赖项,例如将依赖项编译为静态或动态库或使用 x64 或 x86 设置,或者作为项目定义变量以确定如何构建项目的另一个示例(如使用 Wayland 或 X.Org 支持)。

我当前的设置如下所示:

文件夹结构

root_project
|─── CMakeLists.txt
|─── Project 1
|      |─── .h and .cpp files
|      └─── CMakeLists.txt
|─── Dependency 1 (GLFW)
|      |─── include directory
|      |─── source directory
|      |─── ...
|      └─── CMakeLists.txt
└─── Dependency 2 (GLEW)
|─── build
|      └─── cmake
|            └─── CMakeLists.txt
|─── source directory
|─── include directory
└─── ...

CMake 文件

我的根 cmake 文件:

cmake_minimum_required (VERSION 3.8)
project ("EbsiStaller")
add_subdirectory ("EbsiStaller")
# Adds the CMakeLists.txt file located in the specified directory
# as a build dependency.
add_subdirectory ("glfw")
include_directories("glfw/include")
add_subdirectory ("glew/build/cmake")
include_directories("glew/include")

我的项目 cmake 文件:

cmake_minimum_required (VERSION 3.8)
add_executable (EbsiStaller 
"....cpp" 
"....h"
)
SET(CMAKE_CXX_STANDARD 17)
SET(CMAKE_CXX_STANDARD_REQUIRED  ON)
# Links the CMake build output against glfw.
target_link_libraries(EbsiStaller glfw ${GLFW_LIBRARIES} glew ${GLEW_LIBRARIES})

附加说明:

我在Windows下为这个项目使用Visual Studio 2017,而该项目应该是独立于平台的。由于我对 CMake 没有太多经验,因此我总是对我的 CMake 文件的任何建议更改持开放态度。

为我的依赖项定义特定于编译的设置时,我不想编辑它们的 CMake 文件来执行此操作。

在CMake中做这件事有很多困难,但我会尽我所能回答它。

通常,通过add_subdirectory添加的任何项目都将继承当前范围内当前定义的所有设置。更改单个依赖项设置的最简单方法 (IMO) 是将ExternalProject_Add与以下宏一起使用:

include(ExternalProject)
#
#   Add external project.
#
#   param name             Name of external project
#   param path             Path to source directory
#   param external         Name of the external target
#
macro(add_external_project name path)
# Create external project
set(${name}_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/${path})
set(${name}_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${path})
ExternalProject_Add(${name}
SOURCE_DIR "${${name}_SOURCE_DIR}"
BINARY_DIR "${${name}_BINARY_DIR}"
CMAKE_ARGS "-DCMAKE_C_FLAGS=${CMAKE_C_FLAGS}"
"-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}"
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}"
"-DBUILD_SHARED_LIBS=${BUILD_SHARED_LIBS}"
# These are only useful if you're cross-compiling.
# They, however, will not hurt regardless.
"-DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}"
"-DCMAKE_SYSTEM_PROCESSOR=${CMAKE_SYSTEM_PROCESSOR}"
"-DCMAKE_AR=${CMAKE_AR}"
"-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}"
"-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
"-DCMAKE_RC_COMPILER=${CMAKE_RC_COMPILER}"
"-DCMAKE_COMPILER_PREFIX=${CMAKE_COMPILER_PREFIX}"
"-DCMAKE_FIND_ROOT_PATH=${CMAKE_FIND_ROOT_PATH}"
INSTALL_COMMAND ""
)
endmacro(add_external_project)
#
#   Add external target to external project.
#
#   param name             Name of external project
#   param includedir       Path to include directory
#   param libdir           Path to library directory
#   param build_type       Build type {STATIC, SHARED}
#   param external         Name of the external target
#
macro(add_external_target name includedir libdir build_type external)
# Configurations
set(${name}_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${libdir})
# Create external library
add_library(${name} ${build_type} IMPORTED)
set(${name}_LIBRARY "${${name}_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${CMAKE_${build_type}_LIBRARY_PREFIX}${name}${CMAKE_${build_type}_LIBRARY_SUFFIX}")
# Find paths and set dependencies
add_dependencies(${name} ${external})
set(${name}_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/${includedir}")
# Set interface properties
set_target_properties(${name} PROPERTIES IMPORTED_LOCATION ${${name}_LIBRARY})
set_target_properties(${name} PROPERTIES INCLUDE_DIRECTORIES ${${name}_INCLUDE_DIR})
endmacro(add_external_target)

宏观解释

宏基本上配置了一个具有非常相似的 CMake 变量定义的 CMake 新实例。

第一个宏ExternalProject_Add通知 CMake 它需要使用这些自定义 CMake 参数、源目录和输出二进制目录生成一次外部项目。特别是,"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}"等选项告诉 CMake 使用与当前构建类型相同的构建类型(调试、发布等),而"-DBUILD_SHARED_LIBS=${BUILD_SHARED_LIBS}"指示 CMake 在构建共享库时使用相同的首选项(默认情况下,如果BUILD_SHARED_LIBS设置为OFF,项目应生成静态依赖项)。

然后,第二个宏创建一个导入的目标 CMake 可能链接到其属性类似于本机 CMake 库。

使用这些宏

要默认使用这些宏,您可以执行以下操作:

add_external_project(googletest_external googletest)
add_external_target(gtest googletest/googletest/include googletest/googlemock/gtest STATIC googletest_external)
add_external_target(gtest_main googletest/googletest/include googletest/googlemock/gtest STATIC googletest_external)

在此示例中,我配置了外部项目googletest,然后创建目标gtestgtest_main哪些应该是静态库(由于Googletest强制静态链接的方式),可以像任何普通CMake库一样链接。

劫持这些宏进行自定义生成

现在您已经粗略地了解了这些宏的作用,修改它们以允许每个依赖项的自定义配置非常容易。例如,我想要一个 glew 的静态发布版本,而不管我的实际项目设置如何。让我们也假设我希望GLEW_OSMESA设置为ON

#
#   Add external project.
#
macro(add_release_osmesa_glew)
# Create external project
set(${name}_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/glew)
set(${name}_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/glew)
ExternalProject_Add(glew_external
SOURCE_DIR "${${name}_SOURCE_DIR}"
BINARY_DIR "${${name}_BINARY_DIR}"
CMAKE_ARGS "-DCMAKE_C_FLAGS=${CMAKE_C_FLAGS}"
"-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}"
"-DCMAKE_BUILD_TYPE=Release"
"-DBUILD_SHARED_LIBS=${BUILD_SHARED_LIBS}"
# These are only useful if you're cross-compiling.
# They, however, will not hurt regardless.
"-DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}"
"-DCMAKE_SYSTEM_PROCESSOR=${CMAKE_SYSTEM_PROCESSOR}"
"-DCMAKE_AR=${CMAKE_AR}"
"-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}"
"-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
"-DCMAKE_RC_COMPILER=${CMAKE_RC_COMPILER}"
"-DCMAKE_COMPILER_PREFIX=${CMAKE_COMPILER_PREFIX}"
"-DCMAKE_FIND_ROOT_PATH=${CMAKE_FIND_ROOT_PATH}"
"-DGLEW_OSMESA=ON"
INSTALL_COMMAND ""
)

然后,要使用使用这些配置选项构建的 glew,我可以执行以下操作:

add_release_osmesa_glew()
add_external_target(
glew
glew/include 
glew 
SHARED 
glew_external
)
add_external_target(
glew_s 
glew/include 
glew 
STATIC 
glew_external
)

最后,我可以通过以下选项来反对它:

target_link_libraries(my_target
glew_s
...
)

优点

  • 不需要更改项目的 CMakeLists。
  • 支持
  • 依赖项目支持的所有可能配置。
  • 仅生成依赖库一次,并且可以根据需要使用继承的设置或自定义设置。
  • 应该与
  • 目标无关(这意味着它应该与Visual C++项目,Makefiles等一起使用)。

缺点

  • 大量样板
  • 依赖于依赖项目中的 CMakeList 的配置

正确的方法是直接对目标进行操作。 例如(用目标名称猜测,所以请原谅我):

add_subdirectory ("glfw")
set_target_properties(glfw PROPERTIES
COMPILE_FLAGS "-m32 -O2" # Adjust as needed
)
target_link_libraries(glew INTERFACE
${GLFW_LIBRARIES}
)
add_subdirectory ("glew/build/cmake")
target_include_directories(glew PUBLIC
"glfw/include"
)
target_link_libraries(glew INTERFACE
${GLEW_LIBRARIES}
)

这使您可以按目标而不是全局调整内容(这是现代CMake使用的基础)。 您可以使用这些函数及其好友调整目标的几乎任何您喜欢的内容,包括调整编译器标志甚至添加新文件。

您使用的方法有效,但会影响之后声明的每个目标,包括稍后添加的子目录中的目标。

主项目的CMakeLists.txt可能如下所示:

add_executable (EbsiStaller 
"....cpp" 
"....h"
)
target_compile_features(EbsiStaller PUBLIC
cxx_std_17 # might actually be a cmake 3.9 thing, but you get the idea
)
# Links the CMake build output against glfw.
target_link_libraries(EbsiStaller
glfw
glew
)

这里要介绍的内容太多了,但这一切都归结为对您的 CMake 进行现代化改造。 在线文档很棒。