1. Android編譯系統分析 —系統變量解析
LOCAL_PATH – 編譯時的目錄
LOCAL_MODULE – 編譯的目標對象
LOCAL_SRC_FILES – 編譯的源文件
LOCAL_C_INCLUDES – 需要包含的頭文件目錄
LOCAL_SHARED_LIBRARIES – 鏈接時需要的外部庫
LOCAL_PRELINK_MODULE – 是否需要prelink處理
BUILD_STATIC_LIBRARY – 指明要編譯成靜態庫
BUILD_SHARED_LIBRARY – 指明要編譯成動態庫
(1). LOCAL_PATH – 編譯時的目錄
$(call 目錄,目錄….) 目錄引入操作符: 如該目錄下有個文件夾名稱 src,則可以這樣寫 $(call src),那麼就會得到 src 目錄的完整路徑
(2). include $(CLEAR_VARS) -清除之前的一些系統變量
CLEAR_VARS:= $(BUILD_SYSTEM)/clear_vars.mk
在 build/core/config.mk 定義 CLEAR_VARS:= $(BUILD_SYSTEM)/clear_vars.mk
通過include 包含自定義的.mk文件(即是自定義編譯規則)或是引用系統其他的.mk文件(系統定義的編譯規則)。
(3). LOCAL_SRC_FILES – 編譯的源文件
可以是.c, .cpp, .java, .S(匯編文件)或是.aidl等格式,不同的文件用空格隔開。
如果編譯目錄子目錄,采用相對路徑,如子目錄/文件名。
也可以通過$(call 目錄),指明編譯某目錄下所有.c/.cpp/.java/.S/ .aidl文件. 追加文件 LOCAL_SRC_FILES += 文件
(4). LOCAL_C_INCLUDES – 需要包含的頭文件目錄
可以是系統定義路徑,也可以是相對路徑. 如該編譯目錄下有個include目錄,寫法是include/*.h
(5). LOCAL_MODULE – 編譯的目標對象
module 是指系統的 native code,通常針對c,c++代碼
./system/core/sh/Android.mk:32:LOCAL_MODULE:= sh
./system/core/libcutils/Android.mk:71:LOCAL_MODULE := libcutils
./system/core/cpio/Android.mk:9:LOCAL_MODULE := mkbootfs
./system/core/mkbootimg/Android.mk:8:LOCAL_MODULE := mkbootimg
./system/core/toolbox/Android.mk:61:LOCAL_MODULE:= toolbox
./system/core/logcat/Android.mk:10:LOCAL_MODULE:= logcat
./system/core/adb/Android.mk:65:LOCAL_MODULE := adb
./system/core/adb/Android.mk:125:LOCAL_MODULE := adbd
./system/core/init/Android.mk:20:LOCAL_MODULE:= init
./system/core/vold/Android.mk:24:LOCAL_MODULE:= vold
./system/core/mountd/Android.mk:13:LOCAL_MODULE:= mountd
(6). LOCAL_PACKAGE_NAME
Java 應用程序的名字用該變量定義
./packages/apps/Music/Android.mk:9:LOCAL_PACKAGE_NAME := Music
./packages/apps/Browser/Android.mk:14:LOCAL_PACKAGE_NAME := Browser
./packages/apps/Settings/Android.mk:8:LOCAL_PACKAGE_NAME := Settings
./packages/apps/Stk/Android.mk:10:LOCAL_PACKAGE_NAME := Stk
./packages/apps/Contacts/Android.mk:10:LOCAL_PACKAGE_NAME := Contacts
./packages/apps/Mms/Android.mk:8:LOCAL_PACKAGE_NAME := Mms
./packages/apps/Camera/Android.mk:8:LOCAL_PACKAGE_NAME := Camera
./packages/apps/Phone/Android.mk:11:LOCAL_PACKAGE_NAME := Phone
./packages/apps/VoiceDialer/Android.mk:8:LOCAL_PACKAGE_NAME := VoiceDialer
(7).依賴庫
LOCAL_SHARED_LIBRARIES – 鏈接時需要的外部共享庫
LOCAL_STATIC_LIBRARIES – 鏈接時需要的外部外部靜態
LOCAL_JAVA_LIBRARIES 加入jar包
(8). BUILD_SHARED_LIBRARY – 指明要編譯成動態庫。編譯的目標,用include 操作符
BUILD_STATIC_LIBRARY — 來指明要編譯成靜態庫。
如果是java文件的話,用 BUILD_PACKAGE 來指明。會用到系統的編譯腳本host_java_library.mk,
——————-
include $(BUILD_STATIC_LIBRARY)
BUILD_STATIC_LIBRARY:= $(BUILD_SYSTEM)/static_library.mk
——————-
include $(BUILD_SHARED_LIBRARY)
./build/core/config.mk:50:BUILD_SHARED_LIBRARY:= $(BUILD_SYSTEM)/shared_library.mk
——————-
include $(BUILD_HOST_SHARED_LIBRARY)
BUILD_HOST_SHARED_LIBRARY:= $(BUILD_SYSTEM)/host_shared_library.mk
——————-
include $(BUILD_EXECUTABLE)
build/core/config.mk:51:BUILD_EXECUTABLE:= $(BUILD_SYSTEM)/executable.mk
——————-
include $(BUILD_HOST_EXECUTABLE)
./build/core/config.mk:53:BUILD_HOST_EXECUTABLE:= $(BUILD_SYSTEM)/host_executable.mk
——————-
BUILD_HOST_JAVA_LIBRARY:= $(BUILD_SYSTEM)/host_java_library.mk
——————-
BUILD_JAVA_LIBRARY
./build/core/config.mk:58:BUILD_JAVA_LIBRARY:= $(BUILD_SYSTEM)/java_library.mk
——————
BUILD_STATIC_JAVA_LIBRARY 編譯靜態JAVA庫
./build/core/config.mk:59:BUILD_STATIC_JAVA_LIBRARY:= $(BUILD_SYSTEM)/static_java_library.mk
——————
BUILD_HOST_JAVA_LIBRARY 編譯本機用的JAVA庫
./build/core/config.mk:60:BUILD_HOST_JAVA_LIBRARY:= $(BUILD_SYSTEM)/host_java_library.mk
——————
BUILD_HOST_STATIC_LIBRARY:= $(BUILD_SYSTEM)/host_static_library.mk
BUILD_HOST_SHARED_LIBRARY:= $(BUILD_SYSTEM)/host_shared_library.mk
BUILD_STATIC_LIBRARY:= $(BUILD_SYSTEM)/static_library.mk
BUILD_RAW_STATIC_LIBRARY := $(BUILD_SYSTEM)/raw_static_library.mk
BUILD_SHARED_LIBRARY:= $(BUILD_SYSTEM)/shared_library.mk
BUILD_EXECUTABLE:= $(BUILD_SYSTEM)/executable.mk
BUILD_RAW_EXECUTABLE:= $(BUILD_SYSTEM)/raw_executable.mk
BUILD_HOST_EXECUTABLE:= $(BUILD_SYSTEM)/host_executable.mk
BUILD_PACKAGE:= $(BUILD_SYSTEM)/package.mk
BUILD_HOST_PREBUILT:= $(BUILD_SYSTEM)/host_prebuilt.mk
BUILD_PREBUILT:= $(BUILD_SYSTEM)/prebuilt.mk
BUILD_MULTI_PREBUILT:= $(BUILD_SYSTEM)/multi_prebuilt.mk
BUILD_JAVA_LIBRARY:= $(BUILD_SYSTEM)/java_library.mk
BUILD_STATIC_JAVA_LIBRARY:= $(BUILD_SYSTEM)/static_java_library.mk
BUILD_HOST_JAVA_LIBRARY:= $(BUILD_SYSTEM)/host_java_library.mk
BUILD_DROIDDOC:= $(BUILD_SYSTEM)/droiddoc.mk
BUILD_COPY_HEADERS := $(BUILD_SYSTEM)/copy_headers.mk
BUILD_KEY_CHAR_MAP := $(BUILD_SYSTEM)/key_char_map.mk
2.NDK提供的宏功能 及 Anroid.mk 規范
以下是使用GNU make的宏功能,必須通過使用"$(call )",返回一個文本信息。
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,)
這將會找到通過NDK_MODULE_PATH環境變量引用的模塊的目錄列表,並且將其自動包含到
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.so,它是foo模塊的名字。
你可以用LOCAL_MODULE_FILENAME覆蓋默認的那一個
LOCAL_MODULE_FILENAME
這個變量是可選的,並且允許你重新定義生成文件的名字。默認的,模塊將始終生成lib.a或者lib.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來指定額外的包含路徑,然而,如果使用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設備的內核上被啟用。
更多信息,可以參見:
https://en.wikipedia.org/wiki/NX_bit
https://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。