語法
Android.mk 的必備三行
LOCAL_PATH := $(call my-dir) # Android.mk的目錄,call調用函數include $(CLEAR_VARS) # 除了LOCAL_PATH清除所有LOCAL_XXXinclude $(BUILD_SHARED_LIBRARY) # BUILD_XXX, 指定構建類型
# BUILD_SHARED_LIBRARY → .so動態庫
# BUILD_STATIC_LIBRARY → .a動態庫
# BUILD_EXECUTABLE → 生成elf可執行文件
# BUILD_PREBUILT → 使用預編譯文件(不編譯源碼)編譯,比如APK
# PREBUILT_SHAREED_LIBRARY → 使用預編譯so文件
BUILD_EXECUTABLE 用于編譯elf可執行文件,adb shell 可以直接運行;通常在 /system/bin、/vendor/bin(取決于LOCAL_MODULE_PATH);
PREBUILD_SHARED_LIBRARY 和 BUILD_PREBUILT 區別:前者只能處理預編譯的共享庫,后者可以可以處理任意的預編譯產物(apk、jar等等)。二者都是處理已經編譯好的文件(不用自己編譯)
demo:Android.mk(常見模塊類型)
LOCAL_PATH := $(call my-dir)# 模塊:
include $(CLEAR_VARS)
LOCAL_MODUEL := mylib # 模塊名(無擴展名)
LOCAL_SRC_FILES := mylib.c main.c # 源文件
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include # c頭文件
LOCAL_SHARED_LIBRARIES := libfoo libbar # 自己編譯的依賴的so庫(工程內的,無擴展名)
LOCAL_STATIC_LIBRARIES := libabc # 依賴的a庫(無擴展名)
LOCAL_PREBUILT_LIBS := prebuilt/libbaz.so # 外界編譯好的so庫(有擴展名)
LOCAL_LDLIBS := -llog -landroid # 鏈接時的系統庫(NDK常用), liblog.so、libandroid.so
LOCAL_JAVA_LIBRARIES := libabc # 依賴的jar包(無擴展名)
LOCAL_MODULE_SUFFIEX := .so # 模塊的擴展名
LOCAL_CFLAGS := -DDEBUG=1 # C的編譯參數
LOCAL_CPPFLAGS := -std=c++11 # C++的編譯參數
LOCAL_MODULE_TAGS := optional # 是否編譯選項,tests、eng、debug(lunch編譯模式編譯);device.mk優先級更高
LOCAL_MODULE_CLASS := SHARED_LIBRARIES # 編譯模塊的類型,和默認安裝路徑system下,lib bin app etc等
# APPS(apk)、SHARED_LIBRARIES(so)、STATIC_LIBRARIES(a)、JAVA_LIBRARIES(Java庫)、ETC(放/system/etc)
LOCAL_MODULE_PATH := $(TARGET_OUT)/lib # 安裝路徑 /system/bin
# $(TARGET_OUT_VENDOR)/bin → /vendor/bin
# $(TARGET_OUT_DATA)/myapp → /data/myappinclude $(BUILD_SHARED_LIBRARY)
LOCAL_LDLIBS 和 LOCAL_SHARED_LIBRARIES 區別:前者直接-llog 傳給鏈接器,沒有建立模塊依賴關系,鏈接器默認搜索路徑(假定環境已經有了liblog.so),而且要手動確認,系統不會自動打包這個so庫;后者會確保liblog先編譯,建立兩者的模塊依賴關系,自動找到路徑(多架構),由編譯系統確定正確分區比如system、vendor,并且系統會自動打包so庫,最好使用前者。
LOCAL_MODULE_CLASS 和 include $(BUILD_XXX) 區別:前者說明模塊是什么類型,用于默認安裝路徑;后者是如何把源碼做成目標(編譯規則)。LOCAL_MODULE_PATH 才真正決定安裝路徑。
LOCAL_SHARED_LIBRARIES 和 LOCAL_STATIC_LIBRARIES:前者在運行時才會鏈接此庫;后者最終會被打包進此模塊。
demo:Android.mk(多模塊)
LOCAL_PATH := $(call my-dir)# 模塊1: so庫
include $(CLEAR_VARS)
LOCAL_MODULE := libfoo
LOCAL_SRC_FILES := foo.c bar.c baz.c
include $(BUILD_SHARED_LIBRARY)# 模塊2: 編譯為可執行文件
include $(CLEAR_VARS)
LOCAL_MODULE := foo_test
LOCAL_SRC_FILES := test.c
LOCAL_SHARED_LIBRARIES := libfoo
include $(BUILD_EXECUTABLE)
demo2: Android.mk(預編譯庫)
LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)
LOCAL_MODULE:= libmystuff
LOCAL_SRC_FILES := prebuilt/libmystuff.so
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
LOCAL_MODULE_SUFFIX := .soinclude $(PREBUILT_SHARED_LIBRARY) # 使用的是PREBUILT_SHARED_LIBRARY vs BUILD_SHARED_LIBRARY vs BUILD_PREBUILT
看似把libmystuff.so 編譯為libmystuff.so 多次一舉,實際上把libmystuff模塊進入編譯系統,否則其他模塊引入這個庫要寫入絕對路徑。
demo:LOCAL_LDLIBS的作用
#include <android/log.h>void foo() {__android_log_print(ANDROID_LOG_INFO, "TAG", "Hello from native");
}
// 必須要在LOCAL_SHARED_LIBRARIES := liblog,否咋找不到函數
常用函數
# 賦值
NAWE := foo
A := $(NAME).txt # 立即賦值,A必須等于 foo.txt
B = $(NAME).txt # $(B)使用的時候,才展開,跟隨NAME變化
# :=(立即賦值) =(稍后賦值) ?=(條件賦值) +=(追加賦值)# call調用函數
LOCAL_PATH := $(call my-dir) # Android.mk當前所在目錄
LOCAL_SRC_FILES := $(wildcard *.c) # 當前目錄下所有*.c# 遞歸加載當前目錄及子目錄下的所有Android.mk
include $(call all-subdir-makefiles)
# 遞歸加載指定文件夾下的Android.mk
LOCAL_PATH := $(call my-dir)
include $(call all-makefiles-under,$(LOCAL_PATH))$(shell cmd) # 執行shell命令并返回結果,eg: $(shell pwd -P)# 過濾filter,返回所有匹配的單詞
$(filter text,string)
$(filter %.c %.cpp, main.c main.o test.cpp readme.txt) # main.c test.cpp # findstr,如果主串有就返回子串
$(findstring string,text)
$(findstring main, main.c test.cpp) # main# foreach 是 make 內置的循環函數,Android.mk也能使用
SRC := a.c b.c c.c
$(foreach var,$(SRC),$(info hello $(var))) # 格式 $(foreach var,list,code)
hello a.c
hello b.c
hell0 c.c# 打印信息
$(info "...........this is info........")
$(warning "...........this is warning........")# 條件編譯, ifeq、ifneq
ifeq ($(TARGET_ARCH),arm)LOCAL_CFLAGS += -DARM_BUILD
else...
endififneq ($(strip $(MY_FEATURE)),) # strip去掉所有的空格和換行LOCAL_SRC_FILES += feature.c
endif# 文本替換
STR := a/b/c
NEW := $(subst /,-,$(STR)) # a-b-c
# 取文件名
NAME := $(notdir src/main.c) # main.c
# 取目錄
DIR := $(dir src/main.c) # src/# 調用自定義函數
my-func = $(1)-$(2)
RESULT := $(call my-func,hello,world) # hello-world
demo:Android.mk (用于補充之前中LOCAL_XXX沒有提到的)
LOCAL_CC := arm-linux-androideabi-gcc # 指定c compiler, 很少顯示指定,系統會默認 gcc/clang
LOCAL_CXX := arm-linux-androideabi-g++ # 指定c++ compiler
LOCAL_CPP_EXTENSION := .cc .cxx # cpp之外的C++擴展名, 比如.cc .cxx
LOCAL_C_INCLUDES := \ # c頭文件$(LOCAL_PATH)/include \$(LOCAL_PATH)/third_party/libfoo/include# 此項目模塊,會覆蓋名字為CarLauncher的模塊
LOCAL_OVERRIDES_PACKAGES := CarLauncher# 當模塊為app時候
# 路徑,/system/app(priv-app)/myapp/myapp.apk
LOCAL_PRIVILEGED_MODULE := true# 簽名:PRESIGNED(保持原簽名),platform(系統簽名)
LOCAL_CERTIFICATE := PRESIGNED# @開頭表示相對于當前Android.mk路徑,指定jni的so庫路徑
LOCAL_PREBUILT_JNI_LIBS := \@jniLibs/armeabi-v7a/libmonochrome.so
# 不進行odex優化(dex→oat)
LOCAL_DEX_PREOPT := false# 默認路徑為 /product
LOCAL_PRODUCT_MODULE := true
# 修改最終的輸出名字,但是內部構建系統 模塊名依然是原本的
LOCAL_BUILT_MODULE_STEM := new_name.so
tip:
build/target/product/security/platform.{pk8,x509.pem},用于簽名,pk8為密鑰用于加密apk,x509.pem為公鑰用于驗證。另外還有shared用與同一家開發的不同app簽名然后apk之間的數據可以相互訪問,media 也用于簽名
demo:Android.mk (adair_service/Android.mk)
LOCAL_PATH := $(call my-dir)
PROJ_PATH := $(call my-dir)ifneq ($(ADAIR_SERVICE_BUILD_ANDROID), true)
ADAIR_SERVICE_BUILD_ANDROID := true$(warning "...............ADAIR_SERVICE_BUILD_ANDROID.............")
# 引用其他模塊
include $(PROJ_PATH)/MainServer/Android.mk
include $(PROJ_PATH)/ParamUpdate/Android.mk
include $(PROJ_PATH)/MountAll/Android.mk
include $(PROJ_PATH)/Apk/Android.mkinclude $(PROJ_PATH)/external/can-utils/Android.mk
include $(PROJ_PATH)/external/aapt0/Android.mk
endif
其中 Apk/Android.mk:會遍歷所有的子文件夾Android.mk
LOCAL_PATH := $(call my-dir)
include $(call all-makefiles-under,$(LOCAL_PATH))
實例
demo:安裝WeChat的Android.mk(可以無腦復制)
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := WeChat
LOCAL_MODULE_CLASS := APPS
LOCAL_MODULE_TAGS := optional
# 可以不要
LOCAL_BUILT_MODULE_STEM := package.apk
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
#LOCAL_PRIVILEGED_MODULE := true
LOCAL_CERTIFICATE := PRESIGNED
LOCAL_SRC_FILES := $(LOCAL_MODULE).apk
LOCAL_ENFORCE_USES_LIBRARIES := false
LOCAL_DEX_PREOPT := false
include $(BUILD_PREBUILT)
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX) 其中 COMMON_ANDROID_PACKAGE_SUFFIX 已經在 build/make/core/config.mk 中定義了
demo:Apk/Goog_TTS/Android.mk
LOCAL_PATH := $(my-dir)include $(CLEAR_VARS)
LOCAL_MODULE := Goog_TTS
LOCAL_MODULE_CLASS := APPS
# 指定安裝路徑
LOCAL_MODULE_PATH := $(TARGET_OUT_ODM)/bundled_uninstall_back-app
LOCAL_SRC_FILES := $(LOCAL_MODULE)$(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_CERTIFICATE := PRESIGNED
LOCAL_DEX_PREOPT := false
LOCAL_ENFORCE_USES_LIBRARIES := false
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
# 指明目標架構
LOCAL_JNI_SHARED_LIBRARIES_ABI := arm
MY_LOCAL_PREBUILT_JNI_LIBS := \lib/arm/libopusV2JNI.so\lib/arm/libtts_android.so\MY_APP_LIB_PATH := $(TARGET_OUT_ODM)/bundled_uninstall_back-app/$(LOCAL_MODULE)/lib/$(LOCAL_JNI_SHARED_LIBRARIES_ABI)
# None字符串字面常量
ifneq ($(LOCAL_JNI_SHARED_LIBRARIES_ABI), None)
$(warning MY_APP_LIB_PATH=$(MY_APP_LIB_PATH))
# 模塊安裝結束后,執行的命令
LOCAL_POST_INSTALL_CMD := mkdir -p $(MY_APP_LIB_PATH); \$(foreach lib, $(MY_LOCAL_PREBUILT_JNI_LIBS), cp -f $(LOCAL_PATH)/$(lib) $(MY_APP_LIB_PATH)/$(notdir $(lib));)
endif
include $(BUILD_PREBUILT)
LOCAL_ENFORCE_USES_LIBRARIES:默認為true,在構建時會檢查 AndroidManifest.xml 中列出的 uses-library 項,如果指定的庫不存在則會構建報錯或不參與構建;如果false,則不進行檢查,雖然apk可能引用系統不存在的庫,這些庫可能由Play服務或者其他提供。
簡介
Android 使用 GNU Make 語法的構建腳本,主要用于 Android NDK / AOSP(舊版) 編譯系統,
Android.mk 的本質就是 Makefile,只是Google定義了一套變量和構建規范,定義了大量類似LOCAL_*的變量,不用手寫gcc命令了。
Makefile 是 GUN Make 工具使用的規則文件:demo
hello: hello.c # 目標: 依賴gcc -o hello hello.c # 規則
CMake 本身不編譯,生成構建腳本Makefile、Ninja等,然后Makefile 直接進行編譯。
流程:
- 提起編寫好Android.mk 或 Android.bp
- evensetup.sh & lunch:設置環境變量(TOP、OUT_DIR、TARGET_PRODUCT等);加載build/make/core/*.mk
- make / mm :調用GUN make,掃描所有的Android.mk(all-subdir-makefiles)
- build/core/*.mk 解析 LOCAL_XXX 變量,并根據最后一行include $() 產生對應編譯規則makefile,makefile由kata(make-to-ninja) 生成 ninja 規則,Soong(Android.bp)也會生成ninja 規則
- ninja 執行 C/C++編譯,鏈接生成產物(so、apk等),最后復制到out/,并安裝到目標目錄上out/target/product/<device>/ 下(system vendor boot.img system.img等)
模塊的 Android.mk 不會單獨編譯,需要被上級的Android.mk 或者 Android.bp文件引用。比如
# 遞歸加載當前目錄及子目錄下的所有Android.mk
include $(call all-subdir-makefiles)
NDK 是谷歌提供的C/C++原生開發工具包,面向的是APP層開發者,可以使用Android.mk編譯JNI動態庫(so) 給APK使用。可以提供性能,比如視頻庫、圖形渲染、數學大量計算等;并且可以直接使用OpenCV、FFmpeg、libpng等開源庫。
tip:foo、bar、baz 都是常用的偽變量,類似 Tom、Bob