ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

CMake原理

2021-08-01 17:31:30  阅读:424  来源: 互联网

标签:CMake lib add static install 原理 include cv


文章目录

CMake的定义

是Cross Platform Make的缩写。跨平台,支持Linux、Mac和Windows。编译语言简单,易用,简化编译构建过程和编译过程。CMake可以“读取”并执行CMakeLists.txt文件的语句,生成Makefile文件。


与其他编译工具的对比

  • GCC。项目简单时,可以gcc/g++编译目标和项目,复杂时,只用gcc组织编译架构就变得很困难。
  • Makefile。一种有条理的gcc编译命令的文件,利用make工具来执行Makefile文件的编译指令。项目简单时,可以手写Makefile,项目复杂时,一般利用CMake和autotools自动生成Makefile。
  • Autotools。灵活性大,但开发步骤多,配置繁琐。

CMake的主体框架

四个部分:

  • 工程配置部分:工程名、编译调试模式、编译系统语言
  • 依赖执行部分:工程包,头文件、依赖库等。
  • 其他辅助部分:参数打印、遍历目录等
  • 判断控制部分:条件判断、函数定义、条件执行等
command(arg1 arg2 ...)									//运行命令
set(var_name var_value)									//定义变量,或者给已存在的变量赋值
command(arg1 ${var_name})								//使用变量

//************************************工程配置部分*****************************************
cmake_minimum_required (VERSION num)					//CMake最低版本号要求
project (cur_project_name)								//项目信息
set(CMAKE_CXX_FLAGS "XXX")								//设定编译器版本,如:C++17
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_INCLUEDE_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)									//添加一个子目录
AUX_SOURCE_DIRECTORY(. SRC_LIST)						//查找当前目录所有文件,保存到SRC_LIST变量中
FOREACH(one_dir ${SRC_LIST})
	MESSAGE(${one_dir})									//使用message进行打印
ENDFOREACH(onedir)

//************************************判断控制部分*****************************************
if(expression)
	COMMAND1(ARGS)
ELSE(expression)
	COMMAND2(ARGS)
ENDIF(expression)

//expression
IF(var)	//不是空,0,N,NO,OFF,FALSE,NOTFOUND 或 <var> NOTFOUND时,为真
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)	//当dirname是目录
IF(file1 IS_NEWER_THAN file2)	//当file1比file2新,为真
IF(variable MATCHES regex)	//符合正则表达式

//循环
WHILE(condition)
	COMMAND1(ARGS)
	// ...
ENDWHILE(condition)

CMake常用指令

  • PROJECT
  • ADD_EXECUTABLE
  • ADD_SUBDIRECTORY
  • INCLUDE_DIRECTORIES
  • LINK_DIRECTORIES
  • TARGET_LINK_LIBRARIES
  • ADD_LIBRARY
  • AUX_SOURCE_DIRECTORY
  • FOREACH
  • MESSAGE
  • IF ELSE ENDIF
  • WHILE ENDWHILE
  • FIND_PACKAGE
  • SET
  • ADD-DEFINITIONS
    • 为源文件的编译添加由-D引入的 宏定义
    • 命令格式为:add_definitions(-DFOO -DBAR ...)
    • 例:add_definitions(-DWIN32)
    • OpenCV中:
cmake -D CMAKE_BUILD_TYPE=RELEASE \		//定义一个宏,以release形式build
-D CMAKE_INSTALL_PREFIX=/usr/local \	//下载目录
-D WITH_CUDA=ON \						//打开CUDA加速
-D ENABLE_FAST_MATH=1 \
-D WITH_CUBLAS=1 \
-D INSTALL_C_EXAMPLES=OFF \
-D INSTALL PYTHON EXAMPLES=ON \		   //下载PYTHON示例
  • OPTION
    • 提供用户可以选择的选项
    • 命令格式为:option(<variable> "description [initial value])
option(
	   USE_MYMATH
	   "Use tutorial provided math implementation"
	   ON
	   )	//不赋给初值,就是ON,description是描述
  • ADD_CUSTOM_COMMAND/TARGET
    • [COMMAND]:为工程添加一条自定义的构建规则
    • [TARGET]:用于给指定名称的目标执行指定的命令,该目标没有输出文件,并始终被构建
//伪代码:为了说明生成自定义的命令
add_custom_command(TARGET ${CV_ADVANCE_NAME}
	PRE_BUILD
	COMMAD "伪代码 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
    • 当定义的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_LIBRARIES})
//为项目链接一个依赖文件,项目程序依赖CV_ADVANCE
add_dependencies(${PROJECT_NAME} CV_ADVANCE)

ROS1.0在该指令上的应用(工程实例)

//常规语法
cmake_minimum_required (VERSION 2.8.3)
project(HelloCV)
find_package(catkin REQUIRED COMPONENTS roscpp std_msgs genmsg)
//ros生成msg的语法
generate_messages(DEPENDENCIES std_msgs)
add_message_files(FILES HelloCvMsg.msg)
//声明是catkin包
catkin_package()
//创建工程实例
include_directories(include ${catkin_INCLUDE_DIRS})
add_executable(greet src/greet.cpp)
target_link_libraries(greet ${catkin_LIBRARIES})
//添加greet依赖,依赖std_msgs的变量
add_dependencies(greet HelloCV_generate_messages_cpp)
  • INSTALL
    • 用于定义安装规则,安装的内容可以包括目标二进制、动态库、静态库以及文件、目录、脚本等。
    • 常用的如OpenCV一般情况下安装到系统目录,即/usr/lib,/usr/bin和/usr/include [Ubuntu]
INSTALL(
		TARGETS myrun mylib mystaticlib
		RUNTIME DESTINATION bin
		LIBRARY DESTINATION lib
		ARCHIVE DESTINATION libstatic
		)
  • TARGET_INCLUDE_DIRECTORIES
    • 设置include文件查找的目录,具体包含头文件应用形式,安装位置等。
    • 命令格式为:target_include_directories(<target>[SYSTEM][BEFORE] <INTERFACE|PUBLIC|PRIVATE>[items])
    • INTERFACE 对外接口 / PUBLIC 公开 / PRIVATE 私有
//外部链接时include的位置,如果不指明外部依赖无法找到对应的include库
target_include_directories(hello_cv_2_add_static_lib PUBLIC
						   $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include>	//源文件安装位置
						   $<INSTALL_INTERFACE:include>)	//安装文件夹位置
  • SET_TARGET_PROPERTIES

    • 设置目标的一些属性来改变它们构建的方式
    • 命令格式为:set_target_properties(target1 target2 ... PROPERTIES prop1 value1 prop2 value2 ...)
  • ENABLE_TESTING/ADD_TEST

    • [enable_testing]:用来控制Makefile是否构建test目标
    • [add_test]:一般需要和enable_testing()配合使用
    • 命令格式:ADD_TEST(testname Exename arg1 arg2 ...)
    • 生成makefile后可用make test执行测试

CMake常用变量

变量名具体含义
CMAKE_INSTALL_PREFIX构建install的路径
$ENV{HOME}HOME环境下的目录路径
PROJECT_NAME工程名变量
< PKG>_INCLUDE_DIR导入包头文件全路径
< PKG>_LIBRARIES导入库文件全路径
PROJECT_SOURCE_DIR构建工程的全路径
CMAKE_VERSIONCMake的版本号
CMAKE_SOURCE_DIR源码树的顶层路径

实践:从简单CMake说起

构建了三个最简单的C++例程,这里就不贴了,一个头文件(cv.h)内部声明了一个类,两个cpp文件(cv.cpp,main.cpp)一个定义了类,另一个main.cpp,两个cpp文件都include了h文件)

采用外部编译(就是在CMakeLists.txt同级目录下建立build文件夹,将生成的执行文件都存放在build内)

则文件目录如下(其中加粗的为文件夹):

  • build
  • CMakeLists.txt
  • include
    • cv.h
  • src
    • cv.hpp
    • main.hpp

下面为编译过程:

Round One

CMakeLists.txt内容

cmake_minimum_required(VERSION 2.8.3)
project(cv)

add_compile_options(-std=c++11)

include_directories(include)	//include/cv.h
add_executable(cv src/main.cpp src/cv.cpp)

接下来在shell里

mkdir build && cd build		#建立build并进入该文件夹
cmake ..					#生成makefile文件
make						#编译makefile,并生成bin(二进制可执行)文件
./cv						#执行文件

解释1: ..代表上一级目录

解释2: include_directoriesfind_package的区别: include_directories() 用来寻找头文件路径; find_package(<Name>)用来在路径中Find<name>.cmake,找到.cmake。里面就会定义:

<NAME>_FOUND
<NAME>_INCLUDE_DIRS or <NAME>_INCLUDE
<NAME>_LIBRARIES or <NAME>LIBS
<NAME>_DEFINITIONS

这时就可以给这些变量赋路径。最后一定要将找到的库连接到可执行文件上
target_link_libraries(项目名 ${路径})

include_directories => find_package => target_link_libraries 三步走!

Round Two

为CV实例添加一个库

cmake_minimum_required(VERSION 2.8.3)
project(cv_add_static_lib)

add_compile_options(-std=c++11)

include_directories(include)	//cv.h的路径
add_library(cv_add_static_lib STATIC src/cv.cpp)
//STATIC静态链接库 SHARED动态链接库 MODULE(不常用)函数目标链接

Round Three

生成一个静态库 set给变量赋值

cmake_minimum_required(VERSION 2.8.3)
project(cv_add_shared_lib)

add_compile_option(-std=c++11)

//设置库文件的输出目录,默认输出到执行目录空间下
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib}	//工程的根目录	如ROS/practicecode/lib
include_directories(include)

//静态库名为XXX.a 动态库名为XXX.so
add_library(cv_add_shared_lib SHARED src/cv.hpp)
  • Windows中静态库.lib后缀,动态库.dll
  • Linux中静态库.a后缀,动态库.so

同时添加一个动态库和静态库

  • 静态库:简单来说,就是在链接时,里面的所有东西都嗖的变成机器码,然后copy到可执行文件里,这也叫静态链接。可以参考这个玩意儿。但是静态库在内存中可能多次拷贝重新编译,耗费内存,编译时执行
  • 动态库: 程序执行时才会链进来,简单说就是要啥取啥,增量更新,不需要改动一丢丢就全盘改动重新编译。动态库使用时很像指针,简单说在内存中只存在一次拷贝,避免了浪费。但是动态链接存在一个问题对库的依赖性很强,所有的东西都要在
cmake_minimum_required(VERSION 2.8.3)
project(cv_add_both_lib)

add_compile_option(-std=c++11)

//设置库文件的输出目录,默认输出到执行目录空间下
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib}	//工程的根目录	如ROS/practicecode/lib
include_directories(include)

//静态库名为XXX.a 动态库名为XXX.so
add_library(cv_add_both_lib SHARED src/cv.hpp)

add_library(cv_add_static_lib STATIC src/cv.hpp)
//设置文件名替换
set_target_properties(cv_add_static_lib PROPERTIES OUTPUT_NAME "cv_add_both_lib")

Round Four

为实例添加一个内部的链接库

添加内部链接库的原因是,当一个解决方案中除main.cpp外存在多个.cpp文件用来定义函数和方法,直接生成可执行文件,并没有动态链接库效率高,占用内存小。

cmake_minimum_required(VERSION 2.8.3)
project(cv_target_link_lib)

add_compile_options(-std=c++11)

//设置库文件的输出目录,默认输出到执行目录空间下
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
set(LIBRARY_OUTPUT_NAME ${PROJECT_NAME})

add_library(cv_lib SHARED src/cv.cpp)
add_executable(${PROJECT_NAME} src/main.cpp)

target_link_libraries(${PROJECT_NAME} cv_lib)

Round Five

添加一个外部链接库

  • 绝对路径导入
  • install的方式导入
  • find_package方式导入

补充说明: (这里说明一下头文件和库文件的差别)简单来说,头文件就是日常见到的.h文件,是文本文件可供阅读的,而库文件(可以理解为不含main的.cpp文件)是二进制文件;头文件在编译时使用,库文件在链接时使用;头文件含有函数声明、宏定义、内联函数等,库文件则是函数的实现。

//绝对路径导入
cmake_minimum_required(VERSION 2.8.3)
project(cv_target_link_lib)
add_compiler_options(-std=c++11)

set(pkg_lib /home/fengyixiao/ROS/project/src/cv_static_lib)
include_directories(${pkg_lib}/include)
add_executable(${PROJECT_NAME} src/main.cpp)
target_link_libraries(${PROJECT_NAME} ${pkg_lib}/lib/libcv_static_lib.a)
//INSTALL导入
//库文件安装
cmake_minimum_required(VERSION 2.8.3)
project(cv_target_link_lib)
add_compiler_options(-std=c++11)
include_directories(include)	//头文件
add_library(cv_install_lib STATIC src/cv.cpp)	//库文件

//将头文件和库文件安装到/usr目录下
set(CMAKE_INSTALL_PREFIX /usr)	//CMAKE_INSTALL_PREFIX上面提到,构建install的路径
//如不指定则默认/usr/include
message(${CMAKE_INSTALL_PREFIX})
install(FILES include/cv.h DESTINATION include)	//头文件,目的地include
install(TARGETS cv_install_lib	//目的生成这个lib
		ARCHIVE DESTINATION lib	//ARCHIVE有档案的意思,应该是保存形式
		)

//库文件导入
cmake_minimum_required(VERSION 2.8.3)
project(cv_outer_lib)
add_compiler_options(-std=c++11)
include_directories(/usr/include)	//导入头文件

add_executable(${PROJECT_NAME} src/main.cpp)
target_link_libraries(${PROJECT_NAME}
		/usr/lib/libcv_static_lib.a
		)	//导入库文件

find_package原理

  • 一般分为模块模式module配置模式config
  • 指令按照优先级在指定路径查找FindXXX.cmakeXXXConfig.cmake

模块模式

//find_package导入
//模块模式
find_path(cv_install_lib_INCLUDE_DIR
	NAMES cv.h
	//必须绝对路径,推荐安装在"/usr/share/cmake/Modules/"目录下
	PATHS "/usr/fengyixiao/ROS/practicecode/src/cv_install_lib/install/include/")	//这点可能有点乱了,放上安装到的路径

find_library(cv_install_library
	//names也可以制定具体的文件名.a
	NAMES cv_install_lib
	PATHS "/usr/fengyixiao/ROS/practicecode/src/cv_install_lib/install/lib/")

IF(cv_install_lib_INCLUDE_DIR AND cv_install_library)
	SET(cv_install_lib_FOUND TRUE)
ENDIF(cv_install_lib_INCLUDE_DIR AND cv_install_library)
IF(cv_install_lib_FOUND)
	//制定了QUIET选项,如果没有找到包配置文件,会生成一个warnning
	IF(NOT cv_install_lib_FIND_QUIETLY)
		MESSAGE(STATUS "Found cv_install_lib:
				${cv_install_lib_LIBRARY}")
	ENDIF(NOT cv_install_lib_FIND_QUIETLY)
ELSE(cv_install_lib_FOUND)
	//制定REQUIRED选项,如果没有找到,会显示编译错误
	IF(cv_install_lib_FIND_REQUIRED)
		MESSAGE(FATAL_ERROR "Coule not find cv_install_lib")
	ENDIF(cv_install_lib_FINE_REQUIRED)
ENDIF(cv_install_lib_FOUND)
//模块模式INSTALL
cmake_minimum_required(VERSION 2.8.3)
project(cv_install_lib)
add_compiler_options(-std=c++11)
include_directories(include)	//头文件
add_library(cv_install_lib STATIC src/cv.cpp)	//库文件

set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
set(CMAKE_INSTALL_PREFIX ${PROJECT_SOURCE_DIR}/install)
message(${CMAKE_MODULE_PATH})
message(${CMAKE_INSTALL_PREFIX})
install(FILES cmake/Findcv_install_lib.cmake DESTINATION cmake)
install(FILES include/cv.h DESTINATION include)
install(TARGETS cv_install_lib
		//把静态库安装在${CMAKE_INSTALL_PREFIX}/lib下
		ARCHIVE DESTINATION lib
		)

message()就是用来打印变量的

//库文件的导入
cmake_minimum_required(VERSION 2.8.3)
project(cv_find_outer_lib)
add_compile_options(-std=c++11)

set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/.../install/cmake/)
find_package(cv_install_lib REQUIRED)
if(cv_install_lib_FOUND)
	include_directories(${cv_install_lib_INCLUDE_DIR})	//头文件
	add_executable(${PROJECT_NAME} src/main.cpp)//主函数
	target_link_libraries(${PROJECT_NAME} 
						  ${cv_install_library})	//链接库文件
else(cv_install_lib_FOUND)
	message(FATAL_ERROR "cv_install_lib library not found")
endif(cv_install_lib_FOUND)

配置模式

//库文件的生成
cmake_minimum_required(VERSION 2.8.3)
project(cv_add_static_lib)
add_compile_options(-std=c++11)
include_directories(include)
add_library(cv_add_static_lib STATIC src/cv.cpp)

set(CMAKE_INSTALL_PREFIX ${PROJECT_SOURSE_DIR}/install)

target_include_directories(cv_add_static_lib PUBLIC	//设置库的属性
						   $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include>	//源文件安装位置
						   $<INSTALL_INTERFACE:include>)	//安装文件夹位置

//设置目标include的头文件的属性
set_target_properties(cv_add_static_lib
					  PROPERTIES
					  PUBLIC_HEADER "include/cv.h")

install(TARGETS cv_add_static_lib
		EXPORT cv_add_static_lib-targets	//供外部库使用的声明
		PUBLIC_HEADER DESTINATION include
		ARCHIVE DESTINATION lib		//将静态库安装到${CMAKE_INSTALL_PREFIX}/lib下
		)
install(EXPORT cv_add_static_lib-targets
		NAMESPACE cv_add_static_lib::
		//外部要使用find_package一定要命名XX-config.cmake
		FILE cv_add_static_lib-config.cmake
		DESTINATION lib/cmake/cv_add_static_lib
		)
//库文件的导入
cmake_minimum_required(VERSION 2.8.3)
project(cv_target_link_outer_lib)
add_compile_options(-std=c++11)

set(CMAKE_PREFIX_PATH ${PROJECT_SOURCE_DIR}/.../cv_add_static_lib/install)
find_package(cv_add_static_lib REQUIRED)
if(cv_add_static_lib_FOUND)
	add_executable(${PROJECT_NAME} src/main.cpp)
	target_link_libraries(${PROJECT_NAME}
						  cv_add_static_lib::cv_add_static_lib)
else(cv_add_static_lib_FOUND)
	message(FATAL_ERROR "cv_add_static_lib library not found")
endif(cv_add_static_lib_FOUND)


ROS catkin

基于cmake语法的上层编译工具

利用指令catkin create --pkg XXX自动生成CMakeLists和package文件。(include头文件所在文件夹,src源文件)

生成库:

cmake_minimum_required(VERSION 2.8.3)
project(cv_add_static_lib)
add_compile_options(-std=c++11)
find_package(catkin REQUIRED)
catkin_package(
				INCLUDE_DIRS include
				LIBRARIES cv_add_static_lib
				//DEPENDS system_lib //如果依赖其他库添加
			  )	
include_directories(include ${catkin_INCLUDE_DIRS})
add_library(${PROJECT_NAME} STATIC src/cv.cpp)

比起前面的CMakeLists.txt这里还多了一个xml文件

<?xml version="1.0"?>
<package format="2">
	<name>cv_add_static_lib</name>
	<version>0.0.0</version>
	<description>The cv_add_static_lib package</description>

	<maintainer> email="xiaofengbh@163.com">xiaofeng</maintainer>

	<license>TODO</license>

	<buildtool_depend>catkin</buildtool_depend>

	<export>

	</export>
</package>

导入库

cmake_minimum_required(VERSION 2.8.3)
project(cv_target_link_outer_lib)
add_compile_options(-std=c++11)
find_package(catkin REQUIRED
			 cv_add_static_lib)
include_directories(include ${catkin_INCLUDE_DIRS})	//添加头文件
add_executable((${PROJECT_NAME} src/main.cpp)	//生成可执行文件
target_link_libraries(${PROJECT_NAME} ${catkin_LIBRARIES})	//链接库文件

<?xml version="1.0"?>
<package format="2">
	<name>cv_add_static_lib</name>
	<version>0.0.0</version>
	<description>The cv_add_static_lib package</description>

	<maintainer> email="xiaofengbh@163.com">xiaofeng</maintainer>

	<license>TODO</license>

	<buildtool_depend>catkin</buildtool_depend>
	
	<depend>cv_add_static_lib</depend>

	<export>

	</export>
</package>


利用catkin管理项目实例

pkg目录结构

  • src
    • project_lib
      • CMakeLists.txt
      • include
      • src
      • package.xml

如何使用catkin

cd 项目文件夹名
catkin build	#构建完毕生成三个文件夹

生成build, devel, log文件夹

  • 对应lib生成在/devel下
  • 对应bin生成在/build下

标签:CMake,lib,add,static,install,原理,include,cv
来源: https://blog.csdn.net/weixin_46275484/article/details/119297384

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有