前言
Android NDK以前默認(rèn)使用Android.mk與Application.mk進(jìn)行構(gòu)建温峭,但是在Android Studio2.2之后推薦使用CMake進(jìn)行編譯。
CMake是跨平臺編譯工具胖秒,全稱為cross platform make,內(nèi)建c慕的、c++阎肝、java自動相依性分析功能。NDK通過工具鏈支持CMake肮街,
工具鏈文件是用于自定義交叉編譯工具鏈的CMake文件风题。用于NDK的工具鏈位于/build/cmake/android.toolchain.cmake,
關(guān)于CMake更多詳情請參考官網(wǎng):cmake官網(wǎng)嫉父。下面對比下Android.mk與CMakeLists.txt的語法沛硅。
更詳細(xì)腳本分析可查看博客:NDK編譯腳本分析
一、Android.mk語法
以動態(tài)庫編譯hello模塊為例绕辖,完整腳本如下:
WORKING_DIR := $(call my-dir)
LOCAL_PATH := $(WORKING_DIR)
include $(CLEAR_VARS)
LOCAL_ARM_MODE := arm
LOCAL_MODULE := libffmpeg
LOCAL_SRC_FILES := $(LOCAL_PATH)/ffmpeg/$(TARGET_ARCH_ABI)/lib$(LOCAL_MODULE).so
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/ffmpeg/include
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_ARM_MODE := arm
#模塊名稱
LOCAL_MODULE := hello
#源文件
LOCAL_SRC_FILES := hello.c
#頭文件路徑
LOCAL_C_INCLUDES := $(LOCAL_PATH)
#系統(tǒng)庫依賴
LOCAL_LDLIBS := -llog -lz -lm -landroid
#第三方動態(tài)庫
LOCAL_SHARED_LIBRARIES := libffmpeg
#以動態(tài)庫形式編譯
include $(BUILD_SHARED_LIBRARY)
1摇肌、LOCAL_MODULE
聲明模塊名稱,例如這里編譯出來的動態(tài)庫名稱為libhello.so仪际。
2围小、LOCAL_SRC_FILES
聲明源文件列表,文件之間用空格分開树碱,需要換行時使用''換行符肯适。
3、LOCAL_C_INCLUDES
聲明頭文件路徑成榜,例如$(LOCAL_PATH)/xxx
4疹娶、LOCAL_CPP_EXTENSION
指定C++源文件除.cpp以外的文件擴展名,例如這樣LOCAL_CPP_EXTENSION := .cpp .cxx .cc
5伦连、LOCAL_CPP_FEATURES
指定依賴c++的某些功能雨饺,例如RTTI(運行時類型信息): LOCAL_CPP_FEATURES := rtti 使用c++異常檢測: LOCAL_CPP_FEATURES := exceptions
6钳垮、LOCAL_CFLAGS
在編譯c和c++源文件時編譯系統(tǒng)要傳遞的編譯器標(biāo)記,即指定額外的宏定義或編譯選項额港。 LOCAL_CFLAGS += -I
7饺窿、LOCAL_STATIC_LIBRARIES
共享靜態(tài)庫,作為第三方庫被引用 LOCAL_STATIC_LIBRARIES := libavcodec libavutil libavformat libavfilter
8移斩、LOCAL_SHARED_LIBRARIES
共享動態(tài)庫肚医,與共享靜態(tài)庫一樣作為第三方庫被引用 LOCAL_SHARED_LIBRARIES := libffmpeg
9、LOCAL_LDLIBS
額外鏈接器向瓷,一般為系統(tǒng)庫肠套,使用-l來引用 LOCAL_LDLIBS := -lz -lm
10、 LOCAL_ARM_MODE
ndk默認(rèn)使用thumb模式來生成目標(biāo)二進(jìn)制文件猖任,每條指令為16位寬你稚。也可以指定為ARM模式,來生成32位ARM的目標(biāo)文件:
LOCAL_ARM_MODE := arm
11朱躺、LOCAL_ARM_NEON
用于開啟NEON指令加速刁赖,僅對armeabi-v7a平臺有效。為模塊開啟NEON: LOCAL_ARM_NEON := true 為單獨源文件開啟NEON: LOCAL_SRC_FILES := hello.c.neon
12长搀、TARGET_ARCH
用于指向CPU架構(gòu)宇弛,包括x86、x86_64源请、armeabi-v7a枪芒、 arm64-v8a
13、TARGET_PLATFORM
目標(biāo)平臺谁尸,對應(yīng)Android API級別號病苗,例如Android5.0系統(tǒng)鏡像對應(yīng)Android API級別21:android-21
14、打印信息
可用warning症汹、debug、info贷腕、error級別來打印信息背镇,如果是打印error信息,會終止編譯泽裳。以warning為例: $(warning 'This is a test')
15瞒斩、if條件判斷
采用ifeq關(guān)鍵字,然后左右變量放在括號體內(nèi)涮总,用逗號分隔:
ifeq($(TARGET_ABI), arm64-v8a)
$(debug 'This is arm64-v8a')
endif
二胸囱、Application.mk語法
Android.mk依賴Application.mk文件進(jìn)行編譯,一般Application.mk腳本如下所示:
APP_STL := c++_static
APP_DEBUG := false
APP_OPTIM := release
APP_CPPFLAGS := -frtti
APP_PLATFORM := android-16
APP_ABI := armeabi-v7a arm64-v8a
1瀑梗、APP_ABI
與Android.mk的TARGET_ABI對應(yīng)烹笔,包括CPU架構(gòu)有:x86裳扯、x86_64、armeabi-v7a谤职、arm64-v8a饰豺,支持所有平臺這樣表示:
APP_ABI := all
2、APP_BUILD_SCRIPT
指向編譯腳本的路徑允蜈,一般Android.mk和Application.mk都位于jni目錄冤吨,默認(rèn)指向jni/Android.mk路徑,如果是其他路徑饶套,
需要使用此變量來指定絕對路徑: APP_BUILD_SCRIPT := /xx/xx/Android.mk
3漩蟆、APP_OPTIM
編譯優(yōu)化選項,調(diào)試模式為debug妓蛮,發(fā)布模式為release怠李。在調(diào)試模式下,會保留symbol符號表仔引;在發(fā)布模式下扔仓,會開啟優(yōu)化,去掉symbol符號表咖耘。
4翘簇、APP_PLATFORM
指定編譯平臺,面向于Android API級別儿倒,對應(yīng)gradle聲明的minSdkVersion版保。如果不聲明,默認(rèn)為ndk支持的最低API版本
5夫否、APP_STL
聲明使用c++的標(biāo)準(zhǔn)庫彻犁,默認(rèn)為system STL。其他選項包括c++_static凰慈、c++_shared和none
三汞幢、CMakeLists.txt語法
以編譯hello模塊以及依賴ffmpeg模塊為例:
cmake_minimum_required(VERSION 3.4.1)
#添加動態(tài)庫,包含源文件路徑
add_library( hello
SHARED
src/main/jni/hello.c)
#添加第三方動態(tài)庫
add_library( ffmpeg
SHARED
IMPORTED )
#指定第三方庫路徑
set_target_properties( ffmpeg
PROPERTIES IMPORTED_LOCATION
../../../../libs/${CMAKE_ANDROID_ARCH_ABI}/libffmpeg.so )
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
#指定頭文件路徑
include_directories(src/main/cpp)
if(${CMAKE_ANDROID_ARCH_ABI} MATCHES "armeabi-v7a")
include_directories(src/main/cpp/include/armeabi-v7a)
message("This is armeabi-v7a")
elseif(${CMAKE_ANDROID_ARCH_ABI} MATCHES "arm64-v8a")
include_directories(src/main/cpp/include/arm64-v8a)
message("This is arm64-v8a")
endif()
#查找系統(tǒng)庫
find_library( # Sets the name of the path variable.
log-lib
log )
#鏈接目標(biāo)庫
target_link_libraries( hello
ffmpeg
${log-lib} )
1微谓、add_library
傳遞三個參數(shù)森篷,第一個參數(shù)是模塊名稱,第二個參數(shù)是SHARED或者STATIC豺型。如果是源文件模塊仲智,第三個參數(shù)是源文件列表;
如果是第三方庫姻氨,第三個參數(shù)是IMPORTED钓辆。
2、set_target_properties
用于指定第三方庫路徑,IMPORT_LOCATION一般是指向src/main/cpp目錄
3前联、include_directories
用于指定頭文件路徑功戚,頭文件路徑可以有多個
4、find_library
用于查找系統(tǒng)庫蛀恩,比如Android系統(tǒng)的log日志庫
5疫铜、target_link_libraries
鏈接目標(biāo)庫,把依賴庫都鏈接到目標(biāo)庫中
6双谆、if條件判斷
與Android.mk稍有差異壳咕,CMake采用if...MATCHES形式,例如: if(${CMAKE_ANDROID_ARCH_ABI} MATCHES "armeabi-v7a") ...... endif()
7顽馋、打印日志
與Android.mk不同的是谓厘,CMake采用message函數(shù)來打印日志,括號體傳入msg內(nèi)容 message("hello, cmake")
8寸谜、命令行參數(shù)
命令行參數(shù)前面統(tǒng)一加上-D竟稳,常用的參數(shù):
-DANRDOID_ABI :android的ABI架構(gòu)平臺
-DANDROID_NDK :ndk路徑
-DANDROID_ARM_MODE :arm模式/thumb模式
-DANDROID_ARM_NEON :是否開啟arm neon加速,針對armeabi-v7a平臺
-DANDROID_TOOLCHAIN :編譯工具鏈
-DANDROID_NATIVE_API_LEVEL :與ANDROID_PLATFORM相同熊痴,對應(yīng)minSdkVersion
-DCMAKE_BUILD_TYPE :編譯類型他爸,debug或release
-DCMAKE_MAKE_PROGRAM :編譯程序
-DCMAKE_TOOLCHAIN_FILE :編譯文件
9、命令行編譯
以cmake作為關(guān)鍵字果善,后面帶著指定參數(shù)诊笤,示例如下:
cmake \
-DANDROID_ABI=armeabi-v7a \
-DANDROID_NDK=${HOME}/Android/Sdk/ndk-bundle \
-DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_MAKE_PROGRAM=${HOME}/Android/Sdk/cmake/3.6.3155560/bin/ninja \
-DCMAKE_TOOLCHAIN_FILE=${HOME}/Android/Sdk/ndk-bundle/build/cmake/android.toolchain.cmake \
-DANDROID_NATIVE_API_LEVEL=23 \
-DANDROID_TOOLCHAIN=clang
四、ndk編譯配置
1巾陕、Android.mk方式配置
在gradle的defaultConfig配置ndk:
defaultConfig {
......
ndk {
moduleName "hello"
abiFilters "armeabi-v7a", "arm64-v8a"
}
}
然后配置jni源文件路徑:
sourceSets {
main {
jniLibs.srcDir 'src/main/libs' // Enable to use libs
jni.srcDirs 'src/main/jni' // Enable the automatic ndk-build
}
}
另外配置Android.mk文件絕對路徑:
externalNativeBuild {
ndkBuild {
"src/main/jni/Android.mk"
}
}
2讨跟、CMake方式配置
前兩步與Android.mk方式一樣,配置腳本路徑稍有差異:
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
另外在defaultConfig設(shè)置cppFlags:
externalNativeBuild {
cmake {
cppFlags ""
}
}
五鄙煤、ndk編譯過程
1晾匠、ndk-build編譯
在命令行輸入ndk-build后,會根據(jù)聲明所支持的平臺依次編譯梯刚。首先是armeabi-v7a平臺架構(gòu)凉馆,把hello.c源文件編譯成hello
目標(biāo)文件,然后鏈接成libhello.so動態(tài)庫亡资,最終安裝到libs/armeabi-v7a目錄下澜共。
2、cmake在Gradle中編譯
編譯arm64-v8a平臺架構(gòu)的hello模塊沟于。首先把hello.c源文件編譯成hello.c.o目標(biāo)文件,然后鏈接成libhello.so動態(tài)庫植康。
生成的debug模式動態(tài)庫在/build/intermediates/cmake/debug/obj/arm64-v8a目錄下旷太。