语法
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