音視頻開發之旅(19)NDK構建方式 ndk-build與cmake

目錄

  1. ndk-build和makefile
  2. cmake和cMakeLists.txt
  3. 資料
  4. 收獲

AS 2.2 +默認使用CMake進行 NDK 編譯,我們這篇主要學習實踐也是CMake,那么為什么要帶ndk-build吶?

  1. CMake對編輯構建過程做了高級的封裝,方便調用者使用,但是Cmake并不直接建構出最終的so,而是產生標準的建構文檔Makefile,然后再用一般的建構方式使用。
  2. 早期的項目有些Makefile和cmakelists.txt都存在。我們至少有能看懂Makefile吧。

為什么需要makefile?
要回答這個問題,就要了解so是如何生成的?步驟如下

  1. 預處理:宏展開,宏替換,展開include gcc -E -o xxx.i xxx.c
  2. 預編譯:gcc檢查代碼的規范性,把代碼編譯成匯編 gcc -S -o xxx.s xxx.i
  3. 匯編: 把.s匯編文件編譯成.o二進制文件 gcc -c -o xxx.o xxx.s
  4. 鏈接:鏈接其他so,合并數據段 gcc -o xxx xxx.o

涉及到多個文件多個目錄的編譯時,要對所有的文件執行上述操作,如果某個文件發生變化,要做到相關聯的文件重新編譯鏈接,更是十分麻煩,而makefile的出現就是解決這一痛點,通過makefile配置和規則,實現編譯的自動化,提升效率。在AndroidStudio上可以通過配置Android.mk以及Application.mk,使用ndk-build進行編譯成動態或者靜態庫。

一、Makefile文件解析

1.1 Makefile規則介紹

一個完整的 Makefile 中,包含了 5 個東西:顯式規則、隱含規則、變量定義、指示符和注釋。

顯示規則

target… : prerequisites… 
【Tab】command

target:
規則的目標,通常是最后需要生成的文件名或者中間產物。可以是是.o文件,也可以是最后的可執行文件。
也可以是“偽目標”,一個make執行的動作的名稱,比如 “clean”,偽規則需要用.PHONY 申明

prerequisites:
規則的依賴。生成target所需的文件名列表。

command:
規則的命令行。 任意的shell或者可在shell下執行的程序。
一個規則可以有多個命令行,但每一條命令占一行。
注意: 每一個命令行必須以【Tab】字符開始,它告訴make此行是一個命令行

默認的情況下,make執行的是Makefile中的第一個規則,此規則的第一個目標稱
之為“最終目的”

  1. 目標文件不存在,使用其描述規則創建它;
  2. 目標文件存在,目標文件所依賴的.c 源文件、.h 文件中的任何一個比目標
    文件“更新”(在上一次 make 之后被修改)。則根據規則重新編譯生成它;
  3. 目標.o 文件存在,目標.o 文件比它的任何一個依賴文件(的.c 源文件、.h 文件)“更新”(它的依賴文件在上一次 make 之后沒有被修改),則什么也不做。

偽目標

.PHONY:xxx

使用偽目標有兩點原因:

  1. 此目標的目的為了執行執行一些列命令,而不需要創建這個目標。
  2. 提高執行 make 時的效率

變量與函數

“$(objects)”
$(xx):取變量的值

模式規則的自動化變量

$@:表示規則中的目標
$^:表示規則中所有的依賴文件,組成一個列表,以空格隔開,如果這個列表有重復項則消除重復
$<:表示規則中第一個依賴文件,如果運行在模式套用中,相當于依次取出依賴條件,套用該模式規則。

兩個常用函數

$(wildcard PATTERN) 列出當前目錄下所有符合模式“PATTERN”格式的文件名

wildcard : 把當前文件加下的所有*.c文件都賦值為src
src = $(wildcard *.c)
模式替換函數—patsubst
obj = $(patsubst %c,%o,$(src))

指示符
include: 在一個 Makefile 中包含其它的 makefile 文件

“-include”:忽略由于包含文件不存在或者無法創建時的錯誤提示

1.2 Android.mk的用法

基本格式如下

LOCAL_PATH := $(call my-dir)  
include $(CLEAR_VARS) 
include $(LOCAL_PATH)/xxx/xxx.mk
 
LOCAL_MODULE    := xxx 
LOCAL_C_INCLUDES := xxx
LOCAL_SRC_FILES := xxx.c  
LOCAL_SHARED_LIBRARIES := xxx  
LOCAL_LDLIBS := xxx
 
include $(BUILD_SHARED_LIBRARY) 

下面逐一對其解析

LOCAL_PATH := $(call my-dir)  

my-dir是一個宏, 返回Android.mk的所在的文件夾路徑。
call 使用makefile的一個函數,LOCAL_PATH := $(call my-dir) 的意義是,把當前文件所在的文件夾路徑賦值給LOCAL_PATH。

include $(CLEAR_VARS)

CLEAR_VARS 負責清理除上面剛定義的LOCAL_PATH 外的其他LOCAL_xxx.

include $(LOCAL_PATH)/xxx/xxx.mk

如果遇到比較大的項目,會在自文件夾下建立對應的makefile,方便開發維護,在編譯時,痛毆include把他們引用進來一起編譯。

定義一些LOCAL_變量,含義如下

LOCAL_MODULE    : 生成so的名稱,不帶前綴lib和后綴.so
LOCAL_MODULE_PATH : 生成so的輸出目標地址
LOCAL_C_INCLUDES:包含的頭文件,如果多個用\來進行換行
LOCAL_SRC_FILES :需要編譯的源文件
LOCAL_SHARED_LIBRARIES : 會生成依賴關系,當庫不存在時會去編譯這個庫
LOCAL_LDLIBS  : 鏈接的庫不產生依賴關系,用于不需要重新編譯的庫
include $(BUILD_SHARED_LIBRARY) 

BUILD_SHARED_LIBRARY:是Build System提供的一個變量,指向一個GNU Makefile Script。

1.3 Application.mk

基本格式如下

APP_PLATFORM = android-16
APP_ABI := armeabi-v7a arm64-v8a x86
APP_STL := c++_static
APP_CPPFLAGS := -exceptions -fno-rtti
APP_OPTIM := release

下面對其解析

APP_PLATFORM :聲明構建此應用所面向的 Android API 級別,并對應于應用的 minSdkVersion

使用 Gradle 和 externalNativeBuild 時,不應直接設置此參數,有minSdkVersion確定可支持的下限。

APP_ABI : Application Binary Interface 設置為特定cpu架構生成對應的so
注意: Gradle 的 externalNativeBuild 會忽略該APP_ABI

APP_STL : 用于此應用的 C++ 標準庫。
默認情況下使用 system STL。其他選項包括 c++_shared、c++_static 和 none

APP_CPPFLAGS :為項目中的所有 C++ 編譯傳遞的標記

APP_OPTIM: release 或 debug.

有了ndk-build構建工具以及makefile編譯配置文件為什么又引入了Cmake構建工具吶?我們來一起繼續學習。

二、Cmake構建

什么是Cmake
“CMake”:”cross platform make”,是個開源的跨平臺自動化建構系統,它用配置文件CMakeLists.txt控制建構過程(build process)。

如果工程很大,相關性比較強,每個文件夾下都有對應的makefile,就會變得相對繁瑣,cmake的出現就是為了解決這樣的問題。cmake是為了生成makefile而存在,這樣我們就不需要再去寫makefile了,只需要寫簡單的CMakeLists.txt即可

常用變量
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:重新定義目標鏈接庫文件的存放位置

常用指令

  1. 賦值指令
set(SRC_LIST main.cpp test.cpp)
  1. 打印指令
    message(${PROJECT_SOURCE_DIR})
    message("build with debug mode")
  1. 指定 cmake 的最小版本
cmake_minimum_required(VERSION 3.10.2)
  1. 設置項目名稱
project(xxx)

它會引入兩個變量 xxx_BINARY_DIR 和 xxx_SOURCE_DIR,同時,cmake 自動定義了兩個等價的變量 PROJECT_BINARY_DIR 和 PROJECT_SOURCE_DIR。

  1. 生成動態庫
add_library(xxx SHARED xxx.cpp) 
  1. 指定源文件
aux_source_directory(. SRC_LIST) 

搜索當前目錄下所有的cpp源代碼文件并將列表存儲在一個變量中。

  1. 指定一定規則的源文件
file(GLOB SRC_LIST "*.cpp" "xxx/*.cpp")
  1. 查找指定的庫文件
find_library( log-lib
              log )
  1. 設置 target 需要鏈接的庫
target_link_libraries(native-lib
                       ${log-lib} )

示例如下

cmake_minimum_required(VERSION 3.10.2)

project("mediajourney")

message(${PROJECT_SOURCE_DIR})
message(${PROJECT_NAME} "PROJECT_BINARY_DIR :"${PROJECT_BINARY_DIR})

message(${CMAKE_CURRENT_SOURCE_DIR} \n ${CMAKE_CURRENT_BINARY_DIR})
message("--->")

message(${mediajourney_SOURCE_DIR})


add_library(
             native-lib
             SHARED
             native-lib.cpp )

find_library(
              log-lib
              log )
target_link_libraries(
                       native-lib
                       ${log-lib} )

運行后可以如下路徑中看到運行結果
.cxx/cmake/debug/${ABI}/build_output.txt

CMake Warning (dev) at /Users/yabin/work/av/mediaJourneyNative/app/src/main/cpp/CMakeLists.txt:7:
  Syntax Warning in cmake code at column 47

  Argument not separated from preceding token by whitespace.
This warning is for project developers.  Use -Wno-dev to suppress it.

/Users/xxx/work/av/mediaJourneyNative/app/src/main/cpp
mediajourneyPROJECT_BINARY_DIR :/Users/xxx/work/av/mediaJourneyNative/app/.cxx/cmake/debug/x86
/Users/xxx/work/av/mediaJourneyNative/app/src/main/cpp
/Users/xxxx/work/av/mediaJourneyNative/app/.cxx/cmake/debug/x86
--->
/Users/xxx/work/av/mediaJourneyNative/app/src/main/cpp
--->
Configuring done

三、資料

《GNU_makefile中文手冊》
《cmake實戰》
[CMakeLists.txt 語法介紹與實例演練]: https://blog.csdn.net/afei__/article/details/81201039

四、收獲

  1. 了解了ndk-build和cmake編譯構建工具的流程
  2. 了解makefile的規范和基本使用
  3. 了解cmaklists.txt的基本使用

感謝你的閱讀

接下來幾篇,我們來對C和CPP的一些重要知識進行學習回顧,先掌握語言知識再進行下一步學習探索,歡迎關注公眾號“音視頻開發之旅”,一起學習成長。

歡迎交流

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,967評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,273評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,870評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,742評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,527評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,010評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,108評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,250評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,769評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,656評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,853評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,371評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,103評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,472評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,717評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,487評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,815評論 2 372

推薦閱讀更多精彩內容