android.mk 作用,Android.mk的深入介绍

Android.mk的深入介绍

2017-08-09 16:11:24 +08

字数:3945

标签:

Android

Makefile

Android.mk作为Android单模块编译的Makefile,有其独特的写法。

本文从原理、源码的角度触发,介绍Android.mk怎么写,以及如何查找更多信息。

Android的编译系统,总体来说是由Makefile写的一个项目。

而Android.mk,只是其中的一个子集。

详见此前的一篇《Android 6.0中的Makefile》。

新增Android.mk ¶

在Android平台项目任意一个合适的目录下,创建一个Android.mk文件,都可以新增一个模块(Module)。

所谓『合适的』,是一个比较含糊的要求。

在build/core/main.mk中:

subdir_makefiles := \

$(shell build/tools/findleaves.py $(FIND_LEAVES_EXCLUDES) $(subdirs) Android.mk)

$(foreach mk, $(subdir_makefiles), $(info including $(mk) ...)$(eval include $(mk)))

其中的FIND_LEAVES_EXCLUDES,定义在build/core/config.mk中:

FIND_LEAVES_EXCLUDES := $(addprefix --prune=, $(OUT_DIR) $(SCAN_EXCLUDE_DIRS) .repo .git)

而build/tools/findleaves.py中的主要逻辑如下:

def perform_find(mindepth, prune, dirlist, filename):

result = []

pruneleaves = set(map(lambda x: os.path.split(x)[1], prune))

for rootdir in dirlist:

rootdepth = rootdir.count("/")

for root, dirs, files in os.walk(rootdir, followlinks=True):

# prune

check_prune = False

for d in dirs:

if d in pruneleaves:

check_prune = True

break

if check_prune:

i = 0

while i < len(dirs):

if dirs[i] in prune:

del dirs[i]

else:

i += 1

# mindepth

if mindepth > 0:

depth = 1 + root.count("/") - rootdepth

if depth < mindepth:

continue

# match

if filename in files:

result.append(os.path.join(root, filename))

del dirs[:]

return result

其中,prune就是FIND_LEAVES_EXCLUDES传入的--prune=列表,

而mindepth,则是默认值-1。

三段代码组合起来,可以看出查找Android.mk的逻辑:

排除.repo、.git和产物输出目录(默认为out)。

SCAN_EXCLUDE_DIRS默认为空,但可以通过参数传入、修改产品配置等方式设置。

查找目录深度不限。

如果父目录已经有Android.mk,不再查找子目录。

(不得不说一句题外话,代码写得真是不咋滴。)

使新增模块参与编译 ¶

满足上述逻辑的Android.mk,都会被include到Android的Makefile中。

但是,其中定义的模块是否参与编译,则未必。

相关代码,都在build/core/main.mk中。

modules_to_install := $(sort \

$(ALL_DEFAULT_INSTALLED_MODULES) \

$(product_FILES) \

$(foreach tag,$(tags_to_install),$($(tag)_MODULES)) \

$(CUSTOM_MODULES) \

)

可以看到,可以被安装到手机ROM的模块,有四种来源。

ALL_DEFAULT_INSTALLED_MODULES,从字面意思理解,系统默认的那些模块。

product_FILES,产品指定。

通过在产品配置中,指定PRODUCT_PACKAGES,可以进入这个列表。

tags_to_install,指定Tag。

比如,Tag为debug的模块,只在eng或userdebug的编译模式下才编译,在user模式下不编译。

CUSTOM_MODULES,自定义模块。

从实现上来讲,其实也是通过Tag来添加,但主要是支持mm、mmm这种单模块编译。

在模块未被添加到完整编译中时,在该模块的目录下执行mm,仍然可以强制编译,就是通过这里来指定的。

实际上,代码中的情况相当复杂,不像这里看上去这么简单。

不过,复杂的代码就不再进一步剖析了,上述理解与真实情况差别并不大。

四种来源的模块,是有重复的。

而Makefile的$(sort list),不仅可以排序,还可以去重。

修改ALL_DEFAULT_INSTALLED_MODULES需要改核心Makefile,通常不考虑。

而CUSTOM_MODULES是为单模块编译提供支持,也不考虑。

第三种方法是指定LOCAL_MODULE_TAGS,利用Tag来参与编译。

比如,只在eng下编译,可以指定LOCAL_MODULE_TAGS := eng。

原本可以通过指定Tag为user,参与全部类型的编译,但是已经被禁用了。

所以,平台开发者要想把已经准备好Android.mk的模块,添加到编译中,现在只剩指定PRODUCT_PACKAGES一种办法。

每个产品都改一次,虽然略显麻烦,但与做产品的逻辑,恰好吻合。

如何写Android.mk ¶

Android.mk本质上就是用Makefile写的配置文件。

与普通Makefile不同的是,它相当于是Android编译系统的一个子系统。

配置简单,可读性高,但若要凭空手写,基本没可能。

以下以packages/apps/Settings/Android.mk的内容为例,说明Android.mk的配置方式。

这是系统设置的源码,在系统应用中具有一定的代表性。

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

LOCAL_JAVA_LIBRARIES := bouncycastle conscrypt telephony-common ims-common

LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 android-support-v13 jsr305

LOCAL_MODULE_TAGS := optional

LOCAL_SRC_FILES := \

$(call all-java-files-under, src) \

src/com/android/settings/EventLogTags.logtags

LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res

LOCAL_PACKAGE_NAME := Settings

LOCAL_CERTIFICATE := platform

LOCAL_PRIVILEGED_MODULE := true

LOCAL_PROGUARD_FLAG_FILES := proguard.flags

ifneq ($(INCREMENTAL_BUILDS),)

LOCAL_PROGUARD_ENABLED := disabled

LOCAL_JACK_ENABLED := incremental

endif

include frameworks/opt/setupwizard/navigationbar/common.mk

include frameworks/opt/setupwizard/library/common.mk

include frameworks/base/packages/SettingsLib/common.mk

include $(BUILD_PACKAGE)

# Use the following include to make our test apk.

ifeq (,$(ONE_SHOT_MAKEFILE))

include $(call all-makefiles-under,$(LOCAL_PATH))

endif

Android.mk中,可以放置任意多个模块,这里只有一个模块。

对一个模块来说,大致可以分为开头清理、指定模块变量与调用编译文件三部分。

开头清理 ¶

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

include $(CLEAR_VARS),其实就是调用build/core/clear_vars.mk文件。

这个文件中,把单模块编译中,系统可能用到变量全部清理一遍。

如果想知道单模块编译时,可以指定哪些模块变量LOCAL_*,可以查看该文件。

LOCAL_PATH不是系统支持的模块变量,而是一个约定俗成的自定义变量,所以可以在调用CLEAR_VARS前指定。

其它自定义变量,不推荐使用LOCAL_作为前缀,防止和模块变量雷同。

my-dir是在build/core/definitions.mk定义的函数,通过Android.mk的位置来计算模块的路径。

如果想知道其它的常用函数,可以查看该文件。

由于Makefile天生的问题,不支持命名空间。

在需要反复使用一组变量时,要么给每一组变量不同的命名,要么必须反复初始化。

这里,Android使用的方法是后者。

所以在开始编译一个新模块前,必须清理模块变量LOCAL_*,以免受上一模块的干扰。

接下来,就可以开始指定这些变量。

指定模块变量 ¶

对于一个Android模块来说,最重要的是名称、AndroidManifest.xml、源码、资源文件、编译依赖。

LOCAL_MODULE就是模块名,不能和既有模块相同。

如果该变量未设置,则使用LOCAL_PACKAGE_NAME。

如果再没有,就会编译失败。

LOCAL_MANIFEST_FILE可以指定AndroidManifest.xml的位置。

如果未设置,则默认使用Android.mk的相同路径下的AndroidManifest.xml文件。

LOCAL_SRC_FILES是指定源文件列表。

这里用$(call all-java-files-under, src)来指定大部分文件。

all-java-files-under也是定义在definitions.mk中的函数。

LOCAL_RESOURCE_DIR是指定资源文件的目录。

LOCAL_JAVA_LIBRARIES和LOCAL_STATIC_JAVA_LIBRARIES,则指定了所依赖的共享与静态Java模块。

除了这些比较核心的变量以外,还有几十个其它变量可以按需指定。

调用编译文件 ¶

include $(BUILD_PACKAGE)

调用build/core/package.mk文件,执行APK的编译。

前面指定的模块变量LOCAL_*,都是为这一句而准备。

这些用来给Android.mk调用的文件及其变量,定义在build/core/config.mk中。

BUILD_COMBOS:= $(BUILD_SYSTEM)/combo

CLEAR_VARS:= $(BUILD_SYSTEM)/clear_vars.mk

BUILD_HOST_STATIC_LIBRARY:= $(BUILD_SYSTEM)/host_static_library.mk

BUILD_HOST_SHARED_LIBRARY:= $(BUILD_SYSTEM)/host_shared_library.mk

BUILD_STATIC_LIBRARY:= $(BUILD_SYSTEM)/static_library.mk

BUILD_SHARED_LIBRARY:= $(BUILD_SYSTEM)/shared_library.mk

BUILD_EXECUTABLE:= $(BUILD_SYSTEM)/executable.mk

BUILD_HOST_EXECUTABLE:= $(BUILD_SYSTEM)/host_executable.mk

BUILD_PACKAGE:= $(BUILD_SYSTEM)/package.mk

BUILD_PHONY_PACKAGE:= $(BUILD_SYSTEM)/phony_package.mk

BUILD_HOST_PREBUILT:= $(BUILD_SYSTEM)/host_prebuilt.mk

BUILD_PREBUILT:= $(BUILD_SYSTEM)/prebuilt.mk

BUILD_MULTI_PREBUILT:= $(BUILD_SYSTEM)/multi_prebuilt.mk

BUILD_JAVA_LIBRARY:= $(BUILD_SYSTEM)/java_library.mk

BUILD_STATIC_JAVA_LIBRARY:= $(BUILD_SYSTEM)/static_java_library.mk

BUILD_HOST_JAVA_LIBRARY:= $(BUILD_SYSTEM)/host_java_library.mk

BUILD_DROIDDOC:= $(BUILD_SYSTEM)/droiddoc.mk

BUILD_COPY_HEADERS := $(BUILD_SYSTEM)/copy_headers.mk

BUILD_NATIVE_TEST := $(BUILD_SYSTEM)/native_test.mk

BUILD_NATIVE_BENCHMARK := $(BUILD_SYSTEM)/native_benchmark.mk

BUILD_HOST_NATIVE_TEST := $(BUILD_SYSTEM)/host_native_test.mk

BUILD_SHARED_TEST_LIBRARY := $(BUILD_SYSTEM)/shared_test_lib.mk

BUILD_HOST_SHARED_TEST_LIBRARY := $(BUILD_SYSTEM)/host_shared_test_lib.mk

BUILD_STATIC_TEST_LIBRARY := $(BUILD_SYSTEM)/static_test_lib.mk

BUILD_HOST_STATIC_TEST_LIBRARY := $(BUILD_SYSTEM)/host_static_test_lib.mk

BUILD_NOTICE_FILE := $(BUILD_SYSTEM)/notice_files.mk

BUILD_HOST_DALVIK_JAVA_LIBRARY := $(BUILD_SYSTEM)/host_dalvik_java_library.mk

BUILD_HOST_DALVIK_STATIC_JAVA_LIBRARY := $(BUILD_SYSTEM)/host_dalvik_static_java_library.mk

根据以上代码,整理相关信息为以下表格。

其中,加粗的几个是自定义模块里比较常用的变量。

说明中的主机是指编译Android的机器,而设备是指安装Android的机器。

include 变量

Makefile

说明

BUILD_HOST_STATIC_LIBRARY

host_static_library.mk

编译主机上的静态库。

BUILD_HOST_SHARED_LIBRARY

host_shared_library.mk

编译主机上的共享库。

BUILD_STATIC_LIBRARY

static_library.mk

编译设备上的静态库。

BUILD_SHARED_LIBRARY

shared_library.mk

编译设备上的共享库。

BUILD_EXECUTABLE

executable.mk

编译设备上的可执行文件。

BUILD_HOST_EXECUTABLE

host_executable.mk

编译主机上的可执行文件。

BUILD_PACKAGE

package.mk

编译APK文件。

BUILD_PHONY_PACKAGE

phony_package.mk

定义一个不能实际编译的虚假模块。可以指定依赖。

BUILD_HOST_PREBUILT

host_prebuilt.mk

处理一个或多个主机上使用的已编译文件,该文件的实现依赖 multi_prebuilt.mk。

BUILD_PREBUILT

prebuilt.mk

处理一个已经编译好的文件( 例如Jar包)。

BUILD_MULTI_PREBUILT

multi_prebuilt.mk

处理一个或多个已编译文件,该文件的实现依赖prebuilt.mk。

BUILD_JAVA_LIBRARY

java_library.mk

编译设备上的共享Java库。

BUILD_STATIC_JAVA_LIBRARY

static_java_library.mk

编译设备上的静态Java库。

BUILD_HOST_JAVA_LIBRARY

host_java_library.mk

编译主机上的共享Java库。

BUILD_DROIDDOC

droiddoc.mk

编译生成droiddoc或javadoc文件。

BUILD_COPY_HEADERS

copy_headers.mk

复制相关的头文件,被static_library.mk等文件使用,通常不直接include。

BUILD_NATIVE_TEST

native_test.mk

编译设备上的可执行文件测试。

BUILD_NATIVE_BENCHMARK

native_benchmark.mk

添加libbenchmark,编译设备上的可执行文件。

BUILD_HOST_NATIVE_TEST

host_native_test.mk

主机上的可执行文件测试。

BUILD_SHARED_TEST_LIBRARY

shared_test_lib.mk

设备上共享库的测试。

BUILD_HOST_SHARED_TEST_LIBRARY

host_shared_test_lib.mk

主机上共享库的测试。

BUILD_STATIC_TEST_LIBRARY

static_test_lib.mk

设备上静态库的测试。

BUILD_HOST_STATIC_TEST_LIBRARY

host_static_test_lib.mk

主机上静态库的测试。

BUILD_NOTICE_FILE

notice_files.mk

追踪、生成版权相关的NOTICE文件。

BUILD_HOST_DALVIK_JAVA_LIBRARY

host_dalvik_java_library.mk

编译主机上的Dalvik共享Java库。

BUILD_HOST_DALVIK_STATIC_JAVA_LIBRARY

host_dalvik_static_java_library.mk

编译主机上的Dalvik静态Java库。

其它 ¶

在Settings这个模块的Android.mk中,还有几个有趣的地方。

编译额外模块 ¶

# Use the following include to make our test apk.

ifeq (,$(ONE_SHOT_MAKEFILE))

include $(call all-makefiles-under,$(LOCAL_PATH))

endif

在mm这类单模块编译的情况下,再额外include当前目录下的所有其它Android.mk,比如测试APK。

增量编译 ¶

ifneq ($(INCREMENTAL_BUILDS),)

LOCAL_PROGUARD_ENABLED := disabled

LOCAL_JACK_ENABLED := incremental

endif

在增量编译的情况下,禁用混淆、启用Jack工具链的增量编译功能。

引用其它mk文件 ¶

include frameworks/opt/setupwizard/navigationbar/common.mk

include frameworks/opt/setupwizard/library/common.mk

include frameworks/base/packages/SettingsLib/common.mk

引用这三个文件,目的是添加额外的资源文件、编译依赖与aapt参数。

这是一种比较高明的方式来使用自定义的模块。

总结 ¶

Android.mk是Android的编译系统中,独立模块的编译配置文件。

优点突出,兼容Makefile语法、可读性也还不错,代码量也不太高。

缺点也很明显,好读不好写,不过好在有很多既有文件可以借鉴。

在7.0以后的项目,开始逐渐使用更简洁的Android.bp,利用blueprint转换成Ninja,参加编译。

参考 ¶

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值