這裏咱們向Android中添加本身的模塊,只涉及到.so/*.a/elf source的編譯,以及如何將prebuilt file添加進工程。對於APK以及jar的source暫時沒有仔細研究,要添加進去的話能夠參照/pacakge和/framework 裏面的Android.mk ~~
首先是可能用獲得的基礎知識,必須瞭解基礎Makefile的語法,而後下面是Andriod用來編譯相應模塊使用的核心makefile,固然若是要速成的話也能夠不看這些東西,直接按照後面的例子添加就能夠了:
一、prebuilt
/build/core/base_rules.mk
/build/core/prebuilt.mk
/build/core/multi_prebuilt.mk
二、.so/
/build/core/base_rules.mk
/build/core/shared_library.mk
/build/core/dynamic_library.mk
/build/core/binary.mk
三、.a
/build/core/base_rules.mk
/build/core/static_library.mk
/build/core/binary.mk
編寫可執行文件基本上和.so是差很少的,如今分爲兩類來仔細講一下,一類是prebuilt files的編譯,另外就是.so/.a/elf的編譯。
在全部這許makefile中最重要的是base_rules.mk,它是對module進行處理的核心過程,下面先看看這個文件的內容:
每一個模塊在編譯的時候都會產生一個編譯目錄和一個安裝目錄,編譯目錄就是這個模塊編譯之後生成的目標文件,安裝目錄就表明着這個模塊是否會編譯進文件系統,就是是否編譯進IMG啦~~在base_rules.mk提供了兩個變量來定義你要輸出的目錄,仔細弄懂對你瞭解編譯後的生成目錄是頗有幫助的~~
built_module_path := $(intermediates)
LOCAL_BUILT_MODULE := $(built_module_path)/$(LOCAL_BUILT_MODULE_STEM)
LOCAL_INSTALLED_MODULE := $(LOCAL_MODULE_PATH)/$(LOCAL_MODULE_SUBDIR)$(LOCAL_INSTALLED_MODULE_STEM)
built_module_path是編譯生成的中間文件所在的目錄,LOCAL_BUILT_MODULE_STEM就是你要生成的編譯目標啦,若是本地模塊指定了LOCAL_MODULE_STEM的話,它的值就是$(LOCAL_MODULE_STEM)$(LOCAL_MODULE_SUFFIX),若是沒有指定了的話就是$(LOCAL_MODULE)$(LOCAL_MODULE_SUFFIX)。由此能夠看到LOCAL_MODULE的定義是頗有講究的,好比什麼*.so,通常*就用來做模塊名。LOCAL_MODULE_SUFFIX在編譯不一樣的模塊時,GOOGLE內置會給你加上相應的值,若是你不瞭解的話仍是儘可能本身來指定,否則可能編譯出來的東西被篡改了文件名哦噢~~
將編譯目錄的文件拷貝到安裝目錄就是咱們的LOCAL_INSTALLED_MODULE了,是否會安裝就要看你定義的 LOCAL_MODULE_TAGS了,固然你也能夠經過修改LOCAL_MODULE_PATH來自定義安裝。LOCAL_MODULE_PATH是有個頗有用的變量,首先咱們看看當咱們在本地模塊沒有指定這個值的時候,它的值其實是:LOCAL_MODULE_PATH := $($(my_prefix)OUT$(use_data)_$(LOCAL_MODULE_CLASS)),若是你的模塊定義了TAGS := TESTS則user_data的值是DATA,這樣的模塊會被安裝在data/目錄下,那麼經過替換咱們就知道這個LOCAL_MODULE_PATH := TARGET_OUT_$(LOCAL_MODULE_CLASS)。這個LOCAL_MODULE_CLASS在特定的類型編譯會被google賦值成固定內容,可是在prebuilt的編譯中它是由你本身來賦值的,它的值就會用來定義生成的目錄,好比LOCAL_MODULE_CLASS := ETC的時候,則就會被安裝在/system/etc目錄。那麼咱們就知道如何來定義prebuilt模塊裏面的CLASS了,google還提供了一個 LOCAL_MODULE_SUBDIR可讓你來定義子目錄,可是要記得的是在每一個模塊的最後要將這個值清空,由於默認CLEAR_VARS是不會清空這個值的。
固然前面說的是LOCAL_MODULE_PATH的默認值,咱們能夠經過給它賦值來強制指定安裝的目錄,好比說要安裝在system/etc /permissions目錄,則能夠強制指定它的值爲$(TARGET_OUT_ETC)/permissions,這樣模塊就會被強制安裝在這個目錄了,給LOCAL_MODULE_PATH賦值的狀況主要應用在prebuilt模塊的編譯上,其餘的應該儘可能採用其默認值。
下面咱們就具體看看我寫的一個如何編譯本身的.so *.a elf的例子,具體能使用到的變量,和要注意的地方我都寫出來了:
這裏要說明的是這個prelink,prelinke只有在編譯.so的時候纔會有的選項,主要是經過預連接的方式來加快程序啓動和執行的速度,若是你的本地模塊prelink是真的話,那你要在build/core/prelink-linux-arm.map中定義你的庫須要使用的空間,空間不夠的話會報錯(具體使用沒仔細研究過)
下面再看看如何來將咱們本身的prebuilt files編譯進工程,先讓咱們和這個相關的最重要的兩個makefile:
multi_prebuilt.mk只用來加入須要加入的各類庫,仔細注意它裏面的那個對加入文件進行處理的函數,尤爲是 LOCAL_MODULE,LOCAL_MODULE_SUFFIX ,LOCAL_SRC_FILES這3個變量是如何獲得的,使用multi_prebuilt編譯進工程的文件都會自動打上USER的tag。並且它最後仍是會掉用prebuilt.mk來執行真正的編譯操做。
prebuilt.mk實際上能夠編譯任何文件到咱們的工程中,下面是我寫的一個例子,能夠自動將目錄下相應格式的文件編譯進工程:
此外添加prebuilt.mk也有一個粗暴的方式,能夠經過下面的規則簡單的將全部prebuilt files添加到你工程中去,必須一個一個添加:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# your prebuilt file name
LOCAL_MODULE := example.so
LOCAL_MODULE_TAGS := user eng
# your prebuilt file (must be relative directory )
LOCAL_SRC_FILES := lib/example.so
# the path your prebuilt file will be installed $(TARGET_OUT) is the system directory
LOCAL_MODULE_PATH := $(TARGET_OUT)/lib
include (BUILD_PREBUILT)
在最後說一下Android對模塊惟一性檢測的規則,在base_rules.mk裏面經過module_id裏來檢測這個模塊是否已經存在,咱們看看這個值是如何定義的:
module_id := MODULE.$(if \
$(LOCAL_IS_HOST_MODULE),HOST,TARGET).$(LOCAL_MODULE_CLASS).$(LOCAL_MODULE)
因此它是經過LOCAL_MODULE_CLASS和LOCAL_MODULE這兩個變量來檢測模塊的惟一性,所以當你定義同同樣的CLASS和MODULE的時候,Android就會報錯,提示模塊必須是惟一的。所以若是你將LOCAL_MODULE_CALSS進行修改,使用LOCAL_MODULE_PATH來指定安裝目錄的時候就會逃過Android對模塊惟一性的檢測,可是這樣致使的問題就是同一個目標會有多個規則來實現它,並且每一個規則都是相同的命令。這樣致使的結果就是MAKE會將全部的依賴放在一塊兒,而後使用命令來將其生成目標,這樣是很是危險的,會形成庫的覆蓋,並且不少時候你不知道它就究竟使用的是哪一個依賴來最終生成目標,只能用md5sum來檢查。
所以當你使用prebuilt來覆蓋系統原有的文件的時候應該特別當心,若是你確認你的文件在覆蓋系統原有文件之後系統能正常運行的話,好的方法是仍是打開Android對module_id的檢測,同時把原來生成這個文件的Makefile進行修改,讓其不編譯出這個文件~
最後補充一個Android如何來存放模塊的編譯中間文件: 一、若是你的LOCAL_MODULE_CLASS包含COMMON_MODULE_CLASS := JAVA_LIBRARIES NOTICE_FILES,則你編譯出的中間文件會放在: $(TARGET__OUT_COMMON_INTERMEDIATES)/$(LOCAL_MODULE_CLASS)/$(LOCAL_MODULE)_intermediates/ TARGET_OUT_COMMON_INTERMEDIATES := out/target/common/obj 二、若是你的LOCAL_MODULE_CLASS不包含COMMON_MODULE_CLASS,則你編譯出的中間文件會放在: $(TARGET__OUT_INTERMEDIATES)/$(LOCAL_MODULE_CLASS)/$(LOCAL_MODULE)_intermediates/ TARGET_OUT_INTERMEDIATES := out/target/product/msm7627_ffa/obj