Android.mk的解析

2020-08-11 19:28:15

一、概述

Android.mk作用是將原始檔按照module進行分組,然後將module生成靜態庫、共用庫或者可執行檔案

一個Android.mk裏面可能有1個或者多個module,不同的module之間可以使用相同的原始檔。

Android.mk裏面不用列出頭檔案,Build System會幫我們處理

只有動態庫可以被install或者copy到應用程式包,靜態庫可以被鏈接成動態庫

二、語法

舉例說明:

LOCAL_PATH := $(call my-dir)

 

include $(CLEAR_VARS)

 

LOCAL_MODULE    := hello-jni

LOCAL_SRC_FILES := hello-jni.c

 

include $(BUILD_SHARED_LIBRARY)

解析如下:

LOCAL_PATH := $(call my-dir)

Android.mk必須以定義LOCAL_PATH開頭,表示原始檔的位置,my-dir返回的是Android.mk的目錄路徑

 

include $(CLEAR_VARS)

負責清除所有的LOCAL_XXX變數,例如:LOCAL_MODULE、 LOCAL_SRC_FILES、LOCAL_STATIC_LIBRARIES等,但是不包括LOCAL_PATH,這個清理動作必須要有的,因爲這些比阿尼朗都是全域性的,防止之前被賦值,導致當前module解析編譯異常

 

LOCAL_MODULE := hello-jni

LOCAL_MODULE可以設定模組的名字,模組的名字必須是唯一的並且中間不能包含空格,Build System會自動新增字首和後綴,例如:模組名字是foo,要生成動態庫,那麼生成的動態庫名字就是libfoo.so,其中字首是lib,後綴是.so,但是如果名字是libfoo,字首就不會新增了。生成的動態庫的名字依然是libfoo.so

 

LOCAL_SRC_FILES := hello-jni.c

LOCAL_SRC_FILES變數裏面包含的是模組用到的C/C++的原始碼,不必列出頭檔案,Build System會自動幫我們找到依賴檔案,當包含C++原始碼是,這些檔案的後綴預設是.cpp,如果檔案後綴不是.cpp,則需要通過LOCAL_CPP_EXTENSION進行修改(後面單獨介紹這個變數)。

 

include $(BUILD_SHARED_LIBRARY)

BUILD_SHARED_LIBRARY變數指向一個GNU Makefile指令碼,它負責收集自從上次呼叫 include $(CLEAR_VARS)到這一行之間的所有LOCAL_XXX資訊,並編譯成動態庫。也可以使用別的值,例如:

BUILD_STATIC_LIBRARY:編譯爲靜態庫。

BUILD_SHARED_LIBRARY :編譯爲動態庫

BUILD_EXECUTABLE:編譯爲Native C可執行程式

三、NDK Build System變數:

1、NDK Build System 保留了以下變數名:

(1)以LOCAL_  爲開頭的

(2)以PRIVATE_ ,NDK_ 或者APP_ 開頭的名字

(3)小寫字母名字,如my-dir

注意:如果要在Android.mk定義自己使用的變數名,建議使用MY_ 開頭

2、NDK 定義的 include 變數

2.1:CLEAR_VARS:這個變數指向一個編譯指令碼,這個指令碼是用來取消幾乎所有LOCAL_XXX變數(LOCAL_PATH除外)。所以在描述新模組之前,必須使用此變數

例如:

include $(CLEAR_VARS)

 

2.2:BUILD_SHARED_LIBRARY:指向一個編譯指令碼,這個指令碼會收集自從上次呼叫 include $(CLEAR_VARS)  到這一行之間所有的LOCAL_XXX資訊。並決定如何這中間列出的原始檔編譯成動態庫。

注意:在此行之前前,至少應該包含:LOCAL_MODULE和LOCAL_SRC_FILES

例如:

include $(BUILD_SHARED_LIBRARY)

 

2.3:BUILD_STATIC_LIBRARY:與2.2類似,它也指向一個編譯指令碼,這個指令碼會收集自從上次呼叫 include $(CLEAR_VARS) 到這一行之間所有的LOCAL_XXX資訊。並決定如何這中間列出的原始檔編譯成靜態庫。

注意:靜態庫不能夠加入到Project 或者APK中。但它可以用來生成動態庫。

例如:

include $(BUILD_STATIC_LIBRARY)

 

2.4: BUILD_EXECUTABLE: 與2.2類似,它也指向一個編譯指令碼,這個指令碼會收集自從上次呼叫 include $(CLEAR_VARS) 到這一行之間所有的LOCAL_XXX資訊。並決定如何這中間列出的原始檔編譯成Native程式。

例如:

include $(BUILD_EXECUTABLE)

 

2.5:PREBUILT_SHARED_LIBRARY:指向一個編譯指令碼,這個指令碼可以指定一個預編譯的共用庫,與BUILD_SHARED_LIBRARY和BUILD_STATIC_LIBRARY不同,此時LOCAL_SRC_FILES應該被指定爲預編譯動態庫的路徑(例如:/foo/libfoo.so),而不是原始檔

例如:

include $(PREBUILT_SHARED_LIBRARY)

例如:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := foo-prebuilt     # 模組名

LOCAL_SRC_FILES := libfoo.so     # 模組的檔案路徑(相對於 LOCAL_PATH)

include $(PREBUILT_SHARED_LIBRARY)

預編譯庫的使用請參照https://developer.android.google.cn/ndk/guides/prebuilts

 

2.6: PREBUILT_STATIC_LIBRARY: 預先編譯的靜態庫。

與2.5類似

 

3、平臺資訊變數

Build system根據APP_ABI變數所指定的每個 ABI 分別解析一次Android.mk,該變數通常在Application.mk檔案中定義。如果APP_ABI爲all表示Build System會根據NDK支援的每個ABI分別解析一次Android.mk

 

3.1: TARGET_ARCH:

這個參數表示的是CPU架構,可以是arm、arm64、x86或x86_64中的一個

 

3.2: TARGET_PLATFORM:

這個參數表示的是Android API level,例如:Android 5.1的Android API level對應的是android-22

例如:

ifeq ($(TARGET_PLATFORM),android-22)

    # ... do something ...

Endif

 

3.3:TARGET_ARCH_ABI

這個參數表示的是CPU架構及其支援的ABI

例如:

ifeq ($(TARGET_ARCH_ABI),arm64-v8a)

    # ... do something ...

Endif

 

3.4:TARGET_ABI

這個參數表示的是Android API level和ABI

例如:

ifeq ($(TARGET_ABI),android-22-arm64-v8a)

    # ... do something ...

Endif

 

4、模組描述變數

這類變數是用來向Build System描述模組資訊,位於include $(CLEAR_VARS)和 include $(BUILD_XXXXX)之間。其中include $(CLEAR_VARS)用來清空這些變數,所以在一個新模組開始的時候,需要先呼叫include $(CLEAR_VARS)

 

4.1: LOCAL_PATH:

這個變數指定當前檔案的路徑,必須在Android.mk的開頭,另外include $(CLEAR_VARS)不會清理這個變數

例如:

LOCAL_PATH := $(call my-dir)

 

4.2: LOCAL_MODULE:

這個變數用來設定模組名,模組名必須是唯一的,並且模組名內部不能有空格,在include $(BUILD_XXXXX)之前,必須要先定義這個變數。無需新增lib字首和.so或.a後綴,build system會自動新增,如果名字是lib開頭,例如libfoo,則build system不會再新增lib字首

例如:

LOCAL_MODULE := "foo"

 

4.3: LOCAL_MODULE_FILENAME:

這個變數是可選的,LOCAL_MODULE只能生成libxxx.so或者libxxx.a這種名字,如果要生成其他名字可以使用這個變數替換系統自動生成的名字。

例如:

LOCAL_MODULE := foo

LOCAL_MODULE_FILENAME := libnewfoo

這樣生成的名字就是libnewfoo.so而不是libfoo.so

 

4.4:LOCAL_SRC_FILES:

這個變數提供build system用來生成模組的原始檔列表。不需要列出依賴檔案。檔案路徑可以是相對路徑也可以是絕對路徑,絕對路徑是相對於LOCAL_PATH的,官方建議使用相對路徑提高可以執行

例如:

LOCAL_SRC_FILES := $(call all-java-files-under, src)   

LOCAL_SRC_FILES += \

                src/com/sdk/service/abc.aidl \

                src/com/sdk/service/abcCallback.aidl

 

4.5: LOCAL_CPP_EXTENSION:

這個變數是可選的,用來設定C++原始檔的擴充套件名

例如:

LOCAL_CPP_EXTENSION := .cxx

LOCAL_CPP_EXTENSION := .cxx .cpp .cc

 

4.6:LOCAL_CPP_FEATURES:

這個變數是可選的,用來指定C++ features,C++features啓用 -frtti和-fexceptions時,不要使用LOCAL_CPPFLAGS,這個變數會導致編譯器將制定的features用於所有模組,所以要使用LOCAL_CPP_FEATURES

例如:

LOCAL_CPP_FEATURES := rtti

LOCAL_CPP_FEATURES := exceptions

也可以這樣

LOCAL_CPP_FEATURES := rtti features

 

4.7:LOCAL_C_INCLUDES:

這個變數是可選的,新增編譯時,C、C++標頭檔案搜尋路徑

例如:

LOCAL_C_INCLUDES := sources/foo

LOCAL_C_INCLUDES := $(LOCAL_PATH)/<subdirectory>/foo

Define this variable before setting any corresponding inclusion flags via LOCAL_CFLAGS or LOCAL_CPPFLAGS.

 

4.8: LOCAL_CFLAGS:

這個變數是可選的,可以在編譯C/C++原始檔時用來附加編譯選項。

注意:不要在此處修改編譯的優化選項和Debug等級。Build system會使用Application.mk中的資訊自動處理。

可以使用LOCAL_CFLAGS指定標頭檔案檢測路徑

LOCAL_CFLAGS += -I<path>,

這個方法比使用LOCAL_C_INCLUDES要好,這樣也可以被ndk-debug使用。

 

4.9: LOCAL_CPPFLAGS:

這個變數是可選的,只能給C++原始檔附加編譯選項。要在LOCAL_CFLAGS之後

 

4.10: LOCAL_STATIC_LIBRARIES:

這個變數用來列出當前模組需要的靜態庫的列表

 

4.11: LOCAL_SHARED_LIBRARIES:

這個變數用來列出當前模組需要的動態庫的列表

 

4.12:LOCAL_WHOLE_STATIC_LIBRARIES:

具體參考--whole-archive

 

4.13:LOCAL_LDLIBS:

這個變數可以用來新增系統庫。 如 -lz:

例如:

LOCAL_LDLIBS := -lz

注意:這個是編譯動態庫用的,爲靜態庫使用此變數,會被忽略

 

4.14: LOCAL_LDFLAGS

這個變數可以用來指定其他ld,例如要在ARM/X86上使用ld.bfd鏈接器

LOCAL_LDFLAGS += -fuse-ld=bfd

注意:這個是編譯動態庫用的,爲靜態庫使用此變數,會被忽略

 

4.15: LOCAL_ALLOW_UNDEFINED_SYMBOLS:

預設情況下,build system在編譯動態庫的時候遇到未定義的參照,會報錯: undefined symbol?error,如果不想報錯,可以將這個變數設定成true

注意:這個是編譯動態庫用的,爲靜態庫使用此變數,會被忽略

 

4.16: LOCAL_ARM_MODE:

預設情況下,build system會以thumb模式生成ARM平臺的二進制檔案。每個指令16位元。如果指定此變數爲:arm。 則指令爲32位元。

例如:

LOCAL_ARM_MODE := arm

也可以在檔案後面加.arm表示以arm模式編譯此原始檔,例如:

LOCAL_SRC_FILES := foo.c bar.c.arm

表示以arm模式編譯bar.c,以LOCAL_ARM_MODE的模式編譯foo.c

 

4.17: LOCAL_ARM_NEON:

這個變數只有在armeabi-v7a ABI的平臺上纔有意義,也可以在檔案後面加.neon,例如:

LOCAL_SRC_FILES = foo.c.neon bar.c zoo.c.arm.neon

含義是:foo.c按照neon編譯,bar.c按照LOCAL_ARM_MODE編譯,zoo.c按照arm和neon編譯

注意:同時使用.arm和.neon時,.arm必須在.neon前面

 

4.18:LOCAL_DISABLE_FORMAT_STRING_CHECKS:

預設情況下,build system在編譯程式碼的時候會對標準格式的字串進行保護,如果在printf中有非標準格式的字串就會報錯,設定這個變數爲true,可以不報錯,這個變數預設是關閉的,不建議開啓

 

4.19: LOCAL_EXPORT_CFLAGS:

這個變數可以記錄一組 C/C++ 編譯器flags,其他模組通過LOCAL_STATIC_LIBRARIES 或 LOCAL_SHARED_LIBRARIES 變數使用這個模組的時候,這些flags將會新增到其他模組的 LOCAL_CFLAGS中

例如:

include $(CLEAR_VARS)

LOCAL_MODULE := foo

LOCAL_SRC_FILES := foo/foo.c

LOCAL_EXPORT_CFLAGS := -DFOO=1

include $(BUILD_STATIC_LIBRARY)

 

include $(CLEAR_VARS)

LOCAL_MODULE := bar

LOCAL_SRC_FILES := bar.c

LOCAL_CFLAGS := -DBAR=2

LOCAL_STATIC_LIBRARIES := foo

include $(BUILD_SHARED_LIBRARY)

build system在編譯bar.c時候,LOCAL_CFLAGS就會有-DFOO=1和-DBAR=2兩個flags

另外這個變數還有傳遞性:例如:如果zoo依賴於bar,而bar依賴於foo,那麼zoo也會繼承從foo導出的所有flags

但是編譯foo的時候,不會使用LOCAL_EXPORT_CFLAGS中的flags,即-DFOO=1這個flags在編譯foo的時候不會使用

 

4.20 LOCAL_EXPORT_CPPFLAGS

這個變數跟4.19類似,但是隻適用於C++的flags

 

4.21 LOCAL_EXPORT_C_INCLUDES

這個變數跟4.19類似,但適用於C的標頭檔案。例如,當 bar.c 需要包括模組 foo 的標頭檔案時,此變數很有用。

 

4.22 LOCAL_EXPORT_LDFLAGS

這個變數跟4.19類似,但適用於ld

 

4.23 LOCAL_EXPORT_LDLIBS

這個變數跟4.19類似,但是是告訴build system向編譯器傳遞某些系統庫的,每個庫名稱前都要加-l,這些系統庫會被追加其他模組(呼叫這個模組的)的LOCAL_LDLIBS變數下

例如:

include $(CLEAR_VARS)

LOCAL_MODULE := foo

LOCAL_SRC_FILES := foo/foo.c

LOCAL_EXPORT_LDLIBS := -llog

include $(BUILD_STATIC_LIBRARY)

 

include $(CLEAR_VARS)

LOCAL_MODULE := bar

LOCAL_SRC_FILES := bar.c

LOCAL_STATIC_LIBRARIES := foo

include $(BUILD_SHARED_LIBRARY)

build system在編譯libbar.so時,將在鏈接器命令的末尾指定?-llog。這樣就會告知鏈接器,由於libbar.so依賴於foo,因此它也依賴於系統log庫。

 

4.24 LOCAL_SHORT_COMMANDS

Windows下用這個,不建議開啓

 

4.25 LOCAL_THIN_ARCHIVE

編譯靜態庫的時候用可以減小生成的二進制檔案,但是這個檔案不能移動到其他位置,因爲裏面的路徑都是相對路徑

注意:非靜態庫和預編譯靜態庫不能用

 

4.26 LOCAL_FILTER_ASM

 

 

5、NDK提供的函數宏:

這些宏通過類似$(call <function>)的方式來得到其值,將返迴文字資訊。

5.1: my-dir:

$(call my-dir): 這個宏返回的是最近一次include的Makefile的路徑。通常返回Android.mk所在的路徑。它用來作爲Android.mk的開頭來定義LOCAL_PATH.

例如:

LOCAL_PATH := $(call my-dir)

注意:這個宏返回的是最近一次include的Makefile的路徑。所以在Include其它Makefile後,再呼叫$(call my-dir)會返回其它Android.mk 所在路徑。

例如:

LOCAL_PATH := $(call my-dir)

# ... declare one module

include $(LOCAL_PATH)/foo/`Android.mk`

 

LOCAL_PATH := $(call my-dir)

 

# ... declare another module

這裏LOCAL_PATH最後應該是:$PATH/foo,而不是$PATH.

如果要保留第一次的LOCAL_PATH,那麼只能將第一次$(call my-dir)儲存在其他變數中,例如:

MY_LOCAL_PATH := $(call my-dir)

LOCAL_PATH := $(MY_LOCAL_PATH)

 

# ... declare one module

 

include $(LOCAL_PATH)/foo/`Android.mk`

 

LOCAL_PATH := $(MY_LOCAL_PATH)

 

# ... declare another module

這樣就可以將一開始LOCAL_PATH存放到MY_LOCAL_PATH裏面,以備後面使用

 

5.2: all-subdir-makefiles:

返回包含在my-dir中所有子目錄中的Android.mk的列表

例如:

sources/foo/Android.mk

sources/foo/lib1/Android.mk

sources/foo/lib2/Android.mk

在sources/foo/Android.mk 中使用

include $(call all-subdir-makefiles)

那麼會自動include sources/foo/lib1/Android.mk和sources/foo/lib2/Android.mk

 

5.3:this-makefile:

返回當前Android.mk的路徑。

 

5.4:parent-makefile:

返回當前Android.mk的上面一層的Android.mk的路徑

5.5 grand-parent-makefile

返回當前Android.mk的上面兩層的Android.mk的路徑

 

5.6:import-module:

使用模組名尋找並include這個模組的Android.mk,例如:

$(call import-module,<name>)

 

它會從NDK_MODULE_PATH目錄下尋找模組名<name>的模組並include這個模組的Android.mk。

 

6、其他變數

6.1 LOCAL_MODULE_TAGS

定義模組標籤,Build system會根據標籤決定哪些模組被安裝

可以設定下面 下麪這些值:

user:指該模組只在user版本下才編譯

eng:指該模組只在eng版本下才編譯

tests:指該模組只在tests版本下才編譯

optional:指該模組在所有版本下都編譯

例如:

LOCAL_MODULE_TAGS := optional

 

6.2 all-java-files-under

這個宏可以用來獲取java原始碼,例如:

$(call all-java-files-under, <src>)

獲取<src>目錄下的所有 Java 檔案

all-java-files-under宏的定義是在build/core/definitions.mk中

 

6.3 LOCAL_PACKAGE_NAME

這個變數用來設定packet的名字

例如:

LOCAL_PACKAGE_NAME := Bluetooth

 

6.4 LOCAL_PRIVATE_PLATFORM_APIS

設定爲true時,編譯時可以使用sdk隱藏的api,例如

LOCAL_PRIVATE_PLATFORM_APIS := true

 

6.5 LOCAL_SDK_VERSION

編譯時不可以使用sdk隱藏的api,有時一些系統的class被import後編譯時說找不到這個類,就是這個原因造成的。

例如:

LOCAL_SDK_VERSION := current

LOCAL_SDK_VERSION也可以設定其他值中的一個:current system_current test_current core_current,具體含義暫時不清楚

注意:LOCAL_SDK_VERSION和LOCAL_PRIVATE_PLATFORM_APIS只能使用其中的一個,如果都不使用會報錯

關於api可以分析下面 下麪幾類:

Internal api:內部api,只能是sdk內部使用的,這類介面是不對外公開的

Hide api:隱藏api,在原始碼中使用@hide 標記的方法或者類,這類介面本意是公開的,但是目前扔不穩定,所以不建議使用

其他api:第三方app也可以使用的api

 

6.6 LOCAL_CERTIFICATE

指定用什麼簽名,例如:

LOCAL_CERTIFICATE := platform

表示使用platform簽名

 

6.7 LOCAL_USE_AAPT2

表示是否使用aapt2工具

Aapt是android asset packaging tool的縮寫,是編譯和打包資源的工具,aapt2是在aapt的基礎上做了優化

例如:

LOCAL_USE_AAPT2 := true

 

6.8 LOCAL_JNI_SHARED_LIBRARIES

這個變數表示編譯時用到的JNI共用庫

例如:

LOCAL_JNI_SHARED_LIBRARIES := libbluetooth_jni

 

6.9 LOCAL_JAVA_LIBRARIES

這個變數用來指定依賴的java共用庫,這裏只是依賴,不會將庫打包到apk中

例如:

LOCAL_JAVA_LIBRARIES := javax.obex telephony-common services.net

 

6.10 LOCAL_STATIC_JAVA_LIBRARIES

這個變數用來指定依賴的java靜態庫,這裏最終會將對應的庫打包到apk中

例如:

LOCAL_STATIC_JAVA_LIBRARIES := \

        com.android.vcard \

        bluetooth.mapsapi \

        sap-api-java-static \

        services.net \

        libprotobuf-java-lite \

        bluetooth-protos-lite

 

6.11 LOCAL_STATIC_ANDROID_LIBRARIES

這個變數表明要呼叫的android的包,例如v7 v13包等

例如:

LOCAL_STATIC_ANDROID_LIBRARIES := android-support-v4

 

6.12 LOCAL_REQUIRED_MODULES

這個變數可以指定依賴的模組,一旦本模組安裝,那麼這個變數指定的依賴的模組也會被安裝

例如:LOCAL_REQUIRED_MODULES := libbluetooth

 

6.13 LOCAL_PROGUARD_ENABLED

混淆器設定,預設是full obfuscation,即全程式碼混淆,disabled表示不開啓混淆器

例如:

LOCAL_PROGUARD_ENABLED := disabled

 

6.14 all-Iaidl-files-under

這個宏可以獲取指定目錄下aidl檔案

例如:

$(call all-Iaidl-files-under, <src>)

 

6.15 all-c-files-under

這個宏可以獲取指定目錄下C語言檔案

例如:

$(call all-c-files-under, <src>)

 

6.16 all-makefiles-under

這個宏可以獲取指定目錄下makefile檔案

例如:

$(call all-makefiles-under, <folder>)

 

6.17 BUILD_HOST_STATIC_LIBRARY

表示編譯成主機上的靜態庫

 

6.18 BUILD_HOST_SHARED_LIBRARY

表示編譯成主機上的動態庫

 

6.18 BUILD_HOST_EXECUTABLE

表示編譯成主機上的可執行檔案

 

6.19 BUILD_JAVA_LIBRARY

表示編譯成java動態庫

 

6.20 BUILD_STATIC_JAVA_LIBRARY

表示編譯成java靜態庫

 

6.21 BUILD_PACKAGE

表示編譯成apk程式

 

6.22 BUILD_HOST_JAVA_LIBRARY

表示編譯成主機上的java動態庫