Android.mk文件語(yǔ)法詳解

Android.mk簡(jiǎn)介:

Android.mk文件用來(lái)告知NDK Build系統(tǒng)關(guān)于Source的信息。Android.mk將是GNU Makefile的一部分,且將被Build System解析一次或多次。
所以,請(qǐng)盡量少的在Android.mk中聲明變量,也不要假定任何東西不會(huì)在解析過(guò)程中定義。

Android.mk文件語(yǔ)法允許我們將Source打包成一個(gè)"modules",modules可以是:

  • 靜態(tài)庫(kù)
  • 動(dòng)態(tài)庫(kù)

只有動(dòng)態(tài)庫(kù)可以被install/copy到應(yīng)用程序包(APK), 靜態(tài)庫(kù)則可以被鏈接入動(dòng)態(tài)庫(kù)。

可以在一個(gè)Android.mk中定義一個(gè)或多個(gè)modules. 也可以將同一份source加進(jìn)多個(gè)modules。

Build System幫我們處理了很多細(xì)節(jié)而不需要我們?cè)訇P(guān)心。例如:你不需要在Android.mk中列出頭文件和外部依賴(lài)文件。
NDK Build System自動(dòng)幫我們提供這些信息。這也意味著,當(dāng)用戶(hù)升級(jí)NDK后,你將可以受益于新的toolchain/platform而不必再去修改Android.mk。

Android.mk語(yǔ)法

1. 基本語(yǔ)法

首先看一個(gè)最簡(jiǎn)單的Android.mk的例子:

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)

每個(gè)Android.mk文件必須以定義LOCAL_PATH為開(kāi)始,它用于在開(kāi)發(fā)tree中查找源文件;宏my-dir則由Build System提供,返回包含Android.mk的目錄路徑。

include $(CLEAR_VARS)

CLEAR_VARS 變量由Build System提供,并指向一個(gè)指定的GNU Makefile,由它負(fù)責(zé)清理很多LOCAL_xxx。例如:LOCAL_MODULE, LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES等等。但不清理LOCAL_PATH,這個(gè)清理動(dòng)作是必須的,因?yàn)樗械木幾g控制文件由同一個(gè)GNU Make解析和執(zhí)行,其變量是全局的,所以清理后才能避免相互影響。

LOCAL_MODULE    := hello-jni

LOCAL_MODULE模塊必須定義,以表示Android.mk中的每一個(gè)模塊。名字必須唯一且不包含空格。
Build System會(huì)自動(dòng)添加適當(dāng)?shù)那熬Y和后綴。例如,foo,要產(chǎn)生動(dòng)態(tài)庫(kù),則生成libfoo.so。 但請(qǐng)注意:如果模塊名被定為:libfoo,則生成libfoo.so,不再加前綴。

LOCAL_SRC_FILES := hello-jni.c

LOCAL_SRC_FILES 變量必須包含將要打包如模塊的 C/C++ 源碼。
不必列出頭文件,Build System 會(huì)自動(dòng)幫我們找出依賴(lài)文件。
缺省的C++源碼的擴(kuò)展名為.cpp。 也可以修改,通過(guò)LOCAL_CPP_EXTENSION。

include $(BUILD_SHARED_LIBRARY)

BUILD_SHARED_LIBRARY 是Build System提供的一個(gè)變量,指向一個(gè)GNU Makefile Script。
它負(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:編譯為Native C可執(zhí)行程序

2. NDK Build System變量

NDK Build System 保留以下變量名:

  • LOCAL_PRIVATE_,NDK_、APP_ 開(kāi)頭的名字
  • 小寫(xiě)字母名字,如:my-dir
    如果想要定義自己在Android.mk中使用的變量名,建議添加 MY_ 前綴。

2.1 NDK提供的變量

此類(lèi)GNU Make變量是NDK Build System在解析Android.mk之前就定義好了的。

2.1.1 CLEAR_VARS

指向一個(gè)編譯腳本,必須在新模塊前包含之。

include $(CLEAR_VARS)
2.1.2 BUILD_SHARED_LIBRARY

指向一個(gè)編譯腳本,它收集自從上次調(diào)用include $(CLEAR_VARS) 后的所有LOCAL_XXX信息。
并決定如何將你列出的Source編譯成一個(gè)動(dòng)態(tài)庫(kù)。 注意:在包含此文件前,至少應(yīng)該包含:LOCAL_MODULE and LOCAL_SRC_FILES,例如:

include $(BUILD_SHARED_LIBRARY)   
2.1.3 BUILD_STATIC_LIBRARY

與前面類(lèi)似,它也指向一個(gè)編譯腳本,
收集自從上次調(diào)用 include $(CLEAR_VARS) 后的所有LOCAL_XXX信息。
并決定如何將你列出的Source編譯成一個(gè)靜態(tài)庫(kù)。 靜態(tài)庫(kù)不能夠加入到Project 或者APK中。但它可以用來(lái)生成動(dòng)態(tài)庫(kù)。
LOCAL_STATIC_LIBRARIES and LOCAL_WHOLE_STATIC_LIBRARIES將描述之。

include $(BUILD_STATIC_LIBRARY)
2.1.4 BUILD_EXECUTABLE

與前面類(lèi)似,它也指向一個(gè)編譯腳本,收集自從上次調(diào)用 include $(CLEAR_VARS) 后的所有LOCAL_XXX信息。
并決定如何將你列出的Source編譯成一個(gè)可執(zhí)行Native程序。

include $(BUILD_EXECUTABLE) 
2.1.5 PREBUILT_SHARED_LIBRARY

把這個(gè)共享庫(kù)聲明為 “一個(gè)” 獨(dú)立的模塊。
指向一個(gè)build 腳本,用來(lái)指定一個(gè)預(yù)先編譯好多動(dòng)態(tài)庫(kù)。 與BUILD_SHARED_LIBRARY and BUILD_STATIC_LIBRARY不同,
此時(shí)模塊的LOCAL_SRC_FILES應(yīng)該被指定為一個(gè)預(yù)先編譯好的動(dòng)態(tài)庫(kù),而非source file.

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE := foo-prebuilt     # 模塊名
LOCAL_SRC_FILES := libfoo.so     # 模塊的文件路徑(相對(duì)于 LOCAL_PATH)

include $(PREBUILT_SHARED_LIBRARY) # 注意這里不是 BUILD_SHARED_LIBRARY

這個(gè)共享庫(kù)將被拷貝到 $PROJECT/obj/local 和 $PROJECT/libs/ (stripped) 主要是用在將已經(jīng)編譯好的第三方庫(kù)
使用在本Android Project中。為什么不直接將其copy到libs/armabi目錄呢?因?yàn)檫@樣做缺陷很多。

2.1.6 PREBUILT_STATIC_LIBRARY

預(yù)先編譯的靜態(tài)庫(kù),同上。

2.1.7 TARGET_ARCH

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

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

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

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

是反應(yīng)當(dāng)前的cpu/abi的類(lèi)型,取值包括:
32位:armeabi、armeabi-v7a、x86、mips;
64位:arm64-v8a、x86_64、mips64;

ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)  
  RS_TRIPLE := armv7-none-linux-gnueabi  
endif  
ifeq ($(TARGET_ARCH_ABI),armeabi)  
  RS_TRIPLE := arm-none-linux-gnueabi  
endif  
ifeq ($(TARGET_ARCH_ABI),mips)  
  RS_TRIPLE := mipsel-unknown-linux  
endif  
ifeq ($(TARGET_ARCH_ABI),x86)  
  RS_TRIPLE := i686-unknown-linux  
endif  

2.2 NDK提供的功能宏

GNU Make 提供的功能宏,只有通過(guò)類(lèi)似: $(call function) 的方式來(lái)得到其值,它將返回文本化的信息。

2.2.1 my-dir

返回最近一次include的Makefile的路徑,通常返回Android.mk所在的路徑,它用來(lái)作為Android.mk的開(kāi)頭來(lái)定義LOCAL_PATH。

LOCAL_PATH := $(call my-dir)

注意:==返回的是最近一次include的Makefile的路徑。所以在include其它Makefile后,再調(diào)用$(call my-dir)會(huì)返回其它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。

2.2.2 all-subdir-makefiles

返回一個(gè)列表,包含 “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)

那則自動(dòng)include了:
sources/foo/lib1/Android.mk
sources/foo/lib2/Android.mk

2.2.3 this-makefile

當(dāng)前Makefile的路徑。

2.2.4 parent-makefile

返回include tree中父Makefile路徑,也就是include當(dāng)前Makefile的Makefile路徑。

2.2.5 import-module

允許尋找并import其它Modules到本Android.mk中來(lái)。 它會(huì)從NDK_MODULE_PATH尋找指定的模塊名。

==import-module和include區(qū)別:
功能基本一樣。
概念區(qū)別:include導(dǎo)入的是由我們自己寫(xiě)的Makefile。而import-module導(dǎo)入的是外部庫(kù)、外部模塊提供的Makefile。
用法區(qū)別:include的路徑是Makefile文件的絕對(duì)路徑。
而import-module是NDK_MODULE_PATH中路徑列表的相對(duì)路徑。==

$(call import-module, 模塊名/子目錄)

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

  1. 直接將 “NDK_MODULE_PATH=路徑1:路徑2” 加到 ndk-build命令的參數(shù)后面:
$ndk-build -C $HELLOWORLD_ROOT NDK_MODULE_PATH=路徑1:路徑2
  1. 在Android.mk中設(shè)置NDK_MODULE_PATH:
$(call import-add-path,$(LOCAL_PATH)/platform/third_party/android/prebuilt)
  1. 在系統(tǒng)環(huán)境里手動(dòng)添加這個(gè)環(huán)境變量。

例如:
有一個(gè)Android.mk路徑如下:
F:\cocos2d-x\CocosDenshion\android\android.mk
已設(shè)置:NDK_MODULE_PATH=/cygdrive/f/cocos2d-x
那么在Android.mk引入此模塊的方法如下:

$(call import-module,CocosDenshion/android)

2.3 模塊描述變量

此類(lèi)變量用來(lái)給Build System描述模塊信息。在'include $(CLEAR_VARS)' 和 'include $(BUILD_XXXXX)'之間,必須定義此類(lèi)變量。

  • include $(CLEAR_VARS) 用來(lái)清空這些變量。
  • include $(BUILD_XXXXX) 收集和使用這些變量。
2.3.1 LOCAL_PATH

這個(gè)值用來(lái)給定當(dāng)前目錄,必須在Android.mk的開(kāi)是位置定義之。

例如:

LOCAL_PATH := $(call my-dir)  

LOCAL_PATH不會(huì)被include $(CLEAR_VARS)清理。

2.3.2 LOCAL_MODULE

定義Modules名,在include $(BUILD_XXXXX)之前,必須定義這個(gè)變量,此變量必須唯一且不能有空格。通常由此變量名決定最終生成的目標(biāo)文件名。

2.3.3 LOCAL_MODULE_FILENAME

(可選)即允許用戶(hù)重新定義最終生成的目標(biāo)文件名。

LOCAL_MODULE := foo-version-1   
LOCAL_MODULE_FILENAME := libfoo
2.3.4 LOCAL_SRC_FILES

為Build Modules而提供的Source文件列表,不需要列出依賴(lài)文件。
==注意:文件相對(duì)于LOCAL_PATH存放,且可以提供相對(duì)路徑。==

例如:

LOCAL_SRC_FILES := foo.c \ 
                   toto/bar.c
2.3.5 LOCAL_CPP_EXTENSION

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

LOCAL_CPP_EXTENSION := .cxx

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

LOCAL_CPP_EXTENSION := .cxx .cpp .cc    
2.3.6 LOCAL_CPP_FEATURES

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

LOCAL_CPP_FEATURES := rtti  
LOCAL_CPP_FEATURES := exceptions
2.3.7 LOCAL_C_INCLUDES

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

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

(可選)在編譯C/C++ source 時(shí)添加如Flags,用來(lái)附加編譯選項(xiàng)。

==注意:不要嘗試在此處修改編譯的優(yōu)化選項(xiàng)和Debug等級(jí),它會(huì)通過(guò)您Application.mk中的信息自動(dòng)指定。==

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

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

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

  4. -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)作,只是不能利用多線(xiàn)程來(lái)加速程序執(zhí)行。

  5. -D:增加全局宏定義(==常用==)

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

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

LOCAL_CFLAGS += -DXXX # 相當(dāng)于在所有源文件中增加一個(gè)宏定義'#define XXX'
2.3.10 LOCAL_CPPFLAGS

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

2.3.11 LOCAL_STATIC_LIBRARIES

要鏈接到本模塊的靜態(tài)庫(kù)list,這僅僅對(duì)共享庫(kù)模塊才有意義。

Android.mk 1:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := mylib_static
LOCAL_SRC_FILES := src.c
include $(BUILD_STATIC_LIBRARY)

Android.mk 2:

include $(CLEAR_VARS)
LOCAL_MODULE := mylib_shared
LOCAL_SRC_FILES := src2.c
LOCAL_STATIC_LIBRARIES := mylib_static
include $(BUILD_SHARED_LIBRARY)

要編譯Android.mk 2,必須已經(jīng)先編譯mylib_static靜態(tài)庫(kù),即Android.mk 1。

2.3.12 LOCAL_SHARED_LIBRARIES

要鏈接到本模塊的動(dòng)態(tài)庫(kù),同上。

2.3.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ì)用到的代碼段和變量。

2.3.14 LOCAL_LDLIBS

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

LOCAL_LDLIBS += -lm –lz –lc -lcutils –lutils –llog …
2.3.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)

2.3.16 LOCAL_ARM_MODE

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

LOCAL_ARM_MODE := arm  

其實(shí)也可以指定某一個(gè)或者某幾個(gè)文件的ARM指令模式。

2.3.17 LOCAL_ARM_NEON

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

2.3.18 LOCAL_DISABLE_NO_EXECUTE

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

2.3.19 LOCAL_EXPORT_CFLAGS

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

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

最后編輯于
?著作權(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)容

  • Android游戲開(kāi)發(fā)實(shí)踐(1)之NDK與JNI開(kāi)發(fā)02 承接上篇Android游戲開(kāi)發(fā)實(shí)踐(1)之NDK與JNI...
    AlphaGL閱讀 3,900評(píng)論 0 24
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,725評(píng)論 25 709
  • Android.mk文件是GNU Makefile的一小部分,它用來(lái)對(duì)Android程序進(jìn)行編譯。 因?yàn)樗械木幾g...
    forty_seven閱讀 1,302評(píng)論 0 3
  • 打了一天仗,終于可以靜下來(lái)歇歇腳,好想來(lái)杯咖啡犒勞自己[咖啡] 一杯咖啡,一頁(yè)書(shū)[月亮] 夜,很靜 月,不明 北方...
    小葉西府海棠一葉秋閱讀 351評(píng)論 0 1
  • 一、每日必讀:輕熟原則 早起儀式=反思回顧+區(qū)塊鏈 晚間儀式=陪家人閑話(huà)+反思+坐享 日省——每日復(fù)盤(pán),a Tim...
    wenzi2507閱讀 302評(píng)論 0 0

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