目錄
- ndk-build和makefile
- cmake和cMakeLists.txt
- 資料
- 收獲
AS 2.2 +默認使用CMake進行 NDK 編譯,我們這篇主要學習實踐也是CMake,那么為什么要帶ndk-build吶?
- CMake對編輯構建過程做了高級的封裝,方便調用者使用,但是Cmake并不直接建構出最終的so,而是產生標準的建構文檔Makefile,然后再用一般的建構方式使用。
- 早期的項目有些Makefile和cmakelists.txt都存在。我們至少有能看懂Makefile吧。
為什么需要makefile?
要回答這個問題,就要了解so是如何生成的?步驟如下
- 預處理:宏展開,宏替換,展開include gcc -E -o xxx.i xxx.c
- 預編譯:gcc檢查代碼的規范性,把代碼編譯成匯編 gcc -S -o xxx.s xxx.i
- 匯編: 把.s匯編文件編譯成.o二進制文件 gcc -c -o xxx.o xxx.s
- 鏈接:鏈接其他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中的第一個規則,此規則的第一個目標稱
之為“最終目的”
- 目標文件不存在,使用其描述規則創建它;
- 目標文件存在,目標文件所依賴的.c 源文件、.h 文件中的任何一個比目標
文件“更新”(在上一次 make 之后被修改)。則根據規則重新編譯生成它; - 目標.o 文件存在,目標.o 文件比它的任何一個依賴文件(的.c 源文件、.h 文件)“更新”(它的依賴文件在上一次 make 之后沒有被修改),則什么也不做。
偽目標
.PHONY:xxx
使用偽目標有兩點原因:
- 此目標的目的為了執行執行一些列命令,而不需要創建這個目標。
- 提高執行 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:重新定義目標鏈接庫文件的存放位置
常用指令
- 賦值指令
set(SRC_LIST main.cpp test.cpp)
- 打印指令
message(${PROJECT_SOURCE_DIR})
message("build with debug mode")
- 指定 cmake 的最小版本
cmake_minimum_required(VERSION 3.10.2)
- 設置項目名稱
project(xxx)
它會引入兩個變量 xxx_BINARY_DIR 和 xxx_SOURCE_DIR,同時,cmake 自動定義了兩個等價的變量 PROJECT_BINARY_DIR 和 PROJECT_SOURCE_DIR。
- 生成動態庫
add_library(xxx SHARED xxx.cpp)
- 指定源文件
aux_source_directory(. SRC_LIST)
搜索當前目錄下所有的cpp源代碼文件并將列表存儲在一個變量中。
- 指定一定規則的源文件
file(GLOB SRC_LIST "*.cpp" "xxx/*.cpp")
- 查找指定的庫文件
find_library( log-lib
log )
- 設置 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
四、收獲
- 了解了ndk-build和cmake編譯構建工具的流程
- 了解makefile的規范和基本使用
- 了解cmaklists.txt的基本使用
感謝你的閱讀
接下來幾篇,我們來對C和CPP的一些重要知識進行學習回顧,先掌握語言知識再進行下一步學習探索,歡迎關注公眾號“音視頻開發之旅”,一起學習成長。
歡迎交流