Android.mk 極簡入門
- 適合物件
- 自定義變數
- GNU Make 系統變數
- LOCAL_ 模組描述變數
- 編譯module模板
一、適合物件
本部落格僅適合剛接觸Android.mk的新人,簡單上手所作。另外本人只想做一個純粹的僅適合嵌入式C ,不涉及 Java 的Android.mk粗略講解,適合建立一個初步的概念框架,絕不涉及過多更為細節的內容。追求更詳細內容的講解,可以看CSDN 其他大佬博主的Android.mk詳解,非常詳細全面。
Android.mk檔案用來告知NDK Build 系統關於Source 的資訊,個人理解即是連結標頭檔案、原始檔、各類庫來編譯模組,其中無需額外新增依賴關係。由於Android.mk中所有的變數都是全域性的,所以請儘量減少在Android.mk宣告的變數,也不要認為某些變數在解析過程中不會被定義。
對於C/C++ 可編譯的模組型別:
module | 說明 |
---|---|
C/C++應用程式 | 可執行的 C/C++應用程式 |
C/C++靜態庫 | 編譯生成C/C++靜態庫,並打包生成.a檔案 |
C/C++共享庫 | 編譯生成共享庫,也可以是動態連結庫, 兩者只是呼叫庫的方式不同,並打包成.so檔案 |
編譯指令:
- 在編譯模組前, 在android 目錄下, 執行 $. ./build/envsetup.sh
- 在Android.mk 所在目錄下,執行 $mm , 開始編譯模組
我們可以在每一個Android.mk中定義一個或多個模組, 也可以在幾個模組中使用同一個原始碼檔案。編譯系統會為你處理許多細節問題,比如你無需在Android.mk中列出標頭檔案和生成的檔案之間的顯式依賴關係,編譯系統將會為你自動處理這些問題。
符號 | 作用 |
---|---|
:= | 賦值 |
+= | 追加 |
$ | 引用某變數的值 |
\ | ‘\’和 Enter鍵配合來換行,可以連線同一變數上下行的檔案 |
‘\’ 使用例項
LOCAL_SRC_FILES:= \
main.c \
yan.c \
pwm.c
ps: 注意一行中’\‘後不能有空格,否則會編譯出錯;最後一個檔案後儘量避免新增不必要的’\’,以免誤將非空格的無關的下一行新增入變數。另外若一行足夠,同一行的檔案之間可以用空格鍵或Tab鍵進行分割。
四、自定義變數
在Android.mk中允許自定義變數,但是需要避開NDK編譯系統已保留下來的下列變數名成分。 以下是要避開的變數名成分: 1. 以LOCAL_開頭的名字,例如LOCAL_MODULE 2. 以PRIVATE_, NDK_, 或APP_開頭的名字(內部使用) 3. 小寫名字(內部使用),如" my_dir"
為了方便在Android.mk中定義自己的變數,建議使用MY_ 作為字首。
五、GNU Make 系統變數
這些GNU Make變數在你的Android.mk檔案解析之前,就由編譯系統定義好了。注意在某些情況下, NDK可能分析Android.mk幾次, 每一次某些變數的定義會有不同。 1) CLEAR_VARS 指向一個編譯指令碼,幾乎所有未定義的LOCAL_XXX都在"Module-description"節中列出。必須在開始一個新模組之前定義這個指令碼: include $(CLEAR_VARS),用於重置除LOCAL_PATH變數外的所有LOCAL_XXX系列變數。
2) BUILD_SHARED_LIBRARY 指向編譯指令碼, 把在include $(CLEAR_VARS)之後,include $(BUILD_SHARED_LIBRARY)之前所有的LOCAL_XXX變數列出的原始碼檔案編譯成一個共享庫, 即生成一個libxxx.so檔案。
3) BUILD_STATIC_LIBRARY 用於編譯一個靜態庫中,編譯的原始檔方法如BUILD_SHARED_LIBRARY。靜態庫不會複製到APK中, 但是能夠用靜態庫編譯共享庫。
4) BUILD_EXECUTABLE 用於編譯一個可執行程式,如include $(BUILD_EXECUTABLE),生成可執行的 C/C++應用程式。
5) TARGET_ARCH 目標CPU架構的名字,和android開放原始碼中指定的那樣。如果是arm,表示要生成ARM相容的指令,與CPU架 構的修訂版無關。
6) TARGET_PLATFORM: 目標Android.mk平臺的名字,詳情可參考/development/ndk/docs/stable- apis.txt。 android-3 -> Official Android 1.5 system images android-4 -> Official Android 1.6 system images android-5 -> Official Android 2.0 system images
六、LOCAL_ 模組描述變數
下面的變數用於向編譯系統描述你的模組,應該定義在 “include $(CLEAR_VARS) " 和"include $(BUILD_XXX)” 之間。正如前面描述的一樣, include $(CLEAR_VARS)用於重置除LOCAL_PATH變數外的所有LOCAL_XXX系列變數。
1) LOCAL_PATH 用於給出當前需編譯檔案所在的路徑,必須在 Android.mk的開頭定義,使用方法如 LOCAL_PATH := $(call_my_dir),則$(LOCAL_PATH) 為當前 Android.mk所在的路徑。這個變數不會被include $(CLEAR_VARS)清除,因此在每個Android.mk檔案開頭定義一次即可,即使該檔案中已經定義了幾個模組(LOCAL_MOUDEL)。
2)LOCAL_MOUDEL 給定模組的名字,必須是唯一的,且不含空格,必須在任一包含 $(BUILD_XXX) 的指令碼前定義它。模組的名字決定了生成檔案的名字。例如,一個共享庫的模組的名字為libvp_vpu ,則生成的檔案為libvp_vpu.so。另外此時共享庫模組對應的LOCAL_MODULE的名字最好能有lib字首,因為有的 Android.mk並不會自動生成字首lib ,以致不能正確呼叫共享庫編譯可執行程式,編譯出錯。
3)LOCAL_MODULE_PATH 指定生成模組的路徑,可和LOCAL_MODULE搭配使用。 若未設定,則生成的模組在編譯系統預設的生成路徑。用法如 LOCAL_MODULE_PATH := $(LOCAL_PATH)。
4)LOCAL_SRC_FILES 給定要編譯的原始碼檔案列表,檔案也可以是靜態庫,或共享庫。只要列出要傳遞給編譯器的檔案,則編譯系統就能自動計算檔案之間的依賴關係。注意給定的原始碼檔案都相對於 $(LOCAL_PATH),在該路徑下。也可以選擇新增路徑部分,例如 LOCAL_SRC_FILES := taobao/foo.c ,使用的是$(LOCAL_PATH)相對路徑, 絕對路徑為$(LOCAL_PATH)/taobao/。
5)LOCAL_STATIC_LIBRARIES 表示該模組需要使用哪些靜態庫,以便在編譯時進行連結。
6)LOCAL_SHARED_LIBRARIES 表示該模組在編譯時要依賴的共享庫,在連結時就需要,以便在生成檔案時嵌入其相應的資訊,但不像靜態庫那樣把程式程式碼加入。所以程式執行的開始,也 要有相應的共享庫和對應的庫路徑。 簡單的庫路徑新增命令: $export LD_LIBRARY_PATH=/…/…/…/,也可以選擇放在/system/lib/等lib目錄下方便載入。
7)LOCAL_C_INCLUDES 這是一個可選變數,很常用,基本都會用到,表示標頭檔案的搜尋路徑。預設的標頭檔案搜尋路徑是 $(LOCAL_PATH)目錄。如果要新增其他路徑的標頭檔案,新增路徑的方法和 LOCAL_SRC_FILES一樣。
8)LOCAL_CFLAGS 可選的編譯器選項,在編譯 C 程式碼檔案的時候使用。這可能是有用的,指定一個附加的包含路徑(相對於NDK的頂層目錄),巨集定義,或者編譯選項。 注意:不要在 Android.mk中改變 optimization/debugging 級別,只要在 Application.mk中指定合適的資訊,就會自動地為你處理這個問題,在除錯期間,會讓 NDK自動生成有用的資料檔案。
ps: 以下是C++的內容,不需要的可以跳過 9)LOCAL_CPP_EXTENSION 這同樣是一個可選變數,用來指定C++程式碼檔案的副檔名,預設是".cpp", 但是可以改變副檔名,比如LOCAL_CPP_ENTENSION := .cxx ,則可以用.cxx的副檔名,本人尚未嘗試使用。
10)LOCAL_CXXFLAGS 與LOCAL_CFLAGS同理,針對C++原始檔。
11)LOCAL_CPPFLAGS 與LOCAL_CFLAGS同理,對C和C++ 原始檔都適用。
七、編譯module模板
其實編譯各模組的模板都很相似,光看前面是不易看出究竟是編譯什麼模組的,然而在模組編譯的最後一句可以明確區分是編譯哪種型別的模組。
模組 | 模組型別確定 |
---|---|
C/C++應用程式 | include $(BUILD_EXECUTABLE) |
C/C++靜態庫 | include $BUILD_STATIC_LIBRARY) |
C/C++共享庫 | include $(BUILD_SHARED_LIBRARY) |
1. 編譯C/C++ 應用程式模板 備註: 帶#的變數是可選項
#Android.mk 開頭初始化,
#確定當前編譯檔案路徑以及重置除LOCAL_PATH以外的 LOCAL_XXX變數
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
#生成的模組名,可選是否設定模組存放的路徑,我選擇放在當前路徑下
LOCAL_MODULE := XXX
#LOCAL_MODULE_PATH := $(LOCAL_PATH)
#需要的材料,分別是原始檔(必選)、靜態庫.a檔案(可選)、共享庫.so檔案(可選)、標頭檔案(可選)
#若未設定路徑,則材料預設的搜尋路徑為 $(LOCAL_PATH),在該目錄下
#可定義路徑,絕對路徑如 $(TOP)/hardware/rga.c
#或相對於 $(LOCAL_PATH)的路徑,hardware/rga.c 等效於 $(LOCAL_PATH)/hardware/rga.c
LOCAL_SRC_FILES := main.c \
Xxx.c \
xxx.c
#LOCAL_STATIC_LIBRARIES :=
#LOCAL_SHARED_LIBRARIES :=
#LOCAL_C_INCLUDES :=
#生成可執行程式
include $(BUILD_EXECUTABLE)
ps: C/C++執行模組的目標資料夾為 XXX_intermediates,XXX是模組名 。
2. 編譯C/C++靜態庫
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
#生成的靜態庫模組名,將生成xxx.a檔案
LOCAL_MODULE := XXX
#LOCAL_MODULE_PATH := $(LOCAL_PATH)
#需要的材料,分別是原始檔(必選)、靜態庫(可選)、共享庫(可選)、標頭檔案(可選)
LOCAL_SRC_FILES := Xxx.c \
xxx.c
#LOCAL_STATIC_LIBRARIES :=
#LOCAL_SHARED_LIBRARIES :=
#LOCAL_C_INCLUDES :=
#生成C/C++靜態庫
include $(BUILD_STATIC_LIBRARY)
ps: C/C++ 靜態庫模組的目標資料夾為 XXX_static_intermediates,或者XXX_intermediates,我的是後者,編譯系統版本不同會有差異, XXX是模組名。這個在 Android.mk 編譯生成過程中會有顯示。
3. 編譯C/C++ 共享庫
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
#生成的模組名,xxx最好能有lib字首,生成libxxx.so檔案,以適應不同版本的Android.mk
LOCAL_MODULE := XXX
#LOCAL_MODULE_PATH := $(LOCAL_PATH)
#需要的材料,分別是原始檔(必選)、靜態庫(可選)、共享庫(可選)、標頭檔案(可選)
LOCAL_SRC_FILES := Xxx.c \
xxx.c
#LOCAL_STATIC_LIBRARIES :=
#LOCAL_SHARED_LIBRARIES :=
#LOCAL_C_INCLUDES :=
#生成C/C++ 共享庫
include $(BUILD_SHARED_LIBRARY)
ps: 生成的目標資料夾名帶 "_intermediates"結尾,在編譯過程中有顯示,版本不同則資料夾路徑和資料夾名會有差異,根據實際的目錄查詢即可。同時靜態庫可用來編譯共享庫,則LOCAL_SRC_FILES := Xxx.a。
當然也可以在一個Android.mk編譯多個模組,模組之間Include $(CLEAR_VARS) 和Include $(BUILD_)相隔, 其中LOCAL_PATH 只需在Android.mk開頭定義一次即可,後續不必重複定義。
附本人的Android.mk例項:
#是否要編譯庫, true 確定便宜庫, false 不編譯庫,編譯執行程式
COMPILE_LIB := true
ifeq ($(COMPILE_LIB),true)
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE:= libvp_yan
LOCAL_MODULE_PATH := $(LOCAL_PATH)
LOCAL_CFLAGS += -DVERSION=4.3
LOCAL_SRC_FILES:= \
VP_Yan.c \
VP_Pwm.c \
VP_Hdu.c \
LOCAL_SHARED_LIBRARIES := \
libhardware \
liblog
LOCAL_C_INCLUDES += \
hardware/libhardware/include \
$(TOP)/hardware/log
$(LOCAL_PATH)/hfile #有標頭檔案位於當前編譯路徑的hfile子資料夾下
include $(BUILD_SHARED_LIBRARY)
else
#編譯可執行程式vp_yin
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_CFLAGS += -DVERSION=4.3
LOCAL_MODULE:= vp_yin
LOCAL_MODULE_PATH := $(LOCAL_PATH)
LOCAL_SRC_FILES:= \
VP_Main.c
LOCAL_SHARED_LIBRARIES := \
libhardware \
liblog \
libvp_yan #新增入true選項編譯的libvp_yan.so 共享庫
LOCAL_C_INCLUDES += \
hardware/libhardware/include \
$(TOP)/hardware/log
$(LOCAL_PATH)/hfile
include $(BUILD_EXECUTABLE)
endif