Linux学习(三) 展讯Android 4.0编译 -- Makefile

本文详细解析了Android系统中如何通过make命令编译生成bootimage文件的过程。从makebootimage命令开始,逐步介绍了编译过程中涉及的关键文件和路径,包括Makefile、Android.mk等配置文件的依赖关系。

在执行完envsetup操作后,只要输入make bootimage, makebootloader等命令就可以直接编译生成相关的文件,那么,makefile是如何定义才达到这样的目标呢?以Linux kernel生成的bootimage为例,我们一步一步的来学习Android是如何编译各模块的。

 

在命令行输入 make bootimage的命令,可以看到结果如下

 

rickzhang@android15:~/work/vlx40$ make bootimage
============================================
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=4.0.3
TARGET_PRODUCT=sp8810gabase
TARGET_BUILD_VARIANT=userdebug
TARGET_BUILD_TYPE=release
TARGET_BUILD_APPS=
TARGET_ARCH=arm
TARGET_ARCH_VARIANT=armv7-a-neon
HOST_ARCH=x86
HOST_OS=linux
HOST_BUILD_TYPE=release
BUILD_ID=IML74K
============================================
No private recovery resources for TARGET_DEVICE sp8810ga
make: Warning: File `build/target/board/Android.mk' has modification time 4.2e+02 s in the future
make -C kernel O=../out/target/product/sp8810ga/obj/KERNEL ARCH=arm CROSS_COMPILE=arm-eabi- modules_prepare
make[1]: Entering directory `/home/rickzhang/work/vlx40/kernel'
  Using /home/rickzhang/work/vlx40/kernel as source for kernel
  GEN     /home/rickzhang/work/vlx40/out/target/product/sp8810ga/obj/KERNEL/Makefile
  CHK     include/linux/version.h
  CHK     include/generated/utsrelease.h
make[3]: `include/generated/mach-types.h' is up to date.
  CALL    /home/rickzhang/work/vlx40/kernel/scripts/checksyscalls.sh

..........

make[1]: Leaving directory `/home/rickzhang/work/vlx40/kernel'
target Prebuilt:  (out/target/product/sp8810ga/kernel)
Target boot image: out/target/product/sp8810ga/boot.img

 

 

从Build结果可以看出,最后在out/target/product/sp8810ga/目录下生成了kernel, ramdisk.img, bootimage三个文件,其中,bootimage是kernel和ramdisk.img,再加上KERNEL_CMDLINE,FLASH_PAGE_SIZE 4个部分而生成的。那么,这些文件的依赖关系,是如何从Android 根目录下找到的呢?

 

首先给出基本的mk文件依赖关系:

./Makefile  --> build/core/main.mk  --> build/core/Makefile

                               |

                                --> build/target/board/Android.mk  --> device/sprd/sp8810ga/AndroidBoard.mk --> kernel/AndroidKernel.mk --> kernel/Makefile 

 

1. main.mk 分析

 

main.mk是Android编译体系中的核心文件,它通过搜索并include所有目录下的Androd.mk文件来完成所有模块的编译工作。

首先分析 make bootimage命令,如果在Android根目录下运行make命令,GNU make工具会查找当前目录的Makefile来执行,看看根目录下的Makefile内容:

rickzhang@android15:~/work/vlx40$ vi Makefile

  1 ### DO NOT EDIT THIS FILE ###
  2 include build/core/main.mk
  3 ### DO NOT EDIT THIS FILE ###

实际上Makefile是include了build/core/目录下的main.mk文件来执行,继续查看 main.mk:

747 .PHONY: bootimage
748 bootimage: $(INSTALLED_BOOTIMAGE_TARGET)

 

从这条语句可以得知,make bootimage的依赖Target就是 INSTALLED_BOOTIMAGE_TARGET,现在我们要找到这个变量,看看bootimage是如何编译生成的。

另外,从main.mk的体系来看,它include了大部分的makefile以编译各模块:

53 BUILD_SYSTEM := $(TOPDIR)build/core

681 include $(BUILD_SYSTEM)/Makefile

 

从以上681行可以看出,main.mk include 了 build/core/Makefile;

 

525 subdir_makefiles := \
526         $(shell build/tools/findleaves.py --prune=out --prune=.repo --prune=.git $(subdirs) Android.mk)
527
528 include $(subdir_makefiles)

 

从以上528行可以看到,main.mk include了 所有子模块下的Android.mk文件;

 

2. build/core/Makefile 分析

 

从build/core/Makefile中,可以找到关于INSTALLED_BOOTIMAGE_TARGET的定义。

rickzhang@android11:~/work/vlx40$ vi build/core/Makefile

 378 INSTALLED_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/boot.img
 390 $(INSTALLED_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_FILES)
 391         $(call pretty,"Target boot image: $@")
 392         $(hide) $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_ARGS) --output $@
 393         $(hide) $(call assert-max-image-size,$@,$(BOARD_BOOTIMAGE_PARTITION_SIZE),raw)

 

从378行可以看出,INSTALLED_BOOTIMAGE_TARGET的定义被扩展为 $(PRODUCT_OUT)/boot.img,由于之前在envsetup.sh中已经选择了PRODUCT,因此$(PRODUCT_OUT)为 out/target/product/sp8810ga,make image命令就是要在该目录下生成 bootimage文件。从392行来看,需要使用$(MKBOOTIMG)工具,针对INTERNAL_BOOTIMAGE_ARGS来生成 bootimage。实际上,bootimage是由一系列的文件和参数组成的:

 354 # -----------------------------------------------------------------
 355 # the boot image, which is a collection of other images.

 356 INTERNAL_BOOTIMAGE_ARGS := \
 357         $(addprefix --second ,$(INSTALLED_2NDBOOTLOADER_TARGET)) \
 358         --kernel $(INSTALLED_KERNEL_TARGET) \
 359         --ramdisk $(INSTALLED_RAMDISK_TARGET)
 360
 361 INTERNAL_BOOTIMAGE_FILES := $(filter-out --%,$(INTERNAL_BOOTIMAGE_ARGS))
 362
 363 BOARD_KERNEL_CMDLINE := $(strip $(BOARD_KERNEL_CMDLINE))
 364 ifdef BOARD_KERNEL_CMDLINE
 365   INTERNAL_BOOTIMAGE_ARGS += --cmdline "$(BOARD_KERNEL_CMDLINE)"
 366 endif
 367
 368 BOARD_KERNEL_BASE := $(strip $(BOARD_KERNEL_BASE))
 369 ifdef BOARD_KERNEL_BASE
 370   INTERNAL_BOOTIMAGE_ARGS += --base $(BOARD_KERNEL_BASE)
 371 endif
 372
 373 BOARD_KERNEL_PAGESIZE := $(strip $(BOARD_KERNEL_PAGESIZE))
 374 ifdef BOARD_KERNEL_PAGESIZE
 375   INTERNAL_BOOTIMAGE_ARGS += --pagesize $(BOARD_KERNEL_PAGESIZE)
 376 endif

 

从上述信息可以看到,bootimage(INTERNAL_BOOTIMAGE_ARGS)是由 --second $(INSTALLED_2NDBOOTLOADER_TARGET), --kernel $(INSTALLED_KERNEL_TARGET), --ramdisk $(INSTALLED_RAMDISK_TARGET), --cmdline "$(BOARD_KERNEL_CMDLINE)", --base $(BOARD_KERNEL_BASE), --pagesize  $(BOARD_KERNEL_PAGESIZE) 所组成的。

因此,我们目前搜索的目标变成了INSTALLED_KERNEL_TARGET,它就是Linux kernel编译后生成的文件。

 

3. build/target/board/Android.mk 分析

 

  之前已经看到,main.mk文件会搜索并include所有子目录下的Android.mk文件,因此 build/target/board/Android.mk也已经被main.mk所引用。查看该文件,可以发现如下信息:

 

18   INSTALLED_KERNEL_TARGET := $(PRODUCT_OUT)/kernel
 23 -include $(TARGET_DEVICE_DIR)/AndroidBoard.mk

 

因此,如果希望build INSTALLED_KERNEL_TARGET,就需要找到哪里编译 out/target/product/sp8810ga/kernel文件的,这时可以从 $(TARGET_DEVICE_DIR)/AndroidBoard.mk中来寻找;这里 -include的含义是在include AndroidBoard.mk文件时,遇到错误也不会中断;

 

4. device/sprd/sp8810ga/AndroidBoard.mk 分析

 

打开AndroidBoard.mk文件,查看其内容,寻找 INSTALLED_KERNEL_TARGET的依赖关系

 

 14 include kernel/AndroidKernel.mk
 15
 16 file := $(INSTALLED_KERNEL_TARGET)
 17 ALL_PREBUILT += $(file)
 18 $(file) : $(TARGET_PREBUILT_KERNEL) | $(ACP)
 19         $(transform-prebuilt-to-target)


 

从18行来看,为了build  $(file),需要找到 TARGET_PREBUILT_KERNEL,而这个宏又可以从kernel/AndroidKernel.mk来寻找,这一次终于进入到kernel的目录中了;

注:分析其他产品的build规则,也是最终寻找这个TARGET_PREBUILT_KERNEL,例如 samsung平台的tuna:

rickzhang@android15:~/work/vlx40$ vi device/samsung/tuna/device.mk
 22 ifeq ($(TARGET_PREBUILT_KERNEL),)
 23 LOCAL_KERNEL := device/samsung/tuna/kernel
 24 else
 25 LOCAL_KERNEL := $(TARGET_PREBUILT_KERNEL)
 26 endif

 

TI平台的Panda:

rickzhang@android15:~/work/vlx40$ vi device/ti/panda/device.mk
 17 ifeq ($(TARGET_PREBUILT_KERNEL),)
 18 LOCAL_KERNEL := device/ti/panda/kernel
 19 else
 20 LOCAL_KERNEL := $(TARGET_PREBUILT_KERNEL)
 21 endif

 

可以看到,这些平台都是使用预先编译好的kernel文件,该文件已经放在device/xx/xx/目录下,如果需要自己编译kernel文件,则需要定义$TARGET_PREBUILT_KERNEL,本文不再分析其他平台的build实现过程。

 

5. kernel/AndroidKernel.mk 分析

rickzhang@android15:~/work/vlx40$ vi kernel/AndroidKernel.mk
  1
  2 KERNEL_OUT := $(TARGET_OUT_INTERMEDIATES)/KERNEL
  3 KERNEL_CONFIG := $(KERNEL_OUT)/.config
  4 KERNEL_MODULES_OUT := $(TARGET_OUT)/lib/modules
  5
  6 ifeq ($(USES_UNCOMPRESSED_KERNEL),true)
  7 TARGET_PREBUILT_KERNEL := $(KERNEL_OUT)/arch/arm/boot/Image
  8 else
  9 TARGET_PREBUILT_KERNEL := $(KERNEL_OUT)/arch/arm/boot/zImage
 10 endif
 11
 12 $(KERNEL_OUT):
 13         @echo "==== Start Kernel Compiling ... ===="
 14
 15 $(KERNEL_CONFIG): kernel/arch/arm/configs/$(KERNEL_DEFCONFIG)
 16         mkdir -p $(KERNEL_OUT)
 17         $(MAKE) -C kernel O=../$(KERNEL_OUT) ARCH=arm CROSS_COMPILE=arm-eabi- $(KERNEL_DEFCONFIG)
 18
 19 $(TARGET_PREBUILT_KERNEL) : $(KERNEL_OUT) $(KERNEL_CONFIG)
 20         $(MAKE) -C kernel O=../$(KERNEL_OUT) ARCH=arm CROSS_COMPILE=arm-eabi- modules_prepare
 21         $(MAKE) -C kernel O=../$(KERNEL_OUT) ARCH=arm CROSS_COMPILE=arm-eabi- headers_install
 22         $(MAKE) -C kernel O=../$(KERNEL_OUT) ARCH=arm CROSS_COMPILE=arm-eabi- zImage -j4
 23         $(MAKE) -C kernel O=../$(KERNEL_OUT) ARCH=arm CROSS_COMPILE=arm-eabi- modules
 24         @-mkdir -p $(KERNEL_MODULES_OUT)
 25         @-find $(KERNEL_OUT) -name *.ko | xargs -I{} cp {} $(KERNEL_MODULES_OUT)

 

从AndroidKernel.mk来看,如果需要build $TARGET_PREBUILT_TARGET,那么就需要调用到make函数进行真正的编译了,例如:

make -C kernel O=../ .....................  zImage -j4

可以看出来,这里实际上是寻找kernel目录下的Makefile文件,然后编译zImage,在生成zImage的同时,也生成了Image。从AndroidKernel.mk文件来看,使用:

 18 $(file) : $(TARGET_PREBUILT_KERNEL) | $(ACP)
 19         $(transform-prebuilt-to-target)

 

语句,将Image 通过 $(ACP)程序转变为kernel文件。Image可以从如下地址找到:

  7 TARGET_PREBUILT_KERNEL := $(KERNEL_OUT)/arch/arm/boot/Image
 

我们可以通过 make -n bootimage > build_log.txt 文件,记录下来这个make的过程,-n表示只记录,并不真正执行make的编译动作。

echo "target Prebuilt:  (out/target/product/sp8810ga/kernel)"
mkdir -p out/target/product/sp8810ga/
out/host/linux-x86/bin/acp -fp 
out/target/product/sp8810ga/obj/KERNEL/arch/arm/boot/Image out/target/product/sp8810ga/kernel
echo "Target boot image: out/target/product/sp8810ga/boot.img"
out/host/linux-x86/bin/mkbootimg  
--kernel out/target/product/sp8810ga/kernel --ramdisk out/target/product/sp8810ga/ramdisk.img --cmdline "console=ttyS1,115200n8 mem=239M" --base 0x00000000 --output out/target/product/sp8810ga/boot.img

 

从上述信息可以看出来,通过make zImage -j4 的动作以后,在out/target/product/sp8810ga/obj/KERNEL/arch/arm/boot目录下生成了Image和zImage文件,通过out/host/linux-x86/bin/acp工具,将Image转变成为kernel,然后再使用mkbootimage工具,将kernel, ramdisk, cmdline, base等信息一起打包生成了boot.img。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值