Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android編譯系統

Android編譯系統

編輯:關於Android編程

本文的目的是用比較容易理解的方式,介紹一下整個Android項目的編譯。至少知道大概的編譯流程是怎麼樣的,項目裡面的Android.mk文件包含些什麼內容。

makefile的作用

makefile文件用來描述文件之間的依賴關系,並描述文件的編譯規則。我們知道從源代碼到可執行程序,中間要經歷編譯生成中間文件(windows裡面的obj文件,Linux裡面的.o文件),鏈接這些中間文件生成可執行文件的過程。

一個最簡單的makefile文件為:

main.o : main.c defs.h

    cc -c main.c

main.o依賴main.c和defs.h兩個文件,使用命令cc來編譯main.c最終生成main.o這個中間文件。

當然我們在Android中極少看到這種形式的makefile文件。我想講的重點是,Android中的mk文件會定義一些全局變量,描述模塊編譯的先後順序,聲明模塊編譯依賴的其他模塊(包括一些三方庫)。整個Android項目是由很多模塊組成,項目的編譯涉及到譬如Java源碼的編譯,C源碼的編譯,python源碼的編譯等等。在make文件中同樣也定義了這些編譯工具的路徑,這樣我們就可以調用這些工具來編譯不同種類的源碼。

Android項目編譯系統

我們一般編譯項目會執行如下命令:

source build/envsetup.sh

lunch 3 #或者lunch full_eng 取決於你當前的項目配置情況

make -j8

第一條命令引入了shell腳本envsetup.sh,在這個文件頭的注釋裡面就描述清楚了該文件的作用。我簡單翻譯如下:

將下面這些命令引入到編譯環境中:

- lunch:   lunch -,指定編譯哪個目標設備。

- tapas:   tapas [  ...] [arm|x86|mips|armv5|arm64|x86_64|mips64] [eng|userdebug|user]

- croot:   切換工作目錄到項目根目錄.

- m:       在源碼根目錄執行make命令.

- mm:      僅編譯當前目錄下面的模塊,但是不包括他們的依賴(比如某個模塊依賴的模塊再另一個地址,所依賴的模塊就不會被編譯).

- mmm:     同mm,不過是編譯該命令後面制定的目錄下的所有模塊,同樣不包含模塊的依賴。

           To limit the modules being built use the syntax: mmm dir/:target1,target2.

- mma:     編譯當前目錄下所有的模塊和它們的依賴。

- mmma:    編譯制定目錄下所有的模塊和它們的依賴。

- cgrep:   在所有的C/C++文件中搜索,用法同grep。

- ggrep:   在所有的gradle文件中搜索。

- jgrep:   在所有的Java文件中搜索。

- resgrep: 在所有的res/*.xml資源文件中搜索。

- mangrep: 在所有的AndroidManifest.xml資源文件中搜索。

- sepgrep: 在所有的sepolicy文件(後綴為te的文件)中搜索。

- sgrep:   Greps on all local source files.

- godir:   Go to the directory containing a file.

- carrier:  for open cmcc cu and others

上面的命令在執行完第一條命令之後都是可以使用的,在平常使用中可以節省大量的時間,特別是m/mm/mmm/mma/mmma編譯命令和grep的變種命令。

lunch執行載入某個項目的編譯環境和設置,最後執行make命令即可開始編譯。-j8的意思是開啟8個工作線程(j代表job),8這個數值可以根據自己的cpu內核來決定,一般一個內核可以開雙線程,所以如果是4核cpu,那麼就可以用8。執行make命令後,Android編譯系統會根據當前的編譯環境設置,以根目錄下面的Makefile文件內容作為編譯啟動入口,執行編譯指令。包括編譯系統核心源碼,編譯廠商自己添加的模塊源碼,編譯應用模塊的源碼(Android.mk),再將編譯結果鏈接在一起輸出,就是我們最後燒機的img文件了。

編譯結果輸出到源碼根目錄下面的out/文件夾。

/out/host/:在Android編譯過程中要用到的各種主機工具,包括各種SDK tools:emulator,adb,aapt等。所謂主機,是相對你的目標工程機來說的,也就是你用來編譯這套代碼的電腦。

/out/target/common/:針對目標設備的編譯結果,主要是JAVA應用代碼和庫文件。

/out/target/product//:特定產品的編譯結果,裡面包含具體平台的代碼,比如C/C++庫和二進制文件。其實就是庫文件和鏡像文件。

/out/dist/。這個比較少看到就不說了。參考文獻裡面有詳細的說明。

三種不同的make類型

如果太深入細節去看,我們就很容易暈頭,所以要分模塊和分種類。譬如說建房子,我們從一塊塊磚頭來復述建房子過程,那鐵定是痛苦和漫長的過程。但是如果我們先搭建鋼筋混凝土框架,再把磚頭往框架裡面塞。這樣理解起來是不是很快。

上面講到Android編譯系統中make的三種類型,核心代碼make,廠商項目特定make,應用模塊源碼Android.mk。

核心代碼make

前文講到make的入口是根目錄的Makefile文件,內容如下:

### DO NOT EDIT THIS FILE ###

include build/core/main.mk

### DO NOT EDIT THIS FILE ###

其實就是跳轉到了build/core/目錄下面的main.mk,這個目錄就是整個系統核心源碼編譯的所有make文件都在這個目錄了。包括廠商項目特定make和應用模塊的make都會在編譯過程中被覆蓋到。這個文件是頂重要的,描述了編譯的整個流程,串聯起了所有的make文件。

subdir_makefiles := \

    $(shell build/tools/findleaves.py $(FIND_LEAVES_EXCLUDES) $(subdirs) Android.mk)

上面的代碼摘自main.mk,可以看到其實所有的Android.mk文件是使用python腳本來搜索的。而且main.mk裡面include了很多make文件,設置了一些編譯變量,在分析Android.mk的時候,我們會分析其中一部分變量。

注意:所有make文件裡面定義的變量都是在同一個命名空間裡面,所以要考慮到重命名和變量在使用前清空。這個就是為什麼Android.mk裡都包含include $(CLEAR_VARS)這個語句。同樣CLEAR_VARS這個變量對應的是”build/core/”文件夾下面的clear_vars.mk文件。很多我們在Android.mk裡面看到的變量都是在main.mk裡面引用的make文件裡面定義了。

下面是從參考文獻裡面引入的一張main.mk調用圖
這裡寫圖片描述

像一棵樹一張,串聯起了整個項目的編譯過程。這些mk文件的作用,請仔細閱讀參考文獻2.

Android源碼包含的模塊也是分類的,比如Java庫,APK應用,主機庫,目標機器共享庫,可執行文件等等。對於同一種類型的模塊,不適用的編譯方法也是一樣的,所以Android將不同模塊的編譯方法抽取出來,定義了下面的變量:

BUILD_HOST_STATIC_LIBRARY/BUILD_HOST_SHARED_LIBRARY:編譯主機靜態/共享庫

BUILD_STATIC_LIBRARY/BUILD_SHARED_LIBRARY:編譯目標機器靜態共享庫

BUILD_EXECUTABLE/BUILD_HOST_EXECUTABLE:編譯目標機器可執行文件/編譯主機可執行文件

BUILD_PACKAGE:編譯APK文件

BUILD_PREBUILT/BUILD_MULTI_PREBUILT:如何處理一個或多個已經編譯好的文件(例如一個jar包)

BUILD_HOST_PREBUILT:同上,針對主機。

BUILD_JAVA_LIBRARY/ BUILD_STATIC_JAVA_LIBRARY:如何編譯設備上的Java庫/如何編譯設備上的靜態Java庫

BUILD_HOST_JAVA_LIBRARY:如何編譯主機上的Java庫

這些變量都是在同目錄下面的config.mk文件裡面定義的,這個mk文件同樣在main.mk裡面被include了。以BUILD_PACKAGE為例,定義如下:

#config.mk

BUILD_PACKAGE:= $(BUILD_SYSTEM)/package.mk

其實引入的是package.mk模塊。這個跟我們上面說的針對不同模塊,Android抽取了不同的編譯方法。所以針對APK包的編譯,Android使用的是package.mk進行處理,這個也是一種復用,防止出現冗余的編譯代碼。

廠商項目特定make

一般在同一套代碼下面,不同的廠商會定義自家的多個產品。通常這些產品差異是硬件配置,而不同的硬件對應不同的驅動。所以不同的產品都需要有自己的make文件來告訴編譯系統應該編譯哪些產品本身的文件。一般都會在”/device“目錄下面定義廠商的文件夾,而這個文件夾下面會定義具體產品相關的文件夾,這個文件夾下面就放置的是具體產品相關的make文件。device目錄結構如下:
這裡寫圖片描述

在這些mk文件裡面,最重要的是AndroidProducts.mk和BoardConfig.mk文件。一般產品只是有部分配置改變,所以其實這些make文件可以繼承某個make文件,僅僅修改差異的部分。AndroidProducts.mk其實定義了如下語句:

$(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit.mk)

所以可以看到這個make文件其實是繼承了core_64_bit.mk文件的,這個也簡化了我們建立新項目的步驟。

應用源碼Android.mk

Android編譯系統會將源碼分成不同的模塊。大家經常看到Android.mk文件,這個文件一般放在模塊的根目錄下面,描述的是該模塊如何編譯,依賴哪些模塊,最後以什麼形式輸出。我們以Settings模塊為例,介紹Android.mk裡面的元素和用法:

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES :=\

        javalib.jar:libs/javalib.jar

include $(BUILD_MULTI_PREBUILT)

include $(CLEAR_VARS)



LOCAL_JAVA_LIBRARIES := bouncycastle conscrypt telephony-common ims-common audiopostprocess

LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 android-support-v13 common_zxing jsr305 letv-domain-common

LOCAL_MODULE_TAGS := optional

res_dirs := cus_res res

LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs))



LOCAL_SRC_FILES := \

        $(call all-java-files-under, src) 



LOCAL_PACKAGE_NAME := Settings

LOCAL_CERTIFICATE := platform

LOCAL_PRIVILEGED_MODULE := true

LOCAL_PROGUARD_FLAG_FILES := proguard.flags

ifneq ($(INCREMENTAL_BUILDS),)

    LOCAL_PROGUARD_ENABLED := disabled

endif

include frameworks/base/packages/SettingsLib/common.mk

include $(BUILD_PACKAGE)

一般開頭都會有兩行代碼:

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

作用是:

將當前模塊的根目錄賦值為一個變量LOCAL_PATH,方便後面引用。

我們前面說過,所有的mk文件都是被編譯系統集成在一起的,所以所有的變量都是在同一個命名空間裡面。CLEAR_VARS用於清楚一些變量的值,防止影響到本模塊的編譯。從include語法看,這個變量應該指向的是某個mk文件。

剩下的大部分都是變量定義,我們看看這些變量含義:

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 是默認標簽。標簽是提供給編譯類型使用的。

這些都不是最關鍵的,僅僅只是變量賦值罷了,點睛之筆是最後的

include $(BUILD_PACKAGE)

這個變量我們上面提到過

BUILD_PACKAGE:編譯APK文件

#config.mk

BUILD_PACKAGE:= $(BUILD_SYSTEM)/package.mk

所以這個地方是引入package.mk文件,而這個文件會使用在這個Android.mk文件裡面賦值的各種變量,編譯生成制定的目標。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved