1. 程式人生 > >Android基礎總結之七:Android.mk小結

Android基礎總結之七:Android.mk小結

前言:瞭解四大元件之前也有必要熟悉專案的AndroidManifest.xml基礎知識,還要熟悉Android.mk基礎知識,這兩個應該是前提,個人習慣拿到一個沒有看過一個應用的程式,首先看的是Manifest和makefile。之前已經寫了一篇關於Manifest的文章:Android基礎總結之四:AndroidManifest.xml詳解,這裡做一下makefile的小結。

一、舉例說明

一個Android.mk file用來向編譯系統描述你的原始碼。具體來說:該檔案是GNU Makefile的一小部分,會被編譯系統解析一次或多次。你可以在每一個Android.mk file中定義一個或多個模組,你也可以在幾個模組中使用同一個原始碼檔案。選項參考以下檔案:build/core/config.mk,預設的值在以下檔案中定義:build/core/base_rules.mk。編譯系統為你處理許多細節問題。例如,你不需要在你的Android.mk中列出標頭檔案和依賴檔案。NDK編譯系統將會為你自動處理這些問題。

首先看個小例子:

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_SRC_FILES := $(call all-subdir-java-files)

LOCAL_PACKAGE_NAME := AlarmClock

include $(BUILD_PACKAGE)

1. LOCAL_PATH := $(call my-dir) ,一個Android.mk file首先必須定義好LOCAL_PATH變數。它用於在開發樹中查詢原始檔。在這個例子中,巨集函式‘my-dir’, 由編譯系統提供,用於返回當前路徑(即包含Android.mk file檔案的目錄)。

其中my-dir定義在build/core/definitions.mk中,code如下:

# Figure out where we are.
define my-dir
$(strip \
  $(eval LOCAL_MODULE_MAKEFILE := $$(lastword $$(MAKEFILE_LIST))) \
  $(if $(filter $(CLEAR_VARS),$(LOCAL_MODULE_MAKEFILE)), \
    $(error LOCAL_PATH must be set before including $$(CLEAR_VARS)) \
   , \
    $(patsubst %/,%,$(dir $(LOCAL_MODULE_MAKEFILE))) \
   ) \
 )
endef


2. include $( CLEAR_VARS),CLEAR_VARS由編譯系統提供,指定讓GNU MAKEFILE為你清除許多LOCAL_XXX變數(例如 LOCAL_MODULE, LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES, 等等...),除LOCAL_PATH 。這是必要的,因為所有的編譯控制檔案都在同一個GNU MAKE執行環境中,所有的變數都是全域性的。
CLEAR_VARS定義在build/core/config.mk中,code如下:

CLEAR_VARS:= $(BUILD_SYSTEM)/clear_vars.mk

檢視clear_vars.mk就會知道CLEAR_VARS的具體作用了:

LOCAL_MODULE:=
LOCAL_MODULE_PATH:=
LOCAL_MODULE_STEM:=
LOCAL_DONT_CHECK_MODULE:=
LOCAL_CHECKED_MODULE:=
LOCAL_BUILT_MODULE:=
LOCAL_BUILT_MODULE_STEM:=
OVERRIDE_BUILT_MODULE_PATH:=
LOCAL_INSTALLED_MODULE:=
LOCAL_UNINSTALLABLE_MODULE:=
LOCAL_INTERMEDIATE_TARGETS:=
LOCAL_UNSTRIPPED_PATH:=
LOCAL_MODULE_CLASS:=
LOCAL_MODULE_SUFFIX:=
LOCAL_PACKAGE_NAME:=
LOCAL_OVERRIDES_PACKAGES:=
LOCAL_EXPORT_PACKAGE_RESOURCES:=
LOCAL_MANIFEST_PACKAGE_NAME:=
LOCAL_REQUIRED_MODULES:=
LOCAL_ACP_UNAVAILABLE:=
LOCAL_MODULE_TAGS:=

將除LOCAL_PATH之外的所有LOCAL都置空。


3. LOCAL_SRC_FILES :=$(call all-subdir-java-files),LOCAL_SRC_FILES變數必須包含將要編譯打包進模組中的JAVA、C或C++原始碼檔案。

call all-subdir-java-files定義在build/core/definitions.mk中,code如下:

define all-subdir-java-files
$(call all-java-files-under,.)
endef


4. LOCAL_PACKAGE_NAME := AlarmClock,LOCAL_PACKAGE_NAME指的是生成android基本應用程式,apk包。同LOCAL_MODULE

5. include $(BUILD_PACKAGE),BUILD_PACKAGE和CLEAR_VARS一樣也是在build/core/config.mk下定義的,code如下:

BUILD_PACKAGE:= $(BUILD_SYSTEM)/package.mk

最後的include是生成目標的最關鍵所在,例如,如果要生成apk,那麼就要include $(BUILD_PACKAGE);如果要生成java的jar包,那麼就要include $(BUILD_STATIC_JAVA_LIBRARY),等等......下面會慢慢介紹。

這些BUILD_***都會在/system/core/config.mk中定義的,如下:

# ###############################################################
# Build system internal files
# ###############################################################

BUILD_COMBOS:= $(BUILD_SYSTEM)/combo

CLEAR_VARS:= $(BUILD_SYSTEM)/clear_vars.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_PHONY_PACKAGE:= $(BUILD_SYSTEM)/phony_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_NATIVE_TEST := $(BUILD_SYSTEM)/native_test.mk
BUILD_HOST_NATIVE_TEST := $(BUILD_SYSTEM)/host_native_test.mk

二、各種mk小結

一個Android.mk檔案可以編譯多個模組,每個模組屬下列型別之一1)APK程式一般的Android程式,編譯打包生成apk檔案2)JAVA庫java類庫,編譯打包生成jar檔案3)C\C++應用程式可執行的C\C++應用程式4)C\C++靜態庫 編譯生成C\C++靜態庫,並打包成.a檔案5)C\C++共享庫編譯生成共享庫(動態連結庫),並打包成.so文, 有且只有共享庫才能被安裝/複製到您的應用軟體(APK)包中。在一個 Android.mk 中可以生成多個APK應用程式,JAVA庫,C\C++可執行程式,C\C++動態庫和C\C++靜態庫。1)編譯APK應用程式模板。關於編譯APK應用程式,前面已經介紹過。(2)編譯JAVA庫模板
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# Build all java files in the java subdirectory
LOCAL_SRC_FILES := $(call all-subdir-java-files)
# Any libraries that this library depends on
LOCAL_JAVA_LIBRARIES := android.test.runner
# The name of the jar file to create
LOCAL_MODULE := sample
# Build a static jar file.
include $(BUILD_STATIC_JAVA_LIBRARY)
:LOCAL_JAVA_LIBRARIES := android.test.runner表示用到的JAVA庫的jar檔名(3)編譯C/C++應用程式模板如下
LOCAL_PATH := $(call my-dir)
#include $(CLEAR_VARS)
LOCAL_SRC_FILES := main.c
LOCAL_MODULE := test_exe
#LOCAL_C_INCLUDES :=
#LOCAL_STATIC_LIBRARIES :=
#LOCAL_SHARED_LIBRARIES :=
include $(BUILD_EXECUTABLE)
:‘:=’是賦值的意思,'+='是追加的意思,‘$’表示引用某變數的值LOCAL_SRC_FILES中加入原始檔路徑,LOCAL_C_INCLUDES中加入需要的標頭檔案搜尋路徑LOCAL_STATIC_LIBRARIES 加入所需要連結的靜態庫(*.a)的名稱,LOCAL_SHARED_LIBRARIES 中加入所需要連結的動態庫(*.so)的名稱,LOCAL_MODULE表示模組最終的名稱,BUILD_EXECUTABLE 表示以一個可執行程式的方式進行編譯。(4)編譯C\C++靜態庫
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
    helloworld.c
LOCAL_MODULE:= libtest_static
#LOCAL_C_INCLUDES :=
#LOCAL_STATIC_LIBRARIES :=
#LOCAL_SHARED_LIBRARIES :=
include $(BUILD_STATIC_LIBRARY)
和上面相似,BUILD_STATIC_LIBRARY 表示編譯一個靜態庫。(5)編譯C\C++動態庫的模板
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := helloworld.c
LOCAL_MODULE := libtest_shared
TARGET_PRELINK_MODULES := false
#LOCAL_C_INCLUDES :=
#LOCAL_STATIC_LIBRARIES :=
#LOCAL_SHARED_LIBRARIES :=
include $(BUILD_SHARED_LIBRARY)
和上面相似,BUILD_SHARED_LIBRARY 表示編譯一個共享庫。以上三者的生成結果分別在如下目錄中,generic 依具體 target 會變out/target/product/generic/obj/APPSout/target/product/generic/obj/JAVA_LIBRARIESout/target/product/generic/obj/EXECUTABLEout/target/product/generic/obj/STATIC_LIBRARYout/target/product/generic/obj/SHARED_LIBRARY每個模組的目標資料夾分別為1)APK程式:XXX_intermediates2)JAVA庫程式:XXX_intermediates這裡的XXX3)C\C++可執行程式:XXX_intermediates4)C\C++靜態庫: XXX_static_intermediates5)C\C++動態庫: XXX_shared_intermediates

三、常用變數小結

1) LOCAL_PATH: 這個變數用於給出當前檔案的路徑。必須在 Android.mk 的開頭定義,可以這樣使用:LOCAL_PATH := $(call my-dir) 這個變數不會被$(CLEAR_VARS)清除,因此每個 Android.mk 只需要定義一次(即使在一個檔案中定義了幾個模組的情況下)。

(2)LOCAL_MODULE: 這是模組的名字,它必須是唯一的,而且不能包含空格。必須在包含任一的$(BUILD_XXXX)指令碼之前定義它。模組的名字決定了生成檔案的名字。例如,如果一個一個共享庫模組的名字是,那麼生成檔案的名字就是 lib.so。但是,在的 NDK 生成文件中(或者 Android.mk 或者 Application.mk),應該只涉及(引用)有正常名字的其他模組。(3)LOCAL_SRC_FILES: 這是要編譯的原始碼檔案列表。只要列出要傳遞給編譯器的檔案,因為編譯系統自動計算依賴。注意原始碼檔名稱都是相對於 LOCAL_PATH的,你可以使用路徑部分,例如:LOCAL_SRC_FILES := foo.c toto/bar.c\Hello.c檔案之間可以用空格或Tab鍵進行分割,換行請用"\".如果是追加原始碼檔案的話,請用LOCAL_SRC_FILES +=注意在生成檔案中都要使用UNIX風格的斜槓(/).windows風格的反斜槓不會被正確的處理。注意:可以LOCAL_SRC_FILES := $(call all-subdir-java-files)這種形式來包含local_path目錄下的所有java檔案。(4) LOCAL_CPP_EXTENSION: 這是一個可選變數, 用來指定C++程式碼檔案的副檔名,預設是'.cpp',但是可以改變它,比如:LOCAL_CPP_EXTENSION := .cxx(5) LOCAL_C_INCLUDES: 可選變數,表示標頭檔案的搜尋路徑。預設的標頭檔案的搜尋路徑是LOCAL_PATH目錄。示例:LOCAL_C_INCLUDES := sources/foo或LOCAL_C_INCLUDES := $(LOCAL_PATH)/../fooLOCAL_C_INCLUDES需要在任何包含LOCAL_CFLAGS/LOCAL_CPPFLAGS標誌之前進行設定。(6)LOCAL_CFLAGS: 可選的編譯器選項,在編譯 C 程式碼檔案的時候使用。這可能是有用的,指定一個附加的包含路徑(相對於NDK的頂層目錄),巨集定義,或者編譯選項。注意不要在 Android.mk 中改變 optimization/debugging 級別,只要在 Application.mk 中指定合適的資訊,就會自動地為你處理這個問題,在除錯期間,會讓 NDK自動生成有用的資料檔案(7)LOCAL_CXXFLAGS: 與 LOCAL_CFLAGS同理,針對 C++原始檔。(8)LOCAL_CPPFLAGS: 與 LOCAL_CFLAGS同理,但是對 C 和 C++ source files都適用。(9)LOCAL_STATIC_LIBRARIES: 表示該模組需要使用哪些靜態庫,以便在編譯時進行連結。(10)LOCAL_SHARED_LIBRARIES: 表示模組在執行時要依賴的共享庫(動態庫),在連結時就需要,以便在生成檔案時嵌入其相應的資訊。注意:它不會附加列出的模組到編譯圖,也就是仍然需要在Application.mk 中把它們新增到程式要求的模組中。(11)LOCAL_LDLIBS: 編譯模組時要使用的附加的連結器選項。這對於使用‘-l’字首傳遞指定庫的名字是有用的。例如,LOCAL_LDLIBS := -lz表示告訴連結器生成的模組要在載入時刻連結到/system/lib/libz.so可檢視 docs/STABLE-APIS.TXT 獲取使用 NDK發行版能連結到的開放的系統庫列表。(12) LOCAL_ALLOW_UNDEFINED_SYMBOLS: 預設情況下, 在試圖編譯一個共享庫時,任何未定義的引用將導致一個“未定義的符號”錯誤。這對於在原始碼檔案中捕捉錯誤會有很大的幫助。然而,如果因為某些原因,需要不啟動這項檢查,可把這個變數設為‘true’。注意相應的共享庫可能在執行時載入失敗。(這個一般儘量不要去設為 true)。(13) LOCAL_ARM_MODE: 預設情況下, arm目標二進位制會以 thumb 的形式生成(16 位),你可以通過設定這個變數為 arm如果你希望你的 module 是以 32 位指令的形式。'arm' (32-bit instructions) mode. E.g.:LOCAL_ARM_MODE := arm注意:可以在編譯的時候告訴系統針對某個原始碼檔案進行特定的型別的編譯比如,LOCAL_SRC_FILES := foo.c bar.c.arm 這樣就告訴系統總是將 bar.c 以arm的模式編譯。(14)LOCAL_MODULE_PATH 和 LOCAL_UNSTRIPPED_PATH在 Android.mk 檔案中, 還可以用LOCAL_MODULE_PATHLOCAL_UNSTRIPPED_PATH指定最後的目標安裝路徑.不同的檔案系統路徑用以下的巨集進行選擇:TARGET_ROOT_OUT:表示根檔案系統。TARGET_OUT:表示 system檔案系統。TARGET_OUT_DATA:表示 data檔案系統。用法如:LOCAL_MODULE_PATH :=$(TARGET_ROOT_OUT) 至於LOCAL_MODULE_PATH 和LOCAL_UNSTRIPPED_PATH的區別,暫時還不清楚。(15)LOCAL_MODULE_TAGS:= user eng tests optional

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

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

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

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

四、編譯流程小結

我們知道,在編譯android系統的時候,會用到make命令,那麼必須用到對應的Makefile檔案。

我們可以在根目錄中找到這樣的Makefile檔案,開啟一看:

### DO NOT EDIT THIS FILE ###
include build/core/main.mk
### DO NOT EDIT THIS FILE ###
瞭解makefile的知道所有的定義、執行都應該在main.mk中。

很簡單就能找到config.mk,這個是個很關鍵的makefile,其中不但包含了product的相關變數定義,和所有mk變數的定義,也包含了具體的執行mk。

因為mk檔案很多,有的人會問,一般這些都是系統的makefile,device或者vendor下面的mk是什麼時候呼叫到的呢?是最後還是最開始?

這些都可以在config.mk中找到結果。

在config.mk中很快就能找到produc_config.mk,對的,這就是關於product的所有配置、定義、連結。其中最關鍵的就是AndroidProduct.mk,這裡會尋找除了build下的AndroidProduct.mk,還會到device或者vendor下面找,會找到所有定義PRODUCT_MAKEFILES的地方,並且執行。這個時候就進入了product的mk中了。

這裡寫的只是一些小結,如果想要詳細瞭解,還是需要多看看資料、書籍。