android编译分析
- 格式:docx
- 大小:125.19 KB
- 文档页数:29
简述android源代码的编译过程
android源代码的编译过程大致可以分为三个步骤:准备、编译
和构建。
首先,准备。
它要求编译环境符合android源代码编译文档要求,编译机器配置最新版本才能支持android。
为了更好地加快android源码下载进度和构建速度,请使用“repo sync”来同步整个android源
构建,并使用Git工具获取最新的代码树。
其次,编译。
它要求按照Android构建文档的步骤,使用Ninja
构建系统编译原生Android源码,这几乎可以在编译机上完成。
最后,构建。
它要求按照Android构建文档的步骤,使用定制的Android编译系统构建安卓固件文件,该系统将运行在主流移动设备上,并在生成中提供android设备固件。
Android ninja编译启动过程分析---make是如何转换到到ninja编译的1.首先你的得对make的工作机制有个大概的了解:运行的命令在要编译的目录下运行make,或者make target_namea.分析处理保存阶段(没有实际编译动作):它首先对当前目录下的Makefile文件的做一次扫描,语法分析,还有处理,主要是变量的保存,目标依赖列表生成,目标下的action列表的生成,然后记住b.然后按记住的目标执行action列表动作(有实际编译动作).编译启动的入口方式还是运行make:2开始make-jxxx方式进入.....(xxx是本机cpu的数量)make开始做进行第一次扫描....目前USE_NINJA还是没有定义,估计以后很久很久才能启用的了!BUILDING_WITH_NINJA开始也是没定义的看make扫描入口文件:Makefile:include build/core/main.mk在build/core/main.mk:在ninia之前都有include help.mk和config.mk97include$(BUILD_SYSTEM)/help.mk9899#Set up various standard variables based on configuration100#and host information.101include$(BUILD_SYSTEM)/config.mk说明make help//显示make帮助make config//当前显示配置103relaunch_with_ninja:=104ifneq($(USE_NINJA),false)105ifndef BUILDING_WITH_NINJA<==第二次扫描不会到这里了106relaunch_with_ninja:=true107endif108endif116ifeq($(relaunch_with_ninja),true)<===第一次扫描入这里了117#Mark this is a ninja build.118$(shell mkdir-p$(OUT_DIR)&&touch$(OUT_DIR)/ninja_build)119include build/core/ninja.mk//---进入ninja.mk第一次扫描到此为止就结束掉了,因为在当前ifeq else endif后面没有代码了120else#///!relaunch_with_ninja<===第二次扫描入这里了121ifndef BUILDING_WITH_NINJA122#Remove ninja build mark if it exists.123$(shell rm-f$(OUT_DIR)/ninja_build)124endif......endif////////!relaunch_with_ninja这里是文件底了接着上面的include build/core/ninja.mkbuild/core/ninja.mk:$(sort$(DEFAULT_GOAL)$(ANDROID_GOALS)):ninja_wrapper//使nijia_wrapper成为第一扫描后要做的action 的第一个跳入的标签ninja_wrapper:$(COMBINED_BUILD_NINJA)$(MAKEPARALLEL)@echo Starting build with ninja+$(hide)export NINJA_STATUS="$(NINJA_STATUS)"&&source$(KATI_ENV_SH)&&$(NINJA_MAKEPARALLEL) $(NINJA)$(NINJA_GOALS)-C$(TOP)-f$(COMBINED_BUILD_NINJA)$(NINJA_ARGS)这里被依赖$(COMBINED_BUILD_NINJA)要先被处理完后才能返回来继续这里的action了ifeq($(USE_SOONG),true)135$(COMBINED_BUILD_NINJA):$(KATI_BUILD_NINJA)$(SOONG_ANDROID_MK)136$(hide)echo"builddir=$(OUT_DIR)">$(COMBINED_BUILD_NINJA)137$(hide)echo"subninja$(SOONG_BUILD_NINJA)">>$(COMBINED_BUILD_NINJA)138$(hide)echo"subninja$(KATI_BUILD_NINJA)">>$(COMBINED_BUILD_NINJA)139else140COMBINED_BUILD_NINJA:=$(KATI_BUILD_NINJA)141endif继续看:$(KATI_BUILD_NINJA)162$(KATI_BUILD_NINJA):$(KATI)$(MAKEPARALLEL)$(DUMMY_OUT_MKS)$(SOONG_ANDROID_MK)FORCE 163@echo Running kati to generate build$(KATI_NINJA_SUFFIX).ninja...164+$(hide)$(KATI_MAKEPARALLEL)$(KATI)--ninja--ninja_dir=$(OUT_DIR)--ninja_suffix=$(KATI_NINJA_SUFFIX)--regen--ignore_dirty=$(OUT_DIR)/%--no_ignore_dirty=$(SOONG_ANDROID_MK)--ignore_optional_include=$(OUT_DIR)/%.P--detect_android_echo $(KATI_FIND_EMULATOR)-f build/core/main.mk$(KATI_GOALS)--gen_all_targets BUILDING_WITH_NINJA=true SOONG_ANDROID_MK=$(SOONG_ANDROID_MK)在这个位置触发了的第二次build/core/main.mk(由kati处理的)的扫描,看起来他和make功能一样,这里kati要处理一遍: build/core/main.mk,下面他处理的包括的过程:文件开始....117else#!relaunch_with_ninja118ifndef BUILDING_WITH_NINJA119#Remove ninja build mark if it exists.120$(shell rm-f$(OUT_DIR)/ninja_build)121endif.....#endif也就是说kati处理了从main.mk文件开始到末尾的全部,不包括include build/core/ninja.mk部分的全部了,包括他include makefiles了@echo Running kati to generate build$(KATI_NINJA_SUFFIX).ninja.../////////////在屏幕上可以看到是否kati开始了扫描生成.ninja file的过程在这个过程中,kati要判断是否生成.ninja是否需要更新了,这个是在kati内部完成的!./:172:fprintf(stderr,"%s was modified,regenerating...\n",s.c_str());看来东西不少,然后他回到ninja_wrapper:下继续上一层的action执行ninja_wrapper:$(COMBINED_BUILD_NINJA)$(MAKEPARALLEL)//重新贴了一遍上面的东西@echo Starting build with ninja+$(hide)export NINJA_STATUS="$(NINJA_STATUS)"&&source$(KATI_ENV_SH)&&$(NINJA_MAKEPARALLEL) $(NINJA)$(NINJA_GOALS)-C$(TOP)-f$(COMBINED_BUILD_NINJA)$(NINJA_ARGS)这个执行就是启动ninja了,看:$(NINJA)$(NINJA_GOALS)@echo Starting build with ninja/////////////在屏幕上可以看到是否开是在ninja带领下编译开始了.....//====到此,第一阶段准备工作部分分析完成了,下面进入具体的ninjia编译阶段了3.总结kati代替了make过去做的非常相同一样的工作(连输出打印都很一样,例如遇到$(error xxxx)会退出),parse makefile。
android img 的编译过程
Android img的编译过程通常包括以下步骤:
在Android源码根目录下执行初始化环境命令:source build/envsetup.sh。
使用lunch命令选择需要编译的版本,如:lunch full-eng。
执行make命令进行编译,如:make bootimage,这个命令会编译生成boot.img。
boot.img的生成过程主要是将kernel、ramdisk、dtb打包到一起。
在这个过程中,需要用到dtb.img,如果dtb.img过大导致编译不过,那么需要分析dtb.img的生成过程。
dtb.img实际上是将kernel下的dts文件重新命名后拷贝到out目录下生成的。
此外,编译Android系统时,make命令实际上是在执行Makefile文件。
Android系统的Makefile文件在源码根目录下,并且这个Makefile文件会包含另一个主要的Makefile文件,即main.mk。
在main.mk中,定义了一个默认目标droid,这个目标依赖于droid_targets。
droid_targets又依赖于droidcode和dist_files两大伪目标。
当执行make命令时,make工具会检查并解析这些依赖关系,然后按照依赖顺序生成目标文件。
Android编译系统(Android.mk⽂件详解)【Android-NDK(Native Development Kit) docs⽂档】NDK提供了⼀系列的⼯具,帮助开发者快速开发C(或C++)的动态库,并能⾃动将so和java应⽤⼀起打包成apk。
Android.mk⽂件是GNU Makefile的⼀⼩部分,它⽤来对Android程序进⾏编译。
因为所有的编译⽂件都在同⼀个 GNU MAKE 执⾏环境中进⾏执⾏,⽽Android.mk中所有的变量都是全局的。
因此,您应尽量少声明变量,不要认为某些变量在解析过程中不会被定义。
⼀个Android.mk⽂件可以编译多个模块,每个模块属下列类型之⼀:1)APK程序⼀般的Android程序,编译打包⽣成apk⽂件2)JAVA库java类库,编译打包⽣成jar⽂件3)C\C++应⽤程序可执⾏的C\C++应⽤程序4)C\C++静态库编译⽣成C\C++静态库,并打包成.a⽂件5)C\C++共享库编译⽣成共享库(动态链接库),并打包成.so⽂,有且只有共享库才能被安装/复制到您的应⽤软件(APK)包中。
可以在每⼀个Android.mk file 中定义⼀个或多个模块,你也可以在⼏个模块中使⽤同⼀个源代码⽂件。
编译系统为你处理许多细节问题。
例如,你不需要在你的 Android.mk 中列出头⽂件和依赖⽂件。
编译系统将会为你⾃动处理这些问题。
这也意味着,在升级 NDK 后,你应该得到新的toolchain/platform⽀持,⽽且不需要改变你的 Android.mk ⽂件。
注意,NDK的Anroid.mk语法同公开发布的Android平台开源代码的Anroid.mk语法很接近,然⽽编译系统实现他们的⽅式却是不同的,这是故意这样设计的,可以让程序开发⼈员重⽤外部库的源代码更容易。
在描述语法细节之前,咱们来看⼀个简单的"hello world"的例⼦,⽐如,下⾯的⽂件:sources/helloworld/helloworld.csources/helloworld/Android.mk'helloworld.c'是⼀个 JNI 共享库,实现返回"hello world"字符串的原⽣⽅法。
【转】Android编译系统详解(三)——编译流程详解原⽂⽹址:本⽂原创作者: 欢迎转载,请注明出处和1.概述编译Android的第三步是使⽤mka命令进⾏编译,当然我们也可以使⽤make –j4,但是推荐使⽤mka命令。
因为mka将⾃动计算-j选项的数字,让我们不⽤纠结这个数字到底是多少(这个数字其实就是所有cpu的核⼼数)。
在编译时我们可以带上我们需要编译的⽬标,假设你想⽣成recovery,那么使⽤mka recoveryimage,如果想⽣成ota包,那么需要使⽤mka otapackage,后续会介绍所有可以使⽤的⽬标。
另外注意有⼀些⽬标只是起到修饰的作⽤,也就是说需要和其它⽬标⼀起使⽤,共有4个⽤于修饰的伪⽬标:1) showcommands 显⽰编译过程中使⽤的命令2) incrementaljavac⽤于增量编译java代码3) checkbuild⽤于检验那些需要检验的模块4) all如果使⽤all修饰编译⽬标,会编译所有模块研究Android编译系统时最头疼的可能是变量,成百个变量我们⽆法记住其含义,也不知道这些变量会是什么值,为此我专门做了⼀个编译变量的参考⽹站,你可以在该⽹站查找变量,它能告诉你变量的含义,也会给出你该变量的⽰例值,另外也详细解释了编译系统⾥每个Makefile的作⽤,这样你在看编译系统的代码时不⾄于⼀头雾⽔。
编译的核⼼⽂件是和,main.mk主要作⽤是检查编译环境是否符合要求,确定产品配置,决定产品需要使⽤的模块,并定义了许多⽬标供开发者使⽤,⽐如droid,sdk等⽬标,但是⽣成这些⽬标的规则主要在Makefile⾥定义,⽽内核的编译规则放在build/core/task/kernel.mk我们将先整体介绍main.mk的执⾏流程,然后再针对在Linux上编译默认⽬标时使⽤的关键代码进⾏分析。
Makefile主要定义了各个⽬标的⽣成规则,因此不再详细介绍它的执⾏流程,若有兴趣看每个⽬标的⽣成规则,可查看2. main.mk执⾏流程2.1 检验编译环境并建⽴产品配置1) 设置Shell变量为bash,不能使⽤其它shell2) 关闭make的suffix规则,rcs/sccs规则,并设置⼀个规则: 当某个规则失败了,就删除所有⽬标3) 检验make的版本,cygwin可使⽤任意版本make,但是linux或者mac只能使⽤3.81版本或者3.82版本4) 设置PWD,TOP,TOPDIR,BUILD_SYSTEM等变量,定义了默认⽬标变量,但是暂时并未定义默认⽬标的⽣成规则5) 包含,该makefile定义了两个⽬标help和out, help⽤于显⽰帮助,out⽤于检验编译系统是否正确6) 包含,config.mk作了很多配置,包括产品配置,包含该makefile后,会建⽴输出⽬录系列的变量,还会建⽴PRODUCT系列变量,后续介绍产品配置时,对此会有更多详细介绍7) 包含,该makefile会包含所有⼯程的CleanSpec.mk,写了CleanSpec.mk的⼯程会定义每次编译前的特殊清理步骤,cleanbuild.mk会执⾏这些清除步骤8) 检验编译环境,先检测上次编译结果,如果上次检验的版本和此次检验的版本⼀致,则不再检测,然后进⾏检测并将此次编译结果写⼊2.2 包含其它makefile及编译⽬标检测1) 如果⽬标⾥含有incrementaljavac,那么编译⽬标时将⽤incremental javac进⾏增量编译2) 设置EMMA_INSTRUMENT变量的值,emma是⽤于测试代码覆盖率的库3) 包含,该makefile定义了许多辅助函数4) 包含,该makefile定义了⾼通板⼦的⼀些辅助函数及宏5) 包含,该makefile定义了优化dex代码的⼀些宏6) 检测编译⽬标⾥是否有user,userdebug,eng,如果有则告诉⽤户放置在buildspec.mk或者使⽤lunch设置,检测TARGET_BUILD_VARIANT变量,看是否有效7) 包含, PDK主要是能提⾼现有设备升级能⼒,帮助设备制造商能更快的适配新版本的android2.3 根据TARGET_BUILD_VARIANT建⽴配置1) 如果编译⽬标⾥有sdk,win_sdk或者sdk_addon,那么设置is_sdk_build为true2) 如果定义了HAVE_SELINUX,那么编译时为build prop添加属性ro.build.selinux=13) 如果TARGET_BUILD_VARIANT是user或者userdebug,那么tags_to_install += debug 如果⽤户未定义DISABLE_DEXPREOPT为true,并且是user模式,那么将设置WITH_DEXPREOPT := true,该选项将开启apk的预优化,即将apk分成odex代码⽂件和apk资源⽂件4) 判断enable_target_debugging变量,默认是true,当build_variant是user时,则它是false。
详解Android源码的编译本次编译过程主要参考官方文档(/download)和网上相关资料(如/liaoshengjiong/archive/2009/03/04/3957749.aspx)编译环境:Ubuntu8.101、安装一些环境sudo apt-get install build-essentialsudo apt-get install makesudo apt-get install gccsudo apt-get install g++sudo apt-get install libc6-devsudo apt-get install patchsudo apt-get install texinfosudo apt-get install libncurses-devsudo apt-get install git-core gnupgsudo apt-get install flex bison gperf libsdl-dev libesd0-dev libwxgtk2.6-dev build-essential zip curlsudo apt-get install ncurses-devsudo apt-get install zlib1g-devsudo apt-get install valgrindsudo apt-get install python2.5安装java环境sudo apt-get install sun-java6-jre sun-java6-plugin sun-java6-fonts sun-java6-jdk注:官方文档说如果用sun-java6-jdk可出问题,得要用sun-java5-jdk。
经测试发现,如果仅仅make(make不包括make sdk),用sun-java6-jdk是没有问题的。
而make sdk,就会有问题,严格来说是在make doc出问题,它需要的javadoc版本为1.5。
android.bp 编译aidl原理一、引言Android应用程序开发中,Android Interface Definition Language(aidl)是用于定义Android中的进程间通信(IPC)的重要工具。
它主要用于实现跨进程的接口调用,为Android应用程序提供了强大的通信机制。
本文将详细介绍android.bp编译aidl的原理。
二、aidl概述aidl是Android平台上的一个重要组件,它允许不同的应用或服务之间进行跨进程通信。
aidl定义了接口和实现,并生成相应的客户端和服务器代码,以实现进程间的通信。
三、编译aidl的过程1. 编写aidl文件:开发者使用文本编辑器或集成开发环境(IDE)编写aidl文件,该文件描述了需要跨进程调用的接口和实现。
aidl文件使用特殊的XML格式编写,包含了接口名、方法名、参数等信息。
2. 编译aidl:开发者使用构建系统(如Gradle)编译aidl文件。
在编译过程中,aidl工具将解析aidl文件,生成相应的Java接口和实现类。
这些生成的代码将被添加到相应的包中,并在构建过程中被包含到最终的APK文件中。
3. AIDL文件的位置:生成的Java代码通常被放置在`gen`目录下,开发者可以通过在代码中添加`import`语句来使用这些生成的代码。
四、原理分析android.bp编译aidl的原理主要基于以下几个步骤:1. 解析aidl文件:aidl工具首先解析aidl文件,生成相应的Java接口和实现类。
这些类包含了跨进程调用的接口和实现方法。
2. 生成Java代码:生成的Java代码包含了跨进程调用的接口和实现方法,以及必要的序列化和反序列化代码。
这些代码将被添加到相应的包中,并在构建过程中被包含到最终的APK文件中。
3. 添加到构建过程中:在构建过程中,生成的Java代码将被添加到相应的包中,并在编译过程中被编译为字节码。
这些字节码将被打包到最终的APK文件中,供应用程序使用。
androidmanifest 编译AndroidManifest是Android应用程序中的一个重要文件,描述了应用的整体结构和配置信息,包括应用程序组件、权限要求和硬件要求等。
它是一个XML文件,位于应用的根目录下。
首先,AndroidManifest文件中定义了应用的主要组件,包括活动(Activity)、服务(Service)、广播接收器(Broadcast Receiver)和内容提供者(Content Provider)。
这些组件描述了应用程序的各个界面和功能,并且定义了它们之间的关系和交互方式。
例如,通过在AndroidManifest文件中定义一个活动(Activity),可以告诉Android系统如何启动并显示该活动的界面。
活动的定义包括活动的名称、图标、主题、启动模式、标志和意图过滤器等信息。
此外,还可以在AndroidManifest文件中定义活动之间的导航关系,如将某个活动设置为主活动(即应用启动时默认显示的界面)或将某个活动设置为其他活动的父活动。
除了组件的定义,AndroidManifest文件还定义了应用程序对权限的要求。
权限是Android系统用于控制应用程序能否访问特定资源或执行特定操作的机制。
例如,应用程序如果需要访问互联网,就需要声明网络访问权限;如果需要读写外部存储器,就需要声明存储空间访问权限。
权限的定义使得用户可以在安装应用程序时了解到它可能对隐私和资源的使用情况,从而做出明智的决策。
另外,AndroidManifest文件还包含了应用程序对硬件要求的描述。
这些硬件要求可以作为应用市场在用户下载和安装应用时的筛选条件,从而保证应用程序在用户设备上的兼容性和正常运行。
例如,如果应用程序需要使用摄像头功能,就需要在AndroidManifest文件中声明相机硬件的要求。
此外,AndroidManifest文件还包含了一些其他的配置信息,如应用程序的名称、图标、版本号、应用程序的最低运行要求(包括最低API级别和目标API级别)等。
android源码编译问题,解决Android源码编译错误的问题如下所⽰:Building with Jack: out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/with-local/classes.dexFAILED: /bin/bash out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/with-local/classes.dex.rspOut of memory error (version 1.2-a26 'Carnac' (291201 fcd657165d2fd6dcf1bf4c3002c9c8f75383e815 by android-jack-team@)).GC overhead limit exceeded.Try increasing heap size with java option '-Xmx'.Warning: This may have produced partial or corrupted output.ninja: build stopped: subcommand failed.在⽂件/prebuilts/sdk/tools/jack-admin中修正-Xmx参数。
1、修改变量JACK_SERVER_VM_ARGUMENTS,添加参数 -Xmx2048MJACK_SERVER_VM_ARGUMENTS="${JACK_SERVER_VM_ARGUMENTS:=-Dfile.encoding=UTF-8 -XX:+TieredCompilation -mx2048M}"2、进⼀步修正重启参数(该步骤可以省略)找到jack-admin中的start-server语句,直接在⾥⾯增加:start-server)isServerRunningRUNNING=$?if [ "$RUNNING" = 0 ]; thenecho "Server is already running"elseecho "before to start-server,JACK_SERVER_VM_ARGUMENTS=" $JACK_SERVER_VM_ARGUMENTSJACK_SERVER_COMMAND="java -Djava.io.tmpdir=$TMPDIR $JACK_SERVER_VM_ARGUMENTS-Xmx2048M -cp $LAUNCHER_JAR $LAUNCHER_NAME"echo "Launching Jack server" $JACK_SERVER_COMMAND(补充知识:编译android 7.0 出现Try increasing heap size with java option '-Xmx'错误解决⽅案出现这个错误是由于电脑内存不⾜,在命令⾏分别执⾏以下三条语句,然后继续编译export JACK_SERVER_VM_ARGUMENTS="-Dfile.encoding=UTF-8 -XX:+TieredCompilation -Xmx4g"./prebuilts/sdk/tools/jack-admin kill-server./prebuilts/sdk/tools/jack-admin start-server以上这篇解决Android 源码编译错误的问题就是⼩编分享给⼤家的全部内容了,希望能给⼤家⼀个参考,也希望⼤家多多⽀持。
fyne 编译android原理Fyne 编译 Android 原理介绍Fyne 是一个用于创建跨平台用户界面的 Go 语言框架。
它支持在多个操作系统和设备上构建应用程序,其中包括 Android。
本文将详细介绍 Fyne 编译 Android 的原理。
准备工作在开始编译 Android 应用之前,需要先进行一些准备工作:1.安装 Go 编程语言,并设置好相应的环境变量。
2.安装 Android Studio,并确保已准备好 Android 开发环境。
3.安装 Java JDK,并设置好相应的环境变量。
创建 Fyne 项目首先,创建一个新的 Fyne 项目。
可以使用以下命令:$ go get /fyne/v2/cmd/fyne$ fyne init projectname这将创建一个名为projectname的新目录,并在其中初始化一个 Fyne 项目。
配置 Android 编译环境下一步是配置 Android 编译环境,使得我们可以在 Android 设备上运行应用程序。
在项目目录中,创建一个名为 `` 的文件,并添加以下内容:#!/bin/bash# Set necessary environment variablesexport GOPATH=$(go env GOPATH)export ANDROID_HOME=/path/to/android/sdkexport PATH=$PATH:$ANDROID_HOME/tools:$ANDROID_HOME/plat form-tools# Build and run the Fyne app on Androidcd $GOPATH/src/projectnamefyne package -os android -appID -icongo buildfyne package -os android -appID -icon请确保将/path/to/android/sdk替换为正确的 Android SDK 路径。
Android源代码编译命令m/mm/mmm/make分析标签:Androidmmmmmmmake2014-03-10 00:57 44863人阅读评论(19) 收藏举报分类:Android(147)版权声明:本文为博主原创文章,未经博主允许不得转载。
在前文中,我们分析了Android编译环境的初始化过程。
Android编译环境初始化完成后,我们就可以用m/mm/mmm/make命令编译源代码了。
当然,这要求每一个模块都有一个Android.mk文件。
Android.mk实际上是一个Makefile脚本,用来描述模块编译信息。
Android编译系统通过整合Android.mk文件完成编译过程。
本文就对Android源代码的编译过程进行详细分析。
老罗的新浪微博:/shengyangluo,欢迎关注!从前面Android编译系统环境初始化过程分析这篇文章可以知道,lunch命令其实是定义在build/envsetup.sh文件中的函数lunch提供的。
与lunch命令一样,m、mm和mmm 命令也分别是由定义在build/envsetup.sh文件中的函数m、mm和mmm提供的,而这三个函数又都是通过make命令来对源代码进行编译的。
事实上,命令m就是对make命令的简单封装,并且是用来对整个Android源代码进行编译,而命令mm和mmm都是通过make命令来对Android源码中的指定模块进行编译。
接下来我们就先分别介绍一下函数m、mm和mmm的实现,然后进一步分析它们是如何通过make命令来编译代码的。
函数m的实现如下所示:[plain]view plaincopy1.function m()2.{3. T=$(gettop)4. if [ "$T" ]; then5. make -C $T $@6. else7. echo "Couldn't locate the top of the tree. Try setting TOP."8. fi9.}函数m调用函数gettop得到的是Android源代码根目录T。
在执行make命令的时候,先通过-C选项指定工作目录为T,即Android源代码根目录,接着又将执行命令m指定的参数$@作为命令make的参数。
从这里就可以看出,命令m实际上就是对命令make的简单封装。
函数mm的实现如下所示:[plain]view plaincopy1.function mm()2.{3. # If we're sitting in the root of the build tree, just do a4. # normal make.5. if [ -f build/core/envsetup.mk -a -f Makefile ]; then6. make $@7. else8. # Find the closest Android.mk file.9. T=$(gettop)10. local M=$(findmakefile)11. # Remove the path to top as the makefilepath needs to be relative12. local M=`echo $M|sed 's:'$T'/::'`13. if [ ! "$T" ]; then14. echo "Couldn't locate the top of the tree. Try setting TOP."15. elif [ ! "$M" ]; then16. echo "Couldn't locate a makefile from the current directory."17. else18. ONE_SHOT_MAKEFILE=$M make -C $T all_modules $@19. fi20. fi21.}函数mm首先是判断当前目录是否就是Android源码根目录,即当前目录下是否存在一个build/core/envsetup.mk文件和一个Makefile文件。
如果是的话,就将命令mm当作是一个普通的make命令来执行。
否则的话,就调用函数findmakefile从当前目录开始一直往上寻找是否存在一个Android.mk文件。
如果在寻找的过程中,发现了一个Android.mk 文件,那么就获得它的绝对路径,并且停止上述寻找过程。
由于接下来执行make命令时,我们需要指定的是要编译的Android.mk文件的相对于Android源码根目录路径,因此函数mm需要将刚才找到的Android.mk绝对文件路径M中与Android源码根目录T相同的那部分路径去掉。
这是通过sed命令来实现的,也就是将字符串M前面与字符串T相同的子串删掉。
最后,将找到的Android.mk文件的相对路径设置给环境变量ONE_SHOT_MAKE,表示接下来要对它进行编译。
另外,函数mm还将make命令目标设置为all_modules。
这是什么意思呢?我们知道,一个Android.mk文件同时可以定义多个模块,因此,all_modules 就表示要对前面指定的Android.mk文件中定义的所有模块进行编译。
函数mmm的实现如下所示:[plain]view plaincopy1.function mmm()2.{3. T=$(gettop)4. if [ "$T" ]; then5. local MAKEFILE=6. local MODULES=7. local ARGS=8. local DIR TO_CHOP9. local DASH_ARGS=$(echo "$@" | awk -v RS=" " -v ORS=" " '/^-.*$/')10. local DIRS=$(echo "$@" | awk -v RS=" " -v ORS=" " '/^[^-].*$/')11. for DIR in $DIRS ; do12. MODULES=`echo $DIR | sed -n -e 's/.*:.∗$/\1/p' | sed 's/,/ /'`13. if [ "$MODULES" = "" ]; then14. MODULES=all_modules15. fi16. DIR=`echo $DIR | sed -e 's/:.*//' -e 's:/$::'`17. if [ -f $DIR/Android.mk ]; then18. TO_CHOP=`(cd -P -- $T && pwd -P) | wc -c | tr -d ' '`19. TO_CHOP=`expr $TO_CHOP + 1`20. START=`PWD= /bin/pwd`21. MFILE=`echo $START | cut -c${TO_CHOP}-`22. if [ "$MFILE" = "" ] ; then23. MFILE=$DIR/Android.mk24. else25. MFILE=$MFILE/$DIR/Android.mk26. fi27. MAKEFILE="$MAKEFILE $MFILE"28. else29. if [ "$DIR" = snod ]; then30. ARGS="$ARGS snod"31. elif [ "$DIR" = showcommands ]; then32. ARGS="$ARGS showcommands"33. elif [ "$DIR" = dist ]; then34. ARGS="$ARGS dist"35. elif [ "$DIR" = incrementaljavac ]; then36. ARGS="$ARGS incrementaljavac"37. else38. echo "No Android.mk in $DIR."39. return 140. fi41. fi42. done43. ONE_SHOT_MAKEFILE="$MAKEFILE" make -C $T $DASH_ARGS $MODULES $ARGS44. else45. echo "Couldn't locate the top of the tree. Try setting TOP."46. fi47.}函数mmm的实现就稍微复杂一点,我们详细解释一下。
首先,命令mmm可以这样执行:[plain]view plaincopy1.$ mmm <dir-1> <dir-2> ... <dir-N>[:module-1,module-2,...,module-M]其中,dir-1、dir-2、dir-N都是包含有Android.mk文件的目录。
在最后一个目录dir-N 的后面可以带一个冒号,冒号后面可以通过逗号分隔一系列的模块名称module-1、module-2和module-M,用来表示要编译前面指定的Android.mk中的哪些模块。
知道了命令mmm的使用方法之后,我们就可以分析函数mmm的执行逻辑了:1. 调用函数gettop获得Android源码根目录。
2. 通过命令awk将执行命令mmm时指定的选项参数提取出来,也就是将以横线“-”开头的字符串提取出来,并且保存在变量DASH_ARGS中。
3. 通过命令awk将执行命令mmm时指定的非选项参数提取出来,也就是将非以横线“-”开头的字符串提取出来,并且保存在变量DIRS中。
这里得到的实际上就是跟在命令mmm后面的字符串“<dir-1> <dir-2> ... <dir-N>[:module-1,module-2,...,module-M]”。