Android系统编译小小理解
Android系统编译
简介
Android系统源码将Makefile
划分成一个个Makefile
片段,通过Makefile
的include
指令将这些片段组装成一个Makefile,因此Android.mk
编译脚本其实是整个Android编译系统的一个Makefile
片段,编译时编译系统会遍历每个目录下的Android.mk
文件。
搭建编译环境
sudo apt-get install u-boot-tools
sudo apt-get install git-core gnupg flex bison gperf build-essential zip curl zlib1g-dev gcc-multilib g++-multilib libc6-dev-i386 lib32ncurses5-dev lib32ncurses5 lib32z1 x11proto-core-dev libx11-dev lib32z1-dev
sudo apt-get install build-essential subversion git-core libncurses5-dev zlib1g-dev gawk flex quilt libssl-dev xsltproc libxml-parser-perl mercurial bzr ecj cvs unzip lib32z1 lib32ncurses5 lib32bz2-1.0
sudo apt-get install libxml2-utils
sudo apt-get install subversion openssh-client openssh-server samba git-core git-gui vim
sudo add-apt-repository ppa:git-core/ppa
sudo apt-get update
sudo apt-get upgrade
sudo apt-get install libswitch-perl
sudo apt-get install rar unrar
sudo apt-get install gawk
sudo apt-get install bc
系统编译命令
- source build/envsetup.sh
- 此命令会将vendor和device目录中厂商指定的envsetup.sh加载到当前shell中,获取厂商提供的产品配置信息。脚本文件build/envsetup.sh提供编译所需的重要命令用于编译Android源码。build/envsetup.sh脚本中的函数
add_lunch_combo会被多次调用,用来添加Android编译选项。
- lunch
- 用于初始化编译环境,指定目标产品类型和编译类型。第二件事是通过make命令执行build/core/config.mk脚本,并且加载另一个脚本build/core/dumpvar.mk来打印出当前的编译环境配置信息。
- m
- 相当于make命令
- mm和mmm
- 用于模块编译
- 查看编译命令
-
mmm ./ showcommands
常见小知识点
Android编译版本
- user :权限受限;适用于生产环境
- userdebug: 与“user”类似,但具有 root 权限和可调试性;是进行调试时的首选编译类型
- eng : 具有额外调试工具的开发配置
Android.mk文件
Android.mk是编译系统解析一次或多次的微小Makefile片段。Android.mk的语法支持将源文件分组为模块。模块是静态库、动态库或独立的可执行程序。可在每个Android.mk中定义一个或多个模块,也可在多个模块中使用同一个源文件。
LOCAL_PATH := $(call my-dir)
//表示源文件在开发树中的位置,my-dir是编译系统提供的宏函数,返回当前目录的路径
LOCAL_MODULE:= service_manager_my
// 指定生成的模块名,不能包含空格
include $(BUILD_EXECUTABLE)
// BUILD_EXECUTABLE表示我们要编译的是可执行程序
include $(CLEAR_VARS)
// 清空编译环境的变量,会清除许多LOCAL_XXX变量
LOCAL_CFLAGS += -DBINDER_IPC_32BIT=1
LOCAL_CFLAGS += -Wno-unused-parameter
LOCAL_CFLAGS += -Wno-unused-variable
LOCAL_CFLAGS += -Wno-unused-function
LOCAL_CFLAGS += -Wincompatible-pointer-types
// 指定忽略一些未使用的变量或函数
LOCAL_SRC_FILES:= test_server.c binder.c
// 指定源文件,包含要编译到模块中的C/C++源文件列表
LOCAL_C_INCLUDES += system/core/libcutils/include/
// 指定头文件路径,指定的路径是相对于NDK编译系统根目录的路径
LOCAL_MODULE_TAGS := user eng optional test
// 各项具体说明如下:
1、user:只有在user版本时该模块才被编译进去;
2、eng:只有在eng版本时该模块才被编译进去;
3、test:只有在tests版本时该模块才被编译进去;
4、optional:在所有版本中都编译该模块进去。
用于设置该app是否要安装到priv-app下
LOCAL_PRIVILEGED_MODULE = true
不开启odex,不生成APP的dex文件,不进行预先优化,节省空间,可编译出完整的apk
LOCAL_DEX_PREOPT = false
如果预置的APK,要覆盖原生apk,则需在android.mk中添加以下选项,此变量可以使其他的模块不加入编译。
LOCAL_OVERRIDES_PACKAGES
-
特别注意不要在Android.mk文件中添加无谓的空格,编译报错找不到原因很痛苦的!
编译系统中的变量和宏
编译系统提供了许多可在Android.mk中使用的变量。NDK编译系统保留了下列变量名称:
以 LOCAL_ 开头的名称,例如 LOCAL_MODULE。
以 PRIVATE_、NDK_ 或 APP 开头的名称。编译系统在内部使用这些变量名。
小写名称,例如 my-dir。编译系统也是在内部使用这些变量名。
指向用于指定预编译共享库的编译脚本。与 BUILD_SHARED_LIBRARY
和BUILD_STATIC_LIBRARY
的情况不同,这里的 LOCAL_SRC_FILES
值不能是源文件,而必须是指向预编译共享库的一个路径,例如 foo/libfoo.so。使用此变量的语法为:
include $(PREBUILT_SHARED_LIBRARY)
指定生成的模块名称。例如,如果LOCAL_MODULE
的名称为 foo,可以强制系统将它生成的文件命名为 libnewfoo。
LOCAL_MODULE := foo
LOCAL_MODULE_FILENAME := libnewfoo
native layer
本地应用程序指的是可以直接运行在操作系统上,并且处理器直接执行机器码的程序。比如windows上的各种*.exe的程序,而linux上的是各种bin程序。
在Android上,OS是linux,因此各种bin程序就是所谓natvie application了,比如**/system/bin**目录下的所有文件。
这些应用程序都是由GCC(c/c++)编译生成。在Android软件架构里,这些应用程序组成了native layer。
编译碰到的问题
安装lib32bz2-1.0出问题
解决方法:
sudo dpkg --add-architecture i386
sudo apt-get update
sudo apt-get install libbz2-1.0:i386
add-apt-repository命令不存在
解决方法:
apt-get install software-properties-common
Jack server installation not found
解决方法:在prebuilts/sdk/tools 目录下执行以下命令
./jack-admin install-server jack-launcher.jar jack-server-4.11.ALPHA.jar
- Jack编译器工具是一套新的Android编译工具用来将Java代码转换为Android dex字节代码。Jack服务器无法启动原因:$HOME目录下无.jack文件
subcommand failed问题
编译Android系统时经常都是使用多线程编译,有时甚至用到了58个线程,启用太多线程的话,由于Makefile的目标与文件或其它目标的依赖关系,会导致编译目标时需要的源还未编译完成,会提示ninja: build stopped: subcommand failed
,这时就需要减少线程数来编译。
- 在切换编译目标时需要执行make installclean,用以清除之前编译生成的文件。
内核目录下存在.config文件导致编译错误
Using /mnt/kernel-4.4 as source for kernel
/mnt/kernel-4.4 is not clean, please run ‘make mrproper’
in the ‘/mnt/kernel-4.4’ directory.
/mnt/kernel-4.4/Makefile:1033: recipe for target ‘prepare3’ failed
make[1]: *** [prepare3] Error 1
make[1]: *** Waiting for unfinished jobs…
[DCT_INFO]: DWS file path is /mnt/kernel-4.4/drivers/misc/mediatek/dws/m
t6771/8p1_64_bsp.dws
[DCT_INFO]: Gen files path is /mnt/out/target/product/8p1_64_bsp/ob
j/KERNEL_OBJ/arch/arm64/boot/dts/8p1_64_bsp
[DCT_INFO]: Log files path is /mnt/out/target/product/8p1_64_bsp/ob
j/KERNEL_OBJ/arch/arm64/boot/dts/8p1_64_bsp
[DCT_INFO]: Parameter is cust_dtsi
[DCT_INFO]: chip id: MT6771
[DCT_INFO]: Chip ID : MT6771
[DCT_INFO]: Project Info: 8p1_64_bsp
[DCT_INFO]: Start to generate cust_dtsi file…
[DCT_INFO]: Generate cust_dtsi file successfully!
make[1]: *** wait: No child processes. Stop.
Makefile:152: recipe for target ‘sub-make’ failed
make: *** [sub-make] Error 2
make: Leaving directory ‘/mnt/kernel-4.4’
在kernel-4.4目录下搜索“is not clean”会发现导致错误的原因是当kernel-4.4目录下存在.config文件或kernel-4.4/include/目录下存在config目录时,就会报错,因此删除掉这两个对象就可以了。
因为之前有去修改过kernel-4.4目录中的文件,并执行过make menuconfig命令,这在Android系统源码中是不可以的。
输出目录中的重要目录
-
Android系统预装的APP所在目录为:
out/target/product/$(PROJECT_NAME))/system/app/
-
C编译的可执行文件所在目录为:
out/target/product/$(PROJECT_NAME))/system/bin/
-
动态链接库所在目录为:
out/target/product/$(PROJECT_NAME))/system/lib/
-
硬件抽象层接口文件所在目录:
out/target/product/$(PROJECT_NAME))/system/lib/hw/
编译模块
- init模块
mmm system/core/init/
编译完成:
[100% 201/201] Copy: out/target/product/88p1_64_bsp/testcases/init_tests/arm/init_tests
- 单独编译framework的services模块
先在framework/base目录下执行
mmm core/res/
生成framework-res.apk
如果framework/base/services/下也有修改,也要编译:
mmm framework/base/services
然后再单独编译framework
mmm frameworks/base
相关的输出文件在输出目录下的system/framework中,包含:
services.jar
framework-res.apk
framework.jar
可利用adb命令将这些文件push到设备端的system/framework目录下。若需测试这两个新模块,需先杀掉所有使用该模块的进程,进程重启后会重新加载模块。
系统服务被杀掉后一般都会自动重启(由init控制)
系统属性相关目标和依赖文件的定义
- build/make/core/Makefile
intermediate_system_build_prop := $(call intermediates-dir-for,ETC,
system_build_prop)/build.prop
猜想intermediate_system_build_prop 展开即为:
out/target/product/8p1_64_bsp/obj/ETC/system_build_prop_intermediates/build.prop
BUILDINFO_SH := build/make/tools/buildinfo.sh
INTERNAL_BUILD_ID_MAKEFILE := $(wildcard $(BUILD_SYSTEM)/build_id.mk)
即为:build/make/core/build_id.mk
system_prop_file := $(wildcard $(TARGET_DEVICE_DIR)/system.prop)
展开即为:device/mediateksample/8p1_64_bsp/system.prop
INSTALLED_ANDROID_INFO_TXT_TARGET := $(PRODUCT_OUT)/android-info.txt
展开即为:out/target/product/8p1_64_bsp/android-info.txt
- 最后将包括build/make/tools/buildinfo.sh中设置的属性一并写入build.prop文件中,并在out目录下生成build.prop文件。位于输出目录的system目录下。
build.prop记录的都是系统设置,是个属性文件。init进程将会加载/system/build.prop中定义的属性。 - 系统源码中的property_service.cpp源文件中的
load_properties_from_file
函数就是用于init进程加载存储属性值的文件。
bool load_properties_from_file(const char* filename, const char* filter) {
Timer t;
auto file_contents = ReadFile(filename);
if (!file_contents) {
PLOG(WARNING) << "Couldn't load property file '" << filename
<< "': " << file_contents.error();
return false;
}
file_contents->push_back('\n');
LoadProperties(file_contents->data(), filter, filename);
LOG(VERBOSE) << "(Loading properties from " << filename << " took " << t << ".)";
return true;
}
编译时确定系统属性值
在device.mk中添加,例如:PRODUCT_PROPERTY_OVERRIDES += persist.sys.Notification=1
打包新添加的模块至system.img
在device.mk中的PRODUCT_PACKAGES
选项中加入该模块如果将新添加的PRODUCT_PACKAGES
选项放入新创建的Makefile中,需要在build/target/product/core.mk末尾追加一行
例如在build/target/product目录下创建一个新的Makefile文件mytest.mk
build/target/product/core.mk末尾追加:$(call inherit-product, $(SRC_TARGET_DIR)/product/mytest.mk)
本文地址:https://blog.csdn.net/weixin_41388144/article/details/109393921
上一篇: 优秀网管心得三则
推荐阅读
-
Mac系统下如何反编译android apk
-
Android 7.1.1系统源码下载、编译、刷机-Nexus 6实战
-
android根据项目把文件编译到文件系统中操作方法
-
Android系统编译小小理解
-
如何将ffmpeg x264的动态库编译入Android7.1系统源码(详细步骤)
-
【北京讲座】Android系统Framework层源码分析 android深入理解Android源码分析
-
Android 7.0系统源码中如何编译android studio工程
-
NanoPi K2 (Amlogic S905) Ubuntu 16.04 编译Android 5.1系统源码
-
Android编译系统详解(一)——build/envsetup.sh(转)
-
一文理解Android系统中强指针的实现