cmake使用

cmake

  1. #include<stdio.h>#include<cstdio>的区别

    //1
    #include <stdio.h>
    int main() {
        printf("Hello, world!\n");
        return 0;
    }
    
    //2
    #include <cstdio>
    int main() {
        //还提供了前缀的写法,加不加std::都行
        std::printf("Hello, world!\n");
        return 0;
    }
    
  2. 反汇编命令objdump,如:

    objdump -D a.out | less
    
  3. cmake和make对比

    make:

    • make 在 Unix 类系统上是通用的,但在 Windows 则不然。
    • 需要准确地指明每个项目之间的依赖关系,有头文件时特别头疼。
    • make 的语法非常简单,不像 shell 或 python 可以做很多判断等。
    • 不同的编译器有不同的 flag 规则,为 g++ 准备的参数可能对 MSVC 不适用。

    cmake:

    • 只需要写一份 CMakeLists.txt,他就能够在调用时生成当前系统所支持的构建系统。

    • CMake 可以自动检测源文件和头文件之间的依赖关系,导出到 Makefile 里。

    • CMake 具有相对高级的语法,内置的函数能够处理 configure,install 等常见需求。

    • CMake 可以自动检测当前的编译器,需要添加哪些 flag。比如 OpenMP,只需要在 CMakeLists.txt 中指明 target_link_libraries(a.out OpenMP::OpenMP_CXX) 即可。

  4. cmake的命令行调用

    # 读取当前目录的 CMakeLists.txt,并在 build 文件夹下生成 build/Makefile:
    cmake -B build
    
    # 让 make 读取 build/Makefile,并开始构建 a.out:
    make -C build
    # 以下命令和上一个等价,但更跨平台:
    cmake --build build
    
    # 执行生成的 a.out:
    build/a.out
    
  5. cmake中的静态库和动态库

    add_library(test STATIC source1.cpp source2.cpp)  # 生成静态库 libtest.a
    add_library(test SHARED source1.cpp source2.cpp)  # 生成动态库 libtest.so
    
    
    #创建库以后,要在某个可执行文件中使用该库,只需要:
    target_link_libraries(myexec PUBLIC test)  # 为myexec链接刚刚制作的库 libtest.a
    
  6. cmake中的子模块

    复杂的工程中,我们需要划分子模块,通常一个库一个目录,比如:

    hellolib
      -CMakeLists.txt
      -hello.cpp
      -hello.h
    CmakeLists.txt
    main.cpp
    //这里我们把 hellolib 库的东西移到 hellolib文件夹下了,里面的 CMakeLists.txt 定义了 hellolib 的生成规则。
    

    要在项目根目录使用他,可以用 CMake 的 add_subdirectory 添加子目录,子目录也包含一个CMakeLists.txt,其中定义的库在 add_subdirectory 之后就可以在外面使用。

    # CmakeLists.txt
    cmake_minimum_required(VERSION 3.12)
    project(hellocmake LANGUAGES CXX)
    
    add_subdirectory(hellolib)
    
    add_executable(a.out main.cpp)
    target_link_libraries(a.out PUBLIC hellolib)
    
    
    # hellolib/CmakeLists.txt
    # 子目录的 CMakeLists.txt 里路径名(比如 hello.cpp)都是相对路径,这也是很方便的一点。
    add_library(hellolib STATIC hello.cpp)
    target_include_directories(hellolib PUBLIC .)  # 这样可以将该目录加入到头文件搜索路径中
    # 如果不希望让引用 hellolib 的可执行文件自动添加这个路径,把 PUBLIC 改成 PRIVATE 即可。这就是他们的用途:决定一个属性要不要在被 link 的时候传播。
    
  7. 目标的一些其他选项

    对于目标:

    target_include_directories(myapp PUBLIC /usr/include/eigen3)  # 添加头文件搜索目录
    target_link_libraries(myapp PUBLIC hellolib)            # 添加要链接的库
    target_add_definitions(myapp PUBLIC MY_MACRO=1)         # 添加一个宏定义
    target_add_definitions(myapp PUBLIC -DMY_MACRO=1)       # 与 MY_MACRO=1 等价
    target_compile_options(myapp PUBLIC -fopenmp)           # 添加编译器命令行选项
    target_sources(myapp PUBLIC hello.cpp other.cpp)        # 添加要编译的源文件
    

    对于全局(把选项加到所有接下来的目标去):

    include_directories(/opt/cuda/include)     # 添加头文件搜索目录
    link_directories(/opt/cuda)                # 添加库文件的搜索路径
    add_definitions(MY_MACRO=1)                # 添加一个宏定义
    add_compile_options(-fopenmp)              # 添加编译器命令行选项
    
    
  8. 第三方库,作为纯头文件引入

    有时候我们不满足于 C++ 标准库的功能,难免会用到一些第三方库。
    最友好的一类库莫过于纯头文件库了,这里是一些好用的 header-only 库:

    • nothings/stb - 大名鼎鼎的 stb_image 系列,涵盖图像,声音,字体等,只需单头文件!
    • Neargye/magic_enum - 枚举类型的反射,如枚举转字符串等(实现方式很巧妙)
    • g-truc/glm - 模仿 GLSL 语法的数学矢量/矩阵库(附带一些常用函数,随机数生成等)
    • Tencent/rapidjson - 单纯的 JSON 库,甚至没依赖 STL(可定制性高,工程美学经典)
    • ericniebler/range-v3 - C++20 ranges 库就是受到他启发(完全是头文件组成)
    • fmtlib/fmt - 格式化库,提供 std::format 的替代品(需要 -DFMT_HEADER_ONLY)
    • gabime/spdlog - 能适配控制台,安卓等多后端的日志库(和 fmt 冲突!)

    只需要把他们的 include 目录或头文件下载下来,然后 include_directories(spdlog/include) 即可。
    缺点:函数直接实现在头文件里,没有提前编译,从而需要重复编译同样内容,编译时间长。

    如使用glm库:

    cmake_minimum_required(VERSION 3.12)
    project(hellocmake LANGUAGES CXX)
    
    add_executable(a.out main.cpp)
    target_include_directories(a.out PUBLIC glm/include)
    
    //main.cpp
    #include <glm/vec3.hpp>
    #include <iostream>
    
    inline std::ostream &operator<<(std::ostream &os, glm::vec3 const &v) {
        return os << v.x << ' ' << v.y << ' ' << v.z;
    }
    
    int main() {
        glm::vec3 v(1, 2, 3);
        v += 1;
        std::cout << v << std::endl;
        return 0;
    }
    
  9. 第三方库,作为子模块引入

    第二友好的方式则是作为 CMake 子模块引入,也就是通过 add_subdirectory。
    方法就是把那个项目(以fmt为例)的源码放到你工程的根目录。
    这些库能够很好地支持作为子模块引入:

    • fmtlib/fmt - 格式化库,提供 std::format 的替代品
    • gabime/spdlog - 能适配控制台,安卓等多后端的日志库
    • ericniebler/range-v3 - C++20 ranges 库就是受到他启发
    • g-truc/glm - 模仿 GLSL 语法的数学矢量/矩阵库
    • abseil/abseil-cpp - 旨在补充标准库没有的常用功能
    • bombela/backward-cpp - 实现了 C++ 的堆栈回溯便于调试
    • google/googletest - 谷歌单元测试框架
    • google/benchmark - 谷歌性能评估框架
    • glfw/glfw - OpenGL 窗口和上下文管理
    • libigl/libigl - 各种图形学算法大合集

    如使用fmt库:

    cmake_minimum_required(VERSION 3.12)
    project(hellocmake LANGUAGES CXX)
    
    add_subdirectory(fmt)
    
    add_executable(a.out main.cpp)
    target_link_libraries(a.out PUBLIC fmt)
    
    //main.cpp
    #include <fmt/core.h>
    #include <iostream>
    
    int main() {
        std::string msg = fmt::format("The answer is {}.\n", 42);
        std::cout << msg << std::endl;
        return 0;
    }
    
  10. 引用系统中预安装的第三方库

    可以通过 find_package 命令寻找系统中的包/库:

    find_package(fmt REQUIRED)
    target_link_libraries(myexec PUBLIC fmt::fmt)
    

    为什么是 fmt::fmt 而不是简单的 fmt?

    现代 CMake 认为一个包 (package) 可以提供多个库,又称组件 (components),比如 TBB 这个包,就包含了 tbb, tbbmalloc, tbbmalloc_proxy 这三个组件。

    因此为避免冲突,每个包都享有一个独立的名字空间,以 :: 的分割(和 C++ 还挺像的)。

    你可以指定要用哪几个组件:

    find_package(TBB REQUIRED COMPONENTS tbb tbbmalloc REQUIRED)
    target_link_libraries(myexec PUBLIC TBB::tbb TBB::tbbmalloc)
    

    常用package列表:

    • fmt::fmt
    • spdlog::spdlog
    • range-v3::range-v3
    • TBB::tbb
    • OpenVDB::openvdb
    • Boost::iostreams
    • Eigen3::Eigen
    • OpenMP::OpenMP_CXX

    不同的包之间常常有着依赖关系,而包管理器的作者为 find_package 编写的脚本(例如/usr/lib/cmake/TBB/TBBConfig.cmake)能够自动查找所有依赖,并利用刚刚提到的 PUBLIC PRIVATE 正确处理依赖项,比如如果你引用了 OpenVDB::openvdb 那么 TBB::tbb 也会被自动引用。
    其他包的引用格式和文档参考:https://cmake.org/cmake/help/latest/module/FindBLAS.html

  11. 可以使用包管理器来安装第三方库,如apt,pacman,vcpkg。

    Pacman 是 Arch Linux 发行版中的包管理器。它负责在 Arch Linux 系统上下载、安装、升级和删除软件包,以及管理系统的依赖关系。Pacman 是 Arch Linux 的核心组件之一,它使得在 Arch Linux 上管理软件包变得非常方便。
    
    Pacman 允许用户从 Arch Linux 的软件仓库中查找和安装软件包,还可以升级系统中已安装的软件包以保持系统最新。此外,Pacman 还能够处理软件包的依赖关系,确保系统上的所有依赖库都得到满足。
    
    Pacman 的命令行界面非常强大,它允许用户执行各种任务,例如:
    pacman -S package_name:安装一个软件包。
    pacman -Syu:升级系统上的所有软件包。
    pacman -Q:列出已安装的软件包。
    pacman -R package_name:删除一个软件包。
    pacman -Ss search_term:在仓库中搜索软件包。
    
    通过 Pacman,Arch Linux 用户可以轻松地管理系统上的软件包,确保系统安全并保持软件包的最新状态。
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值