Cmakelist知识总结
Cmakelist知識總結
文章目錄
- Cmakelist知識總結
- 程序編譯鏈接過程簡介
- Cmakelist簡介
- Cmakelist寫法
- cmakelist實例解析
程序編譯鏈接過程簡介
一個完整的 C++ 語言項目可能包含多個 .cpp 源文件,項目的運行需要經過“編譯”和“鏈接”兩個過程:
- 編譯 :由編譯器逐個對源文件做詞法分析、語法分析、語義分析等操作,最終生成多個目標文件。每個目標文件都是二進制文件,但由于它們會相互調用對方的函數或變量,還可能會調用某些鏈接庫文件中的函數或變量,編譯器無法跨文件找到它們確切的存儲地址,所以這些目標文件無法單獨執行。
- 鏈接:對于各個目標文件中缺失的函數和變量的存儲地址(后續簡稱“缺失的地址”),由鏈接器負責修復,并最終將所有的目標文件和鏈接庫組織成一個可執行文件。
我們在開發過程中也經常會面臨庫文件和目標文件的概念,因此在做工程文件時,經常需要了解什么是庫文件?什么是目標文件?什么是依賴項?
- 庫文件包含幾種類型:(1)資源類型的,就是存放的東西,供外部調用的沒有函數,只有變量或類的實體;(2)有簡單的函數供外部調用的文件;(3)也是用的比較多的,就是包含一個完整的模塊,緊提供一個外部調用的接口,當外部調用該接口就開始運行這個模塊,外部幾乎就處于休眠狀態;
(1)靜態庫
靜態庫就是一些目標文件的集合,windows下以.lib結尾,linux下以.a結尾。靜態庫在程序鏈接的時候使用,鏈接器會將程序中使用到函數的代碼從庫文件中拷貝到應用程序中。一旦鏈接完成,在執行程序的時候就不需要靜態庫了。由于每個使用靜態庫的應用程序都需要拷貝所用函數的代碼,所以靜態鏈接的文件會比較大。
(2)動態庫
動態庫,windows下叫DLL,linux下叫共享庫以.so結尾. (so ==share object) 在程序的鏈接時候并不像靜態庫那樣在拷貝使用函數的代碼,而只是作些標記。然后在程序開始啟動運行的時候,動態地加載所需模塊。所以,應用程序在運行的時候仍然需要共享庫的支持。 共享庫鏈接出來的文件比靜態庫要小得多。動態庫的好處是,不同的應用程序如果調用相同的庫,那么在內存里只需要有一份該共享庫的實例。
- 目標文件就是編譯過程中產生的,鏈接起來生成可執行文件的。
- 依賴項就是設定項目所依賴的項目,以決定具體生成解決方案時項目編譯的順序(一般一個解決方案會有很多項目組成)。 通常來說,依賴項取決于這個項目引用的組件和項目,系統可以自己決定。
我們明確了上述的具體定義后,可以介紹一個工程文件從源文件到可執行文件的編譯流程;具體編譯流程如下:
① 預處理過程
預處理相當于根據預處理指令組裝新的C/C++程序。經過預處理,會產生一個沒有宏定義,沒有條件編譯指令,沒有特殊符號的輸出文件,這個文件的含義同原本的文件無異,只是內容上有所不同。
讀取C/C++源程序,對其中的偽指令(以#開頭的指令)進行處理,內容如下:
② 編譯過程
將預處理完的文件進行一系列詞法分析、語法分析、語義分析及優化后,產生相應的匯編代碼文件。
③ 匯編過程
將編譯完的匯編代碼文件翻譯成機器指令,并生成可重定位目標程序的.o文件,該文件為二進制文件,字節編碼是機器指令。
匯編器是將匯編代碼轉變成機器可以執行的指令,每一個匯編語句幾乎都對應一條機器指令。所以匯編器的匯編過程相對于編譯器來講比較簡單,它沒有復雜的語法,也沒有語義,也不需要做指令優化,只是根據匯編指令和機器指令的對照表一一翻譯即可。
④ 鏈接(build)過程
通過鏈接器將一個個目標文件(或許還會有庫文件)鏈接在一起生成一個完整的可執行程序。由匯編程序生成的目標文件并不能立即就被執行,其中可能還有許多沒有解決的問題。
例如,某個源文件中的函數可能引用了另一個源文件中定義的某個符號(如變量或者函數調用等);在程序中可能調用了某個庫文件中的函數,等等。所有的這些問題,都需要經鏈接程序的處理方能得以解決。
鏈接程序的主要工作就是將有關的目標文件彼此相連接,也就是將在一個文件中引用的符號同該符號在另外一個文件中的定義連接起來,使得所有的這些目標文件成為一個能夠被操作系統裝入執行的統一整體。
Cmakelist簡介
cmake 是一個跨平臺、開源的構建系統。它是一個集軟件構建、測試、打包于一身的軟件。它使用與平臺和編譯器獨立的配置文件來對軟件編譯過程進行控制。
要了解CMakelist.txt文件,首先我們先了解一下Makefile。Makefile 可以簡單的認為是一個工程文件的編譯規則,描述了整個工程的編譯和鏈接等規則。其中包含了那些文件需要編譯,那些文件不需要編譯,那些文件需要先編譯,那些文件需要后編譯,那些文件需要重建等等。編譯整個工程需要涉及到的,在 Makefile 中都可以進行描述。換句話說,Makefile 可以使得我們的項目工程的編譯變得自動化,不需要每次都手動輸入一堆源文件和參數。
Cmake的所有語句都寫在一個CMakeLists.txt的文件中,CMakeLists.txt文件確定后,直接使用Cmake命令進行運行,但是這個命令要指向CMakeLists.txt所在的目錄,Cmake之后就會產生我們想要的makefile文件,然后再直接make就可以編譯出可執行程序或者動態庫。所以基本步驟就只有兩步:(1)cmake生成MakeLists.txt文件;(2)make執行編譯工作。
Cmakelist寫法
一個cmakelist文件主要包含以下的內容:
(1)聲明 cmake 最低版本
cmake_minimun_required(VERSION 2.8)
(2)聲明 cmake 工程名字
project( HelloSLAM )
加上上述指令,它會引入兩個變量 demo_BINARY_DIR 和 demo_SOURCE_DIR,同時,cmake 自動定義了兩個等價的變量 PROJECT_BINARY_DIR 和 PROJECT_SOURCE_DIR。
PROJECT_SOURCE_DIR: 工程的根目錄
PROJECT_BINARY_DIR: 運行 cmake 命令的目錄,通常是 ${PROJECT_SOURCE_DIR}/build
PROJECT_NAME: 返回通過 project 命令定義的項目名稱
CMAKE_CURRENT_SOURCE_DIR: 當前處理的 CMakeLists.txt 所在的路徑
CMAKE_CURRENT_BINARY_DIR: target 編譯目錄
CMAKE_CURRENT_LIST_DIR: CMakeLists.txt 的完整路徑
CMAKE_CURRENT_LIST_LINE: 當前所在的行
CMAKE_MODULE_PATH: 定義自己的 cmake 模塊所在的路徑,SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake),然后可以用INCLUDE命令來調用自己的模塊
EXECUTABLE_OUTPUT_PATH: 重新定義目標二進制可執行文件的存放位置
LIBRARY_OUTPUT_PATH: 重新定義目標鏈接庫文件的存放位置
(3)設置 cmake 編譯模式
set( CMAKE_BUILD_TYPE “Debug” )
(¥)添加c++11標準支持
set(CMAKE_CXX_FLAGS “-std=c++11 -O3”)
參數CMAKE_CXX_FLAGS含義是: set compiler for c++ language而后面的-O3(是字母opq的o,大寫的歐)是用來調節編譯時的優化程度的,最高為-O3,最低為-O0(即不做優化)-Ox這個參數只有在CMake -DCMAKE_BUILD_TYPE=Release時有效,因為debug 版的項目生成的可執行文件需要有調試信息并且不需要進行優化,而 release 版的不需要調試信息但需要優化。
(5)添加變量
set( Sophus_INCLUDE_DIR ${PROJECT_SOURCE_DIR} )
(6)添加依賴
一般添加依賴項都需要解決三個問題:去哪里找頭文件(.h等) 對應于GCC的參數 -I;去哪里找庫文件(.so/.lib/.ddl等) 對應于GCC的參數 -L;需要鏈接的庫文件名稱 對應于GCC的參數 -l。
find_package的作用就是去尋找該庫的頭文件位置、庫文件位置以及庫文件名稱,并將其設為變量,返回提供給CMakeLists.txt其他部分使用。
(7)添加頭文件
include_directories( “/usr/include/eigen3” ) include_directories( ${Pangolin_INCLUDE_DIRS} ) include_directories( ${Sophus_INCLUDE_DIRS} )(8)設置鏈接庫搜索目錄
link_directories(${CMAKE_CURRENT_SOURCE_DIR}/libs)
(9)添加一個可執行程序
add_executable( helloSLAM helloSLAM.cpp )
或者
或者
file(GLOB SRC_LIST "*.cpp") file(GLOB SRC_PROTOCOL_LIST "protocol/*.cpp") add_library(helloSLAM ${SRC_LIST} ${SRC_PROTOCOL_LIST})其中,GLOB命令將所有匹配globbing-expressions(可選,假如不寫,毛都匹配不到)的文件挑選出來,默認以字典順序排序。
(10)構建靜態庫
add_library( hello_static STATIC libHelloSLAM.cpp )
(11)構建靜動態庫或者共享庫
add_library( hello_shared SHARED libHelloSLAM.cpp )
(12)庫文件鏈接到可執行程序上
或者
link_libraries(libface.a)
link_libraries用在add_executable之前,target_link_libraries用在add_executable之后。
link_libraries用來鏈接靜態庫,target_link_libraries用來鏈接導入庫,即按照header file + .lib + .dll方式隱式調用動態庫的.lib庫。
(13)指定安裝地址
cmake -DCMAKE_INSTALL_PREFIX=/usr/local
(14)Debug和Release版本
CMAKE_BUILD_TYPE 指定基于make的產生器的構建類型(Release,Debug)
debug 版的項目生成的可執行文件需要有調試信息并且不需要進行優化,、
release 版的不需要調試信息但需要優化。這些特性在 gcc/g++ 中是通過編譯時的參數來決定的,如果將優化程度調到最高需要設置參數-O3,最低是 -O0 即不做優化;添加調試信息的參數是 -g -ggdb ,如果不添加這個參數,調試信息就不會被包含在生成的二進制文件中。
(15)調試信息
打印信息,類似于 echo/printf ,主要用于查cmake文件的語法錯誤。
message(“mysql_use_test_sources : ${mysql_use_test_sources}”)
cmakelist實例解析
CmakeLists.txt的重要組成部分:拆分成以下幾部分進行講解。
1、表明Cmake所需要的最低版本:僅針對本項目
cmake_minimum_required(VERSION2.9 FATAL_ERROR)
2、定義需要的特殊變量(optional)(關于set的用法我目前沒有細看,有心的同學自己鉆研)
SET(sampleName MyApp)
如上面的例子:set(project_name cloud_viewer_PointXYZ)
3、查找我們構建工程所需要的package,這一步極其重要。
find_package(PCL 1.8.0 REQUIRED) #主要依賴的package
# //如果PCL被找到的話,那么將會生成幾個包含package信息的Cmake環境變量,下面幾個相關的變量就會被設置
PCL_FOUND:PCL找到后就設置為1,否則未設置
PCL_INCLUDE_DIRS:設置PCL頭文件和依賴項頭文件的安裝路徑
PCL_LIBRARIES:設置已構建和已安裝PCL庫的文件名
PCL_LIBRARY_DIRS:設置PCL庫和第三方依賴的路徑
PCL_VERSION:找到的PCL版本
PCL_COMPONENTS:列出所有的可用部分
PCL_DEFINITIONS:列出所有需要的預處理器定義和編譯器標志
# 這些變量將會在后面的cmakescript中用到。
# 這些環境變量描述了package的外部頭文件位置(include路徑),
# 依賴的庫文件的位置(lib),以及源程序的位置。
# 例如上面命令執行找到PCL后,將會創建環境變量PCL_INCLUDE_DIRS,其中包含指定PCL庫頭文件.h的查找路徑;
# 創建環境變量PCL_LIBRARY_DIRS,其中包含指定PCL庫的.lib文件的所在目錄的路徑.
# //REQUIRED表示如果沒有找到,cmake會停止處理,并報告一個錯誤.
4、指定構建project所需要的資源【 根據find_package結果進行 】
include_directories(${PCL_INCLUDE_DIRS})
# 包含頭文件的位置。當編譯一個需要第三方庫的項目時,為了讓 cmake知道你在項目里包含的外部頭文件, 需使用include_directories()在–
# --我們的PCL_INCLUDE_DIRS宏(完全包含了我們所需要的頭文件.h)中查找,讓cmake查找到它可能包含的頭文件。
link_directories(${PCL_LIBRARY_DIRS})
# 添加鏈接器的lib庫文件路徑,當編譯一個需要第三方庫的項目時,去哪找庫文件(.so/.dll/.lib/.dylib/…)
# 使用 link_directories,讓cmake在PCL_LIBRARY_DIRS宏中查找它可能包含的lib文件
add_definitions(${PCL_DEFINITIONS})
# 使用add_definitions 讓cmake在PCL_DEFINITIONS中找出它可能包含的預處理器定義和編譯器標志
5、從指定源文件構建可執行文件
add_executable (project_name cloud_viewer_PointXYZ.cpp)
# 告訴cmake我們嘗試生成一個可執行文件,從一個單一的源文件cloud_viewer_PointXYZ.cpp生成一個名字是project_name的可執行文件。–
–CMake會注意生成的后綴名suffix (.exe on Windows platform and blank on UNIX) and the permissions。
# 該命令將從源文件cloud_viewer_PointXYZ.cpp構建可執行程序project_name.exe.
# 如果從多個源文件構建可執行程序則可以表示成:
add_executable (project_namemain.cpp test1.cpp test2.cpp)
或者
add_executable (project_namemain.cpp part.h grab.h interface.h test.cpp test.h)
6、為project構建library
add_library(${project_name} ${${project_name}_src})#默認創建共享library
7、指定可執行文件需要連接的庫 設置要鏈接的庫文件的名稱
target_link_libraries (project_name ${PCL_LIBRARIES})#前一個參數為可執行文件的名字
target_link_libraries (project_name ${PCL_LIBRARIES} libeng.lib libmx.lib libmex.lib libmat.lib Aria.lib winmm.libwsock32.lib)
# 我們生成的可執行文件調用 PCL 函數. 到現在, 我們只包含了PCL頭文件,所以編譯器知道我們調用的方法。–
# --我們也需要讓連接器知道我們鏈接所依賴的庫文件。PCL通過使用PCL_LIBRARIES變量尋找相關庫文件,target_link_libraries()宏對可執行文件與變量進行鏈接。
總結
以上是生活随笔為你收集整理的Cmakelist知识总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: IDEA新手入门教程总结
- 下一篇: 【科普】华为5i耳机降噪效果差,没感觉,