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

內部鏈接庫,執行效率比較高。

實踐五:爲實例添加一個外部鏈接庫

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章