Android编译系统分析系列文章:
android编译系统分析(一)-source build/envsetup.sh与lunch
Android编译系统(二)-mm编译单个模块
android编译系统分析(三)-make
android编译系统(四)-实战:新增一个产品
Android编译系统分析(五)-system.img的生成过程


我们在完整编译android系统的时候,最终会生成几个重要的镜像文件,其中有system.img,userdata.img,ramdisk.img等。这篇文章的目的是分析system.img的生成过程。
回想下我们完整编译android系统时的动作,我们会在android源码顶级目录执行make命令,这样就会完整的编译android系统,我们没有传入任何参数(-jx等加快编译的除外),因为我们没有明确指定make的目标,所以android编译系统会执行默认的编译目标,也就是droid。因此,我们还是从droid着手,看看system.img怎么生成。
我们只关注system.img相关的部分,其他部分都忽略,因此会有如下依赖关系:

一.systemimage

# Rules that need to be present for the all targets, even
# if they don't do anything.
.PHONY: systemimage
systemimage:

sytemimage是一个伪目标,它并不会被生成。

systemimage: $(INSTALLED_SYSTEMIMAGE)

systemimage依赖于$(INSTALLED_SYSTEMIMAGE)

二.$(INSTALLED_SYSTEMIMAGE)

INSTALLED_SYSTEMIMAGE := $(PRODUCT_OUT)/system.img

INSTALLED_SYSTEMIMAGE变量的值就是system.img了,也就是说它就是我们最终要生成的目标。那么看看它的定义:

$(INSTALLED_SYSTEMIMAGE): $(BUILT_SYSTEMIMAGE) $(RECOVERY_FROM_BOOT_PATCH) | $(ACP)@echo "Install system fs image: $@"$(copy-file-to-target)$(hide) $(call assert-max-image-size,$@ $(RECOVERY_FROM_BOOT_PATCH),$(BOARD_SYSTEMIMAGE_PARTITION_SIZE))

(INSTALLEDSYSTEMIMAGE)有依赖了 (INSTALLED_SYSTEMIMAGE)有依赖了 (BUILT_SYSTEMIMAGE) 和 (RECOVERYFROMBOOTPATCH)以及 (RECOVERY_FROM_BOOT_PATCH)以及(ACP),我们目前无法知道这三个变量是什么,当然,这里的 (ACP)是一种read−only依赖,也就是说 (ACP)是一种read-only依赖,也就是说(ACP)发生改变时,编译器并不会重新生成system.img, (ACP)其实代表的是acp可执行文件,这个执行文件由acp.c文件生成,代码在build/tools/acp/目录下。因此,acp是一个生成system.img过程中使用的工具,它的改变不会使system.img立刻重新生成。虽然我们暂且不知道 (ACP)其实代表的是acp可执行文件,这个执行文件由acp.c文件生成,代码在build/tools/acp/目录下。因此,acp是一个生成system.img过程中使用的工具,它的改变不会使system.img立刻重新生成。 虽然我们暂且不知道(BUILT_SYSTEMIMAGE) 和$(RECOVERY_FROM_BOOT_PATCH)代表的是什么,但是我们可以先看看system.img的生成规则,看看生成规则是怎么使用这三个依赖来生成system.img镜像文件的的。

2.1$(copy-file-to-target)

copy-file-to-target的定义如下:

# Copy a single file from one place to another,
# preserving permissions and overwriting any existing
# file.
# We disable the "-t" option for acp cannot handle
# high resolution timestamp correctly on file systems like ext4.
# Therefore copy-file-to-target is the same as copy-file-to-new-target.
define copy-file-to-target
@mkdir -p $(dir $@)
$(hide) $(ACP) -fp $< $@
endef

结合注释,这段代码的功能是拷贝文件,并且在拷贝的过程中会保留文件的权限和覆盖已有的文件。 <代表的是第一个依赖,也就是这里的 <script type="math/tex" id="MathJax-Element-5"> <代表的是第一个依赖,也就是这里的< script>(built_systemimage),这里首先会创建 out target product xxx 目录,其中xxx是产品名,然后把 (BUILTSYSTEMIMAGE)拷贝到该目录下并命名为system.img。因此,system.img诞生。所以说它的诞生是由 (BUILT_SYSTEMIMAGE)拷贝到该目录下并命名为system.img。因此,system.img诞生。所以说它的诞生是由(BUILT_SYSTEMIMAGE)变量所代表的文件直接拷贝而来,因此,要搞清system.img的生成过程,必须搞清$(BUILT_SYSTEMIMAGE)的生成过程。

2.2assert-max-image-size

紧随其后的assert-max-image-size函数又做了什么呢?调用它的时候传入了两个参数,分别是1.system.img 2. (RECOVERYFROMBOOTPATCH), (RECOVERY_FROM_BOOT_PATCH),(BOARD_SYSTEMIMAGE_PARTITION_SIZE)
(RECOVERYFROMBOOTPATCH)是一个补丁文件:RECOVERYFROMBOOTPATCH:= (RECOVERY_FROM_BOOT_PATCH) 是一个补丁文件: RECOVERY_FROM_BOOT_PATCH := (intermediates)/recovery_from_boot.p
$(BOARD_SYSTEMIMAGE_PARTITION_SIZE)则是一个数字,不同的产品这个数字不同:
BOARD_SYSTEMIMAGE_PARTITION_SIZE := 1610612736
assert-max-image-size的定义如下:

# Like assert-max-file-size, but the second argument is a partition
# size, which we'll convert to a max image size before checking it
# against the files.
#
# $(1): The file(s) to check (often $@)
# $(2): The partition size.
define assert-max-image-size
$(if $(2), \$(call assert-max-file-size,$(1),$(call image-size-from-data-size,$(2))))
endef

image-size-from-data-size函数如下:

# Convert a partition data size (eg, as reported in /proc/mtd) to the
# size of the image used to flash that partition (which includes a
# spare area for each page).
# $(1): the partition data size
define image-size-from-data-size
$(strip $(eval _isfds_value := $$(shell echo $$$$(($(1) / $(BOARD_NAND_PAGE_SIZE) * \($(BOARD_NAND_PAGE_SIZE)+$(BOARD_NAND_SPARE_SIZE))))))\
$(if $(filter 0, $(_isfds_value)),$(shell echo $$(($(BOARD_NAND_PAGE_SIZE)+$(BOARD_NAND_SPARE_SIZE)))),$(_isfds_value))\
$(eval _isfds_value :=))
endef

可以看到这个函数对分区大小做一个转换,转换为flash芯片上的分区大小。之后把转换后的结果传给assert-max-file-size作为第二个参数。
assert-max-file-size定义如下:

# $(1): The file(s) to check (often $@)
# $(2): The maximum total image size, in decimal bytes.
#    Make sure to take into account any reserved space needed for the FS.
#
# If $(2) is empty, evaluates to "true"
#
# Reserve bad blocks.  Make sure that MAX(1% of partition size, 2 blocks)
# is left over after the image has been flashed.  Round the 1% up to the
# next whole flash block size.
define assert-max-file-size
$(if $(2), \size=$$(for i in $(1); do $(call get-file-size,$$i); echo +; done; echo 0); \total=$$(( $$( echo "$$size" ) )); \printname=$$(echo -n "$(1)" | tr " " +); \img_blocksize=$(call image-size-from-data-size,$(BOARD_FLASH_BLOCK_SIZE)); \twoblocks=$$((img_blocksize * 2)); \onepct=$$((((($(2) / 100) - 1) / img_blocksize + 1) * img_blocksize)); \reserve=$$((twoblocks > onepct ? twoblocks : onepct)); \maxsize=$$(($(2) - reserve)); \echo "$$printname maxsize=$$maxsize blocksize=$$img_blocksize total=$$total reserve=$$reserve"; \if [ "$$total" -gt "$$maxsize" ]; then \echo "error: $$printname too large ($$total > [$(2) - $$reserve])"; \false; \elif [ "$$total" -gt $$((maxsize - 32768)) ]; then \echo "WARNING: $$printname approaching size limit ($$total now; limit $$maxsize)"; \fi \, \true \)
endef

这个函数对system.img的大小做一个检查,如果system.img太大,超过了flash允许的最大分区的大小,这里就会报错。
因此,assert-max-image-size函数可以理解为检查system.img的合法性。

三.$(BUILT_SYSTEMIMAGE)

我们分析system.img的生成规则发现,system.img其实是 (BUILTSYSTEMIMAGE)的一份拷贝。那么 (BUILT_SYSTEMIMAGE)的一份拷贝。那么(BUILT_SYSTEMIMAGE)又是怎么生成的呢?
(BUILTSYSTEMIMAGE)其实也是一个sytem.img文件,只不过它在 (BUILT_SYSTEMIMAGE)其实也是一个sytem.img文件,只不过它在(systemimage_intermediates)目录下:
BUILT_SYSTEMIMAGE := (systemimageintermediates)/system.img (systemimage_intermediates)/system.img (systemimage_intermediates) := target/product/xxx/obj/PACKAGING/systemimage_intermediates
$(BUILT_SYSTEMIMAGE)的依赖与生成规则如下:

$(BUILT_SYSTEMIMAGE): $(FULL_SYSTEMIMAGE_DEPS) $(INSTALLED_FILES_FILE)$(call build-systemimage-target,$@)

我们不知道它依赖的是什么,但是我们可以先看一下它的生成规则:
build-systemimage-target函数定义如下:

# $(1): output file
define build-systemimage-target@echo "Target system fs image: $(1)"$(call create-system-vendor-symlink)@mkdir -p $(dir $(1)) $(systemimage_intermediates) && rm -rf $(systemimage_intermediates)/system_image_info.txt$(call generate-userimage-prop-dictionary, $(systemimage_intermediates)/system_image_info.txt, \skip_fsck=true)$(hide) PATH=$(foreach p,$(INTERNAL_USERIMAGES_BINARY_PATHS),$(p):)$$PATH \./build/tools/releasetools/build_image.py \$(TARGET_OUT) $(systemimage_intermediates)/system_image_info.txt $(1) $(TARGET_OUT) \|| ( echo "Out of space? the tree size of $(TARGET_OUT) is (MB): " 1>&2 ;\du -sm $(TARGET_OUT) 1>&2;\if [ "$(INTERNAL_USERIMAGES_EXT_VARIANT)" == "ext4" ]; then \maxsize=$(BOARD_SYSTEMIMAGE_PARTITION_SIZE); \if [ "$(BOARD_HAS_EXT4_RESERVED_BLOCKS)" == "true" ]; then \maxsize=$$((maxsize - 4096 * 4096)); \fi; \echo "The max is $$(( maxsize / 1048576 )) MB." 1>&2 ;\else \echo "The max is $$(( $(BOARD_SYSTEMIMAGE_PARTITION_SIZE) / 1048576 )) MB." 1>&2 ;\fi; \mkdir -p $(DIST_DIR); cp $(INSTALLED_FILES_FILE) $(DIST_DIR)/installed-files-rescued.txt; \exit 1 )
endef

这个函数做了四件事情:

1.create-system-vendor-symlink

define create-system-vendor-symlink
$(hide) if [ -d $(TARGET_OUT)/vendor ] && [ ! -h $(TARGET_OUT)/vendor ]; then \echo 'Non-symlink $(TARGET_OUT)/vendor detected!' 1>&2; \echo 'You cannot install files to $(TARGET_OUT)/vendor while building a separate vendor.img!' 1>&2; \exit 1; \
fi
$(hide) ln -sf /vendor $(TARGET_OUT)/vendor
endef

如果存在vendor目录,就给vendor目录创建一个软连接。

2.创建target/product/xxx/obj/PACKAGING/systemimage_intermediates目录并删除这个目录下的system_image_info.txt文件。

3.重新向system_image.info.txt中写入数据

# $(1): the path of the output dictionary file
# $(2): additional "key=value" pairs to append to the dictionary file.
define generate-userimage-prop-dictionary
$(if $(INTERNAL_USERIMAGES_EXT_VARIANT),$(hide) echo "fs_type=$(INTERNAL_USERIMAGES_EXT_VARIANT)" >> $(1))
$(if $(BOARD_SYSTEMIMAGE_PARTITION_SIZE),$(hide) echo "system_size=$(BOARD_SYSTEMIMAGE_PARTITION_SIZE)" >> $(1))
$(if $(BOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE),$(hide) echo "system_fs_type=$(BOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE)" >> $(1))
$(if $(BOARD_SYSTEMIMAGE_JOURNAL_SIZE),$(hide) echo "system_journal_size=$(BOARD_SYSTEMIMAGE_JOURNAL_SIZE)" >> $(1))
$(if $(BOARD_HAS_EXT4_RESERVED_BLOCKS),$(hide) echo "has_ext4_reserved_blocks=$(BOARD_HAS_EXT4_RESERVED_BLOCKS)" >> $(1))
$(if $(BOARD_SYSTEMIMAGE_SQUASHFS_COMPRESSOR),$(hide) echo "system_squashfs_compressor=$(BOARD_SYSTEMIMAGE_SQUASHFS_COMPRESSOR)" >> $(1))
$(if $(BOARD_SYSTEMIMAGE_SQUASHFS_COMPRESSOR_OPT),$(hide) echo "system_squashfs_compressor_opt=$(BOARD_SYSTEMIMAGE_SQUASHFS_COMPRESSOR_OPT)" >> $(1))
$(if $(BOARD_USERDATAIMAGE_FILE_SYSTEM_TYPE),$(hide) echo "userdata_fs_type=$(BOARD_USERDATAIMAGE_FILE_SYSTEM_TYPE)" >> $(1))
$(if $(BOARD_USERDATAIMAGE_PARTITION_SIZE),$(hide) echo "userdata_size=$(BOARD_USERDATAIMAGE_PARTITION_SIZE)" >> $(1))
$(if $(BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE),$(hide) echo "cache_fs_type=$(BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE)" >> $(1))
$(if $(BOARD_CACHEIMAGE_PARTITION_SIZE),$(hide) echo "cache_size=$(BOARD_CACHEIMAGE_PARTITION_SIZE)" >> $(1))
$(if $(BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE),$(hide) echo "vendor_fs_type=$(BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE)" >> $(1))
$(if $(BOARD_VENDORIMAGE_PARTITION_SIZE),$(hide) echo "vendor_size=$(BOARD_VENDORIMAGE_PARTITION_SIZE)" >> $(1))
$(if $(BOARD_VENDORIMAGE_JOURNAL_SIZE),$(hide) echo "vendor_journal_size=$(BOARD_VENDORIMAGE_JOURNAL_SIZE)" >> $(1))
$(if $(BOARD_OEMIMAGE_PARTITION_SIZE),$(hide) echo "oem_size=$(BOARD_OEMIMAGE_PARTITION_SIZE)" >> $(1))
$(if $(BOARD_OEMIMAGE_JOURNAL_SIZE),$(hide) echo "oem_journal_size=$(BOARD_OEMIMAGE_JOURNAL_SIZE)" >> $(1))
$(if $(INTERNAL_USERIMAGES_SPARSE_EXT_FLAG),$(hide) echo "extfs_sparse_flag=$(INTERNAL_USERIMAGES_SPARSE_EXT_FLAG)" >> $(1))
$(hide) echo "selinux_fc=$(SELINUX_FC)" >> $(1)
$(if $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_BOOT_SIGNER),$(hide) echo "boot_signer=$(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_BOOT_SIGNER)" >> $(1))
$(if $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VERITY),$(hide) echo "verity=$(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VERITY)" >> $(1))
$(if $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VERITY),$(hide) echo "verity_key=$(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VERITY_SIGNING_KEY)" >> $(1))
$(if $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VERITY),$(hide) echo "verity_signer_cmd=$(VERITY_SIGNER)" >> $(1))
$(if $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SYSTEM_VERITY_PARTITION),$(hide) echo "system_verity_block_device=$(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SYSTEM_VERITY_PARTITION)" >> $(1))
$(if $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VENDOR_VERITY_PARTITION),$(hide) echo "vendor_verity_block_device=$(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VENDOR_VERITY_PARTITION)" >> $(1))
$(if $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VBOOT),$(hide) echo "vboot=$(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VBOOT)" >> $(1))
$(if $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VBOOT),$(hide) echo "vboot_key=$(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VBOOT_SIGNING_KEY)" >> $(1))
$(if $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VBOOT),$(hide) echo "futility=$(FUTILITY)" >> $(1))
$(if $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VBOOT),$(hide) echo "vboot_signer_cmd=$(VBOOT_SIGNER)" >> $(1))
$(if $(filter true,$(BOARD_BUILD_SYSTEM_ROOT_IMAGE)),\$(hide) echo "system_root_image=true" >> $(1);\echo "ramdisk_dir=$(TARGET_ROOT_OUT)" >> $(1))
$(if $(2),$(hide) $(foreach kv,$(2),echo "$(kv)" >> $(1);))
endef

4.使用build_image.py脚本生成system.img镜像文件。

四.$(FULL_SYSTEMIMAGE_DEPS)

FULL_SYSTEMIMAGE_DEPS又有以下两部分组成:
FULL_SYSTEMIMAGE_DEPS := (INTERNALSYSTEMIMAGEFILES) (INTERNAL_SYSTEMIMAGE_FILES) (INTERNAL_USERIMAGES_DEPS)

1.$(INTERNAL_SYSTEMIMAGE_FILES)

INTERNAL_SYSTEMIMAGE_FILES := $(filter $(TARGET_OUT)/%, \$(ALL_PREBUILT) \$(ALL_COPIED_HEADERS) \$(ALL_GENERATED_SOURCES) \$(ALL_DEFAULT_INSTALLED_MODULES) \$(PDK_FUSION_SYSIMG_FILES) \$(RECOVERY_RESOURCE_ZIP) \

从这里就可以看出,INTERNAL_SYSTEMIMAGE_FILES描述的就是从ALL_PREBUILT、ALL_COPIED_HEADERS、ALL_GENERATED_SOURCES、ALL_DEFAULT_INSTALLED_MODULES、PDK_FUSION_SYSIMG_FILES和RECOVERY_RESOURCE_ZIP中过滤出来的存放在TARGET_OUT目录下的那些文件,即在目标产品输出目录中的system子目录下那些文件。
ALL_PREBUILT:要拷贝到目标设备上去的文件。
ALL_COPIED_HEADERS:要拷贝到目标设备上去的头文件。
ALL_GENERATED_SOURCES:要拷贝到目标设备上去的由工具自动生成的源代码文件。
ALL_DEFAULT_INSTALLED_MODULES:要安装要目标设备上的所有的模块文件。
PDK_FUSION_SYSIMG_FILES是从PDK(Platform Development Kit)提取出来的相关文件。
RECOVERY_RESOURCE_ZIP描述的是Android的recovery系统要使用的资源文件,对应于/system/etc目录下的recovery-resource.dat文件。

2.$(INTERNAL_USERIMAGES_DEPS)

ifeq ($(INTERNAL_USERIMAGES_USE_EXT),true)
INTERNAL_USERIMAGES_DEPS := $(SIMG2IMG)
INTERNAL_USERIMAGES_DEPS += $(MKEXTUSERIMG) $(MAKE_EXT4FS) $(E2FSCK)
ifeq ($(TARGET_USERIMAGES_USE_F2FS),true)
INTERNAL_USERIMAGES_DEPS += $(MKF2FSUSERIMG) $(MAKE_F2FS)
endif
endififeq ($(BOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE),squashfs)
INTERNAL_USERIMAGES_DEPS += $(MAKE_SQUASHFS) $(MKSQUASHFSUSERIMG) $(IMG2SIMG)
endififeq ($(BOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE),squashfs)
INTERNAL_USERIMAGES_DEPS += $(MAKE_SQUASHFS) $(MKSQUASHFSUSERIMG) $(IMG2SIMG)
endifINTERNAL_USERIMAGES_BINARY_PATHS := $(sort $(dir $(INTERNAL_USERIMAGES_DEPS)))ifeq (true,$(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VERITY))
INTERNAL_USERIMAGES_DEPS += $(BUILD_VERITY_TREE) $(APPEND2SIMG) $(VERITY_SIGNER)
endifSELINUX_FC := $(TARGET_ROOT_OUT)/file_contexts
INTERNAL_USERIMAGES_DEPS += $(SELINUX_FC)

从以上可以看出INTERNAL_USERIMAGES_DEPS描述的是制作system.img镜像所依赖的工具。例如,如果要制作的system.img使用的是yaffs2文件系统,那么对应工具就是mkyaffs2image。
总结:也就是四小节提供镜像打包工具和所有需要的文件,这些文件在之前的编译中已经生成好了,然后交由三小节的build-systemimage-target函数使用build_image.py生成system.img镜像文件,这个镜像文件在target/product/xxx/obj/PACKAGING/systemimage_intermediates目录下,之后再由二小节中的拷贝函数将其拷贝到target/product/xxx目录下,xxx是产品名。

Android编译系统分析五:system.img的生成过程相关推荐

  1. Android编译系统分析四:实战-新增一个产品

    通过上一节"android编译系统(三)-make"的分析,初步理清楚了编译初期加载产品相关信息的流程,整个过程主要涉及三个文件:1.AndroidProducts.mk,2.具体 ...

  2. Android 编译系统分析之lunch分析

    已开通新的博客,后续文字都会发到新博客 http://www.0xfree.top Android 编译系统解析系列文档 编译系统入口envsetup.sh解析 source build/envset ...

  3. Android 编译系统分析(三)

    自Android开源以来,引起了嵌入式行业一股热潮,很多嵌入式开发者表示对Android有很强的兴趣,并下载Android源码进行编译和移植.Android源码的巨大(repo下来,大概2G)给人以A ...

  4. Android 编译系统分析(一)

    一.Makefile的主要流程 以下主要流程都在build/core/main.mk里安排. 初始化相关的参数设置(buildspec.mk.envsetup.mk.config.mk) 检测编译环境 ...

  5. Android编译系统分析三:make完整编译android系统

    这篇博客的目标是摸清楚默认编译整个android系统时代码的流程. 当我们执行make的时候,会查找当前的Makefie文件或者makefile文件并且执行,在android顶级源码目录下面,确实有个 ...

  6. Android编译系统分析二:mm编译单个模块

    因为Android的编译系统不同于Linux Kernel的递归式的编译系统,它的编译系统是一种称之为independent的模式,每个模块基本独立(它有可能依赖其他模块),每个模块都可以单独编译,这 ...

  7. Android 编译系统分析(二)

    把Android所有的Make文件分为4种: 1.For config 这类文件主要来配置product,board,以及根据你的Host和Target选择相应的工具以及设定相应的通用编译选项: bu ...

  8. Android编译Skia库

    Android编译Skia库 本文档提供两种方法编译Skia库 使用aosp源码进行编译 使用skia源码进行编译 两种编译方法都可以编译,并且都可以使用在多个平台中,且可以使用在不同Android版 ...

  9. Android 编译过程介绍,Android.mk 和 Android.bp 分析, 在源码中编译 AndroidStudio 构建的 App

    目录 一.Android 编译 1. 编译流程 2. Soong 介绍 3. build.sh 二.Android.mk 解析 三.Android.bp 解析 1. 模块类型 2. 模块属性 四.An ...

最新文章

  1. 3行Python代码就能获取海量数据?
  2. 0、为什么推荐学习PowerShell?
  3. 刘铁岩:如何四两拨千斤,高效地预训练NLP模型?
  4. ios自动布局(1)
  5. mysql忘记root密码及修改密码
  6. python爬取图片-Python爬取网页中的图片(搜狗图片)详解
  7. WPF 文本框添加水印效果
  8. asp 中使用Ftp.exe 上传大文件
  9. Java 7 – NIO文件革命
  10. redis 版的 hello world
  11. Redis工作笔记-Redis安装及基本配置
  12. poi设置自动换行后显示不全_抖音企业号权益之POI领多和地址内容编辑,视频编辑...
  13. SaltStack 学习笔记 - 第十二篇: SaltStack Web 界面
  14. Median of Two Sorted Array leetcode java
  15. Postgre SQL学习
  16. 基于PaddleHub实现简易人像抠图
  17. 微信支付接口帮助文档
  18. 《机器人动力学与控制》第九章——动力学 9.4 欧拉-拉格朗日方程法应用举例(下)
  19. 无线共享打印机无法连接服务器,“不能连接网络共享打印机”常见原因及处理方法:...
  20. 绿色环保静态网页设计

热门文章

  1. 自动驾驶(七十五)---------几种硬件平台对比
  2. 解决百度网盘倍速问题
  3. 看“废物”如何重生,看“闲鱼”如何翻身
  4. 文件服务器 磁盘分多少区,硬盘分区图文教程,硬盘分几个区最好,C盘分区多少g合适...
  5. 神经网络一般训练次数,神经网络训练时间
  6. MindManager职场攻略“赶走瞌睡虫”!
  7. 原生小程序配置vant
  8. linux 查看CPU频率
  9. node本地测试ajax CMD窗报错:_http_outgoing.js:436 throw new Error('Header %s value must not be undef
  10. getMeasuredWidth, getWidth 有什么区别?