這一部分需要在學習中不斷熟悉,後期會不斷完善自己對這塊內容的理解。
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)
注意:
- 對於${X},X表示變量名稱,¥{X}表示變量值,if語句除外。
- 導入的功能函數,如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
內部鏈接庫,執行效率比較高。
實踐五:爲實例添加一個外部鏈接庫
不太懂。。。以後慢慢理解