今天是新專欄《AI白身境》的第六篇,,所謂白身,,就是什么都不會,還沒有進(jìn)入角色,。 對于大部分小白來說,,因為python用的太爽,以致于或許都沒有聽說過CMake,。python是腳本語言,,而當(dāng)前大量的AI算法都部署在移動端嵌入式平臺,需要使用c/java語言,,因此熟悉CMake和Makefile也是必備的基礎(chǔ),。 作者 | 湯興旺 言有三 編輯 | 湯興旺 言有三 01 g++必備基礎(chǔ) 在學(xué)習(xí)CMake和和Makefile之前我們先學(xué)下g++這個工具,大家或許會問為什么要學(xué)g++,,不應(yīng)該直接學(xué)CMake和Makefile嗎,。實際上如果你不掌握g++根本就不會寫Makefile,因為它實際上就是對g++代碼的整理,,有了Makefile,,執(zhí)行程序會更加快速方便。另外CMake就是為了簡化Makefile的編寫,,它可以自動生成Makefile,。 1.1 安裝g++ 我們在安裝g++之前可以看一下自己是否已經(jīng)安裝了g++,,因為ubuntu安裝后就默認(rèn)安裝了g++,下面命令可查看自己g++版本,。 Tips:如果不想作死,,就不要手賤去降級或者升級g++版本。 g++ --version 因為我已經(jīng)安裝了g++,,出現(xiàn)了上面安裝的版本號,。如果你出現(xiàn)了上面信息,就不需要再安裝了,,沒有的話,,用下面的命令即可完成安裝。 sudo apt-get install g++ 安裝好后也可以通過g++ --version查看是否安裝成功 1.2 編譯流程 現(xiàn)在我們已經(jīng)安裝好了g++,,接下來通過寫一個簡單的程序來看看整個的編譯流程,。 我們通過vim創(chuàng)建一個test.cpp文件,測試的代碼如下: #include <iostream> using namespace std; int main() { cout << "Hello, world!" <<endl; return 0; } 測試代碼完成后,,我們來進(jìn)行下編譯,,打開終端,在終端輸入g++ 文件名即可,,在這個程序中就是下面命令: g++ test.cpp 注意這里的文件名是包括路徑的,,要是不知道文件路徑的話可以在敲完g++和空格之后直接把文件拖進(jìn)去,系統(tǒng)會自動添加文件路徑,。 在終端完成上面的命令后,,你發(fā)現(xiàn)并沒有任何輸出,但這時候你去主文件夾下(默認(rèn)主文件夾)看下會發(fā)現(xiàn)有個a.out文件 現(xiàn)在你再在終端輸入下面命令就能看到結(jié)果,。 ./a.out 接下來我來解釋下這個.out文件,,實際上這是個經(jīng)過相應(yīng)的鏈接產(chǎn)生的可執(zhí)行文件。還有個.o文件,,它是個中間文件,,一般是通過編譯的但還未鏈接。我們通過看看g++在執(zhí)行編譯工作的時候的流程,,你就會有更好的理解,。如下: 1.預(yù)處理,生成.i的文件 2.將預(yù)處理后的文件轉(zhuǎn)換成匯編語言,,生成.s文件 3.將匯編變?yōu)槟繕?biāo)代碼(機(jī)器代碼),生成.o的文件 4.連接目標(biāo)代碼,生成可執(zhí)行程序 對于這個流程,,我們結(jié)合上面的例子,,再詳細(xì)介紹下,如下: 1.預(yù)處理階段 首先在終端輸入下面代碼: g++ -E test.cpp > test.i 預(yù)處理后的文件在 linux下以.i為后綴名,,這個過程是用來激活預(yù)處理,,執(zhí)行完命令后,,你會發(fā)現(xiàn)主文件夾下多了一個test.i文件 這一步(預(yù)處理)主要做了宏的替換,和注釋的消除,。 上圖是test.i文件的最后部分,,可以看見宏的替換和注釋的消除。 2.將預(yù)處理后的文件轉(zhuǎn)換成匯編語言 在終端輸入下面代碼: g++ -S test.cpp 這一步主要就是生成test.s文件,,.s文件表示匯編文件,,用編輯器打開就都是匯編指令。下圖是test.s文件的一部分,。 3.將匯編語言變?yōu)槟繕?biāo)代碼(機(jī)器代碼) 在終端輸入下面代碼: g++ -c test.cpp 這一步就是生成目標(biāo)文件,,用編輯器打開就都是二進(jìn)制機(jī)器碼。 4.鏈接目標(biāo)代碼,,生成可執(zhí)行程序 在終端輸入下面代碼: g++ test.o -o test 在這一步中生成的可執(zhí)行程序名為test,,如果執(zhí)行命令 g++ test.o 這樣默認(rèn)生成a.out 最后我們再看下這個過程中產(chǎn)生的所有文件,如下: 這就是編譯的整個過程,,你掌握了嗎,,這個過程對于后面編寫Makefile非常重要,一定要深刻理解,。 02 Makefile必備基礎(chǔ) 上面我們對g++和編譯過程進(jìn)行了介紹,,現(xiàn)在我們繼續(xù)學(xué)習(xí)如何編寫Makefile。 2.1 Makefile介紹 Makefile描述了整個工程的編譯,、鏈接等規(guī)則,,它定義了一系列規(guī)則來指定哪些文件需要編譯以及如何編譯、需要創(chuàng)建哪些庫文件以及如何創(chuàng)建這些庫文件,、如何產(chǎn)生我們想要的可執(zhí)行文件,。 而且Makefile可以有效的減少大工程中需要編譯和鏈接的文件,只編譯和鏈接那些需要修改的文件,,可以說使用Makefile,,整個工程都可以完全自動化編譯。 2.2 Makefile基本格式 target ... : prerequisites ... target - 目標(biāo)文件, 可以是 Object File, 也可以是可執(zhí)行文件 prerequisites - 生成target所需要的文件或者目標(biāo) command - make需要執(zhí)行的命令(任意的shell命令),,Makefile中的命令必須以 [tab] 開頭 2.3 Makefile語法 Makefile包含了五個重要的東西:顯示規(guī)則,、隱晦規(guī)則、變量定義,、文件指示和注釋,。詳細(xì)解釋如下: 1. 顯示規(guī)則: 通常在寫makefile時使用的都是顯式規(guī)則,這需要指明target和prerequisite文件,。一條規(guī)則可以包含多個target,,這意味著其中每個target的prerequisite都是相同的。當(dāng)其中的一個target被修改后,,整個規(guī)則中的其他target文件都會被重新編譯或執(zhí)行,。 2. 隱晦規(guī)則: make的自動推導(dǎo)功能所執(zhí)行的規(guī)則 3. 變量的定義: Makefile中定義的變量,,一般是字符串
Makefile中引用其他Makefile;指定Makefile中有效部分,;定義一個多行命令 5. 注釋: Makefile只有行注釋 "#", 如果要使用或者輸出"#"字符, 需要進(jìn)行轉(zhuǎn)義, "\# 2.4 Makefile簡單實例 盡管上面介紹了許多Makefile的知識點,,但我相信一定你很暈,接下來我通過一個實例來說明如何編寫Makefile,。 2.4.1 準(zhǔn)備程序文件 我們使用opencv對下面這只可愛的貓進(jìn)行讀取顯示,。 在這里我們用c++和opencv對圖片進(jìn)行讀取和顯示,程序保存在DisplayImage.cpp這個文件里,,代碼如下: #include <stdio.h> #include <opencv2/opencv.hpp> using namespace cv; int main(int argc, char** argv ) { if ( argc != 2 ) { printf("usage: DisplayImage.out <Image_Path>\n"); return -1; } Mat image; image = imread( argv[1], 1 ); if ( !image.data ) { printf("No image data \n"); return -1; } namedWindow("Display Image", WINDOW_AUTOSIZE ); imshow("Display Image", image); waitKey(0); return 0; } 2.4.2 Makefile編寫 上面我們已經(jīng)準(zhǔn)備好了.cpp文件,,現(xiàn)在我們來編寫Makefile進(jìn)而進(jìn)行編譯,程序如下: 現(xiàn)在我來解釋下應(yīng)該如何編寫這個Makefile,,對于編寫Makefile我建議從下往上寫,。步驟如下: 1.編寫clean 這一步在Makefile中基本差不多,它的作用就是刪除所有的.o文件和可執(zhí)行文件,。為什么這樣做呢?我舉個例子說明下,,如果你有100個.cpp文件,經(jīng)過編譯后會得到一個可執(zhí)行文件,。在這個過程中我們會得到許多不必要的文件,,例如100個.o文件,但這個文件又沒有用,,如果用rm的話那就太麻煩了,,所以我們用了clean,它可以很輕松完成這個任務(wù),。另外請注意Makefile文件在執(zhí)行時不會執(zhí)行clean這個命令,,需要我們調(diào)用才會執(zhí)行,即make clean,。clean代碼如下: 2.編寫目標(biāo)文件1:依賴文件1 目標(biāo)文件就是你想得到的文件,,依賴文件就是你目前所擁有的東西。在本實例中我們現(xiàn)在擁有DisplayImage.cpp,,所以DisplayImage.cpp是依賴文件,,我們想得到DisplayImage.o,所以它是目標(biāo)文件,。代碼如下: 3.編寫目標(biāo)文件2:依賴文件2 這一步的依賴文件2實際就是第二步的目標(biāo)文件1,,在第二步我們通過DisplayImage.cpp得到了DisplayImage.o,現(xiàn)在我們需要通過DisplayImage.o得到可執(zhí)行文件DisplayImage,。所以在這一步目標(biāo)文件是DisplayImage,,依賴文件是DisplayImage.o,代碼如下: 4.應(yīng)用opencv庫和頭文件 這一步就需要根據(jù)自己計算機(jī)來配置了,對于我們初學(xué)者來說挺麻煩的,,可以自己嘗試下。有問題可以聯(lián)系我們,。 編寫完makefile后,,我們在終端make下就行了。下面編譯后的文件: 最后在終端輸入下面代碼即可顯示圖片,。 ./DisplayImage 01.jpg 總體來說編寫Makefile可以按照這個套路寫,,多寫幾次就會了。 03 CMake必備基礎(chǔ) 說完Makefile,,我們再說下CMake,。CMake是一個跨平臺的編譯(Build)工具,可以用簡單的語句來描述所有平臺的編譯過程,,其是在make基礎(chǔ)上發(fā)展而來的,,早期的make需要程序員寫Makefile文件,進(jìn)行編譯,,而現(xiàn)在CMake能夠通過對cmakelists.txt的編輯,,輕松實現(xiàn)對復(fù)雜工程的組織。下面我?guī)Т蠹覍W(xué)習(xí)下CMake的基礎(chǔ)知識,。 3.1 安裝CMake 首先我們看看如何在自己的linux系統(tǒng)(我的系統(tǒng)Ubuntu18.04)下安裝CMake,。方法如下: sudo apt-get install cmake 輸入上面命令后實際上就安裝成功了,可以通過下面命令來檢查: cmake --version 如果你的界面如下圖所示即說明安裝成功,。 3.2 CMake編譯流程 成功安裝好CMake后我們再來說說如何在linux平臺下使用CMake生成Makefile并編譯的流程,,如下: 1.編寫CMake配置文件CMakeLists.txt,我們可以認(rèn)為CMakeLists.txt就是CMake所處理的"代碼",。 2.執(zhí)行命令 cmake path生成Makefile,其中path是CMakeLists.txt所在的目錄,。 3.使用make命令進(jìn)行編譯。 3.3 使用CMake編譯程序 我們通過一個關(guān)于opencv讀取圖片的程序,,讓大家更好的理解整個CMake的編譯過程,。 3.3.1 準(zhǔn)備程序文件 這里程序準(zhǔn)備可以按照第二部分makefile那里準(zhǔn)備。最后文件目錄結(jié)構(gòu)如下: ├── build ├── CMakeLists.txt ├── DisplayImage.cpp opencv讀取圖片的程序?qū)懲旰?,我們需要編寫CMake處理的代碼了,,即CMakeLists.txt。 3.3.2 編寫CMakeLists.txt 現(xiàn)在我們編寫CMakeLists.txt文件,,該文件實際上放在哪里都可以,,只要編寫的路徑能夠正確指向就好了,CMakeLists.txt文件內(nèi)容如下所示: cmake_minimum_required(VERSION 2.8) project( DisplayImage ) find_package( OpenCV REQUIRED ) add_executable( DisplayImage DisplayImage.cpp ) target_link_libraries( DisplayImage ${OpenCV_LIBS} ) 看到這些代碼是不是很悶逼,,為了讓大家明白CMakeLists.txt文件內(nèi)容,,接下來我說一下Cmake的一些常用命令,你就能很好的理解上面的代碼了。 1)cmake_minimum_required命令 命令語法:cmake_minimum_required(VERSION major[.minor[.patch[.tweak]]][FATAL_ERROR]) 命令簡述:用于指定需要的CMake 的最低版本 使用范例:cmake_minimum_required(VERSION 2.8) 2)project 命令 命令語法:project(<projectname> [languageName1 languageName2 … ] ) 命令簡述:用于指定項目的名稱,,一般和項目的文件夾名稱對應(yīng) 使用范例:project(DisplayImage) 3)aux_source_directory命令 命令語法:aux_source_directory(<dir> <variable>) 命令簡述:用于將 dir 目錄下的所有源文件的名字保存在變量 variable 中 使用范例:aux_source_directory(src DIR_SRCS) 4)add_executable 命令 命令語法:add_executable(<name> [WIN32] [MACOSX_BUNDLE][EXCLUDE_FROM_ALL] source1 source2 … sourceN) 命令簡述:用于指定從一組源文件 source1 source2 … sourceN 編譯出一個可執(zhí)行文件且命名為name 使用范例:add_executable( DisplayImage DisplayImage.cpp ) 5)target_link_libraries命令 命令語法:target_link_libraries(<target> [item1 [item2 […]]][[debug|optimized|general] ] …) 命令簡述:用于指定 target 需要的鏈接 item1 item2 …,。這里 target 必須已經(jīng)被創(chuàng)建,鏈接的 item 可以是已經(jīng)存在的 target(依賴關(guān)系會自動添加) 使用范例:target_link_libraries( DisplayImage ${OpenCV_LIBS} ) 6)add_subdirectory 命令 命令語法:add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL]) 命令簡述:用于添加一個需要進(jìn)行構(gòu)建的子目錄 使用范例:add_subdirectory(Lib) 7)include_directories 命令 命令語法:include_directories([AFTER|BEFORE] [SYSTEM] dir1 dir2 …) 命令簡述:用于設(shè)定目錄,,這些設(shè)定的目錄將被編譯器用來查找 include 文件 使用范例:include_directories(${PROJECT_SOURCE_DIR}/lib) 像這樣的命令還有很多,,如find_package()尋找使用第三方庫等,這些都需要我們平時多加積累,。給大家一個查詢命令的方法,,大家可以多去看cmake官網(wǎng)的help,鏈接如下: https:///cmake/help/v2.8.8/cmake.html#section_Commands 3.3 編譯和運行程序 現(xiàn)在CMakeLists.txt文件已經(jīng)編寫好了,,意味著我們的工作即將進(jìn)入尾聲?,F(xiàn)在看看我們的文件結(jié)構(gòu)目錄,如下圖: 接下來我們就需要進(jìn)行編譯了,。編譯的過程相對于CMakeLists.txt文件的編寫是很簡單的,,只有兩步,如下 cmake make 其中cmake命令將CMakeLists.txt文件轉(zhuǎn)化為make所需要的makefile文件,,最后用make命令編譯源碼生成可執(zhí)行程序或共享庫,。對于我們這個實例,編譯如下: 首先我們在命令行輸入cmake .(注意cmake和.之間有空格),,表明Cmakelist.txt文件在當(dāng)前目錄下,。 接下來在命令行輸入make 這樣我們就編譯成功了,我們看下編譯后的文件目錄 解釋下這個build文件夾,,由于cmake后會生成很多編譯的中間文件以及makefile文件,,所以一般建議新建一個新的目錄,專門用來編譯,,這就是這里的build,,打開build后,里面的文件如下: 到這里,,我們不禁要問怎么沒有圖片顯示呢,,別急,在build目錄下的命令行輸入下面命令即可顯示圖片,,這就是生產(chǎn)的DisplayImage可執(zhí)行文件,。 ./DisplayImage ../01.jpg 到這里,關(guān)于CMake的一些基本操作就介紹的差不多了,,其實對于CMake的學(xué)習(xí)我認(rèn)為必須在實例中多加應(yīng)用,,才能更好的掌握,因為它的復(fù)雜命令太多了,。 CMake和Makefile的基礎(chǔ)我們就介紹完了,,對于這兩個工具其實不是一時就能學(xué)會的,,需要大量的實踐積累才能游刃有余。 下期預(yù)告:下一期我們會講AI領(lǐng)域必須掌握的數(shù)據(jù)爬蟲基礎(chǔ),,如果你有建議,,歡迎留言,我們會及時采納的,。 轉(zhuǎn)載文章請后臺聯(lián)系 侵權(quán)必究 |
|