Mil*_*lan 10 c++ linux makefile cmake
我一直在开发一个使用rplidar_sdk 的项目,一开始我遇到了这个问题:
如何在我的 C++ 项目中链接本地安装的 SDK 的静态库?
基本上,SDK 在其本地目录中生成库,并且在其 中
Makefile,它没有install规则。我的意思是我可以运行make,但在那之后,如果我运行sudo make install就会make: *** No rule to make target 'install'. Stop.出错。
因此,在这个和这个答案的帮助下,我能够构建我的本地项目。到目前为止,一切都很好。
然而,主要问题是我必须对CMakeLists.txt我的存储库中的 RPLidar SDK 路径进行硬编码。现在,每当我团队中的其他人开始处理该存储库(这是非常明显的)时,他/她就必须更新第CMakeLists.txt一个。这不是一个好主意/做法!
为了解决这个问题,我更新了MakefileRPLidar SDK,如下所示:
.
.
.
RPLIDAR_RELEASE_LIB := $(HOME_TREE)/output/Linux/Release/librplidar_sdk.a
install: $(RPLIDAR_RELEASE_LIB)
install -d $(DESTDIR)/usr/local/lib/rplidar/Release/
install -m 644 $(RPLIDAR_RELEASE_LIB) $(DESTDIR)/usr/local/lib/rplidar/Release/
RPLIDAR_DEBUG_LIB := $(HOME_TREE)/output/Linux/Debug/librplidar_sdk.a
install: $(RPLIDAR_DEBUG_LIB)
install -d $(DESTDIR)/usr/local/lib/rplidar/Debug/
install -m 644 $(RPLIDAR_DEBUG_LIB) $(DESTDIR)/usr/local/lib/rplidar/Debug/
RPLIDAR_HEADERS := $(HOME_TREE)/sdk/include
install: $(RPLIDAR_HEADERS)
install -d $(DESTDIR)/usr/local/include/rplidar/
cp -r $(RPLIDAR_HEADERS)/* $(DESTDIR)/usr/local/include/rplidar/
RPLIDAR_HEADERS_HAL := $(HOME_TREE)/sdk/src/hal
install: $(RPLIDAR_HEADERS_HAL)
install -d $(DESTDIR)/usr/local/include/rplidar/
cp -r $(RPLIDAR_HEADERS_HAL) $(DESTDIR)/usr/local/include/rplidar/
Run Code Online (Sandbox Code Playgroud)
由于这次更新,现在我可以运行sudo make install它,它基本上将 RPLidar SDK 的头文件从本地目录复制到/usr/local/rplidar/目录。它还将 lib 文件复制到/usr/local/lib/rplidar/<Debug> or <Release>/目录中。
现在,在我的本地项目中,我将其更新CMakeLists.txt为如下:
cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR)
project(<project_name>)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
SET(CMAKE_CXX_FLAGS -pthread)
include_directories(include)
add_executable(${PROJECT_NAME} src/main.cpp src/another_src_file.cpp)
find_package(rplidar REQUIRED)
include_directories(${rplidar_INCLUDE_DIRS})
link_directories(${rplidar_LIBRARY_DIRS})
target_link_libraries(${PROJECT_NAME} ${rplidar_LIBRARY})
Run Code Online (Sandbox Code Playgroud)
但是,在运行cmake ..命令时,我收到此错误:
.
.
.
CMake Error at CMakeLists.txt:12 (find_package):
By not providing "Findrplidar.cmake" in CMAKE_MODULE_PATH this project has
asked CMake to find a package configuration file provided by "rplidar", but
CMake did not find one.
Could not find a package configuration file provided by "rplidar" with any
of the following names:
rplidarConfig.cmake
rplidar-config.cmake
Add the installation prefix of "rplidar" to CMAKE_PREFIX_PATH or set
"rplidar_DIR" to a directory containing one of the above files. If
"rplidar" provides a separate development package or SDK, be sure it has
been installed.
-- Configuring incomplete, errors occurred!
Run Code Online (Sandbox Code Playgroud)
据我所知,RPLidar SDK没有rplidarConfig.cmake或rplidar-config.cmake文件。
我该如何修复这个错误?
Ter*_*sao 24
发自内心的咆哮:
foo当作者未能提供一个foo-config.cmake供您通过调用轻松使用的库时,您必须使用任何库,这很糟糕find_package(foo)。当一个相当现代的项目仍然使用手写的 Makefile 作为其构建系统时,这绝对是令人愤慨的。我自己目前所使用的 SDK 的构造比你的要差得多。
简短回答:
由于 SDK 的作者未能提供配置文件来支持您的 cmake 使用,如果您仍然坚持调用find_package该库(您应该这样做!),您需要编写自己的Module文件来清理它们的混乱。(是的,您正在为图书馆作者做这项工作)。
要真正实现跨平台使用,您应该编写一个Findrplidar.cmake模块文件来查找适合您的库。
要编写合理的模块文件,您很可能将 API 用于find_path头文件和find_library库。你应该查看它的文档并尝试使用它们,也许还可以谷歌一些教程。
这是我的版本Findglog.cmake供您参考。(glog作者已经更新了他们的代码并支持Config模式。不幸的是,Ubuntu构建没有使用它,所以我仍然需要编写自己的文件)
find_path(glog_INCLUDE_DIR glog/logging.h)
message(STATUS "glog header found at: ${glog_INCLUDE_DIR}")
find_library(glog_LIB glog)
message(STATUS "libglog found at: ${glog_LIB}")
mark_as_advanced(glog_INCLUDE_DIR glog_LIB)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(glog REQUIRED_VARS
glog_INCLUDE_DIR
glog_LIB
)
if(glog_FOUND AND NOT TARGET glog::glog)
add_library(glog::glog SHARED IMPORTED)
set_target_properties(glog::glog PROPERTIES
IMPORTED_LINK_INTERFACE_LANGUAGES "CXX"
IMPORTED_LOCATION "${glog_LIB}"
INTERFACE_INCLUDE_DIRECTORIES
"${glog_INCLUDE_DIR}"
)
endif()
Run Code Online (Sandbox Code Playgroud)
你可以这样使用它:
find_package(glog)
target_link_libraries(main PRIVATE glog::glog)
Run Code Online (Sandbox Code Playgroud)
长答案:
开发人员使用 cmake 的历史绝对是一场噩梦。互联网上充满了如何不在项目中使用 cmake 的不良做法/示例,包括旧的官方 cmake 教程(可能仍然存在)。主要是因为没有人真正关心(如果我可以构建我的项目,谁在乎它是否是跨平台的)。另一个合理的原因是 cmake 文档对于初学者来说确实令人望而生畏。
这就是为什么我在这里写下我自己的答案,以免你在其他地方谷歌搜索时被误导。
噩梦不再存在。等待结束了。cmake(来源)的“弥赛亚”来了。他为 2020 年及以后编写的 asm/C/C++/CUDA 项目带来了希望。这是圣言。
上面的链接指出了cmake项目应该如何编写并真正一劳永逸地实现跨平台的唯一方法。请注意,对于初学者来说,这些材料根本不容易理解。我自己花了整整一周的时间来完全掌握《The Word》中所涵盖的内容,当时我对 cmake 概念已经有些熟悉了(但迷失在我过去的罪恶方式中)。
所谓“长答案”其实更短。它只是指向真正答案的指针。祝你阅读圣言好运。拥抱神的道,因为任何反对它的行为都是纯粹的异端。
好问题。其中很多都可以从神的话语中获得。但是,当您更加熟悉 CMake 时,就会更好地理解这个词。让我以降低与您手头问题的相关性来回答它们。
为了便于讨论,我仅使用 libfoo 作为示例。
假设您总是想像这样使用 libfoo:
find_package(foo)
target_link_libraries(your_exe ... foo::foo)
Run Code Online (Sandbox Code Playgroud)
假装 foo 安装在以下位置:
- /home/dev/libfoo-dev/
- include
- foo
- foo.h
- bar.h
- ...
- lib
- libfoo.so
- share
- foo/foo-config.cmake # This may or may not exist. See discussion.
Run Code Online (Sandbox Code Playgroud)
问:只有一个.h文件。为什么?
答:因为在 的情况下libfoo(也适用于glog),只需要搜索一次标头位置。就像 中的示例一样libfoo,其中foo/foo.h和foo/bar.h位于同一位置。所以他们的输出find_path将是相同的:/home/dev/libfoo-dev/include。
问:为什么我要获取NOTFOUND标头和库?
答:文档中指定的功能find_path和仅搜索位置。find_library默认情况下,它们分别搜索系统位置,例如/usr/include和/usr/lib。有关系统位置的详细信息,请参阅官方文档。然而,在 的情况下libfoo,它们位于/home/dev/libfoo-dev。因此,您必须在 cmake 变量中指定这些位置CMAKE_PREFIX_PATH。这是一个;单独的字符串。可以cmake -D CMAKE_PREFIX_PATH="/home/dev/libfoo-dev;/more/path/for/other/libs/;...;/even/more/path" ....在命令行上完成。
一个非常重要的注意事项:与 Unix 命令不同find,find_path它只会搜索内部的特定路径/home/dev/libfoo-dev,而不是一路向下:(
include通常也像x86 Linux 中的include/{arch}{arch} 一样)分别为 的变体。不寻常的位置需要传递更多参数,这是不常见的,而且很可能是不必要的。x86_64-linux-gnufind_pathlibfind_library
正是由于这个原因,对于libfoo,调用find_path(... foo.h ...)是不受欢迎的。有人会想要find_path(... foo/foo.h ...)。请参阅文档了解更多详细信息。你也可以自己尝试一下。
也出于这个原因,最好将库组织bin include lib share在类 Unix 系统上的常用四元组中。我对 Windows 不熟悉。
问:Debug&Release
答:有几种选择。最简单的可能是:
/path/to/debug例如/path/to/releaseDebug&Release构建 ( cmake -D CMAKE_PREFIX_PATH="/path/to/debugORrelease" ....)肯定还有其他方法,但可能需要在Findrplidar.cmake脚本中特别小心(也许是一些if语句)。
问:为什么glog::glog而不是glog?
答:这只是现代的 cmake 实践,好处不大。现在不重要。如果您有兴趣,请参阅《圣经》。
问:你提到你正在写作rplidarConfig.cmake。相反,您应该将文件重命名为Findrplidar.cmake.
A:CMake 的理念是这样的:
foo-config.cmake或fooConfig.cmakeFindfoo.cmake通过猜测如何描述 libfoo 的依赖关系来编写。对于简单的库来说,这还不错。对于复杂的,比如 Boost,这很糟糕!关于这个主题的一些旁注:
Findfoo.cmake通过猜测来编写的。foo-config.cmake对于 libfoo 的作者来说,如果他们严格遵循单词,那么编写文件是非常容易的。foo-config.cmake。Findfoo.cmake问:为什么只有find_package& target_link_libraries?
答:这就是圣经所说的。因此,这是一个很好的做法。为什么圣经这么说是你必须自己找出来的。我不可能在这个答案中解释神的话语的要点,也无法让你信服。我只想说以下几点:
编写意大利面 CMakeList 非常容易,而且几乎无法维护。圣经的精神会迫使你仔细思考以下问题,从而帮助你避免这种情况:
如果您仔细想想,这些方面对于编写跨平台且可维护的库至关重要。
include_directories,link_directories并且add_definitions都是非常糟糕的做法(根据大量来源,包括这些 API 的官方文档)。不好的做法往往会掩盖上述方面,并在以后将所有内容整合在一起时引发问题。例如,include_directories将-I为该目录中编写的每个目标添加到编译器中CMakeLists.txt。读几遍这句话,Spock 就会告诉你这是不合逻辑的。
不用担心。当你不熟悉这个词时,现在可以使用它们(否则为什么会在最后一节)。一旦您了解了这个词,就可以在有时间的时候重构您的 CMakeLists。当您的项目变得更加复杂时,不好的实践可能会在以后引起问题。(根据我的专业经验,5个非常小的群体就足以引起一场噩梦。我所说的噩梦是指在CMakeLists中对所有内容进行硬编码;为每个不同的平台/设备/环境创建一个git分支;修复一个bug意味着cherry-为每个分支选择一个提交。在知道这个词之前我就已经去过那里了。)
Word 的实践很好地利用了现代 CMake 的哲学,将构建规范和使用要求封装在 CMake 目标中。因此,当target_link_libraries被调用时,这些属性会被正确传播。