Cmake的实践与应用

这一部分需要在学习中不断熟悉,后期会不断完善自己对这块内容的理解。

UP主:计算机视觉life

内容概要:

1.认识CMake及其应用。

  • 应用,与gcc,Makefile,Autotools相比较的优势?

2.CMake语句的主体框架。

  • cmake问题分析思路,主体的结构,语法的构成,以及基本模块功能

3.Cmake的常用指令及变量。

  • 基本常用指令(安装,测试,调试等),常用的Cmake语法变量含义

4.CMake的实践应用。

  • 从简单的CMake文件说起——>生成链接库(静态&动态)——>如何引用链接库(内部&引用)——>更简单的组织CMake的编译方式。

1.认识CMake及其应用

CMake是什么?

  • 全称Cross Platform Make,起初为了跨平台需求,后来不断完善并广泛使用。
  • 一款优秀的工程构建工具

特点和优势:

  • 开放源代码,具有BSD许可(BSD我没懂。。以后了解下)
  • 跨平台,支持Linux,Mac,Windows等不同操作系统
  • 编译语言简单,易用,简化编译构建过程和编译过程
  • 编程效率高,可扩展

CMake与其他编译工具的对比

gcc:

  • 由GUN开发的编程语言译器,C++/C,Java等语言的开发
  • 当项目比较简单时,可以使用gcc/g++编译目标和项目
  • 但项目比较复杂时,只用gcc组织编译构架变得极其困难

MakeFile:

  • MakeFile是有条理的gcc编译命令文件,利用make工具来执行MakeFile文件的编译指令
  • 当程序简单时,可以手写MakeFile
  • 当程序复杂是,一般利用CMake和autotools来自动生成MakeFile

Autotools:

  • autotools是一个工具集,具有灵活性大,对用户角度使用友好(cmake生成文件权限较多)
  • 开发步骤太多,配置繁琐[autoscan+autoconf+automake]
  • 通常编译的./configure文件,大多采用autotools构建的,最终生成MakeFile和config.h文件

CMake:

  • Cmake类似Make工具功能,用来”读取“并执行CMakeList.txt文件的语句,最终生成MakeFile
  • CMake语言开发相对简单,易于理解
  • 目前很多项目正在抛弃Autotools,qmake等,转而采用cmake

谈到CMake,可能涉及到的问题:

  • 如何组织一个项目的编译框架
  • 最终输出目标由哪些(可执行文件,动态库,静态库等)
  • 如何配置输出目标文件的指定编译参数(需要哪些编译参数及环境,需要哪些源文件)
  • 如何为指定的输出目标链接参数(怎么配置内外部依赖的pkg及lib,怎么链接外部库)

 

2.CMake语法的主体框架

command(arg1 arg2 ...)  #运行命令
set(var_name var_value) #定义变量,或者给已经存在的变量赋值
command(arg1 ${var_value}) #使用变量

主体框架:

工程编译部分:

工程名、编译调试模式、编译系统语言

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路径,放在include_executable之前
add_executable(cur_project_name XXX.cpp)   #指定生成目标
target_link_libraries(${std_lib_name_LIBARAIES} lib)   #指定libaraies路径,放在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
IF(NOT var)   #与上述条件相反
IF(var1 AND var2)  
IF(var1 OR var2)
IF(COMMAND cmd)  #当给定的cmd确实命令,并且以调用是为真
IF(EXISTS dir)  #目录名存在
IF(EXISTS file)  #文件名存在
IF(IS_DIRECTORY dirmane)  #当dirname是目录
IF(file1 IS_NEWER_THAN file2)  #当file1比file2新,为真
IF(variable MATCHES regex)  #符合正则

循环(不太理解)

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

注意:

  1. 对于${X},X表示变量名称,¥{X}表示变量值,if语句除外。
  2. 导入的功能函数,如SET的大小写均可,没有特殊限制

3. CMake的常用指令及变量

这些指令比较常见,在上面也有大概介绍。接下来介绍一些常用的:

ADD_DEFINITIONS(不太理解)

  • 为源文件的编译添加由-D引入的宏定义。
  • 命令格式为:add_definitions(-DFOO -DBAR ...),例如:add_definitions(-DWIN32)

OPTION(不太理解)

  • 提供用户可以选择的选项。
  • 命令格式为:option(<variable> "description" [initial value])
    option(
           USE_MYMATH
           "Use tutorial provided math implementation"
           ON
           )

     

ADD_CUSTOM_COMMAND/TARGET(慢慢理解吧):

  • [COMMAND]:为工程添加一条自定义的构建规则
  • [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"
                )

    这个不太好理解,感觉ppt上讲的不准确,实践中再学习吧

ADD_DEPENDENCIES(不太理解):

  • 用于解决链接依时的依赖问题,用target_link_libraries可以搞定么?
  • #添加执行文件
    add_executable(cur_project_name XXX.cpp)
    #将库文件链接链接到当前可执行文件(注意:一定在add_executable之后)
    target_link_libraries(cur_project_name ${std_lib_name_LIBRARIES})
    

    当定义的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)
    #常规语法
    cmake_minimum_required(VERSION 2.8)
    project(HelloCV)
    find_package(catkin REQUIRED COMPONENTS roscpp std_msgs genmsg)
    #ros生成msg的语法
    generate_message(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(只能我自己用)

SET_TARGET_PROPERTIES(不太理解)

  • 设置目标的一些属性来改变他们构建的方式
  • 命令格式为:

ENABLE_TESTING/ADD_TEST(不太理解)

3. CMake基本常用变量

4. 实践:从简单的CMake说起

实践一:生成一个简单的Hello_CV工程实例

头文件:hello_cv_1.h

#ifdef HELLO_CV_1_H_
#define HELLO_CV_1_H_
#include <string>
#include <iostream>

namespace hello_cv_1
{
class HelloCv_1
{
private:
std::string say_something;

public:
HelloCv_1();
~HelloCv_1();
std::string getMsgContent(std::string say_something);
};

}

#endif

源文件:hello_cv_1.cpp

#include "hello_cv_1.h"
#include <string>
#include <iostream>

namespace hello_cv_1
{
HelloCv_1::HelloCv_1()
{

}

HelloCv_1::~HelloCv_1()
{

}

std::string HelloCv_1::getMsgContent(std::string say_something)
{
return say_something;
}
}

主函数:main.cpp

#include "hello_cv_1.h"
#include <string>
#include <iostream>

int main()
{
hello_cv_1::HelloCv_1 helloCv_1;
std::cout<<helloCv_1.getMsgContent("Hello_OpenCV")<<std::endl
return 0;
}

工程文件目录

  • 采用外部编译(理解下什么是外部编译):外部编译&&内部编译
  • 文件位置存放规则:src&&include&&build

外部编译的目录结构如下

cmake_minimum_required(VERSION 2.6)
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)

下面是命令行编译:

mkdir build && cd build  #建立build并进入到该文件夹
cmake ..  #生成Makefile文件
make   #编译Makefile,并生成bin文件
./hello_cv_1   #执行文件后输出"Hello_OpenCV"

果然实践中才能掌握得更好。。。

实践二:为Hello_CV实例添加一个库

CMakeList.txt如上所示

实践三:为实例同时添加一个动、静态库

生成指定路径的动态库

注意其中的设置库文件的额输出目录。

个人感觉动态库、静态库可以不同名吧?emmm。这样不就省事了?

不过这个up主用了另一种操作,看起来比较讲究:

注意一下这里讲究的操作,文件名替换~

然后思考,什么是静态库(.a)?什么是动态库(.so)?

这就要从程序的工作过程说起:

知识回顾:前三个实例干了啥?

1.怎样生成一个可执行文件

2.怎样去生成一些库

3.怎样生成一些动态库和静态库?如何生成同名动态库和静态库

接下来干啥?

怎么样调用库。

实践四:为实例添加一个内部的链接库。

怎样导入、调用一个内部的链接库?

nn

内部链接库,执行效率比较高。

实践五:为实例添加一个外部链接库

不太懂。。。以后慢慢理解

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值