Android编译命令

本文详细介绍了Android源码的编译过程,包括source envsetup.sh、lunch和make-j12等命令的用途。讲解了编译指令如m、mm、mmm等的使用,以及代码搜索、导航和信息查询的快捷方式。同时,阐述了Android编译系统的构成,如Makefile分类、编译产物和Android.mk解析。此外,还提到了编译配置的选择方法以及Ninja编译的优化技巧,帮助开发者提高编译效率。
摘要由CSDN通过智能技术生成

转载文章:http://gityuan.com/2016/03/19/android-build/

学习Android源码的第一步就是编译,熟练应用一些编译命令能提高你的效率,本文主要转载上文,并在第四部分进行一些扩展

工欲善其事,必先利其器,对于想要深入学习Android源码,必须先掌握Android编译命令.

一、引言

关于Android Build系统,这个话题很早就打算整理下,迟迟没有下笔,决定跟大家分享下。先看下面几条指令,相信编译过Android源码的人都再熟悉不过的。

source setenv.sh
lunch
make -j12

记得最初刚接触Android时,同事告诉我用上面的指令就可以编译Android源码,指令虽短但过几天就记不全或者忘记顺序,每次编译时还需要看看自己的云笔记,冰冷的指令总是难以让我记忆。后来我决定认真研究下这个指令的含义。知其然还需知其所以然,这样能更深层次的理解并记忆,才能与自身的知识体系建立强连接,或许还有意外收获,果然如此,接下来跟大家分享一下在研究上述几条指令含义的过程中,深入了解到的Android Build(编译)系统。

二、编译命令

准备好编译环境后,编译Android源码的第一步是 source build/envsetup.sh,其中source命令就是用于运行shell脚本命令,功能等价于”.”,因此该命令也等价于. build/envsetup.sh。在文件envsetup.sh声明了当前会话终端可用的命令,这里需要注意的是当前会话终端,也就意味着每次新打开一个终端都必须再一次执行这些指令。起初并不理解为什么新开的终端不能直接执行make指令,到这里总算明白了。

接下来,解释一下本文开头的引用的命令:

source setenv.sh  //初始化编译环境,包括后面的lunch和make指令
lunch  //指定此次编译的目标设备以及编译类型
make  -j12 //开始编译,默认为编译整个系统,其中-j12代表的是编译的job数量为12。

所有的编译命令都在envsetup.sh文件能找到相对应的function,比如上述的命令lunchmake,在文件一定能找到

function lunch(){
    ...
}

function make(){
    ...
}

source envsetup.sh,需要cd到setenv.sh文件所在路径执行,路径可能在build/envsetup.sh,或者integrate/envsetup.sh,再或者不排除有些厂商会封装自己的.sh脚本,但核心思路是一致的。

具体实现这里就不展开说明,下面精炼地总结了一下各个指令用法和功效。

2.1 代码编译
编译指令解释
m在源码树的根目录执行编译
mm编译当前路径下所有模块,但不包含依赖
mmm [module_path]编译指定路径下所有模块,但不包含依赖
mma编译当前路径下所有模块,且包含依赖
mmma [module_path]编译指定路径下所有模块,且包含依赖
make [module_name]无参数,则表示编译整个Android代码

下面列举部分模块的编译指令:

模块make命令mmm命令
initmake initmmm system/core/init
zygotemake app_processmmm frameworks/base/cmds/app_process
system_servermake servicesmmm frameworks/base/services
java frameworkmake frameworkmmm frameworks/base
framework资源make framework-resmmm frameworks/base/core/res
jni frameworkmake libandroid_runtimemmm frameworks/base/core/jni
bindermake libbindermmm frameworks/native/libs/binder

上述mmm命令同样适用于mm/mma/mmma,编译系统采用的是增量编译,只会编译发生变化的目标文件。当需要重新编译所有的相关模块,则需要编译命令后增加参数-B,比如make -B [module_name],或者 mm -B [module_path]。

Tips:

  • 对于m、mm、mmm、mma、mmma这些命令的实现都是通过make方式来完成的。
  • mmm/mm编译的效率很高,而make/mma/mmma编译较缓慢;
  • make/mma/mmma编译时会把所有的依赖模块一同编译,但mmm/mm不会;
  • 建议:首次编译时采用make/mma/mmma编译;当依赖模块已经编译过的情况,则使用mmm/mm编译。

2.2 代码搜索

搜索指令解释
cgrep所有**C/C++**文件执行搜索操作
jgrep所有Java文件执行搜索操作
ggrep所有Gradle文件执行搜索操作
mangrep [keyword]所有AndroidManifest.xml文件执行搜索操作
mgrep [keyword]所有Android.mk文件执行搜索操作
sepgrep [keyword]所有sepolicy文件执行搜索操作
resgrep [keyword]所有本地res/*.xml文件执行搜索操作
sgrep [keyword]所有资源文件执行搜索操作

上述指令用法最终实现方式都是基于grep指令,各个指令用法格式:

xgrep [keyword]  //x代表的是上表的搜索指令

例如,搜索所有AndroidManifest.xml文件中的launcher关键字所在文件的具体位置,指令

mangrep launcher

再如,搜索所有Java代码中包含zygote所在文件

jgrep zygote

又如,搜索所有system_app的selinux权限信息

sepgrep system_app

Tips: Android源码非常庞大,直接采用grep来搜索代码,不仅方法笨拙、浪费时间,而且搜索出很多无意义的混淆结果。根据具体需求,来选择合适的代码搜索指令,能节省代码搜索时间,提高搜索结果的精准度,方便定位目标代码。

2.3 导航指令

导航指令解释
croot切换至Android根目录
cproj切换至工程的根目录
godir [filename]跳转到包含某个文件的目录

Tips: 当每次修改完某个文件后需要编译时,执行cproj后会跳转到当前模块的根目录,也就是Android.mk文件所在目录,然后再执行mm指令,即可编译目标模块;当进入源码层级很深后,需要返回到根目录,使用croot一条指令完成;另外cd - 指令可用于快速切换至上次目录。

2.4 信息查询

查询指令解释
hmm查询所有的指令help信息
findmakefile查询当前目录所在工程的Android.mk文件路径
print_lunch_menu查询lunch可选的product
printconfig查询各项编译变量值
gettop查询Android源码的根目录
gettargetarch获取TARGET_ARCH值

2.5 其他指令

上述只是列举比较常用的指令,还有其他指令,而且不同的build编译系统,支持的指令可能会存在一些差异,当忘记这些编译指令,可以通过执行hmm,查询指令的帮助信息。

最后再列举两个比较常用的指令:

  • make clean:执行清理操作,等价于 rm -rf out/
  • make update-api:更新API,在framework API改动后需执行该指令,Api记录在目录frameworks/base/api

三、编译系统

Android 编译系统是Android源码的一部分,用于编译Android系统,Android SDK以及相关文档。该编译系统是由Make文件、Shell以及Python脚本共同组成,其中最为重要的便是Make文件。关于编译系统可参考 理解 Android Build 系统

3.1 Makefile分类

整个Build系统的Make文件分为三大类:

  • **系统核心的Make文件:**定义了Build系统的框架,文件全部位于路径/build/core,其他Make文件都是基于该框架编写的;
  • **针对产品的Make文件:**定义了具体某个型号手机的Make文件,文件路径位于/device,该目录下往往又以公司名和产品名划分两个子级目录,比如/device/qcom/msm8916
  • **针对模块的Make文件:**整个系统分为各个独立的模块,每个模块都一个专门的Make文件,名称统一为”Android.mk”,该文件定义了当前模块的编译方式。Build系统会扫描整个源码树中名为”Android.mk”的问题,并执行相应模块的编译工作。

3.2 编译产物

经过make编译后的产物,都位于/out目录,该目录下主要关注下面几个目录:

  • /out/host:Android开发工具的产物,包含SDK各种工具,比如adb,dex2oat,aapt等。
  • /out/target/common:通用的一些编译产物,包含Java应用代码和Java库;
  • /out/target/product/[product_name]:针对特定设备的编译产物以及平台相关C/C++代码和二进制文件;

在/out/target/product/[product_name]目录下,有几个重量级的镜像文件:

  • system.img:挂载为根分区,主要包含Android OS的系统文件;
  • ramdisk.img:主要包含init.rc文件和配置文件等;
  • userdata.img:被挂载在/data,主要包含用户以及应用程序相关的数据;

当然还有boot.img,reocovery.img等镜像文件,这里就不介绍了。

3.3 Android.mk解析

在源码树中每一个模块的所有文件通常都相应有一个自己的文件夹,在该模块的根目录下有一个名称为“Android.mk” 的文件。编译系统正是以模块为单位进行编译,每个模块都有唯一的模块名,一个模块可以有依赖多个其他模块,模块间的依赖关系就是通过模块名来引用的。也就是说当模块需要依赖一个jar包或者apk时,必须先将jar包或apk定义为一个模块,然后再依赖相应的模块。

对于Android.mk文件,通常都是以下面两行

LOCAL_PATH := $(call my-dir)  //设置当编译路径为当前文件夹所在路径
include $(CLEAR_VARS)  //清空编译环境的变量(由其他模块设置过的变量)

为方便模块编译,编译系统设置了很多的编译环境变量,如下:

  • LOCAL_SRC_FILES:当前模块包含的所有源码文件;
  • LOCAL_MODULE:当前模块的名称(具有唯一性);
  • LOCAL_PACKAGE_NAME:当前APK应用的名称(具有唯一性);
  • LOCAL_C_INCLUDES:C/C++所需的头文件路径;
  • LOCAL_STATIC_LIBRARIES:当前模块在静态链接时需要的库名;
  • LOCAL_SHARED_LIBRARIES:当前模块在运行时依赖的动态库名;
  • LOCAL_STATIC_JAVA_LIBRARIES:当前模块依赖的Java静态库;
  • LOCAL_JAVA_LIBRARIES:当前模块依赖的Java共享库;
  • LOCAL_CERTIFICATE:签署当前应用的证书名称,比如platform。
  • LOCAL_MODULE_TAGS:当前模块所包含的标签,可以包含多标签,可能值为debgu,eng,user,development或optional(默认值)

针对这些环境变量,编译系统还定义了一些便捷函数,如下:

  • $(call my-dir):获取当前文件夹路径;
  • $(call all-java-files-under, ):获取指定目录下的所有Java文件;
  • $(call all-c-files-under, ):获取指定目录下的所有C文件;
  • $(call all-Iaidl-files-under, ) :获取指定目录下的所有AIDL文件;
  • $(call all-makefiles-under, ):获取指定目录下的所有Make文件;

示例:

  LOCAL_PATH := $(call my-dir)
  include $(CLEAR_VARS)

  # 获取所有子目录中的Java文件
  LOCAL_SRC_FILES := $(call all-subdir-java-files)

  # 当前模块依赖的动态Java库名称
  LOCAL_JAVA_LIBRARIES := com.gityuan.lib

  # 当前模块的名称
  LOCAL_MODULE := demo

  # 将当前模块编译成一个静态的Java库
  include $(BUILD_STATIC_JAVA_LIBRARY)

四、扩展

4.1 指定编译配置的方法有两种方法lunch/choosecombo

指令说明
lunch一次选择编译配置
choosecombo逐步选择编译配置

也可在指令后直接跟参数,例如 lunch aosp_arm-userdebugchoosecombo 1 aosp_arm userdebug

配置完成后可使用 printconfig查看编译选项的值

============================================
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=11
TARGET_PRODUCT=aosp_arm
TARGET_BUILD_VARIANT=userdebug
TARGET_BUILD_TYPE=release
TARGET_ARCH=arm
TARGET_ARCH_VARIANT=armv7-a-neon
TARGET_CPU_VARIANT=generic
HOST_ARCH=x86_64
HOST_2ND_ARCH=x86
HOST_OS=linux
HOST_OS_EXTRA=Ubuntu-18.04
HOST_CROSS_OS=windows
HOST_CROSS_ARCH=x86
HOST_CROSS_2ND_ARCH=x86_64
HOST_BUILD_TYPE=release
BUILD_ID=...
OUT_DIR=out
PRODUCT_SOONG_NAMESPACES=device/generic/goldfish device/generic/goldfish-opengl hardware/google/camera hardware/google/camera/devices/EmulatedCamera device/generic/goldfish device/generic/goldfish-opengl packages/apps/Bluetooth
============================================

更多内容可参考google编译 Android | Android 开源项目 | Android Open Source Project

4.2 Ninja编译

从Android 7.0开始,默认使用ninja进行编译,所以在执行mmm编译单个模块时会多几个步骤,导致编译速度非常慢

  1. Soong的自举(bootstrap),编译Soong的核心组件。
  2. 收集Android.bp并生成out/soong/build.ninja文件。
  3. 收集Android.mk并生成out/build-<product>.ninjaout/combined-<product>.ninja文件。
  4. 执行Ninja文件,进行编译。这个combined-*.ninja文件,就是真正的执行入口。

当进行过一次make编译生成ninja文件后,这时我们使用ninja来编译单个文件就会非常快

prebuilts/build-tools/linux-x86/bin/ninja -f out/combined-aosp_arm64.ninja module-name

  • -f 指定ninja文件,out目录下选择对应的ninja文件

  • module-name为需要编译的目标文件,即LOCAL_MODULE

当然我们也可以使用make nothing来生成全编译的ninja文件

Tips: 当Makefile文件有改动时,需要重新生成ninja文件,不然修改将不会生效

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值