android.mk android源碼編譯


http://www.cnblogs.com/chenbin7/archive/2013/01/05/2846863.html

Android.mk簡單分析

2013-01-05 22:51 by ...平..淡..., 884 閱讀, 0 評論, 收藏, 編輯

復制代碼
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_PATH LOCAL_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_S5PC100 A8開發板。

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

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

   1> source build/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()        # 設置環境變量
function set_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 the supplied 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 will be built up again when the vendorsetup.sh
 406 # files are included at the end of this file.
 # 清空LUNCH_MENU_CHOICES變量,用來存在編譯選項
 407 unset LUNCH_MENU_CHOICES
 408 function add_lunch_combo()  
 409 {
 410     local new_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_combo generic-eng    # 調用上面的add_lunch_combo函數,將generic-eng作為參數傳遞過去
 422
 423 # if we're on linux, add the simulator.  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 can find.
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_combo xxx-xxx。

根據上面的內容,可以推測出,如果要想定義自己的產品編譯項,簡單的辦法是直接在envsetup.sh最后,添加上add_lunch_combo myProduct-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()
{
    local answer

    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

    local selection=

    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

    # special case 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命令了,下篇來分析。
 
 

Android Build System

Specifying what to build

In order to decide what to build, and how to build it, the build system requires that some variables be set. Different products, with different packages and options can be built from the same source tree. The variables to control this can be set via a file with declarations of 'make' variables, or can be specified in the environment.

A device vendor can create definition files that describe what is to be included on a particular board or for a particular product. The definition file is called: buildspec.mk, and it is located in the top-level source directory. You can edit this manually to hardcode your selections.

If you have a buildspec.mk file, it sets all the make variables needed for a build, and you don't have to mess with options.

Another method of specifying options is to set environment variables. The build system has a rather ornate method of managing these options for you.

To set up your build environment, you need to load the variables and functions in build/envsetup.sh. Do this by 'source-ing' the file into your shell environment, like this:

$ . build/envsetup.sh

You can type 'help' (or 'hmm') at this point to see some utility functions that are available to make it easier to work with the source.

To select the set of things you want to build, and what items to build for, you use either the 'choosecombo' function or the 'lunch' function. 'choosecombo' will walk you through the different items you have to select, one-by-one, while 'lunch' allows you select some pre-set combinations.

The items that have to be defined for a build are:

  • the product ('generic' or some specific board or platform name)
  • the build variant ('user', 'userdebug', or 'eng')
  • whether you're running on a simulator ('true' or 'false')
  • the build type ('release' or 'debug')

Descriptions of these different build variants are at http://source.android.com/porting/build_system.html#androidBuildVariants

The build process from a user perspective is described pretty well in this blog post: First Android platform build by CodePainters, December 2009

Actually building the system

Once you have things set up, you actually build the system with the 'make' command.

To build the whole thing, run 'make' in the top directory. The build will take a long time, if you are building everything (for example, the first time you do it).

Seeing the actual commands used to build the software

Use the "showcommands" target on your 'make' line:

$ make -j4 showcommands

This can be used in conjunction with another make target, to see the commands for that build. That is, 'showcommands' is not a target itself, but just a modifier for the specified build.

In the example above, the -j4 is unrelated to the showcommands option, and is used to execute 4 make sessions that run in parallel.

Make targets

Here is a list of different make targets you can use to build different parts of the system:

  • make sdk - build the tools that are part of an SDK (adb, fastboot, etc.)
  • make snod - build the system image from the current software binaries
  • make services
  • make runtime
  • make droid - make droid is the normal build.
  • make all - make everything, whether it is included in the product definition or not
  • make clean - remove all built files (prepare for a new build). Same as rm -rf out/<configuration>/
  • make modules - shows a list of submodules that can be built (List of all LOCAL_MODULE definitions)
  • make <local_module> - make a specific module (note that this is not the same as directory name. It is the LOCAL_MODULE definition in the Android.mk file)
  • make clean-<local_module> - clean a specific module
  • make bootimage TARGET_PREBUILT_KERNEL=/path/to/bzImage - create a new boot image with custom bzImage

Helper macros and functions

There are some helper macros and functions that are installed when you source envsetup.sh. They are documented at the top of envesetup.sh, but here is information about a few of them:

  • croot - change directory to the top of the tree
  • m - execute 'make' from the top of the tree (even if your current directory is somewhere else)
  • mm - builds all of the modules in the current directory
  • mmm <dir1> ... - build all of the modules in the supplied directories
  • cgrep <pattern> - grep on all local C/C++ files
  • jgrep <pattern> - grep on all local Java files
  • resgrep <pattern> - grep on all local res/*.xml files
  • godir <filename> - go to the directory containing a file

Speeding up the build

You can use the '-j' option with make, to start multiple threads of make execution concurrently.

In my experience, you should specify about 2 more threads than you have processors on your machine. If you have 2 processors, use 'make -j4', If they are hyperthreaded (meaning you have 4 virtual processors), try 'make -j6.

You can also specify to use the 'ccache' compiler cache, which will speed up things once you have built things a first time. To do this, specify 'export USE_CCACHE=1' at your shell command line. (Note that ccache is included in the prebuilt section of the repository, and does not have to be installed on your host separately.)

Building only an individual program or module

If you use build/envsetup.sh, you can use some of the defined functions to build only a part of the tree. Use the 'mm' or 'mmm' commands to do this.

The 'mm' command makes stuff in the current directory (and sub-directories, I believe). With the 'mmm' command, you specify a directory or list of directories, and it builds those.

To install your changes, do 'make snod' from the top of tree. 'make snod' builds a new system image from current binaries.

Setting module-specific build parameters

Some code in Android system can be customized in the way they are built (separate from the build variant and release vs. debug options). You can set variables that control individual build options, either by setting them in the environment or by passing them directly to 'make' (or the 'm...' functions which call 'make'.)

For example, the 'init' program can be built with support for bootchart logging by setting the INIT_BOOTCHART variable. (See Using Bootchart on Android for why you might want to do this.)

You can accomplish either with:

$ touch system/init/init.c
$ export INIT_BOOTCHART=true
$ make

or

$ touch system/init/init.c
$ m INIT_BOOTCHART=true

These are some tips for things you can use in your own Android.mk files.

build helper functions

A whole bunch of build helper functions are defined in the file build/core/definitions.mk

Try grep define build/core/definitions.mk for an exhaustive list.

Here are some possibly interesting functions:

  • print-vars - shall all Makefile variables, for debugging
  • emit-line - output a line during building, to a file
  • dump-words-to-file - output a list of words to a file
  • copy-one-file - copy a file from one place to another (dest on target?)

 

Add a file directly to the output area

You can copy a file directly to the output area, without building anything, using the add-prebuilt-files function.

The following line, extracted from prebuilt/android-arm/gdbserver/Android.mk copies a list of files to the EXECUTABLES directory in the output area:

$(call add-prebuilt-files, EXECUTABLES, $(prebuilt_files))

Adding a new program to build

Steps for adding a new program to the Android source tree

  • make a directory under 'external'
    • e.g. ANDROID/external/myprogram
  • create your C/cpp files.
  • create Android.mk as clone of external/ping/Android.mk
  • Change the names ping.c and ping to match your C/cpp files and program name
  • add the directory name in ANDROID/build/core/main.mk after external/zlib as external/myprogram
  • make from the root of the source tree
  • your files will show up in the build output area, and in system images.
    • You can copy your file from the build output area, under out/target/product/..., if you want to copy it individually to the target (not do a whole install)

See http://www.aton.com/android-native-development-using-the-android-open-source-project/ for a lot more detail.

Building the kernel

The kernel is "outside" of the normal Android build system (indeed, the kernel is not included by default in the Android Open Source Project). However, there are tools in AOSP for building a kernel. If you are building the kernel, start on this page: http://source.android.com/source/building-kernels.html

If you are building the kernel for the emulator, you may also want to look at: http://stackoverflow.com/questions/1809774/android-kernel-compile-and-test-with-android-emulator

And, Ron M wrote (on the android-kernel mailing list, on May 21, 2012):

This post is very old - but nothing has changes as far as AOSP is concerned, so in case anyone is interested and runs into this problem when building for QEMU:

There is actually a nice and shorter way to build the kernel for your QEMU target provided by the AOSP:

1. cd to your kernel source dir (Only goldfish 2.6.29 works out of the box for the emulator)

2. ${ANDROID_BUILD_TOP}/external/qemu/distrib/build-kernel.sh -j=64 --arch=x86 --out=$YourOutDir

3. emulator -kernel ${YourOutDir}/kernel-qemu # run emulator:


Step #2 calls the toolbox.sh wrapper scripts which works around the SSE disabling gcc warning - which happens for GCC < 4.5 (as in the AOSP prebuilt X86 toolchain).

This script adds the " -mfpmath=387 -fno-pic" in case it is an X86 and that in turn eliminates the compilation errors seen above.

To have finer control over the build process, you can use the "toolbox.sh" wrapper and set some other stuff without modifying the script files.

An example for building the same emulator is below:

# Set arch
export ARCH=x86
# Have make refer to the QEMU wrapper script for building android over x86 
(eliminates the errors listed above)
export 
CROSS_COMPILE=${ANDROID_BUILD_TOP}/external/qemu/distrib/kernel-toolchain/android-kernel-toolchain-
# Put your cross compiler here. I am using the AOSP prebuilt one in this example
export 
REAL_CROSS_COMPILE=${ANDROID_BUILD_TOP}/prebuilt/linux-x86/toolchain/i686-android-linux-4.4.3/bin/i686-android-linux-
# Configure your kernel - here I am taking the default goldfish_defconfig
make goldfish_defconfig
# build
make -j64
# Run emulator:
emulator -kernel arch/x86/boot/bzImage -show-kernel


This works for the 2.6.29 goldfish branch. If anyone is using the emulator with a 3+ kernel I would be like to hear about it.

 



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM