前言
做Android APP开发,可以基本不用care java的编译过程,java的依赖关系,并且现在Android studio也已经支持了cmake开发,也就是说大部分情况下,就不用关心系统是如何编译的,不过对于想要用以一些第三方库,这就需要对整个编译框架有所了解了,并且这个东东还是很简单的。
正文
Android APP jni 编译控制有四种主要的编译控制系统,
- Android.mk、Application.mk编译
- cMake
- toochain
Android.mk这是一个编译脚本,他是google自己用makefile写的一个编译框架,也就是和编译框架组合在一起,就可以构成一个完整的makefile,cMake这里就不再多说,toochain这个是ndk提供的一种交叉编译工具集,其实无论是Android.mk 和cMake都是用了ndk的toolchain来编译了,只是用这些编译框架更简单,不用纠结一系列问题。下面我们主要分析他们的根本原理
Android的编译框架其实本质上都是gradle调用了
externalNativeBuild {
ndkBuild {
path 'Android.mk'
}
}
/**
* Configures external native build using <a href="https://cmake.org/">CMake</a> or <a
* href="https://developer.android.com/ndk/guides/build.html">ndk-build</a>.
*
* <p>For more information about the properties you can configure in this block, see {@link
* ExternalNativeBuild}.
*/
public void externalNativeBuild(Action<ExternalNativeBuild> action) {
checkWritability();
action.execute(externalNativeBuild);
}
这里我们查看源码,和注释,也就是可以调用的内部一个匿名构造函数的action方法,这里还是很好理解的,就是调用内部的ndkBuild 函数,然后系统帮我们设置好参数,然后调用ndk-build编译工具,交给ndk下的ndk-build工具来处理,其实在下载好的ndk目录下,这个ndk-build工具还是比较简单的,下面我们开始一一解开这个神秘的面纱,
其实这里我们不通过gradle,直接调用ndk-bulid也可以编译Android.mk的jni代码,不过这样貌似不常做,下面我们就看看这个makefile的框架到底是干嘛的,ndk-build其实就是调用$NDK/build/ndk-build,看了好久,其实ndk-build就是调用make,然后传入build-local.mk
#!/bin/sh
$GNUMAKE -f $PROGDIR/core/build-local.mk "$@"
下面我们看看到底这个build-local.mk是干啥的:
#就是目录,看名字
NDK_ROOT := XXXX
#就是初始化一些系统架构、log函数,make工具,python工具,输出工具,等等,然后解析toolchain目录下的东东,得到我们可以可以处理的能力集。
include $(NDK_ROOT)/build/core/init.mk
NDK_PROJECT_PATH=.
#我们解析的文件没有application.mk。所以这里我们直接到这里
NDK_APPLICATION_MK := $(NDK_ROOT)/build/core/default-application.mk
#输出
NDK_APP_OUT := $(NDK_PROJECT_PATH)/obj
NDK_APP_LIBS_OUT := $(NDK_PROJECT_PATH)/libs
# Fake an application named 'local'
_app := local
_application_mk := $(NDK_APPLICATION_MK)
NDK_APPS := $(_app)
#这里开始变得复杂了,通过阅读来慢慢理解这里。
include $(BUILD_SYSTEM)/add-application.mk
# If a goal is DUMP_xxx then we dump a variable xxx instead
# of building anything
#
MAKECMDGOALS := $(filter-out DUMP_$(DUMP_VAR),$(MAKECMDGOALS))
include $(BUILD_SYSTEM)/setup-imports.mk
ifneq (,$(DUMP_VAR))
# We only support a single DUMP_XXX goal at a time for now.
ifneq ($(words $(DUMP_VAR)),1)
$(call __ndk_error,!!TOO-MANY-DUMP-VARIABLES!!)
endif
#这里把APP_BUILD_SCRIPT include进去了
$(foreach _app,$(NDK_APPS),\
$(eval include $(BUILD_SYSTEM)/setup-app.mk)\
)
.PHONY : DUMP_$(DUMP_VAR)
DUMP_$(DUMP_VAR):
@echo $($(DUMP_VAR))
else
# Build it
include $(BUILD_SYSTEM)/build-all.mk
endif
这里我们看下到底add-application.mk干了什么。
#这个搞不懂,。特别是包含我们自己写的Android.mk的时候,就完全搞不懂了,实在懒得搞这个事情了,这里可以不用纠结了,反正就是调用了特定的编译器,bye。
这里我们很容易,最终的build-local.mk会包含我们,emmm,这个makefile的编译框架,还是比较复杂的,我看了两天,也没看明白,反正,从ndk/build/core/目录下文件加上我们的Android.mk加在一起,就构成你个完整的makefile文件,有机会再来补充一下具体的细节,这里我们大概了解一下流程就得了。
cmake和这个一样,只是cmke需要大量的控制参数,比如编译架构,等等参数(有些是gradle自带的参数),除非我们看gradle源码和Android gradle插件源码,才能了解他的默认参数,这里还是多少有些麻烦,反正就是调用toochain编译通过cmake来编译,
其实,这俩都是通过简单的方法调用了tootchain来编译代码的,可是当我们需要一些库函数,需要大量配置的时候,很难很容易的直接用Android.mk 或者cmake来控制,这时我们只需要把握呢需要的特定的toolchain传入我们自己的makefile,然后设定tootchain的参数,我们就可以编出自己的想要的代码。
后记