文章目录
常用cmake代码
if(USE_CUDA)
add_definitions(-DUSE_CUDA=1)
enable_language(CUDA)
find_package(CUDA 11.4 REQUIRED)
set(src
main.cpp
cuda.cu
)
endif()
概述
Cmake从开始到结束是:
- project File Generation(Configure,Generate); 重点阶段
- Build(可以使用cmake,也可以使用visual studio 等IDE工具);
- Test(可以使用cmake自带的单元测试工具 CTest);
- Package(可以使用 cmake自带的打包工具 CPack);
本文为《cmake practice》的个人笔记。
注意,如果仅仅使用qt编程的话,没必要使用cmake,因为qmake已经足够专业。
cmake的正常使用顺序:
- 找一个合适的位置,mkdir build & cd build (也就是 out-of-source build)
- cmake … (…代表的是父目录,也就是cmakelists所在的路径)
- make
注意,清理工程的命令为: make clean
CMake变量
CMake基本变量
使用set来定义一个基本变量:set (foo ab) # foo = "ab"
通过变量名来获得变量: set(bar $(foo)cd)
CMake环境变量
设置环境变量:
set(ENV {PATH} "$ENV{PATH}:/opt/mydir")
获取环境变量:
$ENV{varName}
CMake缓存变量
cmake -D myVar:type=somevalue ...
-U
选项从缓存中删除变量。
编译最简单的hello world
工程结构:
CMakeLists.txt中的内容:
PROJECT(HELLO)
SET(SRC_LIST main.c)
MESSAGE(STATUS "This is BINARY dir" ${
HELLO_BINARY_DIR})
MESSAGE(STATUS "This is SOURCE dir" ${
HELLO_SOURCE_DIR})
ADD_EXECUTABLE(hello ${
SRC_LIST})
- 注意SRC_LIST 外面需要加上
${}
。这是cmake的语法,变量会需要这样去取值。但是后续需要注意的是,在IF语句中会直接使用变量名。
在进行常规cmake和make操作后,在build文件夹中就会出现目标文件 hello.o,可以直接用于运行。
如果想要看到具体的make信息,可以输入 make VERBOSE=1
。
1. PROJECT(projectname [CXX] [C] [Java])
用于定义工程名称,默认情况下支持所有语言。该指令运行的同时,隐式地定义了两个cmake变量,即<projectname>_BINARY_DIR
以及<projectname>_SOURCE_DIR
。注意,通过外部编译进行工程构建,HELLO_SOURCE_DIR 仍然指代工程路径,即
/backup/cmake/t1而 HELLO_BINARY_DIR 则指代编译路径,即/backup/cmake/t1/build。
2. SET(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])
显式定义变量
3. MESSAGE([SEND_ERROR | STATUS | FATAL_ERROR] "message to display" ...)
向终端输出用户定义的信息:
- SEND_ERROR,产生错误,生成过程被跳过;
- SATUS,输出前缀为—的信息;
- FATAL_ERROR,立即终止所有 cmake 过程
4. ADD_EXECUTABLE(hello ${SRC_LIST})"
生成一个文件名为 hello 的可执行文件。
相关的源文件是 SRC_LIST 中定义的源文件列表。
编译工程化的helloWorld
新建src文件夹,在src文件夹中放入main.c文件,且新建一个cmakelists,内容是:ADD_EXECUTABLE(hello main.c)
。
而后,跳出src文件夹,再新建一个cmakelists,内容是:
PROJECT(HELLO) ADD_SUBDIRECTORY(src bin)
。
我们就可以惊奇的发现,编译后的hello文件竟然在build/bin文件夹下。
而能够实现这种惊奇的命令就是: ADD_SUBDIRECTORY
ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
这个命令用于向当前工程添加存放源代码的子目录,且可以指定中间二进制以及目标二进制存放的位置。注意,这个添加的目录,不一定非得是源工程的子目录,不过通常会是。
注意,这个命令会为子目录中的CMakeLists.txt创建一个新的子作用域,而父作用域中所定义的所有变量对于子作用域来说都是可见的,子作用域可以像调用自己变量一样调用父作用域里面的变量。
所谓EXECLUDE_FROM_ALL
参数的含义是将该目录从编译过程中排除掉,举个例子,工程里的example,往往需要工程构建完后,再进到example目录里面单独编译。
ADD_SUBDIRECTORY(src bin)
做的事情就是,在编译时将src重命名为bin,并将中间结果与目标二进制放到bin里面。
此时的目录结构为:
通过set来定义可执行文件或者库输出的位置
实际上,我们还可以通过SET
指令重新定义 EXECUTABLE_OUTPUT_PATH
和LIBRARY_OUTPUT_PATH
来仅仅指定目标二进制输出的位置,注意,此时不包含编译生成的中间文件。
具体为:
SET(EXECUTABLE_OUTPUT_PATH ${
PROJECT_BINARY_DIR}/bin)
SET(LIBRARY_OUTPUT_PATH ${
PROJECT_BINARY_DIR}/lib)
创建一个静态库、一个可执行文件,并在可执行文件中调用这个库
-
创建静态库
add_library(MyStaticLib STATIC MyStaticLib.hpp MyStaticLib.cpp)
-
创建可执行文件
add_executable(hello-world hello-world.cpp)
-
链接库到可执行文件
target_link_libraries(hello-world MystaicLib)
add_library()
可以选择编译成静态库(STATIC)、动态库(SHARED)或者模块(MODULE),模块在Windows平台上就表现为没有lib的dll文件(也就是说,没有那些原本存储于lib中的链接信息,也不能通过引入头文件来进行操作,只能进行动态加载)
如何使用外部共享库和头文件
其他有用的cmake命令
include_directories
添加头文件目录
include_directories("3rdparty\\opencv-3.4.1\\__build1\\install\\include"
"3rdparty\\opencv-3.4.1\\__build1\\install\\include\\opencv"
"3rdparty\\opencv-3.4.1\\__build1\\install\\include\\opencv2" )
find_package
我们可以通过find_package
引入其他cmake编译安装的库,往往会是第三方库。
find_package
本质上是一个查找依赖包的命令。
理想情况下,一句命令就可以把头文件包含的路径、库路径、库名字还有版本号都获取到。
但是在不理想的情况下,就会需要指定搜索路径,比如说:
find_package(fmt CONFIG REQUIRED
PATHS C:\\vcpkg\\vcpkg\\installed\\x64-windows\\share\\fmt
NO_DEFAULT_PATH))
此外,还需要注意 find_package
和find_library
指令得到的都是库文件的绝对路径。
file(GLOB variable [RELATIVE path] [globbing expressions]...)
file(GLOB variable [RELATIVE path] [globbing expressions]...)
file(GLOB_RECURSE variable [RELATIVE path] [FOLLOW_SYMLINKS] [globbing expressions]...)
GLOB选项将会为所有匹配查询表达式的文件生成一个文件list,并将该list存储进变量variable里。
文件名查询表达式与正则表达式类似,只不过更加简单。
如果为一个表达式指定了RELATIVE标志,返回的结果将会是相对于给定路径的相对路径。
文件名查询表达式的例子有:
*.cxx - 匹配所有扩展名为cxx的文件。
*.vt? - 匹配所有扩展名是vta,…,vtz的文件。
f[3-5].txt - 匹配文件f3.txt, f4.txt, f5.txt。
GLOB_RECURSE选项将会生成一个类似于通常的GLOB选项的list,只是它会寻访所有那些匹配目录的子路径并同时匹配查询表达式的文件。
参考文献:https://www.cnblogs.com/coderfenghc/archive/2012/07/08/2581734.html
target_link_libraries
顾名思义,该指令的作用是将目标文件和库文件进行链接,这里的目标文件,既可以是可执行文件,也可以是其他库文件。
完整的指令语法是:
target_link_libraries(<target> [item1] [item2] [...]
[[debug|optimized|general] <item>] ...)
链接库顺序和gcc是一致的,即被链接的库放到后面,例如:
liba.a依赖libb.a
target_link_libraries(target a b)
windows 下 C++开发中的cmake
通常来说,在windows开发的时候,cmakelist写完了生成sln。
往往还是会选择用visual studio 而不是 visual studio code 来进行开发。
调用第三方库的时候,一定要问清楚自己以下几点:
- 是不是正确include了?
- 是不是正确链接 lib库了?
- dll有没有放到跟exe同级的目录下,或者直接编到系统库上?
- 到底是x86还是x64,有没有搞错这里。
注意事项
- Cmake可以为一套源目录,创建多个构建目录,如Debug、Release、x86以及x64;
cmake -G "Unix Makefiles" ../source
- -G 参数表示生成器
cmake --build . --config Release --target MyApp
- –build 选项指向CMake项目生成的构建目录; 默认会生成最高版本。
- –config 指定构建哪个配置;
- –target 告诉构建工具要构建的目标。
- cmakelists如何同时链接debug库和release库?
文章评论