【轉】Android.mk檔案文法規範(Android.mk File)

來源:互聯網
上載者:User

標籤:

原文網址:http://blog.csdn.net/smfwuxiao/article/details/8530742

1、Android.mk檔案概述

Android.mk檔案用來告訴NDK編譯系統,應該如何編譯這些源碼。更確切地說,該檔案其實就是一個小型的Makefile。該檔案會被NDK的編譯工具解析多次,所以要注意不要過多使用環境變數,以免第一次解析時產生的變數影響後面的解析。Android.mk把源碼組織成不同的模組,每個模組可以是一個靜態庫也可以是一個動態庫。動態庫才會被拷貝到安裝包中,靜態庫只能用於編譯產生動態庫。

同一個Android.mk檔案可以定義多個模組,不同的模組可以共用同一個源檔案。

注意,NDK所使用的Android.mk檔案的文法與Android作業系統的開放源碼中的Android.mk的文法非常接近,但是兩個編譯系統對Android.mk的使用方法不同,這是為了方便應用程式開發人員複用以前的代碼。

2、一個簡單的例子

在詳細討論Android.mk檔案的文法之前,先看一個簡單的 “hello JNI“ 的例子,檔案位於 apps/hello-jni/project。其中的 src 子目錄存放Android工程的java源碼,jni子目錄存放C/C++源碼檔案,即 jni/hello-jni.c (實現了一個返回字串給虛擬機器的函數)。jni/Android.mk 檔案是這個模組的編譯指令碼,內容如下:

 

[plain] view plaincopy 
  1. LOCAL_PATH := $(call my-dir)  
  2.   
  3. include $(CLEAR_VARS)  
  4.   
  5. LOCAL_MODULE    := hello-jni  
  6. LOCAL_SRC_FILES := hello-jni.c  
  7.   
  8. include $(BUILD_SHARED_LIBRARY)  

以上內容解釋如下:

 

    LOCAL_PATH := $(call my-dir)

每個Android.mk檔案都必須在開頭定義 LOCAL_PATH 變數。這個變數被用來尋找C/C++源檔案。在該例中,my-dir 是一個由編譯系統提供的宏函數,用於返回Android.mk所在目錄的路徑。

    include $(CLEAR_VARS)

CLEAR_VARS是編譯系統預定義的一個變數,它指向一個特殊的Makefile,這個Makefile負責清除 LOCAL_xxx 的變數(例如 LOCAL_MODULE, LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES 等)但不會清除 LOCAL_PATH。之所以需要清理這些變數是因為所有的編譯控制檔案是在一趟make執行過程中完成的,而所有的變數都是全域的,會對其他Android.mk檔案產生影響。

    LOCAL_MODULE := hello-jni

LOCAL_MODULE 用來給每個模組定義一個名字,不同模組的名字不能相同,不能有空格。這裡的名字會傳給NDK編譯系統,然後加上lib首碼和.so尾碼 (例如,變成libhello-jni.so)。注意,如果你在LOCAL_MODULE定義中自己加上了lib首碼,則ndk在處理的時候就不會再加上lib首碼了(為了相容Android系統的一些源碼)。

    LOCAL_SRC_FILES := hello-jni.c

在LOCAL_SRC_FILES 變數裡面列舉出對應於同一個模組的、要編譯的那些檔案,這裡不要把標頭檔加進來,編譯系統可以自動檢測標頭檔依賴關係。預設情況下,C++源碼檔案的副檔名應該是cpp,如果想修改的話,將變數LOCAL_CPP_EXTENSION修改為你想要的副檔名,注意句點。例如:LOCAL_CPP_EXTENSION := .cxx

    include $(BUILD_SHARED_LIBRARY)

這個 BUILD_SHARED_LIBRARY也是預定義的變數,也是指向一個Makefile,負責將你在 LOCAL_XXX 等變數中定義資訊收集起來,確定要編譯的檔案,如何編譯。如果要編譯的是靜態庫而不是動態庫,則可以用 BUILD_STATIC_LIBRARY。

在NDK安裝目錄的samples目錄下有更加豐富的例子,裡面都有詳細的注釋。

3、變數名的限制

下面這些變數是你可以直接使用或者應該由你來定義的。你也可以定義自己的變數,但是不能用以下NDK所保留的變數名:

    以 LOCAL_ 開頭的名字(例如,LOCAL_MODULE)

    以 PRIVATE_, NDK_,APP_ 開頭的名字(供NDK內部使用)

    小寫字母的變數名也不能使用(供NDK內部使用,例如 my-dir)

例如,你可以隨便用 MY_ 開頭的變數名:

 

[plain] view plaincopy 
  1. MY_SOURCES := foo.c  
  2. ifneq ($(MY_CONFIG_BAR),)  
  3.   MY_SOURCES += bar.c  
  4. endif  
  5.   
  6. LOCAL_SRC_FILES += $(MY_SOURCES)  

 

4、NDK預定義變數

下面這些變數是ndk提前定義好的變數。有時ndk會解析同一個Android.mk檔案多次,每次解析時,這些變數的值可能不相同。

CLEAR_VARS

指向一個特殊的Makefile,負責清理 LOCAL_XXX 變數(LOCAL_PATH除外)。一般在定義新模組之前使用這個變數,用法:

include $(CLEAR_VARS)

BUILD_SHARED_LIBRARY

該變數實際指向了一個Makefile,用來把所有名為 LOCAL_XXX的變數中的資訊收集起來,然後確定如何把你提供的源碼編譯成目標模組。用法:include $(BUILD_SHARED_LIBRARY)     預設檔案名稱:lib<LOCAL_MODULE>.so

BUILD_STATIC_LIBRARY

類似於BUILD_SHARED_LIBRARY,不過它用來編譯靜態庫。靜態庫不會被拷貝到你的安裝包中去,它往往用來編譯其他動態庫。用法:include $(BUILD_STATIC_LIBRARY)    預設檔案名稱:lib<LOCAL_MODULE>.a

PREBUILT_SHARED_LIBRARY

該變數指向一個已編譯好的共用庫。與BUILD_SHARED_LIBRARY和BUILD_STATIC_LIBRARY不同,此時相應的LOCAL_SRC_FILES不再指定源檔案,而是指向這個先行編譯共用庫檔案(例如 foo/libfoo.so)。可以在其他模組中,通過使用LOCAL_PREBUILTS變數來引用這個先行編譯模組。參考Prebuilt。

PREBUILT_STATIC_LIBRARY

與PREBUILT_SHARED_LIBRARY相同,只不過這裡是靜態庫。 參考 Prebuilt。

TARGET_ARCH

目標CPU架構的名字,與Android作業系統的CPU架構名一致。如果想相容所有ARM的CPU,可以用 “arm” 這個名字。

TARGET_PLATFORM

目標Android平台的名字。例如 android-3 對應的是 Android 1.5 系統鏡像(Cupcake)。所有系統鏡像的名字和相應的系統鏡像可參考Stable APIs。

TARGET_ARCH_ABI

目標CPU和ABI組合的名字,目前只有2個值可以用:

armeabi     對於ARMv5TE

armeabi-v7a

注意,一直到Android NDK 1.6_r1,這裡的值都是用“arm”。然而,該值已被重新定義以更好地匹配Android平台內部所使用的。

關於架構和ABI及相容性問題,參考文檔 Cpu Arch ABIs。

其他的目標ABI會在將來的NDK版本中增加,並且是不同的名字。注意,所有相容ARM的ABI的TARGET_ARCH都是arm,但是TARGET_ARCH_ABI不同。

TARGET_ABI

目標平台和ABI的組合,定義為 $(TARGET_PLATFORM)-$(TARGET_ARCH_ABI)。當你想在真機上測試一種系統鏡像時有用。該變數的預設值是android-3-armeabi。

直到Android NDK 1.6_r1,這個值一直是 android-3-arm。

5、NDK預定義的宏函數

下面是NDK預定義的“函數”宏,用法是  $(call <function>) ,返回的是文本資訊

my-dir

返回上一個被包含的Makefile的路徑,典型情況是Android.mk檔案所在的路徑。這個函數對於定義LOCAL_PATH特別有用,例如:

    LOCAL_PATH := $(call my-dir)

注意:由於make的工作原理,該函數返回的確實是上一個被包含的Makefile的路徑(也就是說返回的結果可能不是Android.mk所在目錄)。所以,包含了另外一個檔案之後,就不要再使用 my-dir 了。

例如,下面的例子:

 

[plain] view plaincopy 
  1.     LOCAL_PATH := $(call my-dir)  
  2.     該模組其他聲明  
  3.       
  4.     include $(LOCAL_PATH)/foo/Android.mk  
  5.     LOCAL_PATH := $(call my-dir)  
  6.     另一個模組的聲明  

 

上面的問題就是第二次調用my-dir的時候,得到的是 $PATH/foo 而不是 $PATH,因為它前面有一個include語句。

 

因此,最好在所有include語句之前,把LOCAL_PATH定義好:

 

[plain] view plaincopy 
  1. LOCAL_PATH := $(call my-dir)  
  2. ... declare one module  
  3. LOCAL_PATH := $(call my-dir)  
  4. ... declare another module  
  5. # extra includes at the end of the Android.mk  
  6. include $(LOCAL_PATH)/foo/Android.mk  

如果覺得這樣做不方便,可以把第一次調用的結果儲存到變數中,例如:

 

 

[plain] view plaincopy 
  1. MY_LOCAL_PATH := $(call my-dir)  
  2. LOCAL_PATH := $(MY_LOCAL_PATH)  
  3. ... declare one module  
  4. include $(LOCAL_PATH)/foo/Android.mk  
  5. LOCAL_PATH := $(MY_LOCAL_PATH)  
  6. ... declare another module  

 

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)

那麼,該檔案將自動把 sources/foo/lib1/Android.mk 和 sources/foo/lib2/Android.mk 包含進來。當源碼被組織成很多層次時可以利用該函數,預設情況下,NDK只會尋找 sources/*/Android.mk。

 

this-makefile

返回當前的Makefile的路徑(即該函數調用時的位置)

parent-makefile

如果當前這個Makefile被另一個Makefile包含,則返回那個包含了自己的Makefile的路徑(即parent)

grand-parent-makefile

(你猜猜......)

import-module

該函數用於按模組名尋找另一個模組的Android.mk檔案,並包含進來。用法如下:

        $(call import-module,<name>)

上面將在 NDK_MODULE_PATH變數所指定的目錄列表中尋找名為<name>的模組,找到之後將包含進來。

可以參考 Import Module。

6、模組描述變數

下面這些變數用於對模組進行描述,這些變數應該在 include $(CLEAR_VARS) 和 include $(BUILD_XXXX) 之間定義好。

LOCAL_PATH (必須)

這個變數表示當前檔案(一般是Android.mk)所在的路徑,該變數很重要,必須定義(在Android.mk檔案的開頭處定義)。常見寫法如下:

    LOCAL_PATH := $(call my-dir)

該變數不會被 include $(CLEAR_VARS) 清空,所以不論Android.mk定義了幾個模組,一個Android.mk只需要在開頭定義一次即可。

LOCAL_MODULE (必須)

該變數定義當前模組的名字,名字必須唯一,不能有空格。這個變數必須在 include $(BUILD_XXX) 之前定義好。預設情況下,這裡的名字會用來得到輸出檔案的名字。例如模組名為foo,則得到的輸出檔案為libfoo.so。但是,如果你要在其他模組的Android.mk檔案或Application.mk中引用這個模組,應該用foo這個模組名,而不要用libfoo.so這個檔案名稱。

LOCAL_MODULE_FILENAME (可選)

該變數可以用來重定義輸出檔案的名字。預設情況下,foo模組得到的靜態庫的名字為 libfoo.a,動態庫的名字為libfoo.so(UNIX規範)。當定義了LOCAL_MODULE_FILENAME之後,輸出檔案名就是這個變數指定的名字,例如:

 

[plain] view plaincopy 
  1. LOCAL_MODULE := foo-version-1  
  2. LOCAL_MODULE_FILENAME := libfoo  

注意: LOCAL_MODULE_FILENAME不支援檔案路徑(所以不能有斜杠),不要寫副檔名(檔案路徑和副檔名是由編譯工具自動加上的)

 

LOCAL_SRC_FILES (必須)

該變數用來指定該模組對應的源檔案,只把需要傳給編譯器的源檔案名稱加進LOCAL_SRC_FILES,編譯系統會自動處理標頭檔依賴。這裡的檔案名稱都是以 LOCAL_PATH 作為目前的目錄的(即相對於LOCAL_PATH目錄),例如:

    LOCAL_SRC_FILES := foo.c toto/bar.c
注意:必須使用Unix風格的斜杠,Windows風格的斜杠不能正確處理。

LOCAL_CPP_EXTENSION (可選)

用來定義C++代碼檔案的副檔名。必須以句點開頭(即 “.”),預設值是“.cpp”,可以修改,例如:

    LOCAL_CPP_EXTENSION := .cxx

從 NDK r7 這個版本開始,該變數可以支援多個副檔名了,例如:

    LOCAL_CPP_EXTENSION := .cxx .cpp .cc

LOCAL_CPP_FEATURES (可選)

該變數用來指定C++代碼所依賴的特殊C++特性。例如,如果要告訴編譯器你的C++代碼使用了RTTI(RunTime Type Information):

    LOCAL_CPP_FEATURES := rtti

如果要指定你的C++代碼使用了C++異常,則:

    LOCAL_CPP_FEATURES := exceptions

該變數可以同時指定多個特性。例如:    LOCAL_CPP_FEATURES := rtti features

這個變數的作用就是在編譯模組的時候,開啟相應的編譯器/連結器標誌。對於先行編譯的檔案,該變數表明該先行編譯的庫依賴了這些特性,從而確保最後的連結工作正確進行。與該變數等價的做法是在LOCAL_CPPFLAGS中寫上 -frtti -fexceptions 等標誌選項。但是,推薦用這裡的方法。

LOCAL_C_INCLUDES

一個路徑的列表,是NDK根目錄的相對路徑(LOCAL_SRC_FILES中的檔案相對於LOCAL_PATH)。當編譯C/C++、彙編檔案時,這些路徑將被追加到標頭檔搜尋路徑列表中。例如:

    LOCAL_C_INCLUDES := sources/foo

    或者, LOCAL_C_INCLUDES := $(LOCAL_PATH)/../foo

這裡的搜尋路徑會放在LOCAL_CFLAGS/LOCAL_CPPFALGS等標誌的前面。 當使用ndk-gdb的時候,LOCAL_C_INCLUDES中的路徑也會被用到。

LOCAL_CFLAGS

指定當編譯C/C++源碼的時候,傳給編譯器的標誌。它一般用來指定其他的編譯選項和宏定義。

注意:盡量不要在Android.mk中修改最佳化/調試等級,因為在Application.mk中定義了相關資訊之後編譯系統會自動處理這些問題。

 

LOCAL_CXXFLAGS (廢除, LOCAL_CPPFLAGS的別名)LOCAL_CPPFLAGS (可選)

編譯C++代碼的時候傳遞給編譯器的選項(編譯C代碼不會用這裡的選項)。最後得到的命令列選項中,這裡指定的選項在 LOCAL_CFLAGS 指定的選項的後面。

LOCAL_STATIC_LIBRARIES

指定應該連結到當前模組的靜態庫(可指定多個)。當前模組是動態庫時,該選項才有意義。

LOCAL_SHARED_LIBRARIES

指定的是運行時該模組所依賴共用庫(可指定多個)。這些資訊是連結階段必須的。

LOCAL_WHOLE_STATIC_LIBRARIES

它是LOCAL_STATIC_LIBRARIES的變體,用來表示它對應的模組對於linker來說應該是一個“whole archive”(見GNU linker 文檔,關於 --whole-archive的資料)。當靜態庫之間有循環相依性時,會用到這個選項。注意,當編譯動態庫時,這個選項會強行把所有的對象檔案組裝到一起;不過,在編譯可執行檔的時候情況不是這樣的。

LOCAL_LDLIBS

用來指定模組編譯時間的其餘連接器標誌。例如:

    LOCAL_LDLIBS := -lz

告訴連結器在載入該共用庫的時候必須連結 /system/lib/libz.so 這個共用庫。

如果想知道Android系統中有哪些共用庫可以連結,參考 Stable APIs。

LOCAL_ALLOW_UNDEFINED_SYMBOLS

預設情況下,當編譯一個共用庫的時候,遇到未定義符號引用就會報告一個“undefined symbol”錯誤。這有助於修複你的代碼中存在的bug。

如果因為某種原因,必須禁止該檢測,可以把這個變數設定為true。注意,編譯出的共用庫有可能在載入的時候就報錯導致程式退出。

LOCAL_ARM_MODE

LOCAL_ARM_NEON

LOCAL_DISABLE_NO_EXECUTE

Android NDK r4增加了對“NX bit“安全特性的支援。它是預設開啟的,如果你確定自己不需要該特性,你可以將它關閉,即:

    LOCAL_DISABLE_NO_EXECUTE := true

該變數不會修改ABI,只會在 ARMv6以上的CPU的核心上啟用。開啟該特性編譯出的代碼無需修改可運行在老的CPU上(也就是說所有ARM的CPU都能運行)。

參考資訊:

http://en.wikipedia.org/wiki/NX_bit
http://www.gentoo.org/proj/en/hardened/gnu-stack.xml

LOCAL_EXPORT_CFLAGS

    這個變數定義一些C/C++編譯器flags。這些flags(標誌)會被追加到使用了這個模組(利用LOCAL_STATIC_LIBRARIES和LOCAL_SHARED_LIBRARIES)的模組的LOCAL_CFLAGS 定義中去。

假如foo模組的聲明如下:

 

[plain] view plaincopy 
  1. include $(CLEAR_VARS)  
  2. LOCAL_MODULE := foo  
  3. LOCAL_SRC_FILES := foo/foo.c  
  4. LOCAL_EXPORT_CFLAGS := -DFOO=1  
  5. include $(BUILD_STATIC_LIBRARY)  

bar模組依賴foo模組,聲明如下:

 

 

[plain] view plaincopy 
  1. include $(CLEAR_VARS)  
  2. LOCAL_MODULE := bar  
  3. LOCAL_SRC_FILES := bar.c  
  4. LOCAL_CFLAGS := -DBAR=2  
  5. LOCAL_STATIC_LIBRARIES := foo  
  6. include $(BUILD_SHARED_LIBRARY)  

因此在編譯bar模組的時候,它的編譯器標誌就是 “-DFOO=1 -DBAR=2”。 匯出的flags 加上本模組的 LOCAL_CFLAGS,成為最後傳給編譯器的flags。這樣修改起來就很容易。這種依賴關係是可傳遞的,例如,如果zoo依賴bar,bar依賴foo,那麼zoo就會同時有bar和foo的匯出flags。

 

注意,編譯模組自身時,不會使用它所匯出的flags。例如在編譯上面的foo模組時, -DFOO=1 不會傳遞給編譯器。

LOCAL_EXPORT_CPPFLAGS

    與 LOCAL_EXPORT_CFLAGS 相同,是跟C++相關的標誌。

LOCAL_EXPORT_C_INCLUDES

    與 LOCAL_EXPORT_CFLAGS 相同,但是只用於標頭檔搜尋路徑。當你的共用庫有多個模組,而且互相之間有標頭檔依賴時有用。用法詳見 Import Module。

LOCAL_EXPORT_LDLIBS

    與 LOCAL_EXPORT_CFLAGS 相同,但是只用於連接器的flag。注意這裡被導人的連結器標誌將追加到模組的 LOCAL_LDLIBS。

    例如當foo模組是一個靜態庫並且代碼依賴於系統庫時,該變數非常有用。 LOCAL_EXPORT_LDLIBS 可以用於匯出該依賴:

 

[plain] view plaincopy 
  1. include $(CLEAR_VARS)  
  2. LOCAL_MODULE := foo  
  3. LOCAL_SRC_FILES := foo/foo.c  
  4. LOCAL_EXPORT_LDLIBS := -llog  
  5. include $(BUILD_STATIC_LIBRARY)  
  6.   
  7. include $(CLEAR_VARS)  
  8. LOCAL_MODULE := bar  
  9. LOCAL_SRC_FILES := bar.c  
  10. LOCAL_STATIC_LIBRARIES := foo  
  11. include $(BUILD_SHARED_LIBRARY)  

此處在編譯bar模組的時候,它的連結器標誌將加上一個 -llog,表示它依賴於系統提供的 liblog.so,因為它依賴 foo 模組。

 

LOCAL_FILTER_ASM

    這個變數指定一個shell命令,用於過濾LOCAL_SRC_FILES 中列出的彙編檔案或者LOCAL_SRC_FILES列出的檔案所編譯出的彙編檔案。定義該變數後,將導致以下行為:

    1)所有的C/C++源碼首先翻譯為臨時彙編檔案(如果不定義LOCAL_FILTER_ASM,則C/C++源碼直接編譯為 obj 檔案)

    2)這些彙編檔案被傳給 LOCAL_FILTER_ASM 所指定的shell命令處理,得到一批新的彙編檔案。

    3)這些新的彙編檔案再被編譯成obj檔案。

換句話說,如果你定義:

 

[plain] view plaincopy 
  1. LOCAL_SRC_FILES  := foo.c bar.S  
  2. LOCAL_FILTER_ASM := myasmfilter  

則foo.c首先傳給編譯器(gcc),得到 foo.S.orignal,然後這個 foo.S.original 被傳給你指定的過濾器(LOCAL_ASM_FILTER),得到 foo.S,然後再傳給彙編器(例如as),得到 foo.o。 bar.S 直接傳給過濾器得到 bar.S.new,然後再傳給彙編器,得到 bar.o。即在從*.S到*.o的編譯流程中增加了一個過濾的環節。

 

過濾器必須是獨立的shell命令,輸入檔案作為它的第一個命令列參數,輸出檔案作為第二個命令列參數,例如:

 

[plain] view plaincopy 
    1.     myasmfilter $OBJS_DIR/foo.S.original $OBJS_DIR/foo.S  
    2.     myasmfilter bar.S $OBJS_DIR/bar.S  

【轉】Android.mk檔案文法規範(Android.mk File)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.