CMake笔记之将任意官方库作为third_party完整地包含在工程项目中使用的通用模板

本文档详细介绍了如何使用CMake将官方库(如zlib和Google Test)集成到工程项目中,包括通用CMakeLists.txt模板、库文件定位、git clone以及编译运行步骤。同时,提到了CMake现代实践,如target-specific命令的使用,以及如何处理动态链接库和跨平台项目的路径调整。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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)

注意事项:

  1. 如果你有多个版本的mylibrary库,并且希望确保链接到正确的版本,可能需要更详细地指定库文件的路径或版本。
  2. 如果mylibrary是动态链接库,确保在运行时它可以在系统的库路径中找到,或者设置LD_LIBRARY_PATH环境变量等。
  3. 如果是跨平台项目,一些路径可能需要根据操作系统进行调整,例如使用CMAKE_PREFIX_PATH
  4. 现代CMake推荐使用target-specific命令如target_include_directoriestarget_link_libraries,这样可以更清晰地表示目标的依赖关系,而不是使用全局命令如include_directorieslink_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_directoriesfind_library函数被用来替代include_directorieslink_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.hzconf.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_packagefind_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_targetadd_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_REPOSITORYGIT_TAG指定了远程仓库和要检出的分支或标签。SOURCE_DIR指定了克隆的目标目录。

  • 方法五:使用CMake的ExternalProject模块来执行git clone

6.找到该库的头文件(用于编译时包含)和链接库(用于链接时使用)的方法

在Linux系统中,当您想要将一个官方库移植到您的项目文件中时,通常需要找到该库的头文件(用于编译时包含)和链接库(用于链接时使用)。以下是一些常用的方法来查找这些文件:

  1. 使用dpkg查询已安装的包文件:
    如果您使用的是基于Debian的系统(如Ubuntu),可以使用dpkg命令来查询已安装包的文件列表。例如:

    dpkg -L <package-name>
    

    这将列出安装的包的所有文件,包括库文件和头文件的位置。

  2. 使用apt-file搜索未安装包的文件:
    如果库尚未安装,或者您想要找到不在您系统上的文件,可以使用apt-file命令:

    apt-file update
    apt-file search <library-name>
    
  3. 使用locate命令:
    locate命令可以帮助您快速查找系统中的文件。使用前需要确保已经安装了mlocate包,并且已经建立了数据库:

    sudo updatedb
    locate <filename>
    
  4. 查看库的文档或网站:
    许多开源库在其官方文档或网站上会提供安装指南,包括头文件和库文件的默认安装位置。

  5. 使用ldd检查二进制文件依赖的库:
    ldd命令可以用来查看可执行文件或共享库依赖的动态链接库。例如:

    ldd /path/to/your/binary
    

    但请注意,ldd只能告诉您二进制文件实际运行时需要哪些动态链接库,而不会告诉您头文件的位置。

  6. 使用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;
}

7.8.编译运行

在这里插入图片描述

7.9.在build文件夹内可以找到编译出来的pb.cc和pb.h

在这里插入图片描述

<think>好的,我现在需要回答用户关于如何在CMake中配置第三方库的问题。用户可能已经接触过ExternalProject_Add,并且可能遇到了处理依赖关系的问题,比如库之间的传递依赖。根据用户提供的引用,特别是引用[1],用户提到了如何用ExternalProject_Add处理LibraryA依赖LibraryB的情况。所以,我需要结合这些信息来构建答案。 首先,我应该确定用户的需求是正确配置第三方库,尤其是那些不使用CMake构建的库。用户可能希望避免硬编码构建命令,保持平台无关性。因此,我需要分步骤讲解,包括如何下载、构建和链接这些库。 第一步可能是使用ExternalProject模块来管理第三方库的构建。需要确保先构建依赖项,比如LibraryB,然后再构建LibraryA,设置正确的安装路径。这里需要提到如何设置--prefix和--with-libraryB参数,确保路径正确,如引用[1]中的例子。 接下来,需要处理头文件和库文件的包含路径。这里可能需要使用include_directories和link_directories命令,或者更推荐使用target_include_directories和target_link_directories来指定目标特定的路径,避免全局设置带来的副作用。 然后,链接到最终的可执行文件或库时,应使用target_link_libraries命令,同时链接两个库。这需要确保库的路径已经被正确设置,并且CMake能找到这些库。 另外,用户可能关心如何保持平台无关性,避免硬编码路径或构建命令。这时需要强调使用CMake提供的变量和命令,比如CMAKE_BINARY_DIR,或者find_package,虽然第三方库可能没有提供CMake配置,但ExternalProject可以帮助管理构建过程。 可能还需要提到如何传递编译选项,比如在构建外部项目时设置CFLAGS或CXXFLAGS。例如,引用[3]中提到的add_compile_options(-fPIC),这对于生成位置无关代码很重要,尤其是在构建共享库时。 另外,用户可能对如何处理动态库和静态库的区分有疑问,需要说明在add_library时指定SHARED或STATIC,并确保依赖库的类型兼容。 还需要考虑错误处理,比如如果库的构建失败,如何让CMake正确报错,或者如何处理不同平台的构建差异。不过用户的问题主要集中在配置步骤,可能暂时不需要深入这部分。 最后,生成相关问题,引导用户进一步学习,比如如何优化ExternalProject的使用,如何处理更复杂的依赖关系,或者如何集成使用CMake的第三方库。 在回答中,要确保结构清晰,步骤明确,避免使用硬编码路径,保持示例简洁,并引用提供的参考资料来支持说明。同时,注意用户之前的问题可能涉及传递依赖和平台无关性,因此重点强调这些方面。</think>在CMake中配置第三方库的核心步骤包括依赖管理、构建配置和链接集成,以下是具体实现方法: --- ### 一、使用ExternalProject管理非CMake项目 1. **定义依赖关系层级** ```cmake include(ExternalProject) # 先构建LibraryB ExternalProject_Add( LibraryB URL "http://example.com/libb.tar.gz" CONFIGURE_COMMAND <SOURCE_DIR>/configure --prefix=${CMAKE_BINARY_DIR}/deps BUILD_IN_SOURCE 1 ) # 后构建LibraryA,依赖LibraryB ExternalProject_Add( LibraryA DEPENDS LibraryB URL "http://example.com/liba.tar.gz" CONFIGURE_COMMAND <SOURCE_DIR>/configure --with-libraryB=${CMAKE_BINARY_DIR}/deps BUILD_IN_SOURCE 1 ) ``` 通过`DEPENDS`确保构建顺序,使用`CMAKE_BINARY_DIR`保持路径平台中立[^1] 2. **设置公共安装路径** ```cmake set(DEPS_INSTALL_DIR ${CMAKE_BINARY_DIR}/deps) ``` 统一安装到构建目录下避免污染系统路径 --- ### 二、集成到主项目 1. **声明头文件路径** ```cmake include_directories(${DEPS_INSTALL_DIR}/include) link_directories(${DEPS_INSTALL_DIR}/lib) ``` 或使用现代CMake目标模式: ```cmake target_include_directories(myapp PRIVATE ${DEPS_INSTALL_DIR}/include) target_link_directories(myapp PRIVATE ${DEPS_INSTALL_DIR}/lib) ``` 2. **链接最终目标** ```cmake add_executable(myapp main.cpp) target_link_libraries(myapp PRIVATE ${DEPS_INSTALL_DIR}/lib/libA.a ${DEPS_INSTALL_DIR}/lib/libB.so ) ``` --- ### 三、高级配置技巧 1. **交叉编译支持** ```cmake ExternalProject_Add(... CONFIGURE_COMMAND CC=${CMAKE_C_COMPILER} CXX=${CMAKE_CXX_COMPILER} <SOURCE_DIR>/configure ) ``` 继承主项目的编译工具链 2. **条件编译选项** ```cmake if(UNIX) list(APPEND EXTRA_OPTS "--enable-posix-threads") endif() ``` 3. **动态库位置无关代码** ```cmake add_compile_options(-fPIC) # GCC set_property(TARGET lib1 PROPERTY POSITION_INDEPENDENT_CODE ON)[^3] ``` --- ### 四、验证配置 使用CMake变量检查路径: ```cmake find_library(LIBB NAMES libB PATHS ${DEPS_INSTALL_DIR}/lib REQUIRED) find_path(LIBA_INCLUDE liba.h PATHS ${DEPS_INSTALL_DIR}/include REQUIRED) ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一只野生的善逸

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值