CMake 使用设置编译依赖项
CMake compile dependencies with settings
我想知道如何在为这些依赖项启用特定设置的同时为我的项目编译依赖项,例如将依赖项编译为静态或动态库或使用 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
,然后创建目标gtest
并gtest_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 进行现代化改造。 在线文档很棒。
- GCC,CMake,预编译标头和维护依赖项
- 编译依赖于 QTCore 库的 WASM
- 更新 Visual Studio 2017,现在出现编译错误 C7510:"回调":使用依赖模板名称必须以 'template' 为前缀
- 在 Ubuntu 18.04 上,我编译的 cpp 可执行文件找不到 VTK7 依赖项
- 如何为在 x86 主机上为 arm 目标交叉编译的项目安装依赖项
- CMake:如何在Visual Studio环境中将依赖于模式的编译标志传递给nvcc
- 根据计算机上安装的库依赖项编译不同的代码
- 更改 Cmake 文件以从源代码编译依赖项,而不是使用 FIND_PACKAGE
- 在 NDK 上编译两个带有 gradle 的项目,其中一个依赖于另一个的二进制文件
- 如何 G++ 编译另一个目录中具有依赖项的.cpp文件
- CMake 使用设置编译依赖项
- C++ 生成文件不编译依赖项/链接错误?
- 是否可以编译具有在编译时无法解析的外部依赖项的静态库?
- 如何将所有依赖项和共享库编译为一个二进制文件
- 如何使用 cmake 添加仅编译依赖项
- C 项目依赖项无法在特定的MacOS框上编译
- C++ 上的循环依赖和继承编译错误
- 在不同于依赖DLL的文件夹中输出已编译目标的正确方法是什么
- "生成"不会重新编译头文件更改。尽管包含 .d 依赖项文件
- 使用友元类减少编译时间和依赖关系