如果是在android源碼裡面編譯我們自己的應用,就需要這個android.mk文件,這個文件就告訴android系統應用如何來編譯這個應用以及這個應用它所依賴哪些文件等等信息。我對android.mk的了解也不是很多,我把我們平時經常需要的東西說一下,順便也是幫助自一個己作一下筆記。
其實我們用得最多就是編譯庫文件(.so文件),jar包,apk應用以及bin文件等等,下來看看下面的代碼
復制代碼
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS :=optional
LOCAL_STATIC_JAVA_LIBRARIES := static-library
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := myTest
LOCAL_CERTIFICATE := platform
LOCAL_PROGUARD_ENABLED :=disabled
include $(BUILD_PACKAGE)
復制代碼
看上面的MK文件就是編譯一個簡單的APK文件,
LOCAL_PATH := $(call my-dir)
這句話代表的當前根目錄下,每一個Android.mk文件都必須有這個命令而且是在開頭
include $(CLEAR_VARS)
宏CLEAR_VARS 由編譯系統提供,指定讓GNU MAKEFILE為你清除許多LOCAL_XXX變量(例如 LOCAL_MODULE, LOCAL_SRC_FILES,LOCAL_STATIC_LIBRARIES, 等等...),除LOCAL_PATH 。這是必要的,因為所有的編譯控制文件都在同一個GNU MAKE執行環境中,所有的變量都是全局的。所以我們每一次設置一個目標文件相關的信息之前都要先清空所有變量的設置,這個也是防止這些變量會影響到我們設置的結果,記住在Android.mk中可以定義多個編譯模塊,什麼叫多個模塊?就是我這個文件不知生成apk文件還會生成.so文件或者.a文件,這個後面會講到,那麼這個時候每個編譯模塊都是以include $(CLEAR_VARS)開始
LOCAL_MODULE_TAGS :=optional
這個一共有四個值可以設置的LOCAL_MODULE_TAGS :=user eng tests optional
user: 指該模塊只在user版本下才編譯
eng: 指該模塊只在eng版本下才編譯
tests: 指該模塊只在tests版本下才編譯
optional:指該模塊在所有版本下都編譯
一般情況下我們都是選擇optional
LOCAL_STATIC_JAVA_LIBRARIES := static-library
這個表示的這個APK所依賴的jar包,從上面的代碼中可以看出jar包的名字是static-library.jar。
LOCAL_SRC_FILES := $(call all-subdir-java-files)
這個就是這個源文件,all-subdir-java-files說明編譯這個目錄下的所有.java文件,當然這個文件我們也可以手動指定,比如LOCAL_SRC_FILES :=my.java \ test.java等等,但是這樣顯然就會比較麻煩
LOCAL_PACKAGE_NAME := myTest
這個就是我們要生成這個APK的名字,我們編譯好之後這個APK的名字就是myTest.apk,我們可以在源碼裡面out目錄下system/app裡面看得到
LOCAL_CERTIFICATE := platform
編譯一個需要platform key簽名的APK 注:LOCAL_CERTIFICATE 後面應該是簽名文件的文件名,比如編譯一個需要特殊vendor key簽名的APK , LOCAL_CERTIFICATE := vendor/example/certs/app
LOCAL_PROGUARD_ENABLED :=disabled
這個就是表示是否要混淆,混淆就是防止別人破解你的代碼,這裡是不需要要的,如果我們需要混淆就把上面的這句話替換成下面這兩句話
LOCAL_PROGUARD_ENABLED :=full
LOCAL_PROGUARD_FLAG_FILES :=proguard.cfg
LOCAL_PROGUARD_FLAG_FILES這個文件指定的proguard.cfg是一個混淆文件,如果我們應用中訪問一個已經提供好的jar包接口,但是這個jar包有和JNI打交道的話,我們需要把這個jar包相應的類去掉混淆,java文件和jni打交道一般都是有 public natve......等字樣的,如果有那麼這個類就不能進行混淆,不然的話就會出現無法運行程序,如何在proguard.cfg去掉指定的混淆呢?,看下面的代碼:
-keep public class com.it.cheng.a{*;}這個就是表示對a類去掉混淆的意思,我們就添加在最後就可以了
include $(BUILD_PACKAGE)
這句話就一個執行語句,把上面的設置好的變量開始執行,BUTLD_PACKAGE這個就是表示編譯成一個APK,後面還會有編譯成庫文件等等,這句話介紹就是表示一個模塊結束了。我們再來看看編譯一個庫文件是形式(.so文件)
復制代碼
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := hello.c
LOCAL_MODULE :=hello
include $(BUILD_SHARED_LIBRARY)
復制代碼
上面就是編譯一個庫文件,這個內容和上面的差不多是一樣的,唯一不同的就是最後一句BUILD_SHARED_LIBRARY這個表示的是編譯成動態庫的意思,
include $(BUILD_STATIC_LIBRARY)表示編譯成靜態庫 (*.a)
include $(BUILD_SHARED_LIBRARY)表示編譯成動態庫。
include $(BUILD_EXECUTABLE)表示編譯成可執行程序
在編譯庫文件的時候可能還會經常加一些條件,比如:
LOCAL_C_INCLUDES 這個表示加入所需要包含的頭文件路徑
LOCAL_LDLIBS 本次編譯的鏈接選項,相當於gcc -l後的參數
LOCAL_CFLAGS 同樣是編譯選項,相當於gcc -O後面的參數
在我們的android源碼下一般都是放在system/lib目錄下面的,還有一種情況就是第三方的已經把庫文件生成好了,只是需要我們放到指定的目錄下,如system/lib目錄下
復制代碼
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS :=optional
LOCAL_MODULE :=liba.so
LOCAL_MODULE_CLASS :=LIB
LOCAL_MODULE_PATH :=$(TARGET_OUT)/lib
LOCAL_SRC_FILES :=liba.so
include $(BUILD_PREBUILT)
復制代碼
上面這段代碼的意思就是將一個liba.so庫文件放到system/bin目錄下的,先說一下這個LOCAL_MODULE 定義的名字,如果是庫文件默認為a.so,然後我們在定義LOCAL_MODULE :=a.so名字時,在編譯完成之後我們在system/lib目錄下看到的名字是liba.so,也就是在前面自動的加上了lib作為前綴,但是如果我們在定義LOCAL_MODULE是寫出liba.so,那麼在編譯完成之後就不會在前面自動給你加上的lib的前綴名,關於這個名字的問題還有一個,如果 本地模塊指定了LOCAL_MODULE_STEM的話,它的值就 是$(LOCAL_MODULE_STEM)$(LOCAL_MODULE_SUFFIX),如果沒有指定了的話就 是$(LOCAL_MODULE)$(LOCAL_MODULE_SUFFIX),所以說上面的命名也可以寫成這樣
LOCAL_MODULE=liba
LOCAL_MODULE_SUFFIX=.so
LOCAL_MODULE_CLASS :=LIB
LOCAL_MODULE_PATH :=$(TARGET_OUT)/lib
這兩個從字面的意思都是自定編譯完之後所生成的文件放在哪個目錄下,LOCAL_MODULE_CLASS 標識了所編譯模塊最後放置的位置,如果不指定,不會放到系統中,之後放在最後的obj目錄下的對應目錄中。上面中我們放在LIB目錄下也可以這麼寫
LOCAL_MODULE_CLASS := SHARED_LIBRARIES #放在/system/lib下
LOCAL_MODULE_CLASS := ETC #表示放於system/etc目錄
LOCAL_MODULE_CLASS := EXECUTABLES #放於/system/bin
其實這個的定義就是這樣,我們編譯完一個文件一般是放在system目錄的下的摸個目錄裡面的,比如如果我放在system/vensor/lib目錄下那麼我們的LOCAL_MODULE_CLASS可以這樣定義為LOCAL_MODULE_CLASS :=VENSOR,放在system/bin目錄下我們也可以這樣定義為LOCAL_MODULE_CLASS :=BIN,就是說這個值就是system下的一個子目錄,要大寫,LOCAL_MODULE_PATH這個值也是指定要安裝的目錄,我們可以通過給它賦值來強制指定安裝的目錄,比如說要安裝在system/etc /permissions目錄,則可以強制指定它的值為$(TARGET_OUT_ETC)/permissions,這樣模塊就會被強制安裝在這個目錄 了,給LOCAL_MODULE_PATH賦值的情況主要應用在prebuilt模塊的編譯上,其他的應該盡量采用其默認值。至於這兩個的區別我也搞得不是很清楚,從其它網站上看是這麼解釋的:
“。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了”
$(TARGET_OUT)表示的就是我們的android源碼的system系統目錄
TARGET_ROOT_OUT:表示根文件系統out/target/product/generic/root。
TARGET_OUT:表示system文件系統out/target/product/generic/system。
TARGET_OUT_DATA:表示data文件系統out/target/product/generic/data。
這些都是表示不同的變量。
BUILD_PREBUILT表示的就是執行命令的意思。
這裡說完之後我們日常中可能還會用到下面一些編譯的時候常用的命令
1。all-makefiles-under
獲取某個目錄下及子目錄的所有Android.mk
include $(call all-makefiles-under,a b);
上面這句話就是表示分別獲取a和b目錄下所以android.mk文件進行編譯當然我們也可以這樣
include $(call all-makefiles-under,$(LOCAL_PATH));這個表示獲取當前目錄以及子目錄下的android.mk文件,注意獲取到之後就會自動編譯生成我們需要的目標信息的。
2。all-named-subdir-makefiles
這個表示的是在當前目錄下的一組子目錄下查找Android.mk,用法和上面的那個相同,
3。all-c-files-under
指定子目錄下找C代碼
SRC_FILES := $(call all-c-files-under,src tests)
這個就是自定在src和tests目錄下查找c代碼
4。LOCAL_C_INCLUDES
表示頭文件的搜索路徑,默認的情況下是在當前的目錄下
5。LOCAL_JNI_SHARED_LIBRARIES
定義了要包含的so庫文件的名字,如果程序沒有采用jni,不需要
LOCAL_JNI_SHARED_LIBRARIES := libxxx 這樣在編譯的時候,NDK自動會把這個libxxx打包進apk; 放在youapk/lib/目錄下