Android.mk簡單分析

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

LOCAL_MODULE_TAGS := optional

LOCAL_SRC_FILES := $(call all-java-files-under, src)

LOCAL_PACKAGE_NAME := Settings
LOCAL_CERTIFICATE := platform

include $(BUILD_PACKAGE)

# Use the folloing include to make our test apk.

include $(call all-makefiles-under,$(LOCAL_PATH))


該Android.mk文件路徑是package/app/Settings/Android.mk,來分析該文件

GNU Make‘功能’宏,必須通過使用'$(call  )'來調用,調用他們將返回文本化的信息。

------------------------------------------------------------------------------------------------------------------------------

(1) LOCAL_PATH:= $(call my-dir)

一個Android.mk file首先必須定義好LOCAL_PATH變量。它用於在開發樹中查找源文件。

宏函數’my-dir’,由編譯系統提供,用於返回當前路徑(即包含Android.mk file文件的目錄)。

------------------------------------------------------------------------------------------------------------------------------

(2) Android.mk中可以定義多個編譯模塊,每個編譯模塊都是以include $(CLEAR_VARS)開始,以include $(BUILD_XXX)結束。

(2.1) include $(CLEAR_VARS)

CLEAR_VARS指的是clear_vars.mk,由編譯系統提供,它會讓GNU MAKEFILE爲你清除除LOCAL_PATH以外的所有LOCAL_XXX變量,如 LOCAL_MODULE,LOCAL_SRC_FILES,LOCAL_SHARED_LIBRARIES,LOCAL_STATIC_LIBRARIES 等。

這是必要的,因爲所有的編譯控制文件都在同一個GNU MAKE執行環境中,所有的變量都是全局的。

 

(2.2) include $(BUILD_PACKAGE)      # Tell it to build an APK

$(BUILD_PACKAGE)是用來編譯生成package/app/下的apk。

還有其他幾種編譯情況:

include $(BUILD_STATIC_LIBRARY)   表示編譯成靜態庫

include $(BUILD_SHARED_LIBRARY)  表示編譯成動態庫

include $(BUILD_EXECUTABLE)      表示編譯成可執行程序         

至於例子的話,跳轉到下面的"擴展"

------------------------------------------------------------------------------------------------------------------------------

(3) LOCAL_MODULE_TAGS := optional

解析:

LOCAL_MODULE_TAGS :=user eng tests optional

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

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

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

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

取值範圍debug eng tests optional samples shell_ash shell_mksh。注意不能取值user,如果要預裝,則應定義core.mk。

------------------------------------------------------------------------------------------------------------------------------

(4) LOCAL_SRC_FILES := $(call all-java-files-under, src)

(4.1) 如果要包含的是java源碼的話,可以調用all-java-files-under得到。(這種形式來包含local_path目錄下的所有java文件)

(4.2) 當涉及到C/C++時,LOCAL_SRC_FILES變量就必須包含將要編譯打包進模塊中的C或C++源代碼文件。注意,在這裏你可以不用列出頭文件和包含文件,因爲編譯系統將會自動爲你找出依賴型的文件;僅僅列出直接傳遞給編譯器的源代碼文件就好。

all-java-files-under宏的定義是在build/core/definitions.mk中。

------------------------------------------------------------------------------------------------------------------------------

(5) LOCAL_PACKAGE_NAME := Settings

package的名字,這個名字在腳本中將標識這個app或package。

------------------------------------------------------------------------------------------------------------------------------

(6) LOCAL_CERTIFICATE := platform

LOCAL_CERTIFICATE 後面是簽名文件的文件名,說明Settings.apk是一個需要platform key簽名的APK

  用於指定簽名時使用的KEY,如果不指定,默認使用testkey,LOCAL_CERTIFICATE可設置的值如下:

    LOCAL_CERTIFICATE := platform
    LOCAL_CERTIFICATE := shared
    LOCAL_CERTIFICATE := media

 而在Android.mk中的這些配置,需要在APK源碼的AndroidManifest.xml文件中的manifest節點添加如下內容:

    android:sharedUserId="android.uid.system"
    android:sharedUserId="android.uid.shared"
    android:sharedUserId="android.media"
 這些剛好與上面的mk文件裏的配置對應上。
 在Android源碼的build/target/product/security/目錄下有如下的4對KEY:
    1、media.pk8與media.x509.pem;
    2、platform.pk8與platform.x509.pem;
    3、shared.pk8與shared.x509.pem;
    4、testkey.pk8與testkey.x509.pem;
其中,“*.pk8”文件爲私鑰,“*.x509.pem”文件爲公鑰,這需要去了解非對稱加密方式。

------------------------------------------------------------------------------------------------------------------------------

(7) include $(BUILD_PACKAGE)

在(2.2)中已作解釋

------------------------------------------------------------------------------------------------------------------------------

(8) include $(call all-makefiles-under,$(LOCAL_PATH))

加載當前目錄下的所有makefile文件,all-makefiles-under會返回一個位於當前'my-dir'路徑的子目錄中的所有Android.mk的列表。

all-makefiles-under宏的定義是在build/core/definitions.mk中。

------------------------------------------------------------------------------------------------------------------------------

 

這個Android.mk文件最後就生成了Settings.apk。分析完上面的Android.mk文件後,來總結下各種LOCAL_XXX。

三種情況說明:

    必須定義, 在app或package的Android.mk中必須給定值。

    可選定義,在app或package的Android.mk中可以也可以不給定值。

    不用定義,在app或package的Android.mk中不要給定值,腳本自動指定值。

 

複製代碼
複製代碼
LOCAL_PATH,          當前路徑,必須定義。
LOCAL_PACKAGE_NAME,  必須定義,package的名字,這個名字在腳本中將標識app或package。
LOCAL_MODULE_SUFFIX, 不用定義,module的後綴,=.apk。
LOCAL_MODULE,        不用定義,=$(LOCAL_PACKAGE_NAME)。
LOCAL_JAVA_RESOURCE_DIRS,     不用定義。
LOCAL_JAVA_RESOURCE_FILES,    不用定義。
LOCAL_MODULE_CLASS,  不用定義。
LOCAL_MODULE_TAGS,   可選定義。默認optional。取值範圍user debug eng tests optional samples shell_ash shell_mksh。
LOCAL_ASSET_DIR,     可選定義,推薦不定義。默認$(LOCAL_PATH)/assets
LOCAL_RESOURCE_DIR,  可選定義,推薦不定義。默認product package和device package相應的res路徑和$(LOCAL_PATH)/res。
LOCAL_PROGUARD_ENABLED,       可選定義,默認爲full,如果是user或userdebug。取值full, disabled, custom。
full_android_manifest,        不用定義,=$(LOCAL_PATH)/AndroidManifest.xml。
LOCAL_EXPORT_PACKAGE_RESOURCES,    可選定義,默認null。如果允許app的資源被其它模塊使用,則設置true。
LOCAL_CERTIFICATE,   可選定義,默認爲testkey。最終
        private_key := $(LOCAL_CERTIFICATE).pk8
        certificate := $(LOCAL_CERTIFICATE).x509.pem
複製代碼
複製代碼

 

 

擴展:在一個Android.mk中可以生成多個可執行程序、動態庫和靜態庫。

(1)編譯APK應用程序模板。
關於編譯APK應用程序的模板請參照《Android.mk編譯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 會變(可能是dkb~~):
out/target/product/generic/obj/APPS
out/target/product/generic/obj/JAVA_LIBRARIES
out/target/product/generic/obj/EXECUTABLE
out/target/product/generic/obj/STATIC_LIBRARY
out/target/product/generic/obj/SHARED_LIBRARY

每個模塊的目標文件夾分別爲:

1)APK程序:XXX_intermediates
2)JAVA庫程序:XXX_intermediates
3)C\C++可執行程序:XXX_intermediates
4)C\C++靜態庫: XXX_static_intermediates
5)C\C++動態庫: XXX_shared_intermediates

 

 

http://hubingforever.blog.163.com/blog/static/171040579201152185542166/

Android.mk文件是GNU Makefile的一小部分,它用來對Android程序進行編譯。
因爲所有的編譯文件都在同一個 GNU MAKE 執行環境中進行執行,而Android.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 file 中定義一個或多個模塊,你也可以在幾個模塊中使用同一個
源代碼文件。  編譯系統爲你處理許多細節問題。例如,你不需要在你的 Android.mk 中列出頭文件和依
賴文件。編譯系統將會爲你自動處理這些問題。這也意味着,在升級 NDK 後,你應該
得到新的toolchain/platform支持,而且不需要改變你的 Android.mk 文件。
  注意,NDK的Anroid.mk語法同公開發布的Android平臺開源代碼的Anroid.mk語法很接近,然而編譯系統實現他們的
方式卻是不同的,這是故意這樣設計的,可以讓程序開發人員重用外部庫的源代碼更容易。
  在描述語法細節之前,咱們來看一個簡單的"hello world"的例子,比如,下面的文件:
 sources/helloworld/helloworld.c
 sources/helloworld/Android.mk
 'helloworld.c'是一個 JNI 共享庫,實現返回"hello world"字符串的原生方法。相應的
Android.mk 文件會象下面這樣:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE:= helloworld
LOCAL_SRC_FILES := helloworld.c
include $(BUILD_SHARED_LIBRARY)
  解釋一下幾行代碼:
  LOCAL_PATH := $(call my-dir)
  一個Android.mk file首先必須定義好LOCAL_PATH變量。它表示是當前文件的路徑。
在這個例子中, 宏函數‘my-dir’,  由編譯系統提供, 用於返回當前路徑(即包含Android.mk file
文件的目錄)
  include $(CLEAR_VARS)
  CLEAR_VARS 由編譯系統提供(可以在 android 安裝目錄下的/build/core/config.mk 文件看到其定義,爲 CLEAR_VARS:= $(BUILD_SYSTEM)/clear_vars.mk),指定讓GNU MAKEFILE該腳本爲你清除許多 LOCAL_XXX 變量 ( 例如 LOCAL_MODULE , LOCAL_SRC_FILES ,LOCAL_STATIC_LIBRARIES,等等…),除 LOCAL_PATH。這是必要的,因爲所有的編譯文件都在同一個 GNU MAKE 執行環境中,所有的變量都是全局的。所以我們需要先清空這些變量(LOCAL_PATH除外)。又因爲LOCAL_PATH總是要求在每個模塊中都要進行設置,所以並需要清空它。
另外注意,該語句的意思就是把CLEAR_VARS變量所指向的腳本文件包含進來。
  LOCAL_MODULE := helloworld
  LOCAL_MODULE 變量必須定義,以標識你在 Android.mk 文件中描述的每個模塊。名稱必須是唯一的,而且不包含任何空格。注意編譯系統會自動產生合適的前綴和後綴,換句話說,一個被命名爲'foo'的共享庫模 塊,將會生成'libfoo.so'文件。
注意:如果把庫命名爲‘libhelloworld’,編譯系統將不會添加任何的 lib 前綴,也會生成 libhelloworld.so。
  LOCAL_SRC_FILES := helloworld.c
  LOCAL_SRC_FILES 變量必須包含將要編譯打包進模塊中的 C 或 C++源代碼文件。不用
在這裏列出頭文件和包含文件,編譯系統將會自動找出依賴型的文件,當然對於包含文件,你包含時指定的路徑應該正確。
注意,默認的 C++源碼文件的擴展名是‘.cpp’ 。指定一個不同的擴展名也是可能的,只要定義LOCAL_DEFAULT_CPP_EXTENSION 變量,不要忘記開始的小圓點(也就是定義爲  ‘.cxx’,而不是‘cxx’)
  include $(BUILD_SHARED_LIBRARY)
  BUILD_SHARED_LIBRARY 是編譯系統提供的變量,指向一個 GNU Makefile 腳本(應該
就是在 build/core  目錄下的 shared_library.mk) ,將根據LOCAL_XXX系列變量中的值,來編譯生成共享庫(動態鏈接庫)。
如果想生成靜態庫,則用BUILD_STATIC_LIBRARY
  在NDK的sources/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)
注意:‘:=’是賦值的意思;'+='是追加的意思;‘$’表示引用某變量的值。
三、GNU Make系統變量
  這些 GNU Make變量在你的 Android.mk 文件解析之前,就由編譯系統定義好了。注意在
某些情況下,NDK可能分析 Android.mk 幾次,每一次某些變量的定義會有不同。
  (1)CLEAR_VARS:  指 向一個編譯腳本,幾乎所有未定義的 LOCAL_XXX 變量都在"Module-description"節中列出。必須在開始一個新模塊之前包含這個腳本:include$(CLEAR_VARS),用於重 置除LOCAL_PATH變量外的,所有LOCAL_XXX系列變量。
  (2)BUILD_SHARED_LIBRARY:  指向編譯腳本,根據所有的在 LOCAL_XXX 變量把列出的源代碼文件編譯成一個共享庫。
  注意,必須至少在包含這個文件之前定義 LOCAL_MODULE 和 LOCAL_SRC_FILES。
(3) BUILD_STATIC_LIBRARY:  一個 BUILD_SHARED_LIBRARY 變量用於編譯一個靜態庫。靜態庫不會複製到的APK包中,但是能夠用於編譯共享庫。
示例include $(BUILD_STATIC_LIBRARY) 
注意,這將會生成一個名爲 lib$(LOCAL_MODULE).a 的文件
  (4)TARGET_ARCH: 目標 CPU平臺的名字,  和 android 開放源碼中指定的那樣。如果是
arm,表示要生成 ARM 兼容的指令,與 CPU架構的修訂版無關。
  (5)TARGET_PLATFORM: Android.mk 解析的時候,目標 Android 平臺的名字.詳情可參考/development/ndk/docs/stable- apis.txt.
 android-3 -> Official Android 1.5 system images
 android-4 -> Official Android 1.6 system images
 android-5 -> Official Android 2.0 system images
  (6)TARGET_ARCH_ABI:  暫 時只支持兩個 value,armeabi 和 armeabi-v7a。在現在的版本中一般把這兩個值簡單的定義爲 arm, 通過 android  平臺內部對它重定義來獲得更好的匹配。其他的 ABI 將在以後的 NDK 版本中介紹,它們會有不同的名字。注意雖然所有基於
ARM的ABI都會把 'TARGET_ARCH'定義成‘arm’, 但是會有不同的‘TARGET_ARCH_ABI’。 
( 7 ) TARGET_ABI:  目標平臺和 ABI 的組合,它事實上被定義成$(TARGET_PLATFORM)-$(TARGET_ARCH_ABI)  ,在想要在真實的設備中針對一個特別的目標系統進行測試時,會有用。在默認的情況下,它會是'android-3-arm'。
五、模塊描述變量
  下面的變量用於向編譯系統描述你的模塊。你應該定義在'include  $(CLEAR_VARS)'和'include $(BUILD_XXXXX)'之間。正如前面描寫的那樣,$(CLEAR_VARS)是一個腳本,清除所有這些變量
  (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)/../foo
  LOCAL_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發行版能鏈接到的開放的系統庫列表。
(注意:LOCAL_LDLIBS與LOCAL_STATIC_LIBRARIES及LOCAL_SHARED_LIBRARIES的區別是,LOCAL_LDLIBS是沒有源代碼或代碼未被包含到Android.mk的庫,如庫不存在,則會報錯找不到; 而其餘兩個是有代碼且被包含到Android.mk的庫裏了,當她們的代碼發生變化時候或當庫不存在時,會自動重新編譯他們的代碼)
  (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的區別,暫時還不清楚。
七、GNU Make‘功能’宏
GNU Make‘功能’宏,必須通過使用'$(call  )'來調用,調用他們將返回文本化的信息。
(1)my-dir:返回當前 Android.mk 所在的目錄的路徑,相對於 NDK 編譯系統的頂層。這是有用的,在 Android.mk 文件的開頭如此定義:
LOCAL_PATH := $(call my-dir)
(2)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 中的文件。
(3)this-makefile:  返回當前Makefile 的路徑(即這個函數調用的地方)
(4)parent-makefile:  返回調用樹中父 Makefile 路徑。即包含當前Makefile的Makefile 路徑。
(5)grand-parent-makefile返回調用樹中父Makefile的父Makefile的路徑
八、 Android.mk 使用模板
  在一個 Android.mk 中可以生成多個APK應用程序,JAVA庫,C\C++可執行程序,C\C++動態庫和C\C++靜態庫。
1)編譯APK應用程序模板。
關於編譯APK應用程序的模板請參照Android.mk編譯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/APPS
out/target/product/generic/obj/JAVA_LIBRARIES
out/target/product/generic/obj/EXECUTABLE
out/target/product/generic/obj/STATIC_LIBRARY
out/target/product/generic/obj/SHARED_LIBRARY
每個模塊的目標文件夾分別爲
1)APK程序:XXX_intermediates
2)JAVA庫程序:XXX_intermediates
這裏的XXX
 3)C\C++可執行程序:XXX_intermediates
 4)C\C++靜態庫: XXX_static_intermediates
 5)C\C++動態庫: XXX_shared_intermediates
 
 
 

Android Android.mk 文件一點感悟

Android.mk文件時android 中的一個非常重要的概念。我們有以下幾個方便的時候需要使用它。

1:添加新的apk源碼文件編譯。這裏我們先參考下AlarmClock的Android.mk文件

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

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

LOCAL_PACKAGE_NAME := AlarmClock

include $(BUILD_PACKAGE)



簡單的解釋下:
第一行,賦予LOCAL_PATH一個新的值,表示當前的路徑。
第二行,清楚所有的系統自帶的標準變量值。我們通過查找CLEAR_VARS變量定義,可以發現它實際上代表的是
source\build\core\config.mk中有定義如下:
CLEAR_VARS:= $(BUILD_SYSTEM)/clear_vars.mk
所以查看clear_vars.mk文件可以得到知道:


###########################################################
## Clear out values of all variables used by rule templates.
###########################################################

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_PATH變量。所以這個步驟放在第二行,是沒有關係的。它的這個寫法有點誤導人哈。

第三、四行,給變量賦予新值。這裏賦予的是LOCAL_SRC_FILES,LOCAL_PACKAGE_NAME
第五行,執行BUILD_PACKAGE。它的定義也是在config.mk中定義如下:
BUILD_PACKAGE:= $(BUILD_SYSTEM)/package.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_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

基本上你有用到的就已經提前給你設計好了。你要做的很簡單了。


這裏的思想其實和makefile的是一樣的。

目標:信賴文件
執行命令

只是android在這裏進行了高度的封裝和簡化。
前面四行相當於設置目標文件。
最後一行相當於執行命令。

所以,如果我們要添加自己的apk,就按照這種思想添加就可以了。


2:變量的使用
makefile文件有一個基礎的東西就是變量。大家可以自己瞭解下。這裏我們說說android.mk這個變量的不同的地方。主要是用它來做代碼管理。這個部分是我同事小強總結出來的,我就cp過來。
替它總結下。

首先在執行make文件的時候這些變量是全局有效的,一些公共的變量會通過include $(CLEAR_VARS)給清除掉。我們這裏主要是添加自己的變量。
我們在使用自己定義變量的時候有兩種情況,一種是在mk中使用,一種是在cpp中使用。注意java不支持的。
這兩種情況有點不同,請注意。

首先我們得定義這個變量,一般來講都是在產品的BoardConfig.mk中定義,例如:


TARGET_MEMORY_STYLE := TARGET_MEM_256MB
#TARGET_MEMORY_STYLE := TARGET_MEM_256MB_ST
#TARGET_MEMORY_STYLE := TARGET_MEM_512MB
#TARGET_MEMORY_STYLE := TARGET_MEM_512MB_ST

# board-specific configuration
BOARD_OPENCORE_FLAGS := -DTELECHIPS_ALSA

ifeq ($(BOARD_KERNEL_BASE),0x40000000)
BOARD_MEM_FLAGS := -DTCC_MEM_256MB
else
BOARD_MEM_FLAGS := -DTCC_MEM_512MB
endif


這裏總共有兩個我們經常用的東西。

第一:
   
    如果是mk總使用,定義如前四行就可以了。
    變量 := 字符串
   
    在mk中的使用方法就像後面例子一樣。
    ifeq($(變量),對比值)
   
    else
   
    endif
   
   
第二:如果你想在cpp中使用,請定義方式如下
   
    變量 := -D+變量名
 
  如後面幾行示例一樣的。
 
    如果想在cpp中應用,首先必須在cpp對於的mk文件中,聲明如下
    LOCAL_CFLAGS += $(BOARD_MEM_FLAGS)
       
    格式:   
    LOCAL_CFLAGS += $(變量)
       
   
    然後它的cpp中就可以引用如下:
    此時去掉了-D前綴。
   
    ifdef TCC_MEM_512MB
    ......
    endif
   
    特此感謝小強同學的代碼控制.doc。

 

 

http://www.ibm.com/developerworks/cn/opensource/os-cn-android-build/

理解 Android Build 系統

Android Build 系統是用來編譯 Android 系統,Android SDK 以及相關文檔的一套框架。衆所周知,Android 是一個開源的操作系統。Android 的源碼中包含了許許多多的模塊。 不同產商的不同設備對於 Android 系統的定製都是不一樣的。如何將這些模塊統一管理起來,如何能夠在不同的操作系統上進行編譯,如何在編譯時能夠支持面向不同的硬件設備,不同的編譯類型, 且還要提供面向各個產商的定製擴展,是非常有難度的。 但 Android Build 系統很好的解決了這些問題,這裏面有很多值得我們開發人員學習的地方。對於 Android 平臺開發人員來說,本文可以幫助你熟悉你每天接觸到的構建環境。對於其他開發人員來說,本文可以作爲一個 GNU Make 的使用案例,學習這些成功案例,可以提升我們的開發經驗。

前言

Android Build 系統是 Android 源碼的一部分。關於如何獲取 Android 源碼,請參照 Android Source 官方網站:

http://source.android.com/source/downloading.html

Android Build 系統用來編譯 Android 系統,Android SDK 以及相關文檔。該系統主要由 Make 文件,Shell 腳本以及 Python 腳本組成,其中最主要的是 Make 文件。

衆所周知,Android 是一個開源的操作系統。Android 的源碼中包含了大量的開源項目以及許多的模塊。不同產商的不同設備對於 Android 系統的定製都是不一樣的。

如何將這些項目和模塊的編譯統一管理起來,如何能夠在不同的操作系統上進行編譯,如何在編譯時能夠支持面向不同的硬件設備,不同的編譯類型,且還要提供面向各個產商的定製擴展,是非常有難度的。

但 Android Build 系統很好的解決了這些問題,這裏面有很多值得我們開發人員學習的地方。

對於 Android 平臺開發人員來說,本文可以幫助你熟悉你每天接觸到的構建環境。

對於其他開發人員來說,本文可以作爲一個 GNU Make 的使用案例,學習這些成功案例,可以提升我們的開發經驗。

概述

Build 系統中最主要的處理邏輯都在 Make 文件中,而其他的腳本文件只是起到一些輔助作用,由於篇幅所限,本文只探討 Make 文件中的內容。

整個 Build 系統中的 Make 文件可以分爲三類:

第一類是 Build 系統核心文件,此類文件定義了整個 Build 系統的框架,而其他所有 Make 文件都是在這個框架的基礎上編寫出來的。

圖 1 是 Android 源碼樹的目錄結構,Build 系統核心文件全部位於 /build/core(本文所提到的所有路徑都是以 Android 源碼樹作爲背景的,“/”指的是源碼樹的根目錄,與文件系統無關)目錄下。

圖 1. Android 源碼樹的目錄結構

圖 1. Android 源碼樹的目錄結構

第 二類是針對某個產品(一個產品可能是某個型號的手機或者平板電腦)的 Make 文件,這些文件通常位於 device 目錄下,該目錄下又以公司名以及產品名分爲兩級目錄,圖 2 是 device 目錄下子目錄的結構。對於一個產品的定義通常需要一組文件,這些文件共同構成了對於這個產品的定義。例如,/device/sony/it26 目錄下的文件共同構成了對於 Sony LT26 型號手機的定義。

圖 2. device 目錄下子目錄的結構

圖 2. device 目錄下子目錄的結構

第 三類是針對某個模塊(關於模塊後文會詳細討論)的 Make 文件。整個系統中,包含了大量的模塊,每個模塊都有一個專門的 Make 文件,這類文件的名稱統一爲“Android.mk”,該文件中定義瞭如何編譯當前模塊。Build 系統會在整個源碼樹中掃描名稱爲“Android.mk”的文件並根據其中的內容執行模塊的編譯。

編譯 Android 系統

執行編譯

Android 系統的編譯環境目前只支持 Ubuntu 以及 Mac OS 兩種操作系統。關於編譯環境的構建方法請參見以下路徑:http://source.android.com/source/initializing.html

在完成編譯環境的準備工作以及獲取到完整的 Android 源碼之後,想要編譯出整個 Android 系統非常的容易:

打開控制檯之後轉到 Android 源碼的根目錄,然後執行如清單 1 所示的三條命令即可("$"是命令提示符,不是命令的一部分。):

完整的編譯時間依賴於編譯主機的配置,在筆者的 Macbook Pro(OS X 10.8.2, i7 2G CPU,8G RAM, 120G SSD)上使用 8 個 Job 同時編譯共需要一個半小時左右的時間。

清單 1. 編譯 Android 系統
 $ source build/envsetup.sh 
 $ lunch full-eng 
 $ make -j8

這三行命令的說明如下:

第一行命令“source build/envsetup.sh”引入了 build/envsetup.sh腳本。該腳本的作用是初始化編譯環境,並引入一些輔助的 Shell 函數,這其中就包括第二步使用 lunch 函數。

除此之外,該文件中還定義了其他一些常用的函數,它們如表 1 所示:

表 1. build/envsetup.sh 中定義的常用函數
名稱 說明
croot 切換到源碼樹的根目錄
m 在源碼樹的根目錄執行 make
mm Build 當前目錄下的模塊
mmm Build 指定目錄下的模塊
cgrep 在所有 C/C++ 文件上執行 grep
jgrep 在所有 Java 文件上執行 grep
resgrep 在所有 res/*.xml 文件上執行 grep
godir 轉到包含某個文件的目錄路徑
printconfig 顯示當前 Build 的配置信息
add_lunch_combo 在 lunch 函數的菜單中添加一個條目

 

第二行命令“lunch full-eng”是調用 lunch 函數,並指定參數爲“full-eng”。lunch 函數的參數用來指定此次編譯的目標設備以及編譯類型。在這裏,這兩個值分別是“full”和“eng”。“full”是 Android 源碼中已經定義好的一種產品,是爲模擬器而設置的。而編譯類型會影響最終系統中包含的模塊,關於編譯類型將在表 7 中詳細講解。

如果調用 lunch 函數的時候沒有指定參數,那麼該函數將輸出列表以供選擇,該列表類似圖 3 中的內容(列表的內容會根據當前 Build 系統中包含的產品配置而不同,具體參見後文“添加新的產品”),此時可以通過輸入編號或者名稱進行選擇。

圖 3. lunch 函數的輸出

圖 3. lunch 函數的輸出

第三行命令“make -j8”才真正開始執行編譯。make 的參數“-j”指定了同時編譯的 Job 數量,這是個整數,該值通常是編譯主機 CPU 支持的併發線程總數的 1 倍或 2 倍(例如:在一個 4 核,每個核支持兩個線程的 CPU 上,可以使用 make -j8 或 make -j16)。在調用 make 命令時,如果沒有指定任何目標,則將使用默認的名稱爲“droid”目標,該目標會編譯出完整的 Android 系統鏡像。

Build 結果的目錄結構

所有的編譯產物都將位於 /out 目錄下,該目錄下主要有以下幾個子目錄:

  • /out/host/:該目錄下包含了針對主機的 Android 開發工具的產物。即 SDK 中的各種工具,例如:emulator,adb,aapt 等。
  • /out/target/common/:該目錄下包含了針對設備的共通的編譯產物,主要是 Java 應用代碼和 Java 庫。
  • /out/target/product/<product_name>/:包含了針對特定設備的編譯結果以及平臺相關的 C/C++ 庫和二進制文件。其中,<product_name>是具體目標設備的名稱。
  • /out/dist/:包含了爲多種分發而準備的包,通過“make disttarget”將文件拷貝到該目錄,默認的編譯目標不會產生該目錄。

Build 生成的鏡像文件

Build 的產物中最重要的是三個鏡像文件,它們都位於 /out/target/product/<product_name>/ 目錄下。

這三個文件是:

  • system.img:包含了 Android OS 的系統文件,庫,可執行文件以及預置的應用程序,將被掛載爲根分區。
  • ramdisk.img:在啓動時將被 Linux 內核掛載爲只讀分區,它包含了 /init 文件和一些配置文件。它用來掛載其他系統鏡像並啓動 init 進程。
  • userdata.img:將被掛載爲 /data,包含了應用程序相關的數據以及和用戶相關的數據。

Make 文件說明

整個 Build 系統的入口文件是源碼樹根目錄下名稱爲“Makefile”的文件,當在源代碼根目錄上調用 make 命令時,make 命令首先將讀取該文件。

Makefile 文件的內容只有一行:“include build/core/main.mk”。該行代碼的作用很明顯:包含 build/core/main.mk 文件。在 main.mk 文件中又會包含其他的文件,其他文件中又會包含更多的文件,這樣就引入了整個 Build 系統。

這些 Make 文件間的包含關係是相當複雜的,圖 3 描述了這種關係,該圖中黃色標記的文件(且除了 $開頭的文件)都位於 build/core/ 目錄下。

圖 4. 主要的 Make 文件及其包含關係

圖 4. 主要的 Make 文件及其包含關係

表 2 總結了圖 4 中提到的這些文件的作用:

表 2. 主要的 Make 文件的說明
文件名 說明
main.mk 最主要的 Make 文件,該文件中首先將對編譯環境進行檢查,同時引入其他的 Make 文件。另外,該文件中還定義了幾個最主要的 Make 目標,例如 droid,sdk,等(參見後文“Make 目標說明”)。
help.mk 包含了名稱爲 help 的 Make 目標的定義,該目標將列出主要的 Make 目標及其說明。
pathmap.mk 將許多頭文件的路徑通過名值對的方式定義爲映射表,並提供 include-path-for 函數來獲取。例如,通過 $(call include-path-for, frameworks-native)便可以獲取到 framework 本地代碼需要的頭文件路徑。
envsetup.mk 配置 Build 系統需要的環境變量,例如:TARGET_PRODUCT,TARGET_BUILD_VARIANT,HOST_OS,HOST_ARCH 等。
當前編譯的主機平臺信息(例如操作系統,CPU 類型等信息)就是在這個文件中確定的。
另外,該文件中還指定了各種編譯結果的輸出路徑。
combo/select.mk 根據當前編譯器的平臺選擇平臺相關的 Make 文件。
dumpvar.mk 在 Build 開始之前,顯示此次 Build 的配置信息。
config.mk 整個 Build 系統的配置文件,最重要的 Make 文件之一。該文件中主要包含以下內容:
  • 定義了許多的常量來負責不同類型模塊的編譯。
  • 定義編譯器參數以及常見文件後綴,例如 .zip,.jar.apk。
  • 根據 BoardConfig.mk 文件,配置產品相關的參數。
  • 設置一些常用工具的路徑,例如 flex,e2fsck,dx。
definitions.mk 最重要的 Make 文件之一,在其中定義了大量的函數。這些函數都是 Build 系統的其他文件將用到的。例如:my-dir,all-subdir-makefiles,find-subdir-files,sign-package 等,關於這些函數的說明請參見每個函數的代碼註釋。
distdir.mk 針對 dist 目標的定義。dist 目標用來拷貝文件到指定路徑。
dex_preopt.mk 針對啓動 jar 包的預先優化。
pdk_config.mk 顧名思義,針對 pdk(Platform Developement Kit)的配置文件。
${ONE_SHOT_MAKEFILE} ONE_SHOT_MAKEFILE 是一個變量,當使用“mm”編譯某個目錄下的模塊時,此變量的值即爲當前指定路徑下的 Make 文件的路徑。
${subdir_makefiles} 各個模塊的 Android.mk 文件的集合,這個集合是通過 Python 腳本掃描得到的。
post_clean.mk 在前一次 Build 的基礎上檢查當前 Build 的配置,並執行必要清理工作。
legacy_prebuilts.mk 該文件中只定義了 GRANDFATHERED_ALL_PREBUILT 變量。
Makefile 被 main.mk 包含,該文件中的內容是輔助 main.mk 的一些額外內容。

 

Android 源碼中包含了許多的模塊,模塊的類型有很多種,例如:Java 庫,C/C++ 庫,APK 應用,以及可執行文件等 。並且,Java 或者 C/C++ 庫還可以分爲靜態的或者動態的,庫或可執行文件既可能是針對設備(本文的“設備”指的是 Android 系統將被安裝的設備,例如某個型號的手機或平板)的也可能是針對主機(本文的“主機”指的是開發 Android 系統的機器,例如裝有 Ubuntu 操作系統的 PC 機或裝有 MacOS 的 iMac 或 Macbook)的。不同類型的模塊的編譯步驟和方法是不一樣,爲了能夠一致且方便的執行各種類型模塊的編譯,在 config.mk 中定義了許多的常量,這其中的每個常量描述了一種類型模塊的編譯方式,這些常量有:

  • BUILD_HOST_STATIC_LIBRARY
  • BUILD_HOST_SHARED_LIBRARY
  • BUILD_STATIC_LIBRARY
  • BUILD_SHARED_LIBRARY
  • BUILD_EXECUTABLE
  • BUILD_HOST_EXECUTABLE
  • BUILD_PACKAGE
  • BUILD_PREBUILT
  • BUILD_MULTI_PREBUILT
  • BUILD_HOST_PREBUILT
  • BUILD_JAVA_LIBRARY
  • BUILD_STATIC_JAVA_LIBRARY
  • BUILD_HOST_JAVA_LIBRARY

通過名稱大概就可以猜出每個變量所對應的模塊類型。(在模塊的 Android.mk 文件中,只要包含進這裏對應的常量便可以執行相應類型模塊的編譯。對於 Android.mk 文件的編寫請參見後文:“添加新的模塊”。)

這些常量的值都是另外一個 Make 文件的路徑,詳細的編譯方式都是在對應的 Make 文件中定義的。這些常量和 Make 文件的是一一對應的,對應規則也很簡單:常量的名稱是 Make 文件的文件名除去後綴全部改爲大寫然後加上“BUILD_”作爲前綴。例如常量 BUILD_HOST_PREBUILT 的值對應的文件就是 host_prebuilt.mk。

這些 Make 文件的說明如表 3 所示:

表 3. 各種模塊的編譯方式的定義文件
文件名 說明
host_static_library.mk 定義瞭如何編譯主機上的靜態庫。
host_shared_library.mk 定義瞭如何編譯主機上的共享庫。
static_library.mk 定義瞭如何編譯設備上的靜態庫。
shared_library.mk 定義瞭如何編譯設備上的共享庫。
executable.mk 定義瞭如何編譯設備上的可執行文件。
host_executable.mk 定義瞭如何編譯主機上的可執行文件。
package.mk 定義瞭如何編譯 APK 文件。
prebuilt.mk 定義瞭如何處理一個已經編譯好的文件 ( 例如 Jar 包 )。
multi_prebuilt.mk 定義瞭如何處理一個或多個已編譯文件,該文件的實現依賴 prebuilt.mk。
host_prebuilt.mk 處理一個或多個主機上使用的已編譯文件,該文件的實現依賴 multi_prebuilt.mk。
java_library.mk 定義瞭如何編譯設備上的共享 Java 庫。
static_java_library.mk 定義瞭如何編譯設備上的靜態 Java 庫。
host_java_library.mk 定義瞭如何編譯主機上的共享 Java 庫。

 

不同類型的模塊的編譯過程會有一些相同的步驟,例如:編譯一個 Java 庫和編譯一個 APK 文件都需要定義如何編譯 Java 文件。因此,表 3 中的這些 Make 文件的定義中會包含一些共同的代碼邏輯。爲了減少代碼冗餘,需要將共同的代碼複用起來,複用的方式是將共同代碼放到專門的文件中,然後在其他文件中包含這些文件的方式來實現的。這些包含關係如圖 5 所示。由於篇幅關係,這裏就不再對其他文件做詳細描述(其實這些文件從文件名稱中就可以大致猜出其作用)。

圖 5. 模塊的編譯方式定義文件的包含關係

圖 5. 模塊的編譯方式定義文件的包含關係

Make 目標說明

make /make droid

如果在源碼樹的根目錄直接調用“make”命令而不指定任何目標,則會選擇默認目標:“droid”(在 main.mk 中定義)。因此,這和執行“make droid”效果是一樣的。

droid 目標將編譯出整個系統的鏡像。從源代碼到編譯出系統鏡像,整個編譯過程非常複雜。這個過程並不是在 droid 一個目標中定義的,而是 droid 目標會依賴許多其他的目標,這些目標的互相配合導致了整個系統的編譯。

圖 6 描述了 droid 目標所依賴的其他目標:

圖 6. droid 目標所依賴的其他 Make 目標

圖 6. droid 目標所依賴的其他 Make 目標

圖 6 中這些目標的說明如表 4 所示:

表 4. droid 所依賴的其他 Make 目標的說明
名稱 說明
apps_only 該目標將編譯出當前配置下不包含 user,userdebug,eng 標籤(關於標籤,請參見後文“添加新的模塊”)的應用程序。
droidcore 該目標僅僅是所依賴的幾個目標的組合,其本身不做更多的處理。
dist_files 該目標用來拷貝文件到 /out/dist 目錄。
files 該目標僅僅是所依賴的幾個目標的組合,其本身不做更多的處理。
prebuilt 該目標依賴於 $(ALL_PREBUILT)$(ALL_PREBUILT)的作用就是處理所有已編譯好的文件。
$(modules_to_install) modules_to_install 變量包含了當前配置下所有會被安裝的模塊(一個模塊是否會被安裝依賴於該產品的配置文件,模塊的標籤等信息),因此該目標將導致所有會被安裝的模塊的編譯。
$(modules_to_check) 該目標用來確保我們定義的構建模塊是沒有冗餘的。
$(INSTALLED_ANDROID_INFO_TXT_TARGET) 該目標會生成一個關於當前 Build 配置的設備信息的文件,該文件的生成路徑是:out/target/product/<product_name>/android-info.txt
systemimage 生成 system.img。
$(INSTALLED_BOOTIMAGE_TARGET) 生成 boot.img。
$(INSTALLED_RECOVERYIMAGE_TARGET) 生成 recovery.img。
$(INSTALLED_USERDATAIMAGE_TARGET) 生成 userdata.img。
$(INSTALLED_CACHEIMAGE_TARGET) 生成 cache.img。
$(INSTALLED_FILES_FILE) 該目標會生成 out/target/product/<product_name>/ installed-files.txt 文件,該文件中內容是當前系統鏡像中已經安裝的文件列表。

 

其他目標

Build 系統中包含的其他一些 Make 目標說明如表 5 所示:

表 5. 其他主要 Make 目標
Make 目標 說明
make clean 執行清理,等同於:rm -rf out/。
make sdk 編譯出 Android 的 SDK。
make clean-sdk 清理 SDK 的編譯產物。
make update-api 更新 API。在 framework API 改動之後,需要首先執行該命令來更新 API,公開的 API 記錄在 frameworks/base/api 目錄下。
make dist 執行 Build,並將 MAKECMDGOALS 變量定義的輸出文件拷貝到 /out/dist 目錄。
make all 編譯所有內容,不管當前產品的定義中是否會包含。
make help 幫助信息,顯示主要的 make 目標。
make snod 從已經編譯出的包快速重建系統鏡像。
make libandroid_runtime 編譯所有 JNI framework 內容。
makeframework 編譯所有 Java framework 內容。
makeservices 編譯系統服務和相關內容。
make <local_target> 編譯一個指定的模塊,local_target 爲模塊的名稱。
make clean-<local_target> 清理一個指定模塊的編譯結果。
makedump-products 顯示所有產品的編譯配置信息,例如:產品名,產品支持的地區語言,產品中會包含的模塊等信息。
makePRODUCT-xxx-yyy 編譯某個指定的產品。
makebootimage 生成 boot.img
makerecoveryimage 生成 recovery.img
makeuserdataimage 生成 userdata.img
makecacheimage 生成 cache.img

 

在 Build 系統中添加新的內容

添加新的產品

當我們要開發一款新的 Android 產品的時候,我們首先就需要在 Build 系統中添加對於該產品的定義。

在 Android Build 系統中對產品定義的文件通常位於 device 目錄下(另外還有一個可以定義產品的目錄是 vender 目錄,這是個歷史遺留目錄,Google 已經建議不要在該目錄中進行定義,而應當選擇 device 目錄)。device 目錄下根據公司名以及產品名分爲二級目錄,這一點我們在概述中已經提到過。

通常,對於一個產品的定義通常至少會包括四個文件:AndroidProducts.mk,產品版本定義文件,BoardConfig.mk 以及 verndorsetup.sh。下面我們來詳細說明這幾個文件。

  • AndroidProducts.mk:該文文件中的內容很簡單,其中只需要定義一個變量,名稱爲“PRODUCT_MAKEFILES”,該變量的值爲產品版本定義文件名的列表,例如:
 PRODUCT_MAKEFILES := \ 
 $(LOCAL_DIR)/full_stingray.mk \ 
 $(LOCAL_DIR)/stingray_emu.mk \ 
 $(LOCAL_DIR)/generic_stingray.mk
  • 產品版本定義文件:顧名思義,該文件中包含了對於特定產品版本的定義。該文件可能不只一個,因爲同一個產品可能會有多種版本(例如,面向中國地區一個版本,面向美國地區一個版本)。該文件中可以定義的變量以及含義說明如表 6 所示:
表 6. 產品版本定義文件中的變量及其說明
常量 說明
PRODUCT_NAME 最終用戶將看到的完整產品名,會出現在“關於手機”信息中。
PRODUCT_MODEL 產品的型號,這也是最終用戶將看到的。
PRODUCT_LOCALES 該產品支持的地區,以空格分格,例如:en_GB de_DE es_ES fr_CA。
PRODUCT_PACKAGES 該產品版本中包含的 APK 應用程序,以空格分格,例如:Calendar Contacts。
PRODUCT_DEVICE 該產品的工業設計的名稱。
PRODUCT_MANUFACTURER 製造商的名稱。
PRODUCT_BRAND 該產品專門定義的商標(如果有的話)。
PRODUCT_PROPERTY_OVERRIDES 對於商品屬性的定義。
PRODUCT_COPY_FILES 編譯該產品時需要拷貝的文件,以“源路徑 : 目標路徑”的形式。
PRODUCT_OTA_PUBLIC_KEYS 對於該產品的 OTA 公開 key 的列表。
PRODUCT_POLICY 產品使用的策略。
PRODUCT_PACKAGE_OVERLAYS 指出是否要使用默認的資源或添加產品特定定義來覆蓋。
PRODUCT_CONTRIBUTORS_FILE HTML 文件,其中包含項目的貢獻者。
PRODUCT_TAGS 該產品的標籤,以空格分格。

 

通常情況下,我們並不需要定義所有這些變量。Build 系統的已經預先定義好了一些組合,它們都位於 /build/target/product 下,每個文件定義了一個組合,我們只要繼承這些預置的定義,然後再覆蓋自己想要的變量定義即可。例如:

 # 繼承 full_base.mk 文件中的定義
 $(call inherit-product, $(SRC_TARGET_DIR)/product/full_base.mk) 
 # 覆蓋其中已經定義的一些變量
 PRODUCT_NAME := full_lt26 
 PRODUCT_DEVICE := lt26 
 PRODUCT_BRAND := Android 
 PRODUCT_MODEL := Full Android on LT26
  • BoardConfig.mk:該文件用來配置硬件主板,它其中定義的都是設備底層的硬件特性。例如:該設備的主板相關信息,Wifi 相關信息,還有 bootloader,內核,radioimage 等信息。對於該文件的示例,請參看 Android 源碼樹已經有的文件。
  • vendorsetup.sh:該文件中作用是通過 add_lunch_combo 函數在 lunch 函數中添加一個菜單選項。該函數的參數是產品名稱加上編譯類型,中間以“-”連接,例如:add_lunch_combo full_lt26-userdebug。/build/envsetup.sh 會掃描所有 device 和 vender 二 級目 錄下的名稱 爲"vendorsetup.sh"文件,並根據其中的內容來確定 lunch 函數的 菜單選項。

在配置了以上的文件之後,便可以編譯出我們新添加的設備的系統鏡像了。

首先,調用“source build/envsetup.sh”該命令的輸出中會看到 Build 系統已經引入了剛剛添加的 vendorsetup.sh 文件。

然後再調用“lunch”函數,該函數輸出的列表中將包含新添加的 vendorsetup.sh 中添加的條目。然後通過編號或名稱選擇即可。

最後,調用“make -j8”來執行編譯即可。

添加新的模塊

關於“模塊”的說明在上文中已經提到過,這裏不再贅述。

在 源碼樹中,一個模塊的所有文件通常都位於同一個文件夾中。爲了將當前模塊添加到整個 Build 系統中,每個模塊都需要一個專門的 Make 文件,該文件的名稱爲“Android.mk”。Build 系統會掃描名稱爲“Android.mk”的文件,並根據該文件中內容編譯出相應的產物。

需 要注意的是:在 Android Build 系統中,編譯是以模塊(而不是文件)作爲單位的,每個模塊都有一個唯一的名稱,一個模塊的依賴對象只能是另外一個模塊,而不能是其他類型的對象。對於已經 編譯好的二進制庫,如果要用來被當作是依賴對象,那麼應當將這些已經編譯好的庫作爲單獨的模塊。對於這些已經編譯好的庫使用 BUILD_PREBUILT 或 BUILD_MULTI_PREBUILT。例如:當編譯某個 Java 庫需要依賴一些 Jar 包時,並不能直接指定 Jar 包的路徑作爲依賴,而必須首先將這些 Jar 包定義爲一個模塊,然後在編譯 Java 庫的時候通過模塊的名稱來依賴這些 Jar 包。

下面,我們就來講解 Android.mk 文件的編寫:

Android.mk 文件通常以以下兩行代碼作爲開頭:

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

這兩行代碼的作用是:

  1. 設置當前模塊的編譯路徑爲當前文件夾路徑。
  2. 清理(可能由其他模塊設置過的)編譯環境中用到的變量。

爲了方便模塊的編譯,Build 系統設置了很多的編譯環境變量。要編譯一個模塊,只要在編譯之前根據需要設置這些變量然後執行編譯即可。它們包括:

  • LOCAL_SRC_FILES:當前模塊包含的所有源代碼文件。
  • LOCAL_MODULE:當前模塊的名稱,這個名稱應當是唯一的,模塊間的依賴關係就是通過這個名稱來引用的。
  • LOCAL_C_INCLUDES:C 或 C++ 語言需要的頭文件的路徑。
  • LOCAL_STATIC_LIBRARIES:當前模塊在靜態鏈接時需要的庫的名稱。
  • LOCAL_SHARED_LIBRARIES:當前模塊在運行時依賴的動態庫的名稱。
  • LOCAL_CFLAGS:提供給 C/C++ 編譯器的額外編譯參數。
  • LOCAL_JAVA_LIBRARIES:當前模塊依賴的 Java 共享庫。
  • LOCAL_STATIC_JAVA_LIBRARIES:當前模塊依賴的 Java 靜態庫。
  • LOCAL_PACKAGE_NAME:當前 APK 應用的名稱。
  • LOCAL_CERTIFICATE:簽署當前應用的證書名稱。
  • LOCAL_MODULE_TAGS:當前模塊所包含的標籤,一個模塊可以包含多個標籤。標籤的值可能是 debug, eng, user,development 或者 optional。其中,optional 是默認標籤。標籤是提供給編譯類型使用的。不同的編譯類型會安裝包含不同標籤的模塊,關於編譯類型的說明如表 7 所示:
表 7. 編譯類型的說明
名稱 說明
eng 默認類型,該編譯類型適用於開發階段。
當選擇這種類型時,編譯結果將:
  • 安裝包含 eng, debug, user,development 標籤的模塊
  • 安裝所有沒有標籤的非 APK 模塊
  • 安裝所有產品定義文件中指定的 APK 模塊
user 該編譯類型適合用於最終發佈階段。
當選擇這種類型時,編譯結果將:
  • 安裝所有帶有 user 標籤的模塊
  • 安裝所有沒有標籤的非 APK 模塊
  • 安裝所有產品定義文件中指定的 APK 模塊,APK 模塊的標籤將被忽略
userdebug 該編譯類型適合用於 debug 階段。
該類型和 user 一樣,除了:
  • 會安裝包含 debug 標籤的模塊
  • 編譯出的系統具有 root 訪問權限

 

表 3 中的文件已經定義好了各種類型模塊的編譯方式。所以要執行編譯,只需要引入表 3 中對應的 Make 文件即可(通過常量的方式)。例如,要編譯一個 APK 文件,只需要在 Android.mk 文件中,加入“include $(BUILD_PACKAGE)

除此以外,Build 系統中還定義了一些便捷的函數以便在 Android.mk 中使用,包括:

  • $(call my-dir):獲取當前文件夾路徑。
  • $(call all-java-files-under, <src>):獲取指定目錄下的所有 Java 文件。
  • $(call all-c-files-under, <src>):獲取指定目錄下的所有 C 語言文件。
  • $(call all-Iaidl-files-under, <src>):獲取指定目錄下的所有 AIDL 文件。
  • $(call all-makefiles-under, <folder>):獲取指定目錄下的所有 Make 文件。
  • $(call intermediates-dir-for, <class>, <app_name>, <host or target>, <common?> ):獲取 Build 輸出的目標文件夾路徑。

清單 2 和清單 3 分別是編譯 APK 文件和編譯 Java 靜態庫的 Make 文件示例:

清單 2. 編譯一個 APK 文件
  LOCAL_PATH := $(call my-dir) 
  include $(CLEAR_VARS) 
  # 獲取所有子目錄中的 Java 文件
  LOCAL_SRC_FILES := $(call all-subdir-java-files) 			
  # 當前模塊依賴的靜態 Java 庫,如果有多個以空格分隔
  LOCAL_STATIC_JAVA_LIBRARIES := static-library 
  # 當前模塊的名稱
  LOCAL_PACKAGE_NAME := LocalPackage 
  # 編譯 APK 文件
  include $(BUILD_PACKAGE)
清單 3. 編譯一個 Java 的靜態庫
  LOCAL_PATH := $(call my-dir) 
  include $(CLEAR_VARS) 
   
  # 獲取所有子目錄中的 Java 文件
  LOCAL_SRC_FILES := $(call all-subdir-java-files) 
   
  # 當前模塊依賴的動態 Java 庫名稱
  LOCAL_JAVA_LIBRARIES := android.test.runner 
   
  # 當前模塊的名稱
  LOCAL_MODULE := sample 
   
  # 將當前模塊編譯成一個靜態的 Java 庫
  include $(BUILD_STATIC_JAVA_LIBRARY)

結束語

整個 Build 系統包含了非常多的內容,由於篇幅所限,本文只能介紹其中最主要內容。

由於 Build 系統本身也是在隨着 Android 平臺不斷的開發過程中,所以不同的版本其中的內容和定義可能會發生變化。網絡上關於該部分的資料很零碎,並且很多資料中的一些內容已經過時不再適用,再加上缺少官方文檔,所以該部分的學習存在一定的難度。

這就要求我們要有很強的代碼閱讀能力,畢竟代碼是不會說謊的。 要知道,對於我們這些開發人員來說,源代碼就是我們最忠實的朋友。 Use the Source,Luke!

 

http://blog.sina.com.cn/s/blog_62ef2f1401013icb.html

Android的優勢就在於其開源,手機和平板生產商可以根據自己的硬件進行個性定製自己的手機產品,如小米,LePhone,M9等,因此,在我們在對Android的源碼進行定製的時候,很有必要了解下,Android的編譯過程。

如果你從來沒有做過Android代碼的編譯,那麼最官方的編譯過程就是查看Android的官方網站:http://source.android.com/source/building.html

但是,這兒只是告訴你瞭如何去編譯一個通用的系統,並沒有詳細告訴你細節,我們跟着編譯過程來了解下。

+-------------------------------------------------------------------------------------------------+

 

本文使用Android版本爲2.1,採用開發板爲華清遠見研發的FS_S5PC100A8開發板。

+-------------------------------------------------------------------------------------------------+

按照google給出的編譯步驟如下:

   1> sourcebuild/envsetup.sh:加載命令

   2> lunch:選擇平臺編譯選項

   3> make:執行編譯

我們按照編譯步驟來分析編譯過程的細節,最終添加自己的平臺編譯選項。

1. source build/envsetup.sh

這個命令是用來將envsetup.sh裏的所有用到的命令加載到環境變量裏去,我們來分析下它。

envsetup.sh裏的主要命令如下:

 

function help()                  # 顯示幫助信息
function get_abs_build_var()           # 獲取絕對變量
function get_build_var()             # 獲取絕對變量
function check_product()             # 檢查product
function check_variant()             # 檢查變量
function setpaths()                # 設置文件路徑
function printconfig()              # 打印配置
function set_stuff_for_environment()        #設置環境變量
functionset_sequence_number()           # 設置序號
function settitle()                # 設置標題
function choosetype()               # 設置type
function chooseproduct()              #設置product
function choosevariant()              #設置variant
function tapas()                  #功能同choosecombo
function choosecombo()               #設置編譯參數
function add_lunch_combo()             #添加lunch項目
function print_lunch_menu()            # 打印lunch列表
function lunch()                 # 配置lunch
function m()                   # make from top
function findmakefile()              # 查找makefile
function mm()                   # make from current directory
function mmm()                   # make thesupplied directories
function croot()                 # 回到根目錄
function cproj()
function pid()
function systemstack()
function gdbclient()
function jgrep()                 # 查找java文件
function cgrep()                  #查找c/cpp文件
function resgrep()
function tracedmdump()
function runhat()
function getbugreports()
function startviewserver()
function stopviewserver()
function isviewserverstarted()
function smoketest()
function runtest()
function godir ()                 # 跳到指定目錄 405

 # add_lunch_combo函數被多次調用,就是它來添加Android編譯選項
 # Clear this variable.  It willbe built up again when the vendorsetup.sh
 406 # files are included at the end of thisfile.
 # 清空LUNCH_MENU_CHOICES變量,用來存在編譯選項
 407 unset LUNCH_MENU_CHOICES
 408 function add_lunch_combo() 
 409 {
 410    localnew_combo=$1        # 獲得add_lunch_combo被調用時的參數
 411    local c
    # 依次遍歷LUNCH_MENU_CHOICES裏的值,其實該函數第一次調用時,該值爲空
 412    for c in ${LUNCH_MENU_CHOICES[@]} ; do
 413        if [ "$new_combo" = "$c" ] ;then    #如果參數裏的值已經存在於LUNCH_MENU_CHOICES變量裏,則返回
 414            return
 415        fi
 416    done
    # 如果參數的值不存在,則添加到LUNCH_MENU_CHOICES變量裏
 417    LUNCH_MENU_CHOICES=(${LUNCH_MENU_CHOICES[@]} $new_combo)
 418 }


# 這是系統自動增加了一個默認的編譯項 generic-eng
 420 # add the default one here
 421 add_lunch_combogeneric-eng    #調用上面的add_lunch_combo函數,將generic-eng作爲參數傳遞過去
 422
 423 # if we're on linux, add thesimulator.  There is a special case
 424 # in lunch to deal with the simulator
 425 if [ "$(uname)" = "Linux" ] ; then
 426    add_lunch_combo simulator
 427 fi

# 下面的代碼很重要,它要從vendor目錄下查找vendorsetup.sh文件,如果查到了,就加載它
1037 # Execute the contents of any vendorsetup.sh files we canfind.
1038 for f in `/bin/ls vendorbuild/vendorsetup.sh 2>/dev/null`
1039 do
1040    echo "including $f"
1041    .$f      # 執行找到的腳本,其實裏面就是廠商自己定義的編譯選項
1042 done
1043 unset f

envsetup.sh其主要作用如下:

  1. 加載了編譯時使用到的函數命令,如:help,lunch,m,mm,mmm等
  2. 添加了兩個編譯選項:generic-eng和simulator,這兩個選項是系統默認選項
  3.查找vendor/<-廠商目錄>/和vendor/<廠商目錄>/build/目錄下的vendorsetup.sh,如果存在的話,加載執行它,添加廠商自己定義產品的編譯選項
 其實,上述第3條是向編譯系統添加了廠商自己定義產品的編譯選項,裏面的代碼就是:add_lunch_comboxxx-xxx。

根據上面的內容,可以推測出,如果要想定義自己的產品編譯項,簡單的辦法是直接在envsetup.sh最後,添加上add_lunch_combomyProduct-eng,當然這麼做,不太符合上面代碼最後的本意,我們還是老實的在vendor目錄下創建自己公司名字,然後在公司目錄下創建一個新的vendorsetup.sh,在裏面添加上自己的產品編譯項

 
?
#mkdir vendor/farsight/
#touch vendor/farsight/vendorsetup.sh
#echo "add_lunch_combo fs100-eng" >vendor/farsight/vendorsetup.sh

 

這樣,當我們在執行source build/envsetup.sh命令的時候,可以在shell上看到下面的信息:

 
?
including vendor/farsight/vendorsetup.sh

 
2. 按照android官網的步驟,開始執行lunch full-eng

 

當然如果你按上述命令執行,它編譯的還是通用的eng版本系統,不是我們個性系統,我們可以執行lunch命令,它會打印出一個選擇菜單,列出可用的編譯選項

如果你按照第一步中添加了vendorsetup.sh那麼,你的選項中會出現:

You're building on Linux
 
generic-eng simulator fs100-eng
Lunch menu... pick a combo:
    1. generic-eng
    2. simulator
    3. fs100-eng

 

其中第3項是我們自己添加的編譯項。

 

lunch命令是envsetup.sh裏定義的一個命令,用來讓用戶選擇編譯項,來定義Product和編譯過程中用到的全局變量。

我們一直沒有說明前面的fs100-eng是什麼意思,現在來說明下,fs100是我定義的產品的名字,eng是產品的編譯類型,除了eng外,還有user,userdebug,分別表示:

eng: 工程機,

user:最終用戶機

userdebug:調試測試機

tests:測試機

由此可見,除了eng和user外,另外兩個一般不能交給最終用戶的,記得m8出來的時候,先放出了一部分eng工程機,然後出來了user機之後,可以用工程機換。

 

那麼這四個類型是幹什麼用的呢?其實,在main.mk裏有說明,在Android的源碼裏,每一個目標(也可以看成工程)目錄都有一個Android.mk的makefile,每個目標的Android.mk中有一個類型聲明:LOCAL_MODULE_TAGS,這個TAGS就是用來指定,當前的目標編譯完了屬於哪個分類裏。

 

   PS:Android.mk和Linux裏的makefile不太一樣,它是Android編譯系統自己定義的一個makefile來方便編譯成:c,c++的動態、靜態庫或可執行程序,或java庫或android的程序,

 

好了,我們來分析下lunch命令幹了什麼?

 

function lunch()
{
    localanswer

    if [ "$1" ]; then
      # lunch後面直接帶參數
       answer=$1
    else
      # lunch後面不帶參數,則打印處所有的target product和variant菜單提供用戶選擇
       print_lunch_menu  
       echo -n "Which would you like? [generic-eng] "
       read answer
    fi

    localselection=

    if [ -z"$answer" ]
    then
          # 如果用戶在菜單中沒有選擇,直接回車,則爲系統缺省的generic-eng
       selection=generic-eng
    elif ["$answer" = "simulator" ]
    then
       # 如果是模擬器
       selection=simulator
    elif (echo-n $answer | grep -q -e "^[0-9][0-9]*$")
    then
       # 如果answer是選擇菜單的數字,則獲取該數字對應的字符串
       if [ $answer -le ${#LUNCH_MENU_CHOICES[@]} ]
       then
           selection=${LUNCH_MENU_CHOICES[$(($answer-$_arrayoffset))]}
       fi
       # 如果 answer字符串匹配 *-*模式(*的開頭不能爲-)
    elif (echo-n $answer | grep -q -e "^[^\-][^\-]*-[^\-][^\-]*$")
    then
       selection=$answer
    fi

    if [ -z"$selection" ]
    then
       echo
       echo "Invalid lunch combo: $answer"
       return 1
    fi

    # specialcase the simulator
    if ["$selection" = "simulator" ]
    then
       # 模擬器模式
       export TARGET_PRODUCT=sim
       export TARGET_BUILD_VARIANT=eng
       export TARGET_SIMULATOR=true
       export TARGET_BUILD_TYPE=debug
    else

       # 將 product-variant模式中的product分離出來
       local product=$(echo -n $selection | sed -e "s/-.*$//")

       # 檢查之,調用關係check_product()->get_build_var()->build/core/config.mk比較羅嗦,不展開了
       check_product $product
       if [ $? -ne 0 ]
       then
           echo
           echo "** Don't have a product spec for: '$product'"
           echo "** Do you have the right repo manifest?"
           product=
       fi

       # 將 product-variant模式中的variant分離出來
       local variant=$(echo -n $selection | sed -e "s/^[^\-]*-//")

       # 檢查之,看看是否在 (user userdebug eng) 範圍內
       check_variant $variant
       if [ $? -ne 0 ]
       then
           echo
           echo "** Invalid variant: '$variant'"
           echo "** Must be one of ${VARIANT_CHOICES[@]}"
           variant=
       fi

       if [ -z "$product" -o -z "$variant" ]
       then
           echo
           return 1
       fi
 # 導出環境變量,這裏很重要,因爲後面的編譯系統都是依賴於這裏定義的幾個變量的
       export TARGET_PRODUCT=$product
       export TARGET_BUILD_VARIANT=$variant
       export TARGET_SIMULATOR=false
       export TARGET_BUILD_TYPE=release
    fi #!simulator

    echo

    #設置到環境變量,比較多,不再一一列出,最簡單的方法 set >env.txt 可獲得
   set_stuff_for_environment
    # 打印一些主要的變量,調用關係printconfig()->get_build_var()->build/core/config.mk->build/core/envsetup.mk比較羅嗦,不展開了
   printconfig
}

 

由上面分析可知,lunch命令可以帶參數和不帶參數,最終導出一些重要的環境變量,從而影響編譯系統的編譯結果。導出的變量如下(以實際運行情況爲例)

 

TARGET_PRODUCT=fs100
TARGET_BUILD_VARIANT=eng
TARGET_SIMULATOR=false
TARGET_BUILD_TYPE=release

 
執行完上述兩個步驟,就該執行:make命令了,下篇來分析。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章