cmake 的窘境
The cmake quandary
我正在做一个 C++ 项目。到目前为止它并不复杂,但依赖于一堆“流行”库(nlohmann/json、ToruNiina/toml11 仅举几例)。他们都有一些CMakeLists.txt
,从我没有经验的角度来看,我认为他们结构很好。
现在我当然可以一个一个地编译这些库,或者在我的项目仓库中包含一个“副本”,但我想做得更好。在研究了可用的构建工具之后,我决定使用 cmake
来构建和管理 C++ 项目。承诺是获得一个稳定的、广泛支持的工具,这将有助于简化和统一构建过程。而且,从项目性质来看,我无权对目标机器强加任何要求;我需要为部署打包所有东西。
我花了几天时间阅读、观看和测试各种 cmake 教程、手册和手册。我不得不承认,我很快就开始觉得,一个本应澄清开发过程的工具却不断引入新的晦涩难懂的东西,这与其目的背道而驰。本来,我把这归功于我缺乏经验,然而...
我阅读了 target_link_libraries
的 articles about why not to bundle dependencies, only to be followed by methods of doing so. I have found recommendation to use one way A over B, C over B and later A over C. It took me a while to figure out the differences between 2.8 and 3.0, the obscurity,设置了 cxx 版本 and/or 编译器警告标志等等。
我的观点是,即使在 cmake
的海域进行了一次精疲力尽的探险之后,我仍然不确定一些基本问题:
How is cmake meant to be used?
What is a standard, what is a courtesy, and what is none of those?
How can I tell that something is a feature, an archaic backwards compatibility, or both?
现在我将在我的项目中说明这一点。我只需要这样的东西
cmake_minimum_required(VERSION 3.14)
project(CaseCore CXX)
add_executable(myBinary list/of/cpp/sources.cpp)
target_link_libraries(myBinary PUBLIC someExternalLibs likeForExample nlohmann_json::nlohmann_json oqs)
唯一的问题是库(反正没有 space 其他问题)。我想用项目构建它们,不想制作本地副本(不要一直拖放大量不相关的文件)。首先,我创建了库回购的分支,以便拥有可靠的来源并能够将更新的版本合并到我的分支中。
现在决定是使用 git submodule
还是其他一些方案,我读过子模块性能不佳并且更喜欢整个事情由 cmake
单独管理。我从 ExternalProject_Add 开始,但后来我发现了 FetchContent
,它帮助我轻松地将外部依赖项添加到我的 cmake 列表中
FetchContent_Declare(nlohmann
GIT_REPOSITORY https://github.com/my-reliable-fork-of/json
GIT_TAG v3.7.3
)
message(STATUS "Fetching Json...this may take a while")
FetchContent_MakeAvailable(nlohmann)
看起来和工作都很好而且很小。但是,我总是必须搜索库本身,以便 find/guess 哪个目标 link 到我的可执行目标 ?约定似乎接近于零,除非相应的 CMakeLists.txt
足够简单,可以阅读,否则我倾向于猜测目标名称,直到找到为止。
最近我想 link 来自 here 的 liboqs,但上述情况并没有真正帮助;出于某种原因,我可以 link oqs,#include "oqs/oqs.h"
,但未创建动态库并且执行终止。我很确定我可以在谷歌搜索和使用各种 cmake 变量的另一段时间后解决问题。然而,这并不是我期望 cmake
帮助我管理项目的方式;实际上恰恰相反。
为了清楚起见,我拒绝了其他方法,包括
add_subdirectory from local repo copy (git submodule)
ExternalProject_Add from local repo copy (git submodule)
ExternalProject_Add from online repo
find_package
因为它们似乎更多 obscure/old-style 等等(尽管经过数小时的研究,它们对我来说似乎仍然有很多方法可以做同样的事情)
现在我有
Am I doing something wrong, or is it really what working with cmake
should look like?
Do I really have to "reverse-engineer" other people's CMakeLists in order to use a library?
Under these circumstances, how can I convince my coworkers to use similar work process?
最后
How can I adjust my work in order to ease these difficulities for others?
我越用越喜欢 C++。然而,我花费了大量的生产时间来解决依赖关系......我不想让 this guy 更加生气。
How is cmake meant to be used?
典型的 cmake 用法与旧的 autotools 用法相匹配:
$ cmake /path/to/src #replaces /path/to/src/configure
$ make
$ make install
一些目标已更改(例如,make check
vs make test
),并且 cmake 不提供所有相同的标准目标(例如,make distclean
),但我的用法以上是大多数开发人员会做的事情(并且由于 cmake 会自行重新运行,所以大多数时候它实际上只是第二步)。
如果您的 CMakeLists.txt
不支持此工作流程,您应该有充分的理由。大多数工具都采用这样的工作流程,因此您严重限制了自己。
What is a standard, what is a courtesy, and what is none of those?
除此之外,cmake 几乎就是狂野的西部。由于更好的文档和培训,事情变得更加标准化,但它远非完美。
一个行为良好的 cmake 项目应该导出它的目标(Stack Overflow 上有很多关于这个的问题和答案)并传播标志和依赖项。这使得依赖项目更容易使用导出的目标,幸运的是这很容易做到。
How can I tell that something is a feature, an archaic backwards compatibility, or both?
据我所知,这些区别并不明显。通常,较新的方法利用 target_*
函数而不是全局函数(例如,target_include_directories
与 include_directories
)。 target_*
函数还用于传播标志,包括目录、编译器功能和我上面提到的依赖库。
Am I doing something wrong, or is it really what working with cmake should look like?
您在谈论管理外部依赖项,我将跳过此部分以避免产生意见。简而言之,C 和 C++ 依赖关系很难,并且在项目中有许多相互竞争的方式来管理它们。它们各有利弊,但大多数仍然是为作者的用例而设计的。您必须弄清楚您需要哪些用例,并根据此选择工具和工作流程。
Do I really have to "reverse-engineer" other people's CMakeLists in order to use a library?
一个行为良好的 cmake 项目将正确导出其目标,即使它们使用与您不同的依赖管理。如果他们不这样做,请向项目发送拉取请求(导出并不难,学习如何做是很好的)或者只是针对他们提交错误,特别是如果他们已经使用 cmake 作为构建系统。
Under these circumstances, how can I convince my coworkers to use similar work process?
这取决于您的同事,并且里程会有所不同。我曾与希望采用最佳实践并支持灵活性的同事打交道,也曾与那些只满足于解决我们目前面临的问题的同事打交道。
我正在做一个 C++ 项目。到目前为止它并不复杂,但依赖于一堆“流行”库(nlohmann/json、ToruNiina/toml11 仅举几例)。他们都有一些CMakeLists.txt
,从我没有经验的角度来看,我认为他们结构很好。
现在我当然可以一个一个地编译这些库,或者在我的项目仓库中包含一个“副本”,但我想做得更好。在研究了可用的构建工具之后,我决定使用 cmake
来构建和管理 C++ 项目。承诺是获得一个稳定的、广泛支持的工具,这将有助于简化和统一构建过程。而且,从项目性质来看,我无权对目标机器强加任何要求;我需要为部署打包所有东西。
我花了几天时间阅读、观看和测试各种 cmake 教程、手册和手册。我不得不承认,我很快就开始觉得,一个本应澄清开发过程的工具却不断引入新的晦涩难懂的东西,这与其目的背道而驰。本来,我把这归功于我缺乏经验,然而...
我阅读了 target_link_libraries
的 articles about why not to bundle dependencies, only to be followed by methods of doing so. I have found recommendation to use one way A over B, C over B and later A over C. It took me a while to figure out the differences between 2.8 and 3.0, the obscurity,设置了 cxx 版本 and/or 编译器警告标志等等。
我的观点是,即使在 cmake
的海域进行了一次精疲力尽的探险之后,我仍然不确定一些基本问题:
How is cmake meant to be used?
What is a standard, what is a courtesy, and what is none of those?
How can I tell that something is a feature, an archaic backwards compatibility, or both?
现在我将在我的项目中说明这一点。我只需要这样的东西
cmake_minimum_required(VERSION 3.14)
project(CaseCore CXX)
add_executable(myBinary list/of/cpp/sources.cpp)
target_link_libraries(myBinary PUBLIC someExternalLibs likeForExample nlohmann_json::nlohmann_json oqs)
唯一的问题是库(反正没有 space 其他问题)。我想用项目构建它们,不想制作本地副本(不要一直拖放大量不相关的文件)。首先,我创建了库回购的分支,以便拥有可靠的来源并能够将更新的版本合并到我的分支中。
现在决定是使用 git submodule
还是其他一些方案,我读过子模块性能不佳并且更喜欢整个事情由 cmake
单独管理。我从 ExternalProject_Add 开始,但后来我发现了 FetchContent
,它帮助我轻松地将外部依赖项添加到我的 cmake 列表中
FetchContent_Declare(nlohmann
GIT_REPOSITORY https://github.com/my-reliable-fork-of/json
GIT_TAG v3.7.3
)
message(STATUS "Fetching Json...this may take a while")
FetchContent_MakeAvailable(nlohmann)
看起来和工作都很好而且很小。但是,我总是必须搜索库本身,以便 find/guess 哪个目标 link 到我的可执行目标 ?约定似乎接近于零,除非相应的 CMakeLists.txt
足够简单,可以阅读,否则我倾向于猜测目标名称,直到找到为止。
最近我想 link 来自 here 的 liboqs,但上述情况并没有真正帮助;出于某种原因,我可以 link oqs,#include "oqs/oqs.h"
,但未创建动态库并且执行终止。我很确定我可以在谷歌搜索和使用各种 cmake 变量的另一段时间后解决问题。然而,这并不是我期望 cmake
帮助我管理项目的方式;实际上恰恰相反。
为了清楚起见,我拒绝了其他方法,包括
add_subdirectory from local repo copy (git submodule)
ExternalProject_Add from local repo copy (git submodule)
ExternalProject_Add from online repo
find_package
因为它们似乎更多 obscure/old-style 等等(尽管经过数小时的研究,它们对我来说似乎仍然有很多方法可以做同样的事情)
现在我有
Am I doing something wrong, or is it really what working with
cmake
should look like?Do I really have to "reverse-engineer" other people's CMakeLists in order to use a library?
Under these circumstances, how can I convince my coworkers to use similar work process?
最后
How can I adjust my work in order to ease these difficulities for others?
我越用越喜欢 C++。然而,我花费了大量的生产时间来解决依赖关系......我不想让 this guy 更加生气。
How is cmake meant to be used?
典型的 cmake 用法与旧的 autotools 用法相匹配:
$ cmake /path/to/src #replaces /path/to/src/configure
$ make
$ make install
一些目标已更改(例如,make check
vs make test
),并且 cmake 不提供所有相同的标准目标(例如,make distclean
),但我的用法以上是大多数开发人员会做的事情(并且由于 cmake 会自行重新运行,所以大多数时候它实际上只是第二步)。
如果您的 CMakeLists.txt
不支持此工作流程,您应该有充分的理由。大多数工具都采用这样的工作流程,因此您严重限制了自己。
What is a standard, what is a courtesy, and what is none of those?
除此之外,cmake 几乎就是狂野的西部。由于更好的文档和培训,事情变得更加标准化,但它远非完美。
一个行为良好的 cmake 项目应该导出它的目标(Stack Overflow 上有很多关于这个的问题和答案)并传播标志和依赖项。这使得依赖项目更容易使用导出的目标,幸运的是这很容易做到。
How can I tell that something is a feature, an archaic backwards compatibility, or both?
据我所知,这些区别并不明显。通常,较新的方法利用 target_*
函数而不是全局函数(例如,target_include_directories
与 include_directories
)。 target_*
函数还用于传播标志,包括目录、编译器功能和我上面提到的依赖库。
Am I doing something wrong, or is it really what working with cmake should look like?
您在谈论管理外部依赖项,我将跳过此部分以避免产生意见。简而言之,C 和 C++ 依赖关系很难,并且在项目中有许多相互竞争的方式来管理它们。它们各有利弊,但大多数仍然是为作者的用例而设计的。您必须弄清楚您需要哪些用例,并根据此选择工具和工作流程。
Do I really have to "reverse-engineer" other people's CMakeLists in order to use a library?
一个行为良好的 cmake 项目将正确导出其目标,即使它们使用与您不同的依赖管理。如果他们不这样做,请向项目发送拉取请求(导出并不难,学习如何做是很好的)或者只是针对他们提交错误,特别是如果他们已经使用 cmake 作为构建系统。
Under these circumstances, how can I convince my coworkers to use similar work process?
这取决于您的同事,并且里程会有所不同。我曾与希望采用最佳实践并支持灵活性的同事打交道,也曾与那些只满足于解决我们目前面临的问题的同事打交道。