Android.mk檔案文法詳述

來源:互聯網
上載者:User

Android.mk檔案文法詳述

介紹:
------------

這篇文檔是用來描述你的C或C++源檔案中Android.mk編譯檔案的文法的,為了理解她們我們需要您先看完
docs/OVERVIEW.html(http://hualang.iteye.com/blog/1135105)檔案來瞭解它的作用

概覽:
------------
Android.mk檔案是用來描述build system(編譯系統)的,更準確的說:

--該檔案是一個微型的GNU Makefile片段,將由build system解析一次或者多次。這樣,您就可以盡量減少您聲明的變數,並且不要以為在解析過程中沒有任何定義。

--這個檔案但文法是用來允許你將源檔案組織成模組,這個模組中含有:
  -一個靜態庫(.a檔案)
  -一個動態庫(.so檔案)
  只有動態庫才會被安裝/複製到你的應用程式套件組合,儘管靜態庫可以被用來產生動態庫。你可以在每個模組中  都定義一個Android.mk檔案,你也可以讓多個模組共用一個Android.mk檔案。

--build system可以為你處理許多細節,例如:你不許要在Android.mk檔案中列出標頭檔或者其他的依賴關係,這些NDK的build system會自動為你計算並處理。

這也意味著,當更新到新版本的NDK的時候,你應該得益於新的toolchain/platform的支援,而無需修改你的Android.mk檔案。

注意:這些文法非常接近於分布在完整的開源的Android原始碼中的Android.mk檔案,儘管是build system實現的,但是它們的用法是不同的。這樣故意設計的決定是為了讓應用程式開發人員重用“外部”庫的原始碼更容易。

簡單一實例:
-------------
再詳細講解文法之前,讓我們先看看一個簡單的例子"hello JNI",它在apps/hello-jni/project下

--'src'目錄下用於存放java源檔案
--‘jni’目錄下用於存放本地源檔案,例如"jni/hello-jni.c"

這個源檔案實現了一個簡單的共用庫(shared library):實現了一個本地方法,為VM應用程式返回一個字串。

--‘jni/Android.mk’檔案描述了如何產生一個共用庫,它的內容是:
-----------------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)

Android.mk檔案必須以LOCAL_PATH變數開始,它用於在樹中定位檔案。在這個例子中,宏功能'my-dir'是由build system提供的,用於返回目前的目錄路徑(包括Android.mk檔案本身)

include $(CLEAR_VARS)

CLEAR_VARS變數是由build system提供的,並且指明了一個GNU makefile檔案,這個功能會清理掉所有以LOCAL_開頭的內容(例如LOCAL_MODULE,LOCAL_SRC_FILES,LOCAL_STATIC_LIBRARIES等),除了LOCAL_PATH,這句話是必須的,因為如果所有的變數都是全域變數的話,所有的可控的編譯檔案都需要在一個單獨的GNU中被解析並執行

LOCAL_MODULE :=hello-jni

LOCAL_MODULE變數必須被定義,用來區分Android.mk中的每一個模組。檔案名稱必須是唯一的,不能有空格。注意,這裡編譯器會為你自動加上一些首碼和尾碼,來保證檔案是一致的,比如:這裡表明一個動態串連庫模組被命名為"hello-jni",但是最後會產生為"libhello-jni.so"檔案。但是在Java中裝載這個庫的時候還是使用"hello-jni"名稱。當然如果我們使用"IMPORTANT NOTE:",編譯系統就不會為你加上首碼,但是為了支援Android平台源碼中的Android.mk檔案,也同樣會產生libhello-jni.so這樣的檔案。

重要提示:如果你將你的模組命名為'libfoo',編譯系統將不會將首碼'lib'加上去,並且也會產生libfoo.so檔案。

LOCAL_SRC_FILES := hello-jni.c

LOCAL_SRC_FILES變數被需包括一個C和C++源檔案的列表,這些會編譯並彙總到一個模組中。
注意:這裡並不需要你列出標頭檔和被包含的檔案,因為編譯系統會自動為你計算相關的屬性,原始碼中的列表會直接傳遞給編譯器。

C++預設檔案的副檔名是“.cpp”,我們可以通過定義一個LOCAL_DEFAULT_CPP_EXTENSION變數來定義一個不同的C檔案。不要忘記在初始化前面的“.”點(也就是說".cpp"可以正常工作,但是cpp不能正常工作)

include $(BUILD_SHARED_LIBRARY)
BUILD_SHARED_LIBRARY這個變數是由系統提供的,並且指定給GNU Makefile的指令碼,它可以收集所有你定義的"include $(CLEAR_VARS)"中以LOCAL_開頭的變數,並且決定哪些要被編譯,哪些應該做的更加準確。編譯產生的是以"lib<module_name>.so"的檔案,這個就是共用庫了。我們同樣也可以使用BUILD_STATIC_LIBRARY編譯系統便會產生一個以"lib<module_name>.a"的檔案來供動態庫調用。

在samples目錄下有很多複雜的例子,那裡的Android.mk檔案可以供我們參考

參考:
-----------------------
這是個變數的列表,你可以依賴或者定義它們到Android.mk中。你可以定義自己使用的其他變數,但是NDK辨析系統只保留了以下名字:

--以LOCAL_開頭的名稱(如:LOCAL_MODULE)
--以PRIVATE_、NDK_、或APP_(內部使用)開始的名稱
--小寫名稱(例如'my-dir',內部使用)

如果你需要在Android.mk中定義自己的變數的話,我們建議使用MY-首碼,一個簡單的例子:
----------------------------
MY_SOURCES := foo.c
ifneq($(MY_CONFIG_BAR),)
  MY_SOURCES += bar.c
endif

LOCAL_SRC_FILES +=$(MY_SOURCES)
----------------------------

我們繼續:

NDK提供的變數:
在您的Android.mk檔案被解析之前這些GNU Make變數由編譯系統定義,注意,在某些情況下,NDK可能被解析幾次,每次以不同的變數的定義解析的

CLEAR_VARS
CLEAR_VARS這個變數由系統提供,功能是清理掉所有以LOCAL_開頭的內容,再開始一個新的模組之前,你必須包括這段指令碼

include ($CLEAR_VARS)

BUILD_SHARED_LIBRARY

在編譯指令碼中收集所有以LOCAL_開頭的資訊並且決定從列出的原始碼中編譯一個目標共用庫。注意,你必須定義了LOCAL_MODULE和LOCAL_SRC_FILES變數,使用它的時候,可以這樣定義

include $(BUILD_SHARED_LIBRARY)

注意,我們會產生一個以lib<module_name>.so為名的檔案

BUILD_STATIC_LIBRARY

用來構建一個靜態庫,該靜態庫將不會被拷貝到你的project/packages下,但是可以被用於動態庫
(看下面的LOCAL_STATIC_LIBRARY和LOCAL_WHOLE_STATIC_LIBRARY介紹)

例如:
include $(BUILD_STATIC_LIBRARY)

注意,這將產生一個lib<module_name>.a為名字的模組

PREBUILD_SHARED_LIBRARY
在編譯指令碼中用於指定一個預先編譯的動態庫,不像BUILD_SHARED_LIBRARY和BUILD_STATIC_LIBRARY,LOCAL_SRC_FILES的預先共用庫必須是一個單獨的路徑(如:foo/libfoo.so),而不是源檔案。

你可以在另一個模組中引用先行編譯的庫(參見docs/pribuilds.html)

PRIBUILD_STATIC_LIBRARY
這個變數類似於PREBUILD_SHARED_LIBRARY,但是是針對靜態庫的,(詳見docs/prebuilds.html)

TARGET_ARCH
TARGET_ARCH指架構中CPU的名字已經被Android開原始碼明確指出了,這裡的arm包含了任何ARM-獨立結構的架構,以及每個獨立的CPU版本

TARGET_PLATFORM
Android平台的名字在Android.mk中被解析,比如"android-3"對應Android 1.5系統鏡像,對於平台的名稱對應Android系統的列表,請看docs/STABLE-APIS.html

TARGET_ARCH_ABI
在Android.mk中被解析時指CPU+ABI的名字。

目前支援的兩個值
armeabi for ARMv5TE
armeabi-v7a

注意,到Android NDK 1.6_r1,這個值被簡化為"arm"。然而,這個值被重定義可以更好的匹配Android平台內部使用的是什麼

更多的資訊可以參見docs/CPU-ARCH-ABIS.html

未來的NDK版本中得到支援,它們會有一個不同的名字,注意所有基於ARM的ABI都會有一個"TARGET_ARCH"被定義給arm,但也有可能有不同的"TARGET_ARCH_ABI"

TARGET_ABI

目標平台和ABI的連結,這裡要定義$(TARGET_PLATFORM)-$(TARGET_ARCH_ABI)它們都非常有用,特別是當你想測試一下具體的系統鏡像在一個真實裝置環境的時候

預設地,這個是"android-3-armeabi"

(到Android NDK 16_R1版本,使用"android-3-arm"作為預設)

NDK提供的宏功能
--------------------------------
以下是使用GNU make的宏功能,必須通過使用"$(call <function>)",返回一個文本資訊。

my-dir

返回最後包含的makefile的路徑,這通常是當前Android.mk所在目錄的路徑,在Android.mk開始之前定義
LOCAL——PATH是很有用的。

在Android.mk檔案的開始位置定義
LOCAL_PATH :=$(call my-dir)

...聲明一個模組
include $(LOCAL_PATH)/foo/Android.mk

LOCAL_PATH :=($call my-dir)

...聲明另一個模組
這裡的問題是第二次調用"my-dir"定義LOCAL_PATH替換$PATH為$PATH/foo,由於在此之前執行過。

對於這個原因,最好是將額外的其他所有東西都在Android.mk中包含進來

LOCAL_PATH :=$(call my-dir)

...聲明一個模組

LOCAL_PATH :=$(call my-dir)
...聲明另一個模組

#在Android.mk的最後額外包括進來
include $(LOCAL_PATH)/foo/Android.mk

如果這樣不方便的話,儲存第一個my-dir調用的值到另一個變數中,例如

MY_LOCAL_PATH :=$(call my-dir)
LOCAL_PATH :=$(MY_LOCAL_PATH)
...聲明一個模組

include $(LOCAL_PATH)/foo/Android.mk
LOCAL_PATH :=$(MY_LOCAL_PATH)
...聲明另一個模組

all-subdir-makefiles
返回一個Android.mk檔案所在位置的列表,以及當前的my-dir的路徑。比如
sources/foo/Android.mk
sources/foo/lib1/Android.mk
sources/foo/lib2/Android.mk

如果sources/foo/Android.mk包含了這行語句
include $(call all-subdir-makefiles)

那麼,它將會自動將sources/foo/lib1/Android.mk和sources/foo/lib2/Android.mk包含進來。

此功能可以用於提供深層嵌套的原始碼目錄build system的階層。請注意,預設情況下,NDK只會尋找
sources/*Android.mk

this-makefile
返回當前makefile的路徑(也就是那個功能被調用了)

parent-makefile
返回makefile的包含樹,也就是包含Makefile當前的檔案

grand-parent-makefile
你猜?

import-module
一個允許你通過名字找到並包含另一個模組的的Android.mk的功能,例如

$(call import-module,<name>)

這將會找到通過NDK_MODULE_PATH環境變數引用的模組<name>的目錄列表,並且將其自動包含到
Android.mk中

詳細資料請參閱:docs/IMPORT-MODULE.html

模組變數描述:
----------------------------------
下面的這些變數是用來描述怎樣用你的模組來編譯系統的。你可以定義它們中的一些比如
"include $(CLEAR_VARS)"和"include $(BUILD_XXX)",正如前面所寫的,$(CLEAR_VARS)是一個可以取消定義/清楚所有變數的指令碼。

LOCAL_PATH
這個變數是用來給出當前檔案的路徑。您比系再您的Android.mk開始位置定義:
LOCAL_PATH :=$(call my-dir)
注意,這個變數是不被$(CLEAR_VARS)清除的,其他的都要被清除(我們可以定義幾個模組到一個檔案中)

LOCAL_MODULE
這個是你模組的名稱,它在你的所有模組中名稱必須是唯一的,並且不能包含空格。你必須在包含任何
$(BUILD-XXX)指令碼之前定義它。

預設情況下,模組的名稱決定了產生的檔案的名稱,例如lib<foo>.so,它是foo模組的名字。

你可以用LOCAL_MODULE_FILENAME覆蓋預設的那一個

LOCAL_MODULE_FILENAME

這個變數是可選的,並且允許你重新定義組建檔案的名字。預設的,模組<foo>將始終產生lib<foo>.a或者lib<foo>.so檔案,這是標準的UNIX公約

你可以通過LOCAL_MODULE_FILENAME覆蓋它

LOCAL_MODULE :=foo-version-1
LOCAL_MODULE_FILENAME :=libfoo
注意:你不能將檔案路徑或者副檔名寫到LOCAL_MODULE_FILENAME裡,這些將有build system自動處理。

LOCAL_SRC_FILES
這是你模組中將要編譯的源檔案清單。只列出將被傳遞到編譯器的檔案,因為build system自動為您計算了它們的依賴。

注意:源檔案的名稱都是相對LOCAL_PATH的,您可以使用路徑組件,例如
LOCAL_SRC_FILES :=foo.c\
toto/bar.c

注意:在build system時請務必使用UNIX風格的斜杠(/),windows風格的斜杠將不會得到處理

LOCAL_CPP_EXTENSION
這是個可選的變數,可以被定義為副檔名為c++的源檔案,預設是".cpp",但是你可以改變它,比如

LOCAL_CPP_EXTENSION:=.cxx

LOCAL_C_INCLUDES

可選的路徑列表,相對於NDK的根目錄,當編譯所有的源檔案(C、C++、或者彙編)時將被追加到搜尋路徑中
例如:
LOCAL_C_INCLUDES:=sources/foo
或者
LOCAL_C_INCLUDES:=$(LOCAL_PATH)/../foo

這些都在任何相應列入標誌之前被放置在
LOCAL_CFLAGS / LOCAL_CPPFLAGS

當用用ndk-gdb啟動本機調試時,LOCAL_C_INCLUDES也會自動被使用到

LOCAL_CFLAGS

當編譯C/C++源檔案時傳遞一個可選的編譯器標誌。
這對於指定額外的宏定義或編譯選項很有用

重要提示:盡量不要改變Android.mk中的最佳化/調試層級,這個可以通過在Application.mk中設定相應的資訊來自動為你處理,並且會會讓NDK產生在調試過程中使用的有用的資料檔案。

注意:在Android-ndk-1.5_r1中,只使用於C源檔案,而不適用於C++源檔案。在匹配所有Android build system的行為已經得到了糾正。(現在你可以為C++源檔案使用LOCAL_CPPFLAGS來指定標誌)

它可以用LOCAL_CFLAGS += -I<path>來指定額外的包含路徑,然而,如果使用LOCAL_C_INCLUDES會更好,因為用ndk-gdk進行本地調試的時候,那些路徑依然是需要使用的

LOCAL_CXXFLAGS
LOCAL_CPPFLAGS的別名。請注意,這個標誌在NDK的未來的版本中將會消失

LOCAL_CPPFLAGS
當只編譯C++原始碼的時候,將傳遞一個可選的編譯器標誌。它們將會出現再LOCAL_CFLAGS之後。

注意:在Android NDK-1.5_r1版本中,相應的標誌可以應用於C或C++源檔案上。在配合完整的Android build system的時候,這已經得到了糾正。(你可以使用LOCAL_CFLAGS去指定C或C++源檔案)

LOCAL_STATIC_LIBRARIES

靜態庫模組的列表(通過BUILD_STATIC_LIBRARY建立)應與此模組連結。這僅僅是為了使動態庫敏感。

LOCAL_SHARED_LIBRARY
共用庫的列表“模組”,這個模組依賴於運行時.這在連結的時候和在產生的檔案中嵌入相應的資訊是非常必要的

LOCAL_WHOLE_STATIC_LIBRARIES

LOCAL_WHOLE_STATIC_LIBRARIES是一個用於表示相應的庫模組被用作為“整個檔案”到連結程式的變數。

當幾個靜態庫之間有循環相依性關係的時候,通常是很有益的。注意,當用來編譯一個動態庫的時候,這將迫使你將所有的靜態庫中的對象檔案添加到最終的二進位檔案中。但產生可執行程式時,這是不確定的。

LOCAL_LDLIBS
當額外的連結標誌列表被用於在編譯你的模組時,通過用"-l"首碼的特定系統庫傳遞名字是很有用的。例如,下面的舊愛哪個告訴你產生一個在載入時連結到/system/lib/libz.so的模組。

LOCAL_LDLIBS :=-lz

LOCAL_ALLOW_UNDEFINED_SYMBOLS
預設情況下,當試圖編譯一個共用庫的時候遇到任何未定義的引用都可能導致"未定義符號"(undefined symbol)的錯誤。這在你的原始碼中捕獲bug會很有用。

然而,但是由於某些原因,你需要禁用此檢查的話,設定變數為"true"即可。需要注意的是,相應的共用庫在運行時可能載入失敗。

LOCAL_ARM_MODE
預設情況下,在"thumb"模式下會產生ARM目標二進位,其中每個指令都是16位寬。你可以定義這個變數為"arm",如果你想在"arm"模式下(32位指令)強迫模組對象檔案的產生。例如:

LOCAL_ARM_MODE := arm

注意,你需要執行編譯系統為在ARM模式下通過檔案的名字增加尾碼的方式編譯指定的源檔案。比如:

LOCAL_SRC_FILES :=foo.c bar.c.arm

這會告訴編譯系統一直以ARM模式編譯"bar.c",並且通過LOCAL_ARM_MODE的值編譯foo.c。

注意:在Application.mk檔案中設定APP_OPTIM為"debug"也會強制ARM二進位檔案的產生。這是因為工具鏈調試其中的bug不會處理thumb代碼。

LOCAL_ARM_NEON

定義這個變數為"true"會允許在你的C或C++源檔案的GCC的內建函式中使用ARM進階SIMD(又名NEON),以及在彙總檔案中的NEON指令。

當針對"armeabi-v7a"ABI對應的ARMv7指令集時你應該定義它。注意,並不是所有的ARMv7都是基於NEON指令集擴充的CPU,你應該執行運行時來檢測在運行時中這段代碼的安全。

另外,你也可以指定特定的源檔案,比如用支援NEON".neon"尾碼的源檔案也可以被編譯。

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

在這個例子中,"foo.c"將會被編譯在thumb+neon模式中,"bar.c"以thumb模式編譯,zoo.c以arm+neon模式編譯。

注意,如果你使用兩個的話,".neon"尾碼必須出現在".arm"尾碼之後
(就是foo.c.arm.neon可以工作,但是foo.c.neon.arm不工作)

LOCAL_DISABLE_NO_EXECUTE

Android NDK r4開始添加了支援"NX位"安全功能特性。它是預設啟用的,如果你需要的話,可以通過設定變數為“true”來禁用它。

注意:此功能不修改ABI,並且只在ARMv6及以上的CPU裝置的核心上被啟用。

更多資訊,可以參見:
 http://en.wikipedia.org/wiki/NX_bit
 http://www.gentoo.org/proj/en/hardened/gnu-stack.xml

LOCAL_EXPORT_CFLAGS

定義這個變數用來記錄C/C++編譯器標誌集合,並且會被添加到其他任何以LOCAL_STATIC_LIBRARIES和LOCAL_SHARED_LIBRARIES的模組的LOCAL_CFLAGS定義中。

例如:這樣定義"foo"模組
include $(CLEAR_VARS)
LOCAL_MODULE :=foo
LOCAL_SRC_FILES :=foo/foo.c
LOCAL_EXPORT_CFLAGS :=-DFOO=1
include $(BUILD_STATIC_LIBRARY)

另一個模組,叫做"bar",並且依賴於上面的模組
include $(CLEAR_VARS)
LOCAL_MODULE :=bar
LOCAL_SRC_FILES :=bar.c
LOCAL_CFLAGS:=-DBAR=2
LOCAL_STATIC_LIBRARIES:=foo
include $(BUILD_SHARED_LIBRARY)

然後,當編譯bar.c的時候,標誌"-DFOO=1 -DBAR=2"將被傳遞到編譯器。

輸出的標誌被添加到模組的LOCAL_CFLAGS上,所以你可以很容易複寫它們。它們也有傳遞性:如果"zoo"依賴"bar",“bar”依賴"foo",那麼"zoo"也將繼承"foo"輸出的所有標誌。

最後,當編譯模組輸出標誌的時候,這些標誌並不會被使用。在上面的例子中,當編譯foo/foo.c時,
-DFOO=1將不會被傳遞給編譯器。

LOCAL_EXPORT_CPPFLAGS

類似LOCAL_EXPORT_CFLAGS,但適用於C++標誌。

LOCAL_EXPORT_C_INCLUDES

類似LOCAL_EXPORT_C_CFLAGS,但是只有C能包含路徑,如果"bar.c"想包含一些由"foo"模組提供的標頭檔的時候這會很有用。

LOCAL_EXPORT_LDLIBS

類似於LOCAL_EXPORT_CFLAGS,但是只用於連結標誌。注意,引入的連結標誌將會被追加到模組的LOCAL_LDLIBS,這是因為UNIX連接器的工作方式。

當模組foo是一個靜態庫的時候並且代碼依賴於系統庫時會很有用的。LOCAL_EXPORT_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)

這裡,在連接器命令最後,libbar.so將以-llog參數進行編譯來表明它依賴於系統日誌庫,因為它依賴於foo。

LOCAL_FILTER_ASM

這個變數定義了一個shell命令,將用於過濾,從你的LOCAL_SRC_FILES中產生的或者彙總檔案。

當它被定義了,將會出現如下的情況:

--任何C或C++源檔案將會產生到一個臨時的彙總的檔案中(而不是被編譯成目標檔案)
--任何臨時彙總檔案,任何在LOCAL_SRC_FILES中列出的彙總檔案將通過LOCAL_FILER_ASM命令產生另一個臨時彙總檔案

--這些過濾彙總檔案被編譯成目標檔案。

換種說法,如果
LOCAL_SRC_FILES  := foo.c bar.S
LOCAL_FILTER_ASM := myasmfilter

foo.c --1--> $OBJS_DIR/foo.S.original --2--> $OBJS_DIR/foo.S --3--> $OBJS_DIR/foo.o
    bar.S                                 --2--> $OBJS_DIR/bar.S --3--> $OBJS_DIR/bar.o

“1”對應的編譯器,“2”的過濾器,和“3”的彙編。過濾器必須是一個獨立的shell命令作為第一個參數輸入檔案的名稱,和輸出的名稱第二,如檔案為:

myasmfilter$ OBJS_DIR/ foo.S.original$ OBJS_DIR/ foo.S
myasmfilter bar.S$ OBJS_DIR/ bar.S

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.