当前位置: 首页 > news >正文

Android.mk教程

语法

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_SHAREED_LIBRARYBUILD_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_LDLIBSLOCAL_SHARED_LIBRARIES 区别:前者直接-llog 传给链接器,没有建立模块依赖关系,链接器默认搜索路径(假定环境已经有了liblog.so),而且要手动确认,系统不会自动打包这个so库;后者会确保liblog先编译,建立两者的模块依赖关系,自动找到路径(多架构),由编译系统确定正确分区比如system、vendor,并且系统会自动打包so库,最好使用前者

LOCAL_MODULE_CLASSinclude $(BUILD_XXX) 区别:前者说明模块是什么类型,用于默认安装路径;后者是如何把源码做成目标(编译规则)。LOCAL_MODULE_PATH 才真正决定安装路径。

LOCAL_SHARED_LIBRARIESLOCAL_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// 下(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

http://www.dtcms.com/a/325824.html

相关文章:

  • RFID系统:物联网时代的数字化管理中枢
  • 算法训练营day45 动态规划⑫ 115.不同的子序列、583. 两个字符串的删除操作、72. 编辑距离、编辑距离总结篇
  • Java -- 集合 --Collection接口和常用的方法
  • (3万字详解)Linux系统学习:深入了解Linux系统开发工具
  • leetcode 15 三数之和
  • 【《数字货币量化交易:Linux下策略回测平台的搭建》】
  • 2025-2026 专升本论文写作【八项规范】
  • [202404-B]画矩形
  • 微信小程序常用 API
  • Arcpy-重采样记录
  • B站直播, 拼接4个窗口,能否实现
  • 从源码看 Coze:Agent 的三大支柱是如何构建的?
  • 【优化】图片批量合并为word
  • 嵌入式学习day24
  • MySQL的索引(索引的数据结构-B+树索引):
  • P2865 [USACO06NOV] Roadblocks G
  • 音视频学习(五十三):音频重采样
  • 数据备份与进程管理
  • AI大模型:(二)5.1 文生视频(Text-to-Video)模型发展史
  • Apache ECharts 6 核心技术解密 – Vue3企业级可视化实战指南
  • Apache Ignite 核心组件:GridClosureProcessor解析
  • ChatML vs Harmony:深度解析OpenAI全新对话结构格式的变化
  • 基于Spring Boot房源信息推荐系统的设计与实现 -项目分享
  • Maven <pom.xml> 标签详尽教程
  • perl notes【1】
  • 云原生环境Prometheus企业级监控
  • 【Node.js从 0 到 1:入门实战与项目驱动】1.3 Node.js 的应用场景(附案例与代码实现)
  • 论文阅读:Aircraft Trajectory Prediction Model Based on Improved GRU Structure
  • 《开源标准推动Linux驱动生态繁荣》
  • 实现分页功能【jQuery】