CMake 实战指南:从基础语法到静态库/动态库制作与链接
发布时间:2026/11/5 5:29:15 锦皓数字建站

目录1.CMAKE的基本使用1.1 注释1.2 使用set命令在cmake中进行变量的定义1.3 CMAKE 中设置 C 标准1.4 可执行文件的输出路径1.5 文件的查找1.6 头文件的引入2. 制作动态库和静态库2.1 指定库文件的输出路径3. 包含库文件3.1 链接系统动态库4. 总结与经典面试题4.1 小结4.2 经典面试题✅️1.CMAKE的基本使用1.1 注释CMAKE中使用符号#来表示单行注释使用#[[ 需要注释的内容 ]]来表示多行注释。#1.收集文件列表-注释行file(GLOB SRC_LISTS*.cpp)# 添加构建目标add_executable(main ${SRC_LISTS})#3.添加依赖库列表target_link_libraries(main PRIVATE MyMath)#4.设置输出路径set_target_properties(main PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)# 多行注释-注释块 #[[打印库文件相对main的相对路径 说明上面打印的就是获取静态库的输出路径相对 main 所在目录的结果]]1.2 使用set命令在cmake中进行变量的定义❓ 问题为了解决CMakeLists.txt文件中某些变量被反复使用比如某些.cpp或.h源文件需要在不同的地方被反复使用如果每次都在需要的位置编写对应的源文件会很麻烦此时就可以使用set命令使用一个变量来代替这些源文件当在其他地方使用这些源文件时就只需要使用这个 变量来表示即可。# 语法如下 # 注意变量名一般都是大写set(VAR[VAL])#[]中的参数是可选项不需要是可以不用写 # 其中 VAL 可以是字符串、列表、变量、表达式、字符串列表、字符串变量、字符串表达式、列表变量、列表表达式 # 如果不写 VAL则 VAR 的值为空 # 举例如下set(SRC main.cpp add.cpp sub.cpp)set(SRC main.cpp;add.cpp;sub.cpp)# 源文件之间可以使用空格隔开亦可以使用;来隔开add_executable(test ${SRC})# 当使用变量时需要使用 ${变量名}的形式1.3 CMAKE中设置C标准 问题当我们需要在cmake中指定使用的C标准时使用使用cmake中一个宏DCMAKE_CXX_STANDARD来进行设置。# 使用DCMAKE_CXX_STANDARD指定C标准版本 # 方式1在构建文件中指定c标准set(CMAKE_CXX_STANDARD17)set(CMAKE_CXX_STANDARD_REQUIRED ON)# 强制要求使用C17标准 # 方式2在执行cmake命令的时候指定c标准#cmakeCMakeLists.txt文件路径-DCMAKE_CXX_STANDARD171.4 可执行文件的输出路径cmake中可以使用一个宏EXECUTABLE_OUTPUT_PATH和set函数配合使用来设置可执行文件的输出路径。举例如下# 定义一个绝对路径set(APP/home/lj/cmake)# 如果/home/lj/cmake路径不存在cmake会自动生成无需手动创建set(EXECUTABLE_OUTPUT_PATH ${APP}/bin)1.5 文件的查找在cmake中查找文件的函数有两个file命令和aux_source_directory命令.#aux_source_directory# 使用如下命令查找某个路径下的所有源文件 # 参数1表示要搜索的路径 # 参数2表示查找到的文件列表存储在哪个变量中aux_source_directory(dir variable)# 举例如下aux_source_directory(src SRC_LIST)add_executable(TestMyMath ${SRC_LIST})#file# 语法如下#GLOB表示在指定路径下查找所有符合条件的文件并将满足条件的文件列表保存到变量中#GLOB_RECURSE表示在指定路径下递归查找所有符合条件的文件并将满足条件的文件列表保存到变量中包括子目录中的文件#file(GLOB/GLOB_RECURSE 变量名 要搜索的文件路径和文件类型)file(GLOB SRC ${CMAKE_CURRENT_SOURCE_DIR}/my_lib/src/*.cpp) file(GLOB MAIN ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h) # 注意此处的CMAKE_CURRENT_SOURCE_DIR宏表示当前CMakeLists.txt文件所在的目录即项目根目录1.6 头文件的引入在编译大型项目的时候我们往往需要将头文件的路径指定出来使其在编译过程中方便使用这些头文件来完成编译工作可以通过include_directories命令来解决。命令原型如下include_directories(项目中头文件所在的路径)示例[rootlocalhost my_math]# tree my_lib/my_lib/ ├── CMakeLists.txt ├── include │ └── math.h └── src ├── add.cpp └── sub.cpp2directories,4files对应的CMakeLists.txt文件如下cmake_minimum_required(VERSION3.20)project(MyMath LANGUAGES CXX)set(CMAKE_CXX_STANDARD17)set(BIN/home/lj/cmake/build/)set(EXECUTABLE_OUTPUT_PATH ${BIN}/bin)include_directories(${PROJECT_SOURCE_DIR}/my_lib/include)# 引入头文件file(GLOB SRC_LISTS ${PROJECT_SOURCE_DIR}/my_lib/src/*.cpp) add_executable(math ${SRC_LISTS})✅️ 2.制作动态库和静态库在cmake中如果我们需要制作一个静态库需要使用如下命令# 参数1表示需要生成的库的名称 # 参数2STATIC表示生成静态库 # 后续参数表示生成库所需要的所有源文件add_library(库名称 STATIC 源文件1,【源文件列表...】)此处需要注意Linux和Windows中对于库的后缀是不同的对于静态库Linux中使用后缀.a,而对于WIndows中使用后缀.lib;对于动态库Linux使用后缀.soWindows中使用后缀.dll。但是在使用cmake中的add_library命令不需要指定后缀cmake会根据不同的平台添加对应的后缀名。完整示例如下[rootlocalhost static_lib]# tree ../static_lib/../static_lib/ ├── build ├── CMakeLists.txt ├── include │ └── head.h ├── main.cpp └── src ├── add.cpp └── sub.cpp3directories,5files完整CMakeLIsts.txt文件如下cmake_minimum_required(VERSION3.18)project(MATH)include_directories(${PROJECT_SOURCE_DIR}/include)# 指定头文件所在目录file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) add_library(math STATIC ${SRC_LIST}) # 生成静态库完整构建过程如下[rootlocalhost build]# ll总用量0[rootlocalhost build]# cmake ../-- The C compiler identification is GNU9.2.1 -- The CXX compiler identification is GNU9.2.1 -- Detecting C compiler ABI info -- Detecting C compiler ABI info -done-- Checkforworking C compiler: /opt/rh/gcc-toolset-9/root/usr/bin/cc - skipped -- Detecting C compile features -- Detecting C compile features -done-- Detecting CXX compiler ABI info -- Detecting CXX compiler ABI info -done-- Checkforworking CXX compiler: /opt/rh/gcc-toolset-9/root/usr/bin/c - skipped -- Detecting CXX compile features -- Detecting CXX compile features -done-- Configuringdone(0.8s)-- Generatingdone(0.0s)-- Build files have been written to: /home/lijing/cmake/static_lib/build[rootlocalhost build]# make[33%]Building CXX object CMakeFiles/math.dir/src/add.cpp.o[66%]Building CXX object CMakeFiles/math.dir/src/sub.cpp.o[100%]Linking CXX static library libmath.a[100%]Built target math[rootlocalhost build]# tree ../../static_lib/../../static_lib/ ├── build │ ├── CMakeCache.txt │ ├── CMakeFiles │ │ ├──3.28.3 │ │ │ ├── CMakeCCompiler.cmake │ │ │ ├── CMakeCXXCompiler.cmake │ │ │ ├── CMakeDetermineCompilerABI_C.bin │ │ │ ├── CMakeDetermineCompilerABI_CXX.bin │ │ │ ├── CMakeSystem.cmake │ │ │ ├── CompilerIdC │ │ │ │ ├── a.out │ │ │ │ ├── CMakeCCompilerId.c │ │ │ │ └── tmp │ │ │ └── CompilerIdCXX │ │ │ ├── a.out │ │ │ ├── CMakeCXXCompilerId.cpp │ │ │ └── tmp │ │ ├── cmake.check_cache │ │ ├── CMakeConfigureLog.yaml │ │ ├── CMakeDirectoryInformation.cmake │ │ ├── CMakeScratch │ │ ├── Makefile2 │ │ ├── Makefile.cmake │ │ ├── math.dir │ │ │ ├── build.make │ │ │ ├── cmake_clean.cmake │ │ │ ├── cmake_clean_target.cmake │ │ │ ├── compiler_depend.make │ │ │ ├── compiler_depend.ts │ │ │ ├── DependInfo.cmake │ │ │ ├── depend.make │ │ │ ├── flags.make │ │ │ ├── link.txt │ │ │ ├── progress.make │ │ │ └── src │ │ │ ├── add.cpp.o │ │ │ ├── add.cpp.o.d │ │ │ ├── sub.cpp.o │ │ │ └── sub.cpp.o.d │ │ ├── pkgRedirects │ │ ├── progress.marks │ │ └── TargetDirectories.txt │ ├── cmake_install.cmake │ ├── libmath.a# Linux环境下生成的静态库 - 其组成是 lib 静态库名称math .a│ └── Makefile ├── CMakeLists.txt ├── include │ └── head.h ├── main.cpp └── src ├── add.cpp └── sub.cpp13directories,39files制作动态库同样使用命令add_library只需要将第二个参数STATIC改为SHARED即可最终会生成libmath.so文件。cmake_minimum_required(VERSION3.18)project(MATH)include_directories(${PROJECT_SOURCE_DIR}/include)# 指定头文件所在目录file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) add_library(math SHARED ${SRC_LIST}) # 生成动态库完整构建过程[rootlocalhost cmake]# cd shared_lib/[rootlocalhost shared_lib]# cd build/[rootlocalhost build]# cmake ../-- The C compiler identification is GNU9.2.1 -- The CXX compiler identification is GNU9.2.1 -- Detecting C compiler ABI info -- Detecting C compiler ABI info -done-- Checkforworking C compiler: /opt/rh/gcc-toolset-9/root/usr/bin/cc - skipped -- Detecting C compile features -- Detecting C compile features -done-- Detecting CXX compiler ABI info -- Detecting CXX compiler ABI info -done-- Checkforworking CXX compiler: /opt/rh/gcc-toolset-9/root/usr/bin/c - skipped -- Detecting CXX compile features -- Detecting CXX compile features -done-- Configuringdone(0.6s)-- Generatingdone(0.0s)-- Build files have been written to: /home/lijing/cmake/shared_lib/build[rootlocalhost build]# make[33%]Building CXX object CMakeFiles/math.dir/src/add.cpp.o[66%]Building CXX object CMakeFiles/math.dir/src/sub.cpp.o[100%]Linking CXX shared library libmath.so[100%]Built target math[rootlocalhost build]# tree ../../shared_lib/../../shared_lib/ ├── build │ ├── CMakeCache.txt │ ├── CMakeFiles │ │ ├──3.28.3 │ │ │ ├── CMakeCCompiler.cmake │ │ │ ├── CMakeCXXCompiler.cmake │ │ │ ├── CMakeDetermineCompilerABI_C.bin │ │ │ ├── CMakeDetermineCompilerABI_CXX.bin │ │ │ ├── CMakeSystem.cmake │ │ │ ├── CompilerIdC │ │ │ │ ├── a.out │ │ │ │ ├── CMakeCCompilerId.c │ │ │ │ └── tmp │ │ │ └── CompilerIdCXX │ │ │ ├── a.out │ │ │ ├── CMakeCXXCompilerId.cpp │ │ │ └── tmp │ │ ├── cmake.check_cache │ │ ├── CMakeConfigureLog.yaml │ │ ├── CMakeDirectoryInformation.cmake │ │ ├── CMakeScratch │ │ ├── Makefile2 │ │ ├── Makefile.cmake │ │ ├── math.dir │ │ │ ├── build.make │ │ │ ├── cmake_clean.cmake │ │ │ ├── compiler_depend.make │ │ │ ├── compiler_depend.ts │ │ │ ├── DependInfo.cmake │ │ │ ├── depend.make │ │ │ ├── flags.make │ │ │ ├── link.txt │ │ │ ├── progress.make │ │ │ └── src │ │ │ ├── add.cpp.o │ │ │ ├── add.cpp.o.d │ │ │ ├── sub.cpp.o │ │ │ └── sub.cpp.o.d │ │ ├── pkgRedirects │ │ ├── progress.marks │ │ └── TargetDirectories.txt │ ├── cmake_install.cmake │ ├── libmath.so#此时生成的是Linux环境下的动态库文件 - lib 动态库名称 .so│ └── Makefile ├── CMakeLists.txt ├── include │ └── head.h ├── main.cpp └── src ├── add.cpp └── sub.cpp13directories,38files2.1 指定库文件的输出路径Linux环境下对于使用cmake制作的动态库默认是可以直接执行的故可以按照可执行文件的输出路径执行方式来进行库文件的路径指定。具体CMakeLists.txt文件片段如下set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)# 设置动态库的生成路径除了上面的方式还有一种方式可以同时适配动态库和静态库这也是本人推荐和经常使用的一种方式。可以使用宏LIBRARY_OUTPUT_PATH.set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)# 设置库文件输出路径add_library(math SHARED ${SRC})add_library(math STATIC ${SRC})# 上面的动态库和静态库文件都放到同一个路径下完整构建如下[rootlocalhost build]# cmake ../-- Configuringdone(0.0s)-- Generatingdone(0.0s)-- Build files have been written to: /home/lijing/cmake/static_lib/build[rootlocalhost build]# make[16%]Building CXX object CMakeFiles/staticMath.dir/src/add.cpp.o[33%]Building CXX object CMakeFiles/staticMath.dir/src/sub.cpp.o[50%]Linking CXX static library /home/lijing/cmake/static_lib/lib/libstaticMath.a[50%]Built target staticMath[66%]Building CXX object CMakeFiles/sharedMath.dir/src/add.cpp.o[83%]Building CXX object CMakeFiles/sharedMath.dir/src/sub.cpp.o[100%]Linking CXX shared library /home/lijing/cmake/static_lib/lib/libsharedMath.so[100%]Built target sharedMath[rootlocalhost build]# ll总用量28-rw-r--r--.1root root1438811月412:55 CMakeCache.txt drwxr-xr-x.6root root26811月412:56 CMakeFiles -rw-r--r--.1root root163411月412:56 cmake_install.cmake -rw-r--r--.1root root707811月412:56 Makefile[rootlocalhost build]# cd ..[rootlocalhost static_lib]# ll总用量8drwxrwxr-x.3lijing lijing8911月412:56 build -rw-rw-r--.1lijing lijing36311月412:56 CMakeLists.txt drwxrwxr-x.2lijing lijing2011月412:22 include drwxr-xr-x.2root root5311月412:56 lib -rw-rw-r--.1lijing lijing14811月412:29 main.cpp drwxrwxr-x.2lijing lijing3611月412:22 src[rootlocalhost static_lib]# tree lib/lib/ ├── libsharedMath.so └── libstaticMath.a0directories,2files✅️ 3.包含库文件 在一个大型项目中我们往往需要使用一些我们自己构建的或者额外的动态库或者静态库。那么我们如何在我们自己的项目中引入想要的库呢此时我们就需要使用连接库的命令 link_libraries。语法如下link_libraries(静态库 【静态库列表】)# 可以导入多个库文件 注意1.上述命令的参数可以是libxxx.a这种全名也可以是去掉lib和后缀.a的名称xxx.2.如果需要连接的库不是系统提供的是第三方库就需要使用link_directories来指定第三三方库所在的位置具体语法如link_directories(第三方库所在位置路径)。示例如下cmake_minimum_required(VERSION3.18)project(MATH)include_directories(${PROJECT_SOURCE_DIR}/include)# 指定头文件所在目录file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) link_directories(${PROJECT_SOURCE_DIR}/lib) # 包含静态库路径 link_libraries(staticMath) # 连接静态库 add_executable(testStatic ${SRC})上述示例演示了如何在我们的项目中链接静态库如果需要链接动态库我们需要使用如下命令targt_link_libraries(targetPRIVATE|PUBLIC|INTERFACE库【...】)该命令用于指定一个目标在编译时需要链接哪些库可以通过这个命令来指定库的名称路径以及链接顺序。 说明1.参数1target表示谁需要链接库可以是一个源文件动态库、静态库 或者可执行文件。2.参数2动态库的访问权限默认是PUBLIC.如果各个库之间没有依赖关系使用默认权限即可。3.动态库的链接具有传递性即a链接了b和c,x链接了a,x也间接的链接了b和c.关于目标链接库的权限解释-PUBLIC:在public后面的库会被链接到前面的target中并且里面的符号也会被导出。-PRIVATE:当前目标需要这个库同时依赖当前目标的其他目标也需要这个库。-INTERFACE:当前目标编译时不需要这个库但依赖当前目标的其他目标在编译时必须链接这个库。通常用于纯头文件库Header-only libraries场景比喻借工具假设 A 是你正在写的库B 是用了 A 的上层项目C 是 A 依赖的底层库。三种权限对 B 的影响如下表关键字关系描述对 B使用者的影响PRIVATEA 用了 C但 A 把 C 封装得很好。B 不需要知道 C 的存在B 直接链接 A 就能跑。INTERFACEA 只是个头文件壳子核心功能全靠 C。A 自己编译不需要 C但 B 链接 A 时必须同时链接 C。PUBLICA 用了 C且 A 的头文件里暴露了 C 的类型。B 链接 A 时必须同时链接 C否则 B 编译会报错找不到 C 的定义。3.1 链接系统动态库 动态库和静态库的区别1.静态库会在生成可执行程序的链接阶段被打包到可执行程序中后续可执行程序启动时静态库就会被加载到内存中。2.动态库在生成可执行程序的连接阶段不会被打包到可执行程序中只有当可执行程序启动并且调用动态库时静态库才会被加载到内存。# 目标在链接系统动态库的时候写法如下target_link_libraries(test pthread)链接第三方动态库只需要使用命令include_directories(库路径)来指定第三方库的路径即可链接方式和连击系统库的方式相同如下include_directories(${PROJECT_SOURCE_DIR}/lib)target_link_libraries(test pthread sharedLib)说明对于上述的两个链接命令其实都可以用于链接动态库和静态库只是使用方式和场景不同而已但是这里推荐使用target_link_libraries命令因为这个命令更加精确使用起✅️ 4.总结与经典面试题4.1 小结本文从CMake的基础用法出发依次梳理了注释、变量定义set、C 标准设置CMAKE_CXX_STANDARD、可执行文件输出路径EXECUTABLE_OUTPUT_PATH、文件查找file/aux_source_directory以及头文件引入include_directories等常用命令随后讲解了如何用add_library制作静态库STATIC与动态库SHARED并通过LIBRARY_OUTPUT_PATH统一库文件输出路径最后介绍了link_libraries与target_link_libraries两种链接方式重点辨析了PRIVATE/INTERFACE/PUBLIC三种链接权限的传递性差异。一句话概括CMake的核心思路是「先描述目标再描述依赖」——用命令把源文件、头文件、库、输出路径等信息声明清楚剩下的交给CMake去生成对应平台的构建文件。4.2 经典面试题Q1CMake中静态库和动态库有什么区别分别用什么命令生成静态库在链接阶段会被完整打包进可执行程序程序体积较大但部署时无需额外携带库文件动态库在链接阶段只记录引用运行时才加载多个程序可共享同一份库节省内存但部署时需要保证库文件可被找到。生成命令均为add_library第二个参数为STATIC生成静态库为SHARED生成动态库例如add_library(math STATIC ${SRC})。后缀差异Linux下静态库为.a、动态库为.soWindows下静态库为.lib、动态库为.dll。使用CMake时无需手动写后缀它会按平台自动补全。Q2target_link_libraries中PRIVATE、INTERFACE、PUBLIC有什么区别假设 A 是当前库B 是依赖 A 的上层项目C 是 A 依赖的底层库PRIVATEA 内部使用 C但不对外暴露。B 只需链接 A无需关心 C。INTERFACEA 自身编译不需要 C但使用 A 的目标必须链接 C典型场景是纯头文件库。PUBLICA 既自己使用 C又把 C 暴露给使用者B 链接 A 时必须同时链接 C。记忆口诀PRIVATE自己用INTERFACE别人用PUBLIC都用到。Q3link_libraries和target_link_libraries有什么区别推荐用哪个link_libraries是「全局」设置作用于其后定义的所有目标作用范围大、不够精确且不便于表达依赖权限。target_link_libraries针对具体目标设置可以精确指定链接的库、路径、顺序以及PRIVATE/INTERFACE/PUBLIC权限支持依赖传递。实际项目中推荐使用target_link_libraries可读性和可维护性更好。Q4如何在CMake中指定 C 标准有两种常用方式在CMakeLists.txt中设置set(CMAKE_CXX_STANDARD 17)配合set(CMAKE_CXX_STANDARD_REQUIRED ON)强制要求该标准。在执行命令时传入cmake CMakeLists.txt 路径 -DCMAKE_CXX_STANDARD17。Q5file(GLOB ...)和aux_source_directory都能查找源文件如何选择aux_source_directory(dir var)只能收集指定目录下的源文件不递归子目录写法简单。file(GLOB var path/*.cpp)支持通配符匹配GLOB_RECURSE还能递归子目录灵活性更高。需要注意的是GLOB在新增文件后不会自动触发重新配置通常需要重新执行cmake对文件列表变化频繁的项目显式列出源文件反而更稳妥。Q6EXECUTABLE_OUTPUT_PATH和LIBRARY_OUTPUT_PATH有什么区别EXECUTABLE_OUTPUT_PATH用于设置可执行文件的输出目录。LIBRARY_OUTPUT_PATH用于设置库文件动态库和静态库的输出目录可同时适配两种库。两者都通过set配合使用路径不存在时CMake会自动创建无需手动mkdir。来更加方便。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。