写在前面:主要参考cmake practice(cmake实践)这个经典pdf教程,这里主要是做了一个汇总整理并添加一些实践过程中自己碰到的细节
- cmakelists的功能,就是自动生成makefile,如果有cmakelists,则编译过程就是如下:
这是典型out_source方式,也就是外部搜索,然后生成文件到当前指定文件夹下,避免大量文件干扰代码目录。
而在clion中则不需要考虑out_source,他会自动把根目录下cmakelists生成的makefile等文件放入一个cmake-build-debug文件夹中.
注意:cmakelists的指令与大小写无关,但参数和变量名都是大小写相关的。有人推荐都大写,不过我感觉命令小写,变量和参数大写更舒服。
mkdir build
cd build
cmake .. //或者cmake.这里两个点表示在上层文件夹下寻找cmakelists生成makefile和相关文件
make //执行makefile
make install //(optional)安装
make clean // 清理工程
-
cmake的调试很简单:写好以后,把相应的输出通过message()打印出来进行验证,然后在clion的cmake选项卡里边就能够看到输出了。
注意:不是在terminal终端看到的,也不是在message选项卡看到。而是在cmake选项卡中有显示。 -
在一个大项目下面,可以有独立的小项目,每个独立小项目都对应一个独立cmakelists:
- 创建小项目文件夹,里边生成小项目cmakelists, cpp, .h文件
- 右键点击任何一个cmakelists,都可以看到2个选项之一:如果已经是当前cmakelists,会看到reload cmakelists选项。
而如果不是当前cmakelists,则会看到load cmakelists选项,点击以后就会切换到该cmakelists对应的子项目中,并且能独立运行。
- project()创建项目: 采用project(name)指令
指令之后会得到name_BINARY_DIR(指向编译路径,比如build文件夹)和name_SOURCE_DIR(指向项目路径)
同时得到PROJECT_BINARY_DIR和PROJECT_SOURCE_DIR两个变量,跟前面两个变量相同,所以为了以后改项目名不用改cmakelists,尽可能用后边这两个变量。
- set()设置变量和引用变量,以及常用系统变量
set(tensorRT_path "/usr/tensorRT/") // 这是创建变量tensorRT_path
file(GLOB Sources *.cpp) // 这也是创建变量Sources
${tensorRT_path} // 这是引用变量
if tensorRT_path // 注意只有在if后边引用变量时不需要${}
// 常用系统变量
${PROJECT_SOURCE_DIR} // 指向项目路径,也就是src路径,如果没有就是项目路径
${PROJECT_BINSARY_DIR} // 指向编译路径,也就是build文件夹,如果没有则默认跟项目路径相同
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINSARY_DIR}/bin) // 修改常用系统变量路径EXECUTABLE_OUTPUT_PATH,也就是可执行文件输出路径
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib) // 修改常用系统变量路径LIBRARY_OUTPUT_PATH,也就是库文件输出路径
set(CMAKE_INSTALL_PREFFIX=/usr) // 修改cmake安装的默认前缀路径,默认是/usr/local,也就是默认是针对本用户/usr/local,可以改为针对所有用户/usr
- add_difinitions()用于增加编译选项,本质上就是cmake指令的-D选项
- 如果要增加c++11的相关特性,比如nullptr,则增加该句,放在add_executable()前面:
add_definitions(-std=c++11)
- 如果要单独一个src子文件夹放置.cpp和.h文件,则需要在根路径的cmakelists增加这句,同时src文件夹下增加额外cmakelists文件。
参考Teris源码。此时,主目录下的cmakelists主要用来包含src文件夹,并没有生成可执行文件,而src里边的cmakelists则会有生成可执行文件。
add_subdirectory(src)
- include_directories()添加头文件路径,则需要增加这句: 注意如果不确定路径地址可以在终端用locate xxx.h把头文件所在文件夹搜索出来。该句功能相当于g++中-I的作用。
- 有两个语句都可以实现添加头文件路径,一个是include_directories()这是需要放在生成可执行文件之前。
另一个是target_include_directories()这是需要放在在生成可执行文件之后。
include_directories(./common)
target_include_directories(main ./common)
- link_libraries()添加.so动态库文件路径,则需要增加这句:
- 实测发现默认的.so路径只有一个/usr/lib,也就是库文件如果在这个路径下则不需要欧手动链接。
但如果不是在这个路径下,或者是在这个路径下的文件夹中,则需要手动链接,即添加link_libraries(“lib_path.so”) - 有两个语句都可以实现链接库文件,一个是link_libraries(path)这是在生成可执行文件之前就指定库文件路径(必须放在add_executable()的前边)。
另一个语句也可以实现链接库文件,即用target_link_libraries(main path)这是在生成target可执行文件之后链接(必须放在add_executable()的后边)
两者的区别就是在定义链接文件时实在可执行文件生成之前还是之后,都可以实现功能。
- 另外一种方法实现链接库文件:就是把该库文件所在路径加入到CMAKE_INCLUDE_PATH中然后用find_path()指令。
但注意:加入到CMAKE_INCLUDE_PATH并不代表他会把路径提供给编译器,还是需要自己用find_path找到该头文件。
这种方式的优点在于,只要加入到路径后,所有find_指令都可以使用
export CMAKE_INCLUDE_PATH=/usr/local/include/test
find_path(myHeader hello.h) // 把头文件找到,并赋值给变量myHeader
include_directories(${myHeader}) // 把该路径包含进来
- 第三种方法实现链接库文件:就是利用find_package()来查找cmake支持的模块,或者自定义的模块来获得对应头文件和库文件路径。
这种方法参考find_package()的使用。
- find_library()和find_path()查找库文件和头文件
- 查找库文件需提供库文件名,用find_library(var name HINTS path PATH_SUFFIXES suff1 suff2), 即搜索名称为name的库文件(实际名称是libname.so),找到后存入var中,
并可以带多个关键参数,其中HINTS关键参数代表搜索路径,PATH_SUFFIXES代表路径后缀
find_library(_NVINFER_LIB nvinfer HINTS ${TRT_LIB} PATH_SUFFIXES lib lib64)
- 查找头文件需提供头文件名,用find_path()
- cmake高级指令find_package: 是另外一种查找头文件和库文件的方法,是针对第三方库的常用方法(比如CUDA/opencv)。
- 如果要使用find_package()查找第三方库的头文件和链接库文件路径:
注意:采用find_package()命令cmake的模块查找顺序是:先在变量${CMAKE_MODULE_PATH}查找,然后在/usr/shared/cmake/Modules/里边查找。
find_package(CUDA REQUIRED) # 查找某个第三方库的cmake module,比如CUDA代表的就是FindCUDA.cmake这个module
find_package(OpenCV REQUIRED) # 多个库分别查找, 然后统一加到include_directories和link_libraries即可
target_include_directories(tensorrt PUBLIC ${CUDA_INCLUDE_DIRS} ${TENSORRT_INCLUDE_DIR})
target_link_libraries(tensorrt ${CUDA_LIBRARIES} ${TENSORRT_LIBRARY} ${CUDA_CUBLAS_LIBRARIES} ${CUDA_cudart_static_LIBRARY} ${OpenCV_LIBS})
- 如果要查看cmake所自带支持的所有module和内容,就在如下路径中:
/usr/shared/cmake/Modules/ # 这个路径下所有FindXXXX.cmake都是cmake module文件(大部分是以Find开头,也有不是这么开头的)
- 如果要为自己写的库定义一个cmake module,则本质上就是先自己查找好头文件、库文件路径,然后欧放到某几个变量中。
并且cmake统一规定这几个变量的写法:name_FOUND, name_INCLUDE_DIR, name_LIBRARY.
从而只要知道某些库的module关键字(一般大写),然后运行find_package(关键字),然后就能得到两个变量:关键字_INCLUDE_DIR, 关键字_LIBRARY,从而就可以用include_directories(), link_libraries()进行设置了。
# 如下是一个名为FindTEST.cmake的module的写法: 关键字是TEST
# 先找到自己安装的头文件(test.h): 为了避免安装时prefix路径设置不同,这里同时在两个默认存放头文件的路径寻找,一个是所有用户路径,一个是登录用户路径。
find_path(TEST_INCLUDE_DIR test.h /usr/include/mytest # 表示寻找test.h文件,找到则把路径赋值给变量TEST_INCLUDE_DIR
/usr/local/include/mytest)
# 再找到自己安装的动态库(libmytest.so): 为了避免安装时prefix路径设置不同,这里也同时在两个路径搜索
find_library(TEST_LIBRARY NAMES mytest PATH /usr/lib # NAMES是关键字,表示寻找名称为mytest(实际名称libmytest.so)的头文件,PATH也是关键字,表示在接下来2个路径中寻找
usr/local/lib)
if(TEST_INCLUDE_DIR AND TEST_LIBRARY) # 如果找到则设置标志
set(TEST_FOUND TRUE)
endif(TEST_INCLUDE_DIR AND TEST_LIBRARY)
- 可见:如果已知头文件库文件路径,可直接用target_include_directories()和target_link_libraries()进行添加。
而如果不知道头文件库文件路径,可以有另外2个方法,一种采用find_package()相当于变量赋值然后用target_include_directories()和target_link_libraries()设置即可。
另一种采用find_library()和
- message()输出某个信息:
- 其中输出类型可选择:STATUS(输出前缀为-的信息),SEND_ERROR(产生错误),FATAL_ERROR(终止cmake过程)
message(STATUS "messages")
- file()对文件和文件夹的操作:比如搜索文件、打开文件、写入文件
- 如果要自动搜索所有支持文件,则可以考虑自动搜索命令
file(GLOB Sources *.cpp)
file(GLOB Includes *.h)
add_executable(Cars ${Sources} ${Includes})
- 如果要创建文件夹路径:
file(MAKE_DIRECTORY ${xxx}) # 创建某个路径文件夹
- add_library()如果要生成动态或者静态链接库,则采用add_library
- 可以选择生成SHARED动态库, STATIC静态库
- 注意:库名称不需要写前缀lib,系统会自动在给出的库名称前面加lib.
// 创建动态库
add_library(algorithms SHARED ${src_path}) // 创建动态链接库:库名称libalgorithms, SHARED表示为.so动态链接库,src_path是.cpp文件所在路径
// 创建静态库
set_target_properties(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1) // 设置不清除同名动态库hello.so
set_target_properties(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1) // 设置不清除同名静态库hello.a
set_target_properties(hello_static PROPERTIES OUTPUT_NAME "hello") // 设置静态库的输出名称为hello,从而即使跟动态库重名也能实现
- install()如果要安装生成的头文件和库文件:
- 可以生成的目标文件包括3种:RUNTIME是可执行文件, LIBRARY是动态库, ARCHIVE是静态库
- 可以指定安装路径,采用关键字DESTINATION接路径,注意如果是/开头的路径就是绝对路径,如果不是斜杠开头则默认基于CMAKE_INSTALL_PREFIX,也就是{CMAKE_INSTALL_PREFIX}/相对路径
- 注意在clion中如果要安装,还需要手动进入项目cmake-build-debug文件夹,执行sudo make install执行,否则clion不会自动帮你安装。
// 安装头文件
install(FILES hello.h DESTINATION include/hello) //安装头文件
// 安装bin/lib库文件或者头文件
${CMAKE_INSTALL_PREFIX} // 这个路径可以在cmake命令中通过-D CMAKE_INSTALL_PREFIX=/usr来设置到绝对路径
install(TARGETS myrun mylib mystaticlib // 表示有3个目标文件,分别对应下面3行
RUNTIME DESTINATION bin // 目标文件类型RUNTIME也就是可执行文件,存放
LIBRARY DESTINATION lib
ARCHIVE DESTINATION libstatic) // 安装头文件和库文件
install(TARGETS test test_static // 同时安装动态库test.so和静态库test_static.a
LIBRARY DESTINATION lib // 动态库安装路径是相对路径/lib
ARCHIVE DESTINATION lib) //
// 安装sh头文件:
install(PROGRAMS runhello.sh DESTINATION bin)// 安装sh文件