CMake笔记之将任意官方库作为third_party完整地包含在工程项目中使用的通用模板
—— 2024-03-20 凌晨1:06

code review!
参考笔记
1.CMake笔记之将任意官方库作为third_party完整地包含在工程项目中使用的通用模板
2.在项目中集成 libpcap 的完整指南
文章目录
1.通用CMakeLists.txt模板
代码
# 设置CMake的最低版本要求
cmake_minimum_required(VERSION 3.0)
# 设置项目名称和版本
project(MyProject)
# 设置C++标准
set(CMAKE_CXX_STANDARD 11)
# 假设你的库文件名为libmylibrary.a (静态库) 或 libmylibrary.so (动态库)
# 并且库文件位于项目的third_party目录下
# 添加自己的库的头文件目录
include_directories(${PROJECT_SOURCE_DIR}/third_party/mylibrary/include)
# 添加自己的库的库目录
link_directories(${PROJECT_SOURCE_DIR}/third_party/mylibrary/lib)
# 指定生成目标
add_executable(${PROJECT_NAME} src/main.cpp)
# 链接库到你的可执行文件
target_link_libraries(${PROJECT_NAME} mylibrary)
2.GPT4给出的改进建议
这个CMakeLists.txt
文件看上去基本是正确的,但是有几个小地方可能需要根据具体情况进行调整。以下是一个加了注释和可能需要注意的地方的版本:
# 设置CMake的最低版本要求
cmake_minimum_required(VERSION 3.0)
# 设置项目名称和版本
project(MyProject VERSION 1.0.0) # 如果你需要指定版本号,应该这样设置
# 设置C++标准
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True) # 确保使用了C++11
# 假设你的库文件名为libmylibrary.a (静态库) 或 libmylibrary.so (动态库)
# 并且库文件位于项目的third_party目录下
# 添加自己的库的头文件目录
include_directories(${PROJECT_SOURCE_DIR}/third_party/mylibrary/include)
# 添加自己的库的库目录
# 注意: 在现代CMake中,更推荐使用target_link_directories和target_link_libraries结合使用
# link_directories应该尽量避免使用,因为它是全局的,会影响所有的target
link_directories(${PROJECT_SOURCE_DIR}/third_party/mylibrary/lib)
# 指定生成目标
add_executable(${PROJECT_NAME} src/main.cpp)
# 链接库到你的可执行文件
# 如果mylibrary是第三方库,且你没有使用add_subdirectory或find_package来包含它,
# 你可能需要使用find_library来查找库文件的确切位置
target_link_libraries(${PROJECT_NAME} mylibrary)
注意事项:
- 如果你有多个版本的
mylibrary
库,并且希望确保链接到正确的版本,可能需要更详细地指定库文件的路径或版本。 - 如果
mylibrary
是动态链接库,确保在运行时它可以在系统的库路径中找到,或者设置LD_LIBRARY_PATH
环境变量等。 - 如果是跨平台项目,一些路径可能需要根据操作系统进行调整,例如使用
CMAKE_PREFIX_PATH
。 - 现代CMake推荐使用target-specific命令如
target_include_directories
和target_link_libraries
,这样可以更清晰地表示目标的依赖关系,而不是使用全局命令如include_directories
和link_directories
。
如果你遵循现代CMake的实践,可以这样写:
# 设置CMake的最低版本要求
cmake_minimum_required(VERSION 3.0)
# 设置项目名称和版本
project(MyProject VERSION 1.0.0)
# 设置C++标准
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
# 添加可执行文件
add_executable(${PROJECT_NAME} src/main.cpp)
# 添加自己的库的头文件目录和库文件
# 假设库文件在third_party/mylibrary下的include和lib目录中
target_include_directories(${PROJECT_NAME} PRIVATE ${PROJECT_SOURCE_DIR}/third_party/mylibrary/include)
find_library(MYLIBRARY_LIBRARY NAMES mylibrary PATHS ${PROJECT_SOURCE_DIR}/third_party/mylibrary/lib NO_DEFAULT_PATH)
# 链接库到你的可执行文件
target_link_libraries(${PROJECT_NAME} ${MYLIBRARY_LIBRARY})
这里target_include_directories
和find_library
函数被用来替代include_directories
和link_directories
。这种方式将库的链接过程更明确地关联到了特定的目标上。
3.git clone或apt-get install的官方库,以zlib库为例
3.1.获取zlib库
sudo apt install zlib1g-dev
3.2.找到头文件和库文件的位置并拷贝至工程项目
可以使用dpkg
查询这些文件的位置:
sh dpkg -L zlib1g-dev
这个命令会列出所有`zlib1g-dev`包的文件。
常见的头文件和库文件的路径可能如下:
- 头文件:`/usr/include/zlib.h`
- 静态库:`/usr/lib/x86_64-linux-gnu/libz.a`
- 动态库:`/usr/lib/x86_64-linux-gnu/libz.so`
注意:这些路径可能会根据你的Linux发行版和系统架构有所不同。
实际执行结果:
可以看到zlib
的头文件位于/usr/include
目录下,库文件位于/usr/lib/x86_64-linux-gnu
目录下。
复制zlib
的头文件和库文件:
cp /usr/include/zlib.h my_project/third_party/zlib/include/
cp /usr/include/zconf.h my_project/third_party/zlib/include/
cp /usr/lib/x86_64-linux-gnu/libz.a my_project/third_party/zlib/lib/
cp /usr/lib/x86_64-linux-gnu/libz.so my_project/third_party/zlib/lib/
注意:为了确保在编译时能找到所有必要的头文件和库文件,通常需要复制zlib
的所有相关头文件(在这个例子中是zlib.h
和zconf.h
)以及库文件。
3.3.CMakeLists.txt
cmake_minimum_required(VERSION 3.14)
project(my_project)
# 添加头文件的搜索路径
include_directories(${CMAKE_SOURCE_DIR}/third_party/zlib/include)
# 添加库文件的搜索路径
link_directories(${CMAKE_SOURCE_DIR}/third_party/zlib/lib)
add_executable(my_project src/main.cpp)
# 链接zlib库,这里,z是zlib库的基本名字(通常,库文件的全名是libz.a或libz.so,但在CMake中你只需指定z)。
target_link_libraries(my_project z)
3.4.此时的文件结构
3.5.main.cpp
#include <zlib.h>
#include <iostream>
int main() {
// 初始化zlib结构
z_stream strm;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
// 初始化zlib
if (deflateInit(&strm, Z_DEFAULT_COMPRESSION) != Z_OK) {
std::cerr << "zlib initialization failed!" << std::endl;
return -1;
}
// 清理zlib
deflateEnd(&strm);
std::cout << "zlib initialized and cleaned up successfully!" << std::endl;
return 0;
}
3.6.编译运行
cd build/ && cmake .. && make
4.简单地将克隆的git clone下的目录放在项目树中的某个位置,以Google Test为例
4.1.git clone
git clone https://github.com/google/googletest.git third_party/googletest
4.2.CMakeLists.txt
cmake_minimum_required(VERSION 3.14)
project(my_project)
# 添加googletest子目录
add_subdirectory(third_party/googletest)
# 包含googletest的头文件
include_directories(third_party/googletest/googletest/include)
add_executable(my_project src/main.cpp)
# 链接googletest到您的项目
target_link_libraries(${PROJECT_NAME} gtest)
4.3.main.cpp
#include <gtest/gtest.h>
TEST(SampleTest, AssertionTrue) {
ASSERT_TRUE(true);
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
4.4.编译运行
cd build/ && cmake .. && make
5.在CMakeLists.txt文件中执行git clone(略,个人没用过,但知道有这个方法)
- 方法一:直接使用
execute_process
execute_process(
COMMAND git clone https://github.com/your/repo.git
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)
这将在构建目录下克隆仓库。
- 方法二:使用
find_package
和find_program
首先,尝试找到Git,如果未找到,再执行克隆操作:
find_package(Git QUIET)
if(NOT GIT_FOUND)
find_program(GIT_EXECUTABLE NAMES git git.exe)
endif()
if(GIT_EXECUTABLE)
execute_process(
COMMAND ${GIT_EXECUTABLE} clone https://github.com/your/repo.git
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)
else()
message(FATAL_ERROR "Git not found, cannot clone repository.")
endif()
- 方法三:通过自定义目标和
add_custom_command
如果你想要更细粒度的控制,例如仅在特定目标需要时才克隆,你可以使用add_custom_target
或add_custom_command
:
add_custom_target(
CloneRepo ALL
COMMAND ${GIT_EXECUTABLE} clone https://github.com/your/repo.git
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMENT "Cloning repository"
)
- 方法四:使用 CMake 外部项目
CMake 提供了一个模块 ExternalProject
,可以用来下载和构建外部项目。
include(ExternalProject)
ExternalProject_Add(
MyExternalRepo
GIT_REPOSITORY https://github.com/your/repo.git
GIT_TAG master
SOURCE_DIR "${CMAKE_BINARY_DIR}/your_repo"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
TEST_COMMAND ""
)
在这个例子中,GIT_REPOSITORY
和GIT_TAG
指定了远程仓库和要检出的分支或标签。SOURCE_DIR
指定了克隆的目标目录。
- 方法五:使用CMake的ExternalProject模块来执行git clone
6.找到该库的头文件(用于编译时包含)和链接库(用于链接时使用)的方法
在Linux系统中,当您想要将一个官方库移植到您的项目文件中时,通常需要找到该库的头文件(用于编译时包含)和链接库(用于链接时使用)。以下是一些常用的方法来查找这些文件:
-
使用
dpkg
查询已安装的包文件:
如果您使用的是基于Debian的系统(如Ubuntu),可以使用dpkg
命令来查询已安装包的文件列表。例如:dpkg -L <package-name>
这将列出安装的包的所有文件,包括库文件和头文件的位置。
-
使用
apt-file
搜索未安装包的文件:
如果库尚未安装,或者您想要找到不在您系统上的文件,可以使用apt-file
命令:apt-file update apt-file search <library-name>
-
使用
locate
命令:
locate
命令可以帮助您快速查找系统中的文件。使用前需要确保已经安装了mlocate
包,并且已经建立了数据库:sudo updatedb locate <filename>
-
查看库的文档或网站:
许多开源库在其官方文档或网站上会提供安装指南,包括头文件和库文件的默认安装位置。 -
使用
ldd
检查二进制文件依赖的库:
ldd
命令可以用来查看可执行文件或共享库依赖的动态链接库。例如:ldd /path/to/your/binary
但请注意,
ldd
只能告诉您二进制文件实际运行时需要哪些动态链接库,而不会告诉您头文件的位置。 -
使用
pkg-config
查找库的编译和链接标志:
pkg-config
是一个用于查询已安装库的编译和链接信息的工具。如果库支持pkg-config
,您可以这样使用它:pkg-config --cflags <library-name> pkg-config --libs <library-name>
通常,最好的方法是结合使用多种工具,以确保您能够找到所有必要的信息。如果是编译时寻找头文件和链接时寻找库文件,pkg-config
通常是首选,因为它可以直接提供编译器和链接器所需的标志。此外,查看库的官方文档通常会提供最准确的安装和使用指南。
7.包含可执行文件的库,如protobuf移植到自己的项目文件夹中
7.1.找到可执行文件和lib文件夹的路径
which protoc
locate libprotobuf | grep '\.so'
locate libprotobuf | grep '\.a'
7.2.创建third_party目录
mkdir -p third_party/protobuf/include
mkdir -p third_party/protobuf/lib
mkdir -p third_party/protobuf/bin
7.3.拷贝相关文件
cp /usr/bin/protoc third_party/protobuf/bin/
cp -r /usr/include/google third_party/protobuf/include/
cp /usr/lib/x86_64-linux-gnu/libprotobuf.* ~/my_project/third_party/protobuf/lib/
7.4.文件结构
7.5.CMakeLists.txt
cmake_minimum_required(VERSION 3.9)
project(HelloProto)
# 设定 C++ 标准
set(CMAKE_CXX_STANDARD 11)
# 添加 third_party/protobuf 的 include 目录和 lib 目录
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/third_party/protobuf/include)
link_directories(${CMAKE_CURRENT_SOURCE_DIR}/third_party/protobuf/lib)
# 找到 Protocol Buffers 编译器 protoc
set(PROTOC_PATH "${CMAKE_CURRENT_SOURCE_DIR}/third_party/protobuf/bin/protoc")
# 设定生成的 C++ 代码的路径
set(GENERATED_PROTOBUF_PATH "${CMAKE_BINARY_DIR}/protobuf")
# 创建目录用于存放生成的 C++ 代码
file(MAKE_DIRECTORY ${GENERATED_PROTOBUF_PATH})
# 包含生成的 *.pb.h 文件的路径
include_directories(${GENERATED_PROTOBUF_PATH})
# 生成 C++ 源文件和头文件
add_custom_command(
OUTPUT "${GENERATED_PROTOBUF_PATH}/hello.pb.cc"
"${GENERATED_PROTOBUF_PATH}/hello.pb.h"
COMMAND ${PROTOC_PATH}
ARGS --cpp_out ${GENERATED_PROTOBUF_PATH}
--proto_path ${CMAKE_CURRENT_SOURCE_DIR}/proto
${CMAKE_CURRENT_SOURCE_DIR}/proto/hello.proto
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/proto/hello.proto)
# 生成一个静态库从 protobuf 源文件
add_library(hello_proto STATIC
${GENERATED_PROTOBUF_PATH}/hello.pb.cc
${GENERATED_PROTOBUF_PATH}/hello.pb.h)
# 链接 protobuf 库
target_link_libraries(hello_proto protobuf)
# 添加 main.cpp 文件的可执行文件
add_executable(hello_proto_exec src/main.cpp)
# 链接库文件
target_link_libraries(hello_proto_exec hello_proto)
# 添加 third_party/protobuf 的 lib 目录到链接器
target_link_directories(hello_proto_exec PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/third_party/protobuf/lib)
7.6.main.cpp
// main.cpp
#include <iostream>
#include "hello.pb.h"
int main() {
hello::HelloMessage msg;
msg.set_greeting("Hello, Protocol Buffers!");
std::cout << msg.greeting() << std::endl;
return 0;
}
7.7.hello.proto
// hello.proto
syntax = "proto3";
package hello;
message HelloMessage {
string greeting = 1;
}