Android Build系統要點總結

weixin_34291004發表於2018-08-12

在之前的公司參與專案開發的時候,雖然不負責系統韌體編譯指令碼的維護,但為了工作的過程中更加的順利,便學習了一下Android的Build系統。本文就是對之前的學習做個總結。

  • 系統版本:AOSP 5.1.1

1. Android Build 系統核心

Android Build系統的核心位於原始碼目錄的build/core,該目錄下有幾十個mk檔案以及若干個shell指令碼。

通常是使用下面的命令來編譯Android系統:

$ . build/envsetup.sh
$ lunch
$ make
  • envsetup.sh 檔案的作用

該指令碼會建立Android的編譯環境,該指令碼中,定義了很多的shell命令,比如m、mm、mmm和lunch等命令就是在該指令碼中定義的,如圖:

6936994-91e3f1afffd90422.png
image.png

除了命令的定義外,來看看執行envsetup.sh指令碼實際執行的程式碼:

6936994-8a6bdbd7ac7f9694.png
image.png

6936994-639a73a893b3a6e2.png
image.png

可以看到,envsetup.sh有兩段程式碼會執行:

  • 中間的6條add_lunch_combo命令的呼叫
  • 結尾處的在device和vendor目錄下搜尋vendorsetup.sh檔案

這裡以mako產品為例(對應Nexus 4手機),看一下device/lge/mako目錄下的vendorsetup.sh的內容:

6936994-7858fd75427e007a.png
image.png

可以看到它仍然是呼叫了add_lunch_combo命令。這樣看來,build/core/envsetup.sh 除了定義shell命令外,就是執行add_lunch_combo命令而已。它的定義也是在envsetup.sh中,如下:

6936994-4c286836d0be831c.png
image.png

add_lunch_combo命令的功能就是將呼叫該命令的引數存放到一個全域性陣列變數LUNCH_MENU_CHOICES中。
執行lunch命令時的列印出來的選單項正是這個陣列的內容。

envsetup.sh指令碼定義了一些挺有用的命令,如下表:

命令 說明
lunch 用法:lunch <product_name>-<build_variant>,指定當前編譯的產品
croot 快速切換到原始碼的根目錄,方便開始編譯
m 編譯整個原始碼,但是不用將當前目錄切換到原始碼的根目錄
mm 編譯當前目錄下的所有模組,但不編譯它們的依賴模組
mmm 編譯指定目錄下的所有模組,但不編譯它們的依賴模組
mma 編譯當前目錄下的所有模組,同時編譯它們的依賴模組
mmma 編譯指定目錄下的所有模組,同時編譯它們的依賴模組
cgrep 對系統所有的C/C++檔案執行grep命令
ggrep 對系統中所有本地的gradle檔案執行grep命令
jgrep 對系統所有的Java檔案執行grep命令
resgrep 對系統中所有的res目錄下的XML檔案執行grep命令
sgrep 對系統中所有原始檔執行grep命令
godir 根據godir後的引數檔名在整個原始碼目錄中查詢,然後切換到目錄
  • lunch命令

lunch命令如果沒有引數,系統會列印出產品列表供選擇。lunch命令也可以有引數,格式為:lunch <product_name>-<build_variant>
引數前半部分的 "product_name"必須是系統中已經定義的產品名稱,後半部分的 "build_variant" 必須是 "eng"、"userdebug" 和 "user" 三者之一。

lunch命令的主要功能是根據使用者輸入或選擇的產品名來設定與之相關的環境變數。這些環境變數與產品編譯相關的,主要有下面三項:

  • TARGET_PRODUCT:對應 “product_name”;
  • TARGET_BUILD_VARIANT:對應 "build_variant";
  • TARGET_BUILD_TYPE:一般是release。
  • 與編譯相關的環境變數

執行lunch命令後,系統會列印出當前配置所生成的環境變數。比如選擇 full_mako-eng後,列印如下資訊:

6936994-a832ef22a0d72cc0.png
image.png

如上圖,這些環境變數將影響編譯過程,其中:

  • PLATFORM_VERSION_CODENAME:表示平臺版本的名稱
  • PLATFORM_VERSION:Android平臺的版本號
  • TARGET_PRODUCT:所編譯的產品名稱
  • TARGET_BUILD_VARIANT:表示編譯的產品的型別。可能的值有:eng、user和userdebug
  • TARGET_BUILD_TYPE:表示編譯的型別,可選的值是:release和debug。當選擇debug版時,編譯系統會加入除錯資訊,方便追蹤底層的bug。
  • TARGET_BUILD_APPS:編譯Android系統時,該變數的值為NULL。使用Build系統編譯單個模組時,這個變數的值是所編譯模組的路徑。
  • TARGET_ARCH:表示編譯目標的CPU架構
  • TARGET_ARCH_VARIANT:表示編譯目標的CPU架構版本
  • TARGET_CPU_VARIANT:表示編譯目標的CPU代號
  • TARGET_2ND_ARCH:表示編譯目標的第二CPU架構
  • TARGET_2ND_ARCH_VARIANT:表示編譯目標的第二CPU架構版本
  • TARGET_2ND_CPU_VARIANT:表示編譯目標的第二CPU的代號
  • HOST_ARCH:表示編譯平臺的CPU架構
  • HOST_OS:表示編譯平臺使用的作業系統
  • HOST_OS_EXTRA:編譯平臺作業系統的一些其他資訊,如核心版本號,產品名稱和系統代號。
  • BUILD_ID:BUILD_ID的值會出現在編譯的版本資訊中,可以利用這個環境變數來定義公司特有的標識
  • OUT_DIR:指定編譯結果的輸出目錄

這些變數中,TARGET_2ND_ARCH、TARGET_2ND_ARCH_VARIANT和TARGET_2ND_CPU_VARIANT是Android 5.0新增加的。當系統執行在64位環境時,考慮到還要支援32位的應用,因此這裡定義了兩套CPU架構.

  • Build相關指令碼的層次包含關係

下圖顯示了Build系統中編譯指令碼的包含關係,圖中不帶路徑的檔案都位於build/core下,同時 combo 目錄和 clang目錄也都位於 build/core目錄下,而device目錄位於系統原始碼根目錄下。

6936994-f39867b58135b545.png
main.png

Build系統會在其中的 $(TOP)/*/Android.mk、device/*/AndroidProducts.mk 和 device/*/BroadConfig.mk 三處地方引入具體的產品配置檔案AndroidProduct.mk和BroadConfig.mk,以及各個模組的編譯檔案Android.mk。

combo目錄下的這些mk檔案定義了gcc編譯器的版本和引數
clang目錄下的mk檔案定義了LLVM的編譯器clang的版本和引數

下表羅列了Build系統各編譯指令碼的簡介:

檔名 說明
main.mk Android Build系統的主控檔案。該檔案主要作用是包含進其他mk檔案,以及定義幾個最重要的編譯目標,如droid、sdk等。同事檢查編譯工具的版本,如make、gcc、javac等
help.mk Android Build系統的幫助。檔案中定義了一個名為help的編譯目標,因此,輸入"make help"會列印出Build系統的使用說明
config.mk Android Build系統的配置檔案,主要定義了許多常量來負責不同型別模組的編譯,定義編譯器引數並引入產品的BoardConfig.mk檔案來配置產品引數,同時也定義了一些編譯工具的路徑,如aapt、mkbootimg等
pathmap.mk 給一些標頭檔案所在的目錄定義別名,將framework下的一些原始碼目錄按類別組合在一起並定義了別名,方便引用
buildspec.mk 放在build目錄下的buildspec.mk檔案可以定義產品編譯的引數,但一般很少用它
envsetup.mk 包含進product_config.mk檔案並根據其內容設定編譯產品所需要的環境變數,如TARGET_PRODUCT、TARGET_BUILD_VARIANT等,並檢查這些變數值的合法性,同時還制定了各種編譯結果的輸出路徑
version_defaults.mk 定義系統版本相關的變數
build_id.mk 定義了環境變數BUILD_ID
product_config.mk 包含進了系統中所有的AndroidProduct.mk檔案,並根據當前產品的配置檔案來設定產品編譯相關的變數
product.mk 定義product_config.mk檔案中使用的各種函式
combo/select.mk 根據環境變數的設定,指定對應的系統和架構中所使用的編譯工具路徑
clang/config.mk 定義了LLVM編譯器clang在不同架構下的路徑和引數
dumpvar.mk 列印輸出本次編譯的配置資訊
cleanbuild.mk 包含了原始碼中所有CleanSpec.mk,定義編譯目標dataclean和installclean
definitions.mk 定義了大量Build系統中使用的函式。如果熟悉這些函式,編寫產品配置檔案將會更加得心應手
dex_preopt.mk 定義與dex優化有關的路徑和引數
pdk_config.mk 編譯pdk的配置檔案
post_clean.mk 比較當前系統的overlay目錄與上一次build時是否發生變化,如果有變化,重新生成受影響模組的資源定義檔案R.java
legacy_prebuilts.mk 定義系統prebuild模組列表
Makefile 定義了系統最終編譯完成所需要的各種目標和規則

Android Build系統中定義了大量的編譯變數,通過改變這些編譯變數的值就能控制整個編譯過程和結果。產品的配置檔案實際上是對這些編譯變數賦值的指令碼檔案。學習Android Build系統很大程度上是在瞭解這些編譯變數的含義和用法。

2. Android的產品配置檔案

產品配置檔案的作用就是按照Build系統的要求,將生成產品的各種image檔案所需要的配置資訊(如版本號、各種引數等)、資源(圖片、字型、鈴聲等)、二進位制檔案(apk、jar包、so庫等)有機地組織起來,同時進行裁剪,加入或去掉一些模組。

Android的產品配置檔案位於系統原始碼根目錄的device目錄下,但產品配置檔案也可以放在vendor目錄下。這兩個目錄從Build系統的角度看沒有太大的區別,Build系統中搜尋產品配置的關聯檔案時會同時在這兩個目錄進行,但實際使用時,往往會讓這兩個目錄配合使用,通常產品配置檔案放在device目錄下,而vendor目錄下則存放一些硬體的HAL庫。比如我給Nexus 4手機編譯韌體之前,會從Google官方的相關站點下載適用於Nexus 4手機的不開源的那些HAL庫、驅動,並將其放在系統原始碼根目錄下並釋放出放在vendor目錄下。

  • 與mako相關的配置檔案

通常device目錄下有以下幾個子目錄:

  • common:用來存放各個產品通用的配置指令碼、檔案等
  • sample:一個產品配置的例子,寫一個新的產品配置時可以使用sample目錄下的檔案作為模板
  • google:用途不詳
  • generic:存放的是用於模擬器的產品,包括x86、arm、mips架構
  • asus、lge、samsung:分別代表巨集碁、LG、三星3家公司。各家產品放在對應的目錄下。

如果需要新增新的產品,可在device目錄下新建一個目錄。

mako手機(Nexus 4) 是由LG代工的,所以它的產品配置檔案位於lge目錄下,內容如下:

  • mako:存放 Google Nexus4(mako) 的產品配置檔案。
  • mako-kernel:存放的是mako kernel的二進位制檔案
  • hammerhead:存放Google Nexus5(hammerhead) 的產品配置檔案。
  • hammerhead:存放的是 hammerhead kernel的二進位制檔案。

下面簡單說一下 device/lge/mako目錄下與Build系統相關的幾個產品配置的關鍵檔案:

  • vendorsetup.sh
    該檔案在初始化編譯環境時被envsetup.sh檔案包含進去。它作用是呼叫add_lunch_combo命令來新增產品名稱串。

  • AndroidProduct.mk
    AndroidProduct.mk會在Build系統的ProductConfig.mk檔案中被包含進去,這個檔案最重要的作用是定義了一個變數 PRODUCT_MAKEFILES,它定義了本配置目錄中所有編譯入口檔案,但每種產品編譯時只會使用其中之一。

  • BoardConfig.mk
    BoardConfig.mk檔案會被Build系統的envsetup.sh檔案包含進去。該檔案主要定義了和裝置硬體(包括CPU、WIFI、GPS等)相關的一些引數。

  • device.mk
    device.mk是產品配置中經常需要修改的一個檔案。產品定義中需要包含進的模組、檔案以及各種環境變數的定義一般都放在這個檔案裡。

device.mk中一些重要的編譯變數說明如下:
(1) PRODUCT_COPY_FILES:一個格式為"原始檔路徑:目標檔案路徑" 字串的組合。使用PRODUCT_COPY_FILES變數能方便地將編譯目錄下的一個檔案複製到目標檔案系統中。需要注意的是,PRODUCT_COPY_FILES僅僅複製檔案。如果複製的是apk或者Java庫,這些檔案的簽名會保留。

(2) PRODUCT_PACKAGES:用來定義產品的模組列表,所有在模組列表中的模組的定義都會被執行。
(3) PRODUCT_AAPT_CONFIG:指定了系統中能夠被支援的螢幕密度型別(dip)。所謂支援,是指系統編譯時,會將相應的資原始檔新增到framework_res.apk檔案中。
(4) PRODUCT_AAPT_PREF_CONFIG:指定系統實際的螢幕密度型別。
(5) DEVICE_PACKAGE_OVERLAYS:這是一個很重要的變數,它指定了系統的overlay目錄。系統編譯時會使用overlay目錄下存放的資原始檔替換系統或模組原有的資原始檔。這樣在不覆蓋原生資原始檔的情況下,就能實現產品的個性化。而且overlay的目錄可以有多個,它們會按照在變數中的先後順序來替換資原始檔,利用這個特性可以定義公共的overlay目錄,以及各個產品專屬的overlay目錄,最大限度地重用資原始檔。
(6) PRODUCT_PROPERTY_OVERRIDES:定義系統的屬性值。如果屬性名稱以 "ro." 開頭,那這個屬性就是隻讀屬性。一旦設定,屬性值將不能改變。如果屬性名稱以 "persist."開頭,則當設定這個屬性時,它的值將寫入檔案/data/property中。

  • 編譯型別 eng、user和userdebug

(1) eng

預設的編譯型別。執行 "make" 相當於執行 "make eng"
編譯時會將下列模組安裝到系統:

  • 在Android.mk 中用 LOCAL_MODULE_TAGS 變數定義了標籤:eng、debug、shell_$(TARGET_SHELL)、user和development 的模組
  • 非APK模組並且不帶任何標籤的模組
    所有產品配置檔案中指定的APK模組

編譯的系統帶有如下屬性:

  • ro.secure=0
  • ro.debuggable=1
  • ro.kernel.android.checkjni=1

編譯的系統中預設情況下adb是可用的

(2) user

編譯時會將下列模組安裝到系統:

  • 在Android.mk 中用 LOCAL_MODULE_TAGS 變數定義了標籤:shell_$(TARGET_SHELL)、user的模組
  • 非APK模組並且不帶任何標籤的模組
    所有產品配置檔案中指定的APK模組,同時忽略其標籤屬性

編譯的系統帶有如下屬性:

  • ro.secure=1
  • ro.debuggable=0

編譯的系統中預設情況下adb是不可用的,需要在系統設定中手動開啟

(3) userdebug

編譯時會將下列模組安裝到系統:

  • 在Android.mk 中用 LOCAL_MODULE_TAGS 變數定義了標籤:shell_$(TARGET_SHELL)、debug和user的模組
  • 非APK模組並且不帶任何標籤的模組
    所有產品配置檔案中指定的APK模組,同時忽略其標籤屬性

編譯的系統帶有如下屬性:

  • ro.secure=1
  • ro.debuggable=1

編譯的系統中預設情況下adb是不可用的,需要在系統設定中手動開啟

3. 編譯Android的模組

Android中的各種模組,無論是APK應用、可執行程式還是jar包,都可以通過Build系統編譯生成。在每個模組的原始碼目錄下,都有一個Android.mk檔案,裡面包含了模組程式碼的位置、模組的名稱、需要連結的動態庫等一系列的定義。

這裡以 package/apps/Settings 目錄下的 Android.mk檔案為例:

6936994-6ff65430c5fee1b3.png
image.png
  • 模組的編譯變數

Android.mk 檔案能編譯出不同的模組,是通過包含某個模組編譯檔案實現的,如上面例子中的 include $(BULID_PACKAGE) 。Android的Build系統定義了很多模組編譯變數,如下表:

模組編譯變數 說明
BUILD_HOST_STATIC_LIBRARY 對應的檔案是host_static_library.mk,用來產生編譯平臺使用的本地靜態庫
BUILD_HOST_SHARED_LIBRARY 對應的檔案是host_shared_library.mk,用來產生編譯平臺使用的本地共享庫
BUILD_STATIC_LIBRARY 對應的檔案是 static_library.mk,用來產生目標系統使用的本地靜態庫
BUILD_RAW_STATIC_LIBRARY 對應的檔案是raw_static_library.mk,用途不明
BUILD_SHARED_LIBRARY 對應的檔案是shared_library.mk,用來產生目標系統使用的本地共享庫
BUILD_EXECUTABLE 對應的檔案是executable.mk,用來產生目標系統使用的Linux可執行程式
BUILD_RAW_EXECUTABLE 對應的檔案是raw_executable.mk,用途不明
BUILD_HOST_EXECUTABLE 對應的檔案是host_executable.mk,用來產生編譯平臺下使用的可執行程式
BUILD_PACKAGE 對應的檔案是package.mk。用來產生apk檔案
BUILD_PHONY_PACKAGE 對應的檔案是phony_package.mk
BUILD_HOST_PREBUILT 對應的檔案是host_prebuilt.mk。用來定義編譯平臺下的預編譯模組目標
BUILD_PREBUILT 對應的檔案是prebuilt.mk,定義預編譯的模組目標,作用是將這些預編譯的模組引入系統
BUILD_MULTI_PREBUILT 對應的檔案是multi_prebuilt.mk,定義多個預編譯模組目標
BUILD_JAVA_LIBRARY 對應的檔案是java_library.mk,用來產生目標平臺的Java共享庫
BUILD_STATIC_JAVA_LIBRARY 對應的檔案是static_java_library.mk,用來產生目標平臺的Java靜態庫
BUILD_HOST_JAVA_LIBRARY 對應的檔案是host_java_library.mk,用來產生編譯平臺下的Java共享庫
BUILD_DROIDDOC 對應的檔案是droiddoc.mk
BUILD_COPY_HEADERS 對應的檔案是copy_headers.mk,用來將LOCAL_COPY_HEADERS變數定義的檔案複製到LOCAL_COPY_HEADERS_TO變數定義的路徑中
BUILD_NATIVE_TEST 對應的檔案是native_test.mk,用來產生一個目標系統的可執行程式,相比較BUILD_EXECUTABLE只是多定義了測試相關的庫的路徑和標頭檔案路徑
BUILD_HOST_NATIVE_TEST 對應的檔案是host_native_test.mk,用來產生一個編譯平臺下的可執行程式,相比較BUILD_HOST_EXECUTABLE只是多定義了測試相關的庫的路徑和標頭檔案路徑
  • 常用模組的定義示例

(1) 編譯一個apk檔案

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

LOCAL_JAVA_LIBRARIES :=                      # 指定依賴的共享Java類庫
LOCAL_STATIC_JAVA_LIBRARIES :=        # 指定依賴的靜態Java類庫

# 指定原始碼列表。這裡使用系統定義的函式搜尋src目錄下的檔案形成列表
LOCAL_SRC_FILES := $(call all-java-files-under, src)

LOCAL_MODULE_TAGS := optional        #指定模組的標籤
LOCAL_CERTIFICATE := shared             #指定模組的簽名方式
LOCAL_PACKAGE_NAME := testapk      # 指定模組的名稱
include $(BUILD_PACKAGE)

(2) 編譯一個Java共享庫

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

LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_MODULE_TAGS := optional           # 指定模組的標籤
LOCAL_MODULE := javadynamiclib           # 指定模組的名稱
include $(BUILD_JAVA_LIBRARY)

(3) 編譯一個Java靜態庫

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

LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_MODULE := javastaticlib            # 指定模組的名稱
include $(BUILD_STATIC_JAVA_LIBRARY)

(4) 編譯一個Java資源包檔案

資源包也是一個apk檔案,但沒有程式碼,類似 framework_res.apk

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

LOCAL_NO_STANDARD_LIBRARIES := true       # 指定依賴的靜態Java類庫
LOCAL_PACKAGE_NAME := javareslib                #  定義模組名
LOCAL_CERTIFICATE := platform                        # 指定簽名型別
LOCAL_AAPT_FLAGS := -x                                  # 定義AAPT工具引數
LOCAL_MODULE_TAGS := user                          # 定義模組標籤為user
LOCAL_MODULE_PATH := $(TARGET_OUT_JAVA_LIBRARIES)  #指定模組的安裝路徑

LOCAL_EXPORT_PACKAGE_RESOURCES := true   #為true時,其他的apk模組能引用本模組的資源
include $(BUILD_PACKAGE)

(5) 編譯一個可執行檔案

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := service.cpp
LOCAL_SHARED_LIBRARIES := libutils libbinder  #指定模組需要連結的動態庫
ifeq ($(TARGET_OS), linux)
    LOCAL_CFLAGS += -DXP_UNIX          #定義編譯標誌
endif
LOCAL_MODULE := service                    #指定模組的名稱
include $(BUILD_EXECUTABLE)            #指定模組的名稱

(6) 編譯一個native 的共享庫

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := libnativedynamic           # 指定模組的名稱
LOCAL_SRC_FILES := \                                # 指定模組的原始檔
          nativedynamic.cpp
LOCAL_SHARED_LIBRARIES := \               # 指定模組需要連結的動態庫
          libcutils \
          libutils

LOCAL_STATIC_LIBRARIES := libnativestatic            # 指定模組依賴的靜態庫
LOCAL_C_INCLUDES += \                                          # 指定標頭檔案的查詢路徑
       $(JNI_H_INCLUDE) \
       $(LOCAL_PATH)/../include
LOCAL_CFLAGS += -O                                             # 定義編譯標誌
include $(BUILD_SHARED_LIBRARY)

(7) 編譯一個native靜態庫

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

LOCAL_MODULE_TAGS := optional           #指定模組的標籤
LOCAL_MODULE := libnativestatic             #指定模組的名稱
LOCAL_SRC_FILES := \                             #指定模組的原始檔
       nativestatic.cpp
LOCAL_C_INCLUDES +=  
        LOCAL_CFLAGS += -o                       #定義編譯標誌

include $(BUILD_STATIC_LIBRARY)
  • 預編譯模組的目標定義

在實際的系統開發中,並不會像Android一樣將所有原始碼集中在一起編譯,有很多APK檔案,jar包等都是預先編譯好的,編譯系統時需要將這些二進位制檔案複製到生成的image檔案中。

常用的方法是通過 PRODUCT_COPY_FILES 變數將這些檔案直接複製到生成的image檔案中。但有些APK檔案或jar包,需要使用系統的簽名檔案才能正常執行,這樣用複製的方式就行不通了。另外,一些動態庫檔案可能是原始碼中的某些模組所依賴的,用複製的方法也無法建立依賴關係,這將導致這些模組編譯失敗。

Android 可通過定義預編譯模組的方式來解決上述問題。

定義一個預編譯模組和定義一個普通的編譯模組格式相似。不同的是LOCAL_SRC_FILES 變數指定的不是原始檔,而是二進位制檔案的路徑,同時還要通過 LOCAL_MODULE_CLASS 來指定模組的型別,最後 include 的是 BUILD_PREBUILT 變數定義的編譯檔案。

下面是常見預編譯模組的示例:
(1) 定義apk檔案目標

include $(CLEAR_VARS)
LOCAL_MODULE := ThemeManager.apk   #這裡可以是任何字串,但必須是系統唯一的目標
LOCAL_SRC_FILES := app/$(LOCAL_MODULE)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := APPS                    #這裡的值是APPS
LOCAL_CERTIFICATE := platform                      #這裡可以指定簽名方式
include $(BUILD_PREBUILT)

(2) 定義靜態jar包目標

include $(CLEAR_VARS)
LOCAL_MODULE := libfirewall.jar
LOCAL_SRC_FILES := app/$(LOCAL_MODULE)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := JAVA_LIBRARIES         #這裡的值是JAVA_LIBRARIES
LOCAL_CERTIFICATE := platform
include $(BUILD_PREBUILT)

(3) 定義動態庫檔案目標

include $(CLEAR_VARS)
LOCAL_MODULE := libglobaltheme_jni.so
LOCAL_MODULE_OWNER :=
LOCAL_SRC_FILES := lib/$(LOCAL_MODULE)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := SHARED_LIBRARIES        # 這裡的值是SHARED_LIBRARIES
include $(BUILD_PREBUILT)

(4) 定義可執行檔案目標

include $(CLEAR_VARS)
LOCAL_MODULE := bootanimation
LOCAL_MODULE_OWNER :=
LOCAL_SRC_FILES := bin/bootanimation
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := EXECUTABLES   #這裡的值是EXECUTABLES
LOCAL_MODULE_PATH := $(TARGET_OUT)/bin         #還可以指定複製到的目標目錄
include $(BUILD_PREBUILT)

(5) 定義xml檔案目標

include $(CLEAR_VARS)
LOCAL_MODULE := apns-conf-cu.xml
LOCAL_MODULE_OWNER :=
LOCAL_SRC_FILES := etc/$(LOCAL_MODULE)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := ETC               # ETC表示檔案將複製到/system/etc 目錄下
include $(BUILD_PREBUILT)

(6) 定義host平臺下的jar包

這例子挺有意思的,將系統編譯時用到的 signapk.jar 預編譯,然後複製到out目錄下,這樣Build系統將能夠使用這個檔案來給其他檔案簽名:

include $(CLEAR_VARS)
LOCAL_MODULE := signapk
LOCAL_PREBUILT_JAVA_LIBRARIES := lib/$(LOCAL_MODULE).jar
include $(BUILD_HOST_PREBUILT)

該例子中除了使用 BUILD_HOST_PREBUIT 表示目標定義是針對編譯平臺而不是裝置平臺外,還給出了定義預編譯jar包模組的另外一種定義方式,即使用變數 LOCAL_PREBUILT_JAVA_LIBRARIES 來定義。

  • 常用的 "LOCAL_" 變數

編寫模組的編譯檔案,實際就是定義一系列以 "LOCAL_" 開頭的編譯變數,下表羅列了一些經常使用的編譯變數和說明:

變數名 說明
LOCAL_ASSET_FILES 編譯APK檔案時用於指定資源列表,通常寫成 LOCAL_ASSET_FILES += $(call find-subdir-assets)
LOCAL_CC 自定義C編譯器來代替預設的編譯器
LOCAL_CXX 自定義C++編譯器來代替預設的編譯器
LOCAL_CFLAGS 定義額外的C/C++編譯器的引數
LOCAL_CPPFLAGS 僅定義額外的C++編譯器的引數,不用在C編譯器中
LOCAL_CPP_EXTENSION 自定義C++原始檔的字尾。例如:LOCAL_CPP_EXTENSION := .cc 注意:一旦定義,模組中所有原始檔都必須使用該字尾,目前不支援混合字尾
LOCAL_C_INCLUDES 指定標頭檔案的搜尋路徑
LOCAL_FORCE_STATIC_EXECUTABLE 如果編譯時需要連結的庫有共享和靜態兩者並存的情況。設定此變數值為true將會優先連結靜態庫。通常這種情況只會在編譯root/sbin目錄下的應用才會用到,因為它們執行的時間比較早,檔案系統的其他部分還沒有載入
LOCAL_GENERATED_SOURCES 指定由系統自動生成的原始檔列表
LOCAL_MODULE_TAGS 定義模組標籤,Build系統根據標籤決定哪些模組將安裝
LOCAL_REQUIRED_MODULES 指定依賴的模組。一旦本模組被安裝,通過此變數指定的模組也將被安裝
LOCAL_JAVACFLAGS 定義額外的Javac編譯器的引數
LOCAL_JAVA_LIBRARIES 指定模組依賴的Java共享庫
LOCAL_LDFLAGS 定義連結器ld的引數
LOCAL_LDLIBS 指定模組連結時依賴的庫。如果這些庫檔案不存在,並不會引發對它們的編譯。這是此變數和LOCAL_SHARED_LIBRARIES的主要區別
LOCAL_NO_MANIFEST 在一個資源apk中可以指定此變數為true,表示此apk檔案沒有AndroidManifest.xml檔案
LOCAL_PACKAGE_NAME 指定APP應用名稱
LOCAL_PATH 指定Android.mk 檔案所在的目錄
LOCAL_POST_PROCESS_COMMAND 在編譯host相關的模組時,可以用此變數定義一條命令在link完成後執行
LOCAL_PREBUILT_LIBS 指定預編譯C/C++動態和靜態庫列表。用於預編譯模組定義中
LOCAL_PREBUILT_JAVA_LIBRARIES 指定預編譯Java庫列表。用於預編譯模組定義中
LOCAL_SHARED_LIBRARIES 指定模組依賴的C/C++共享庫列表
LOCAL_SRC_FILES 指定原始檔列表
LOCAL_STATIC_LIBRARIES 指定依賴的C/C++靜態庫列表
LOCAL_MODULE 除應用(apk)以 LOCAL_PACKAGE_NAME指定模組名以外,其餘的模組都以LOCAL_MODULE指定模組名
LOCAL_MODULE_PATH 指定模組在目標系統的安裝路徑
LOCAL_UNSTRIPPED_PATH 指定模組的unstripped版本在out目錄下的儲存路徑
LOCAL_WHOLE_STATIC_LIBRARIES 這個變數也定義了模組依賴的靜態庫列表,和 LOCAL_STATIC_LIBRARIES 類似。但通過這個變數定義,連結時連結器不會將靜態庫中無人呼叫的程式碼去掉
LOCAL_YACCFLAGS 指定yacc的引數
LOCAL_ADDITIONAL_DEPENDENCIES 指定本模組的依賴。用在不方便使用別的方法來指定依賴關係時
LOCAL_BUILT_MODULE 指定編譯時存放中間檔案的目錄
LOCAL_INSTALLED_MODULE 指定模組的安裝路徑
LOCAL_MODULE_CLASS 定義模組的分類。根據分類,生成的模組檔案會安裝到目標系統相應的目錄下。例如,APPS:安裝到/system/app下;SHARED_LIBRARIES:安裝到/system/lib下; EXECUTABLES:安裝到/system/bin下;ETC:安裝到/system/etc 下;但如果同時用LOCAL_MODULE_PATH定義了路徑,則安裝到該路徑
LOCAL_MODULE_NAME 指定模組的名稱
LOCAL_MODULE_SUFFIX 指定當前模組的字尾。一旦指定,系統在產生目標檔案時,會以模組名加字尾建立目標檔案
LOCAL_STRIP_MODULE 指定模組是否需要strip,該模組是可執行檔案或動態庫時才能使用該變數
LOCAL_STRIPPABLE_MODULE 此變數的值通常由Build系統設定,一般編譯可執行檔案和動態庫時設為true
LOCAL_SYSTEM_SHARED_LIBRARIES 此變數在編譯系統的基本庫,如libc、libm、libdl時,用來定義這些庫的依賴庫。通常在應用模組定義中不應該使用該變數
LOCAL_PRELINK_MODULE 編譯 .so 模組時,定義是否需要prelink。prelink通過預連結的方式來加快程式啟動速度。如果要設定此值為true,要先在build/core/prelink-linux-arm.map檔案中定義該庫的地址和大小,否則報錯。但在Android4.2以後的程式碼中找不到檔案prelink-linux-arm.map了,在build目錄下也搜尋不到這個變數。可能Android 已經取消了prelink的功能

參考

http://gityuan.com/2016/03/19/android-build/
https://blog.csdn.net/luoshengyang/article/details/18928789
https://www.jianshu.com/p/4a6d8c0e034a
https://www.ibm.com/developerworks/cn/opensource/os-cn-android-build/index.html
https://source.android.com/setup/develop/64-bit-builds
《深入解析Android 5.0系統》

相關文章