您的位置:首页 > 运维架构 > Linux

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

2015-08-06 16:41 501 查看
在执行完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。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: