Android的.mk檔案分析

來源:互聯網
上載者:User

 

從對Makefile一無所知開始,折騰了一個多星期,終於對Android.mk有了一個全面些的瞭解。瞭解了標準的Makefile後,發現 Android.mk其實是把真正的Makefile封裝起來,做成了一個對使用者來說很簡單的東西。使用它來編譯器時,不管是動態庫、可執行檔二進位檔案,還是Jar庫、APK包,只要沿著一個簡單的思路來做三大步就可以了:清除舊變數,設定新變數,調用編譯函數。

 

明白了以後,發現Makefile文法不是問題,有很多教程和高手。編譯模組時如何清除變數、調用編譯函數等也不是問題,源碼當中無處不在這樣的例子。而對初學者來說,更需要明白的可能是,Android如何讓使用指令碼的人從Makefile文法當中解放出來,簡單地按照上面的三大步就可以編譯出任何模組。

 

拿AlarmClock來做例子的話:

//清除舊變數

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

//設定新變數

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

LOCAL_PACKAGE_NAME := AlarmClock

//調用編譯函數

include $(BUILD_PACKAGE)

下面簡單解釋一下這三步:

1、清除舊變數,是因為Android.mk中所有的變數都是全域的,編譯函數在編譯時間會調用這些變數。為了防止編譯函數使用了編譯其它模組時設定的變數,每次開始編譯一個新的模組時清除所有的變數是個好習慣。

2、設定新變數就是把本次編譯時間用到的源碼地址,包名等設定好。

3、調用編譯函數其實就是include一個固定的mk檔案,這個mk檔案會根據設定的變數提取出編譯模組需要的target,Command等資訊並執行固定的編譯命令。

 

看明白Android.mk後,最深的感受還是Android的這種封裝思想,讓我想起了很多以前沒有思考過的東西。呵。。。

我會一些個人的理解記錄Android一步步地演化出這種封裝思想。真誠地歡迎批評指正。

 

Makefile這個東西,往最簡單處說,就是這樣的模式:

目標:信賴檔案

執行命令

這裡暫不考慮它的文法細節,舉個不完整的例子,就是這樣的:

AlarmClock.apk:AlarmClock.java

javac AlarmClock.java

java AlarmClock.class

要產生AlarmClock.apk,就需要AlarmClock.java這個信賴檔案,然後執行後面兩行的兩個命令。假如我們簡單粗暴地用這種寫法去編譯Android系統的話,會相當累。Android.mk就很巧妙地把它們進行了抽象。我們現在來類比一下:

定義三個變數target,source,command,分別代表編譯目標、信賴檔案和編譯命令,就成了這樣:

$(target):$(source)

$(command)

這三個變數的值分別是:

target := AlarmClock.apk

source := AlarmClock.java

commnd := /

javac AlarmClock.java/

java AlarmClock.class

這樣的話,在編譯每個APK時,只要分別給target、source、command賦值,然後再這樣寫就可以了:

$(target):$(source)

$(command)

嗯,還是有點兒麻煩,那再抽象一次。定義一個變數name,讓它存放要編譯的模組的名字:

name := AlarmClock

然後再更改一下前面的三個變數:

target := $(name).apk

source := $(name).java

commnd := /

javac $(name).java/

java $(name).class

現在要編譯一個模組Contacts需要做些什麼呢?

name := Contacts

$(target):$(source)

$(command)

三行代碼就可以了,很簡單,對吧?但後面兩行還是每次都要寫,那再抽象。把後面兩行放到一個build_apk.mk檔案當中。然後定義一個變數:

BUILD_PACKAGES := build_apk.mk

全部完了,這回編譯一個模組Contacts時只要這樣做就可以了:

name := Contacts

include $(BUILD_PACKAGES)

一個不夠複雜,那就多點兒:

name := Contacts

include $(BUILD_PACKAGES)

name := Phone

include $(BUILD_PACKAGES)

name := Email

include $(BUILD_PACKAGES)

//================強壯的分隔線=========

現在我們來完善一下上面的過程,把它系統化。

定義一個專門用來清除變數的clear_vars.mk裡面的內容如下:

name :=

target :=

source :=

command :=

再定義一個變數CLEAR_VARS := clear_vars.mk。上面的編譯指令碼就變成了這樣:

include $(CLEAR_VARS)

name := Contacts

include $(BUILD_PACKAGES)

include $(CLEAR_VARS)

name := Phone

include $(BUILD_PACKAGES)

include $(CLEAR_VARS)

name := Email

include $(BUILD_PACKAGES)

這回,清除舊變數、設定新變數、調用編譯函數三大步全都有了。

 

好了,現在我們進入到Android源碼的build/core/目錄下。先看其中的main.mk,config.mk這兩個檔案。

main.mk裡面是具體的指令碼,在這裡控制編譯模組。

config.mk中定義了下面這樣的檔案調用:

CLEAR_VARS:= $(BUILD_SYSTEM)/clear_vars.mk

BUILD_HOST_STATIC_LIBRARY:= $(BUILD_SYSTEM)/host_static_library.mk

編譯模組時,每一個include其實都是一次功能調用,或者對mk檔案的調用,或者對變數和函數的調用。做為單獨的功能模組抽象出來的mk檔案都在build/core/目錄下,而函數的定義全部在definitions.mk中。

 

萬事開頭難,有了上面的思路,再看這些指令碼就很簡單了。一路順風,呵。。。

轉自:http://blog.csdn.net/a345017062/article/details/6130264

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.