Cmake
简介
cmake
- 全称Cross Platform Make,起初为了跨平台使用,后被广泛使用
- 特点:开放源代码,具有BSD许可;支持Linux、Mac、windows等不同操作系统;编译语言简单,易用;编程高效(比autotools快40%),可扩展(ros中catkin,ament,colcon都是基于cmake构建)
gcc
- 由GNU开发的编程语言译器,C/C++,Java等语言的开发;
- 当项目简单,可以gcc/g++编译目标和项目;
- 但比较复杂时,只用gcc组织编译架构变得极其困难;
Makefile
- Makefile是有条理的gcc编译命令的文件,利用make工具来执行Makefile文件的编译指令;
- 当程序简单时,可以手写Makefile;
- 当程序复杂时,一般利用CMake和autotools来自动生成Makefile;
CMake语法的主体框架
- 基础命令
command(arg1 arg2...). #运行命令
set(var_name var_value). #定义变量,或给已存在的变量赋值
commond(arg1 ${var_name}). #使用变量
- 工程配置部分
cmake_minimum_required(VERSION num). #CMake最低版本号要求
project(cur_project_name). #项目信息
set(CMAKE_CXX_FLAGS "XXX") #设定编译器版本,如-std=c++11
set(CMAKE_BUILD_TYPE "XXX") #设定编译模式,如Debug/Release
- 依赖执行部分
find_package(std_lib_name VERSION REQUIRED). #引入外部依赖
add_library(<name> [lib_type] source1). #生成库类型(动态,静态)
include_directories(${std_lib_name_INCLUDE_DIRS}). #指定include路径,放在add_executable前面
add_executable(cur_project_name xxx.cpp). #指定生成目标
target_link_libraries(${std_lib_name_LIBRARIES}). #指定libraries路径,放在add_executable后面
- 其他辅助部分
function(function_name arg) #定义一个函数
add_subdirectory(dir) #定义一个子目录,用于编译子目录下有cmakelist文件,编译完成后,在子目录下会生成对应的库
AUX_SOURCE_DIRECTORY(. SRC_LIST) #查找当前目录所有文件,并保存到SRC_LIST变量中
FOREACH(one_dir ${SRC_LIST})
MESSAGE(${one_dir}) #使用message进行打印
ENDFOREACH(one_dir)
- 判断控制部分
if(expression)
COMMAND1(ARGS)
ELSE(expression)
COMMAND2(ARGS)
ENDIF
IF(var)
IF(NOT var)
IF(var1 AND var2)
IF(var1 OR var2)
IF(COMMAND cmd). #当给定的cmd确实是命令并可以调用是为真
IF(EXISTS dir)
IF(EXISTS file)
IF(IS_DIRECTORY dirname)
IF(file1 IS_NEWER_THAN file2). #当file1比file2新,为真
IF(variable MATCHES regex). #符合正则
WHILE(condition)
COMMAND1(ARGS)
ENDWHILE(condition)
CMake的常用指令及变量
- ADD_DEFINITIONS
(1)为源文件的编译添加由-D引入的宏定义。
(2)命令格式:add_definitions(-DFOO -DBAR …)
例如:add_definitions(-DWIN32) - OPTION
(1)提供用户可以选择的选项
(2)命令格式:option( "description [initial value])
例如:
option(USR_MYMATH
"use tutorial......."
ON)
- ADD_CUSTOM_COMMAND/TARGET
(1)COMMAND:为工程添加一条自定义的构建规则
(2)TARGET:用于给指定名称的目标执行指定的命令,该目标没有输出文件,并始终被构建。
例如:
#伪代码:为了说明生成自定义的命令
add_custom_command(TARGET ${CV_ADVANCE_NAME}
PRE_BUILD
COMMAND "伪代码 find_package std_msgs"
)
#为了说明导入生成自定义构建的命令
add_custom_target(CV_ADVANCE ALL
DEPENDS ${CV_ADVANCE_NAME}. #依赖add_custom_command输出的package包
COMMENT "ros package std_msgs"
)
- ADD_DEPENDENCIES
(1)用于解决链接时依赖的问题,
(2)当定义的target依赖的另一个target,确保在源码编译 本target之前,其他的target已经被构建,使用该语句
#添加一个自定义构建的规则
add_custom_target(CV_ADVANCE DEPENDS ${CV_ADVANCE_NAME})
#添加一个可执行文件
add_executable(${PROJECT_NAME} ${SRC_LIST})
#链接一个标准的库文件
target_link_libraries(${PROJECT_NAME} ${std_lib_name_LIB})
#为项目链接一个依赖文件,项目程序依赖CV_ADVANCE
add_dependencies(${PROJECT_NAME} CV_ADVANCE)
- INSTALL
(1)用于定义安装规则,安装的内容可以包括目标二进制、 动态库、静态库以及文件、目录、脚本等。
(2)常用的如OpenCV一般情况下安装到系统目录,即 /usr/lib, /usr/bin和/usr/include
INSTALL(TARGETS myrun mylib mystaticlib
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION libstatic)
- TARGET_INCLUDE_DIRECTORIES
(1)设置include文件查找的目录,具体包含头文件应用形 式,安装位置等
target_include_directories(<target>[SYSTEM][BEFORE]
<INTERFACE|PUBLIC|PRIVATE> [items])
- SET_TARGET_PROPERTIES
(1)设置目标的一些属性来改变它们构建的方式。
set_target_properties(target1 target2 ...
PROPERTIES prop1 values
prop2 value2)
- ENABLE_TESTING/ADD_TEST
(1)[enable_testing] :用来控制Makefile是否构建test目标。
(2)[add_test] : 一般需要和enable_testing()配合使用.
(3)ADD_TEST(testname Exename arg1 arg2 …)
(4)生成makefile后可用make test执行测试
ADD_TEST(mytest ${PROJECT_BINARY_DIR}/bin/main)
ENABLE_TESTING()
实例
预备概念
- 静态库
(1)定义:链接阶段,库中目标文件所含的所有将被程序使用的函数的机器码,被copy到最终的可执行文件中。因此对应的链接方式称为静态链接。
(2)特点:静态库对函数库的链接是放在编译时期完成的;程序在运行时与函数库再无瓜葛,移植方便;运行效率相对快;占用磁盘和内存空间,因为所有相关的目标文件与牵涉到的函数库被链接合成一个可执行文件。 - 动态库
(1)定义:程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入。
(2)特点:可执行文件只包含它需要的函数的引用表,而不是所有的函数代码;只有在程序执行时, 那些需要的函数代码才被拷贝到内存中;动态库在程序运行是才被载入,也解决了静态库对程序的更新、部署和发布页会带来麻烦;用户只需要更新动态库即可,增量更新。 - 静态库是牺牲了空间效率,换取了时间效率; 动态库是牺牲了时间效率,换取了空间效率;
- 使用find_package
(1)cmake不提供搜索库的方法,需要手动设置环境变量
(2)模块模式和配置模式,modeule和config
(3)指令按照优先级顺序在指定路径查找Findxxx.cmake和xxxConfig.cmake
(4)cmake能够找到这两个文件中的任何一个,我们都能成功使用该库。
- FIND_PACKAGE-----【MODULE】
(1)FindXXX.cmake文件定义
find_path(hello_cv_install_include_dir
NAMES hell_cv.h
#<必须绝对路径,推荐安装在“/usr/share/cmake/Modules/”>
PATHS “.........”)
find_library(hello_cv_install_lib_libarary
NAMES hello_cv_install_lib
PATHS "............")
IF(hello_cv_install_include_dir AND hello_cv_install_lib_libarary)
SET(hello_cv_install_lib_found TRUE)
ENDIF(hello_cv_install_include_dir AND hello_cv_install_lib_libarary)
IF (hello_cv_install_lib_found)
IF (NOT hello_cv_install_lib_FIND_QUIETLY)
MESSAGE(STATUS "")
ENDIF(hell0_cv_install_lib_FIND_QUIETLY)
ELSE (hello_cv_install_lib_found)
IF (NOT hello_cv_install_lib_FIND_REQUIRED)
MESSAGE(FATAL_ERROR "")
ENDIF(hello_cv_install_lib_FIND_REQUIRED)
ENDIF (hello_cv_install_lib_found)
(2)库文件的生成
cmake_minimum_required(VERSION 2.8.3)
project(hello_cv_install_lib)
add_compile_options(-std=c++11)
include_directories(include)
add_library(hello_cv_install_lib STATIC src/hello_cv_1.cpp)
#设置指定的cmake文件生成目录
set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
#设置install的生成目录,统一管理三个文件
set(CMAKE_INSTALL_PREFIX ${PROJECT_SOURCE_DIR}/install)
install(FILES cmake/Findhello_cv_install_lib.cmake DESTINATION cmake)
install(FILES include/hello_cv_1.h DESTINATION include)
install(TARGETS hello_cv_install_lib ARCHIVE DESTINATION lib) #将静态库安装到${CMAKE_INSTALL_PREFIX}/lib下
(3)库文件的导入
cmake_minimum_required(VERSION 2.8.3)
project(hello_cv_outer_lib)
add_compile_options(-std=c++11)
set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/../hello_cv_install_lib/install/cmake/)
find_package(hello_cv_install_lib REQUIRED)
if(hello_cv_install_lib_found)
include_directories(${hello_cv_install_include_dir})
add_executable(${PROJECT_NAME} src/main.cpp)
target_link_libraries(${PROJECT_NAME}
${hello_cv_install_lib_libarary})
else(hello_cv_install_lib_found)
message(FATAL_ERROR "")
endif(hello_cv_install_lib_found)
- FIND_PACKAGE----配置模式
(1)库文件的生成
cmake_minimum_required(VERSION 2.8.3)
project(hello_cv_add_static_lib)
add_compile_options(-std=c++11)
include_directories(include)
add_library(hello_cv_add_static_lib STATIC src/hello_cv_1.cpp)
#设置install安装目录,类似于cmake -DCMAKE_INSTALL_PREFIX指令
set(CMAKE_INSTALL_PREFIX ${PROJECT_SOURCE_DIR}/include)
#设置自定义的install路径
target_include_directories(hello_cv_add_static_lib PUBLIC $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
#设置目标include的头文件的属性
set_target_properties(hello_cv_add_static_lib PROPERTIES
PUBLIC_HEADER "include/hello_cv_1.h")
install(TARGETS hello_cv_add_static_lib
EXPORT hello_cv_add_static_lib-targets #供外部库使用的申明
PUBLIC_HEADER DESTINATION include
ARCHIVE DESTINATION lib) #将静态库安装到${CMAKE_INSTALL_PREFIX}/lib下
install(EXPORT hello_cv_add_static_lib-targets
NAMESPACE hello_cv_add_static_lib::
#外部要使用find_package一定要使用,命名必须XX-config.cmake
FILE hello_cv_add_static_lib-config.cmake
DESTINATION lib/cmake.hello_cv_add_static_lib)
(2)库文件的导入
cmake_minimum_required(VERSION 2.8.3)
project(hello_cv_add_outer_lib)
add_compile_options(-std=c++11)
set(CMAKE_PREFIX_PATH ${PROJECT_SOURCE_DIR}/../hello_cv_add_static_lib/install)
find_package(hello_cv_add_static_lib REQUIRED)
if(hello_cv_add_static_lib_FOUND)
add_executable(${PROJECT_NAME} src/main.cpp)
target_link_libraries(${PROJECT_NAME} hello_cv_add_static_lib::hello_cv_add_static_lib)
else(hello_cv_add_static_lib_FOUND)
message(FATAL_ERROR ".......")
endif(hello_cv_add_static_lib_FOUND)
实例1
cmake_minimum_required(VERSION 2.8.3)
project(hello_cv_1)
add_compile_options(-std=c++11)
include_directories(include)
add_executable(hello_cv_1 src/main.cpp src/hello_cv_1.cpp)
实例2
添加一个库
cmake_minimum_required(VERSION 2.8.3)
project(hello_cv_2_add_static)
add_compile_options(-std=c++11)
include_directories(include)
add_library(hello_cv_2_add_static STATIC/SHARED/MODULE src/hello_cv_1.cpp)
实例3
同时添加动静态库
cmake_minimum_required(VERSION 2.8.3)
project(hello_cv_3_add_shared_lib)
add_compile_options(-std=c++11)
#设置库文件的输出目录
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
include_directories(include)
#静态库xxx.a,动态库xxx.so
add_library(hello_cv_3_lib SHARED src/hello_cv_1.cpp)
add_library(hello_cv_3_add_static_lib STATIC src/hello_cv_1.cpp)
#设置文件名替换,注意和上行的先后顺序
set_target_properties(hello_cv_3_add_static_lib PROPERTIES OUTPUT_NAME "hello_cv_3_lib")
实例4
链接一个内部库的依赖
cmake_minimum_required(VERSION 2.8.3)
project(hello_cv_4)
add_compile_options(-std=c++11)
#设置库文件的输出目录,默认输出到执行目录空间下
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
set(LIBRARY_OUTPUT_NAME ${PROJECT_NAME})
include_directories(include)
#静态库xxx.a,动态库xxx.so
add_library(hello_cv_4_lib SHARED src/hello_cv_1.cpp)
add_executable(${PROJECT_NAME} src/main.cpp)
target_link_libraries(${PROJECT_NAME} hello_cv_4_lib)
实例5
利用INSTALL的方式导入
cmake_minimum_required(VERSION 2.8.3)
project(hello_cv_5)
add_compile_options(-std=c++11)
include_directories(include)
add_library(hello_cv_5 STATIC src/hello_cv_1.cpp)
#将头文件和库文件安装到/usr目录下
set(CMAKE_INSTALL_PREFIX /usr)
#如不指定,默认安装到/usr/local
message(${CMAKE_INSTALL_PREFIX})
install(FILES include/hello_cv_1.h DESTINATION include)
install(TARGETS hello_cv_5 ARCHIVE DESTINATION lib)
#将静态库安装到${CMAKE_INSTALL_PREFIX}/lib目录下
#导入库
cmake_minimum_required(VERSION 2.8.3)
project(hello_cv_5)
add_compile_options(-std=c++11)
include_directories(/usr/include)
add_executable(${PROJECT_NAME} src/main.cpp)
target_link_libraries(${PROJECT_NAME} /usr/lib/libhello_cv_5.a)
实例6
cpp文件和h文件分开,编译成可执行文件
cmake_minimum_required(VERSION 2.8)
add_compile_options(-std=c++11 -fstack-protector-all -Wl,-z,relro,-z,noexecstack -fPIC)
project(hello)
find_package(XXX REQUIRED)
include_directories(${XXX1_INCLUDE_DIRS})
if(NOT DEFINED XXX2_PATH)
message(FATAL_ERROR "please use -DXXX2_PATH=<>")
endif()
set(XXX2_DIR ${XXX2_PATH} CACHE INTERNAL "XXX location")
link_directories(${XXX2_DIR}/lib)
#h文件位置
include_directories(. ./include/)
# main,cpp位置
file(GLOB SRC source/*.cpp)
add_executable(main ${SRC})
target_link_libraries(facedemo_main ${XXX1_LIBRARIES} pthread avcodec avdevice avfilter avformat avutil swscale)