NDK<第六篇>:Android.mk在AS中的使用

【1】ndkBuild配置

android {
    compileSdk 32

    defaultConfig {
        // ...
        externalNativeBuild {
            ndkBuild {
                abiFilters 'armeabi-v7a'
            }
        }
        ndk { // "armeabi-v7a", "arm64-v8a"
            abiFilters 'armeabi-v7a'
        }
    }

     // ...

    externalNativeBuild {
        ndkBuild{
            path file('src/main/cpp/Android.mk')
        }
}

在模塊下的 build.gradle 中配置 ndkBuild,確認(rèn)CPU架構(gòu)以及Android.mk路徑。

【2】將源碼編譯成動(dòng)態(tài)庫(kù)

# 每個(gè)Android.mk文件必須以定義LOCAL_PATH為開(kāi)始
# 宏my-dir則由Build System提供,返回包含Android.mk的目錄路徑
LOCAL_PATH := $(call my-dir)

# 清除所有LOCAL_開(kāi)頭的變量,除了LOCAL_PATH
include $(CLEAR_VARS)

# 模塊名稱,需要保證模塊名稱的唯一性
LOCAL_MODULE := native-lib

# 指定將要打包的源碼,多個(gè)文件用空格隔開(kāi),如果需要換行,在換行處添加“\”
LOCAL_SRC_FILES := native-lib.cpp

# 負(fù)責(zé)收集自從上次調(diào)用 include $(CLEAR_VARS) 后的所有LOCAL_XXX信息,并決定編譯成什么
# BUILD_STATIC_LIBRARY:編譯為靜態(tài)庫(kù)
# BUILD_SHARED_LIBRARY:編譯為動(dòng)態(tài)庫(kù)
# BUILD_EXECUTABLE:編譯為可執(zhí)行程序
include $(BUILD_SHARED_LIBRARY)

編譯之后,動(dòng)態(tài)庫(kù)libnative-lib.so會(huì)在模塊下的 build/intermediates/ndkBuild/ 目錄下生成。

image.png

【3】Android.mk 編譯時(shí)日志

#輸出字符串的方式有如下三種,程度越來(lái)越高,有點(diǎn)像Android里面的Log等級(jí)
$(info string)    #一般打印信息,僅僅是顯示調(diào)試信息
$(warning string) #警告級(jí)別打印信息,不會(huì)停止腳本運(yùn)行,一般用于嚴(yán)重警告
$(error string)   #錯(cuò)誤級(jí)別,會(huì)停止編譯直接退出來(lái)

【4】c/c++代碼日志在AS控制臺(tái)上現(xiàn)實(shí)

# 加載NDK的日志庫(kù)
LOCAL_LDLIBS := -llog
# 負(fù)責(zé)收集自從上次調(diào)用 include $(CLEAR_VARS) 后的所有LOCAL_XXX信息,并決定編譯成什么
# BUILD_STATIC_LIBRARY:編譯為靜態(tài)庫(kù)
# BUILD_SHARED_LIBRARY:編譯為動(dòng)態(tài)庫(kù)
# BUILD_EXECUTABLE:編譯為可執(zhí)行程序
include $(BUILD_SHARED_LIBRARY)

需要在 include $(BUILD_SHARED_LIBRARY) 前面添加 LOCAL_LDLIBS := -llog,加載NDK中自帶的日志庫(kù)。

在源代碼中定義宏:

#define LOG_TAG "native-lib"
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)

在源代碼中,打印日志到AS控制臺(tái):

LOGI("start thread task");

【5】Android.mk 生成動(dòng)態(tài)庫(kù)

LOCAL_PATH := $(call my-dir)
# 清除所有LOCAL_開(kāi)頭的變量,除了LOCAL_PATH
include $(CLEAR_VARS)
# 模塊名稱,需要保證模塊名稱的唯一性
LOCAL_MODULE := Test
# 指定將要打包的源碼,多個(gè)文件用空格隔開(kāi),如果需要換行,在換行處添加“\”
LOCAL_SRC_FILES := Test.cpp
# 負(fù)責(zé)收集自從上次調(diào)用 include $(CLEAR_VARS) 后的所有LOCAL_XXX信息,并決定編譯成什么
# BUILD_STATIC_LIBRARY:編譯為靜態(tài)庫(kù)
# BUILD_SHARED_LIBRARY:編譯為動(dòng)態(tài)庫(kù)
# BUILD_EXECUTABLE:編譯為可執(zhí)行程序
include $(BUILD_SHARED_LIBRARY)

編譯之后, 自動(dòng)生成動(dòng)態(tài)庫(kù),可以在build的以下目錄中找到。

image.png

【6】使用預(yù)先編譯好的動(dòng)態(tài)庫(kù)

編寫(xiě)mk:

# 每個(gè)Android.mk文件必須以定義LOCAL_PATH為開(kāi)始
# 宏my-dir則由Build System提供,返回包含Android.mk的目錄路徑
LOCAL_PATH := $(call my-dir)

# 清除所有LOCAL_開(kāi)頭的變量,除了LOCAL_PATH
include $(CLEAR_VARS)
# 模塊名稱,需要保證模塊名稱的唯一性
LOCAL_MODULE := Test
# 指定將要打包的動(dòng)態(tài)庫(kù)
LOCAL_SRC_FILES := libTest.so
# PREBUILT_SHARED_LIBRARY:動(dòng)態(tài)庫(kù)
# PREBUILT_STATIC_LIBRARY:靜態(tài)庫(kù)
include $(PREBUILT_SHARED_LIBRARY)

# 清除所有LOCAL_開(kāi)頭的變量,除了LOCAL_PATH
include $(CLEAR_VARS)
# 模塊名稱,需要保證模塊名稱的唯一性
LOCAL_MODULE := native-lib
# 指定將要打包的源碼,多個(gè)文件用空格隔開(kāi),如果需要換行,在換行處添加“\”
LOCAL_SRC_FILES := native-lib.cpp
# 加載Test庫(kù)
LOCAL_SHARED_LIBRARIES := Test
# 加載NDK的日志庫(kù)
LOCAL_LDLIBS := -llog
# 負(fù)責(zé)收集自從上次調(diào)用 include $(CLEAR_VARS) 后的所有LOCAL_XXX信息,并決定編譯成什么
# BUILD_STATIC_LIBRARY:編譯為靜態(tài)庫(kù)
# BUILD_SHARED_LIBRARY:編譯為動(dòng)態(tài)庫(kù)
# BUILD_EXECUTABLE:編譯為可執(zhí)行程序
include $(BUILD_SHARED_LIBRARY)

include $(PREBUILT_SHARED_LIBRARY) 預(yù)編譯動(dòng)態(tài)庫(kù),LOCAL_SHARED_LIBRARIES := Test 加載動(dòng)態(tài)庫(kù)到apk中:

image.png

在c++源碼中聲明libTest.so中的函數(shù)

// 如果是C,需要添加 extern "C"{}
extern int test();

如果調(diào)用的是C代碼,需要更改為:

extern "C"{
    extern int test();
}

【7】變量和宏

定義自己的任意變量。在定義變量時(shí)請(qǐng)注意,NDK 構(gòu)建系統(tǒng)會(huì)預(yù)留以下變量名稱:

  • LOCAL_ 開(kāi)頭的名稱,例如 LOCAL_MODULE
  • PRIVATE_、NDK_APP 開(kāi)頭的名稱。構(gòu)建系統(tǒng)在內(nèi)部使用這些變量。
  • 小寫(xiě)名稱,例如 my-dir。構(gòu)建系統(tǒng)也是在內(nèi)部使用這些變量。

如果為了方便而需要在 Android.mk 文件中定義自己的變量,建議在名稱前附加 MY_

【8】其它語(yǔ)法詳解

1、`LOCAL_MODULE` 可以設(shè)置模塊名稱:

    LOCAL_MODULE := Test

2、使用 `LOCAL_MODULE_FILENAME` 可以覆蓋 `LOCAL_MODULE` 設(shè)置的模塊名稱。

    LOCAL_MODULE_FILENAME := libTest

3、LOCAL_ARM_MODE

缺省模式下,ARM目標(biāo)代碼被編譯為thumb模式。每個(gè)指令16位。如果指定此變量為'arm',則指令為32位。

    LOCAL_ARM_MODE := arm  

4、TARGET_ARCH

目標(biāo)CPU架構(gòu)名,如果為“arm” 則聲稱ARM兼容的指令,與CPU架構(gòu)版本無(wú)關(guān)。

    ifeq ($(TARGET_ARCH),arm)
      ...
    endif

5、TARGET_PLATFORM

目標(biāo)平臺(tái)的名字,對(duì)應(yīng)android版本號(hào),取值包括:android-8、android-9...android-21。

    ifeq ($(TARGET_PLATFORM),android-8)  
      ... 
    endif

6、TARGET_ARCH_ABI:cpu架構(gòu)

取值包括:

32位:armeabi、armeabi-v7a、x86、mips;
64位:arm64-v8a、x86_64、mips64;

ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
  $(warning armeabi-v7a)
endif
ifeq ($(TARGET_ARCH_ABI),armeabi)
  $(warning armeabi)
endif
ifeq ($(TARGET_ARCH_ABI),mips)
  $(warning mips)
endif
ifeq ($(TARGET_ARCH_ABI),x86)
  $(warning x86)
endif

7、NDK_MODULE_PATH

NDK_MODULE_PATH 是一個(gè)環(huán)境變量,不是Android.mk中設(shè)置的變量,
可以在 Android.mk中設(shè)置NDK_MODULE_PATH:

$(call import-add-path,$(LOCAL_PATH))
$(call import-add-path,$(LOCAL_PATH)/../external)

環(huán)境變量可以設(shè)置多個(gè),以上配置只設(shè)置了兩個(gè)。

$(call import-module,android/cpufeatures)
$(call import-module,freetype2/prebuilt/android)
$(call import-module,platform/android)
$(call import-module,png/prebuilt/android)

以上配置的意思是:在環(huán)境變量的目錄下:
    尋找android目錄,在android目錄下尋找cpufeatures目錄;
    尋找freetype2目錄,在freetype2目錄下尋找prebuilt目錄,在prebuilt目錄下尋找android目錄;
    尋找platform目錄,在platform目錄下尋找android目錄;
    尋找png目錄,在png目錄下尋找prebuilt目錄,在prebuilt目錄下尋找android目錄。

8、LOCAL_CPP_EXTENSION
(可選)指出C++擴(kuò)展名。

    LOCAL_CPP_EXTENSION := .cxx

從NDK R7后,可以寫(xiě)多個(gè):

    LOCAL_CPP_EXTENSION := .cxx .cpp .cc    

9、LOCAL_CPP_FEATURES
(可選)用來(lái)指定C++ features。

    LOCAL_CPP_FEATURES := rtti  
    LOCAL_CPP_FEATURES := exceptions

10、LOCAL_C_INCLUDES

一個(gè)可選的path列表,相對(duì)于NDK ROOT目錄,編譯時(shí)將會(huì)把這些目錄附上,主要為了頭文件的引用。

    LOCAL_C_INCLUDES := sources/foo  
    LOCAL_C_INCLUDES := $(LOCAL_PATH)/../foo

11、LOCAL_CFLAGS

(可選)在編譯C/C++ source 時(shí)添加如Flags,用來(lái)附加編譯選項(xiàng)。
注意:不要嘗試在此處修改編譯的優(yōu)化選項(xiàng)和Debug等級(jí),它會(huì)通過(guò)您Application.mk中的信息自動(dòng)指定。

-Wall:是打開(kāi)警告開(kāi)關(guān)。

-O:代表默認(rèn)優(yōu)化,可選:-O0不優(yōu)化,-O1低級(jí)優(yōu)化,-O2中級(jí)優(yōu)化,-O3高級(jí)優(yōu)化,-Os代碼空間優(yōu)化。

-g:是生成調(diào)試信息,生成的可執(zhí)行文件具有和源代碼關(guān)聯(lián)的可調(diào)試的信息。

-fopenmp:OpenMp是由OpenMP Architecture Review Board牽頭提出的,并已被廣泛接受的,
    用于共享內(nèi)存并行系統(tǒng)的多處理器程序設(shè)計(jì)的一套指導(dǎo)性的編譯處理方案(Compiler Directive)。
    OpenMP支持的編程語(yǔ)言包括C語(yǔ)言、C++和Fortran;而支持OpenMp的編譯器包括Sun Compiler,GNU Compiler和Intel Compiler等。
    OpenMp提供了對(duì)并行算法的高層的抽象描述,程序員通過(guò)在源代碼中加入專(zhuān)用的pragma來(lái)指明自己的意圖,
    由此編譯器可以自動(dòng)將程序進(jìn)行并行化,并在必要之處加入同步互斥以及通信。 
    當(dāng)選擇忽略這些pragma,或者編譯器不支持OpenMp時(shí),程序又可退化為通常的程序(一般為串行),
    代碼仍然可以正常運(yùn)作,只是不能利用多線程來(lái)加速程序執(zhí)行。

-D:增加全局宏定義

-ffast-math:浮點(diǎn)優(yōu)化選項(xiàng),極大地提高浮點(diǎn)運(yùn)算速度。

-mfloat-abi=softfp 浮點(diǎn)運(yùn)算

12、LOCAL_CPPFLAGS

    C++ Source 編譯時(shí)添加的C Flags,這些Flags將出現(xiàn)在LOCAL_CFLAGS flags 的后面。

13、LOCAL_WHOLE_STATIC_LIBRARIES

    靜態(tài)庫(kù)全鏈接,不同于LOCAL_STATIC_LIBRARIES,類(lèi)似于使用--whole-archive,
    LOCAL_WHOLE_STATIC_LIBRARIES在連接靜態(tài)連接庫(kù)的時(shí)候不會(huì)移除"daed code",
    何謂dead code呢,就是調(diào)用者模塊永遠(yuǎn)都不會(huì)用到的代碼段和變量。

14、LOCAL_LDLIBS

鏈接flags,鏈接的庫(kù)不產(chǎn)生依賴關(guān)系,一般用于不需要重新編譯的庫(kù),可以用它來(lái)添加系統(tǒng)庫(kù)。

    LOCAL_LDLIBS += -lm –lz –lc -lcutils –lutils –llog …

15、LOCAL_ALLOW_UNDEFINED_SYMBOLS

默認(rèn)情況下,在試圖編譯一個(gè)共享庫(kù)時(shí),任何未定義的引用將導(dǎo)致一個(gè)“未定義的符號(hào)”錯(cuò)誤。
然而,如果你因?yàn)槟承┰?,需要不啟?dòng)這項(xiàng)檢查,把這個(gè)變量設(shè)為'true'。
注意相應(yīng)的共享庫(kù)可能在運(yùn)行時(shí)加載失敗。(這個(gè)一般盡量不要去設(shè)為true)

16、LOCAL_ARM_NEON

設(shè)置為true時(shí),會(huì)將浮點(diǎn)編譯成neon指令。
這會(huì)極大地加快浮點(diǎn)運(yùn)算(前提是硬件支持),只有targeting為'armeabi-v7a'時(shí)才可以。

17、LOCAL_DISABLE_NO_EXECUTE

Android NDK r4版本開(kāi)始支持這種"NX bit"的安全功能。
默認(rèn)是啟用的,你也可以設(shè)置該變量的值為true來(lái)禁用它。
但不推薦這么做。該功能不會(huì)修改ABI,只在ARMv6+CPU的設(shè)備內(nèi)核上啟用。

18、LOCAL_EXPORT_CFLAGS

定義這個(gè)變量用來(lái)記錄C/C++編譯器標(biāo)志集合,
并且會(huì)被添加到其他任何以LOCAL_STATIC_LIBRARIES和LOCAL_SHARED_LIBRARIES的模塊的LOCAL_CFLAGS定義中。

注意:此處NDK版本為NDK R7C。(不同NDK版本,ndk-build所產(chǎn)生的Makefile并不完全相同)

19、LOCAL_LDFLAGS 

# 指定動(dòng)態(tài)庫(kù)的路徑以及ffmpeg庫(kù)
LOCAL_LDFLAGS := -L$(LOCAL_PATH)/src/main/jniLibs/$(TARGET_ARCH_ABI) -lffmpeg

[本章完...]

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容