山寨内核Makefile之“天龙八部”

“山寨,它不仅是个名词,更多是个动词。”
内核的Makefile谈不上博大精深,但也是构思精巧。层层编译,递归链接,最后才生成个zImage。
在此设计个比较通用的 Makefile,秉承"实践出真知"的精神,在山寨的过程中细细咀嚼。


--------
第一部分
--------

-->
我们的编译工具是:

CROSS_COMPILE   ?= arm-linux-

 

为啥是个 "?=" ?
这叫条件赋值:只有此变量在之前没有赋值的情况下才会对这个变量进行赋值。
我们不知到是否有个叫CROSS_COMPILE的环境变量已被定义,比如在~/.bashrc或~/.bash_profile里。“要以大局为重”,所以,用"?="。

等价于:

ifeq ($(origin FOO), undefined)
FOO=bar
endif



origin FOO:
使用系统环境变量的定义覆盖Makefile中的同名变量定义。
返回值有:undefined, default, environment, environment override。

 

-->
各种编译命令:

AS  = $(CROSS_COMPILE)as
LD = $(CROSS_COMPILE)ld
CC = $(CROSS_COMPILE)gcc
CPP = $(CC) -E
AR = $(CROSS_COMPILE)ar

NM = $(CROSS_COMPILE)nm
LDR = $(CROSS_COMPILE)ldr
STRIP = $(CROSS_COMPILE)strip
OBJCOPY = $(CROSS_COMPILE)objcopy
OBJDUMP = $(CROSS_COMPILE)objdump

 

-->
OK, 该部分结束,export它们:

export AS LD CC CPP AR NM LDR STRIP OBJCOPY OBJDUMP


 

--------
第二部分
--------

make参数:

MAKE    = make -s -e

-s,关闭回显。
-e,使用系统环境变量的定义覆盖Makefile中的同名变量定义。

 

--------
第三部分
--------

根目录:

TOPDIR  = $(shell pwd)


子目录:

SUBDIRS := kernel/
SUBDIRS += lib/
SUBDIRS += drivers/


“起始”目录:  (也就是放start.S的地方)

STARTDIR := $(TOPDIR)/kernel/cpu


 

--------
第四部分
--------

最终输出文件:

OUT_BIN := g-boot.bin
OUT_ELF := g-boot.elf


最终输出文件组合前的目标文件:

SUBLIBS := $(addsuffix built-in.o,$(SUBDIRS))

 

组合过程:

$(OUT_BIN): $(OUT_ELF)
@$(OBJCOPY) -O binary $< $@

$(OUT_ELF): $(STARTDIR) $(SUBDIRS)
@$(LD) $(SUBLIBS) -o $@ -T$(LDSCRIPT)

==>

LDSCRIPT    := $(TOPDIR)/g-boot.lds


 

--------
第五部分
--------

目标文件的递归生成过程:

从start开始,
执行该文件夹下的 Makefile, 文件内的start为入口:

$(STARTDIR):
@$(MAKE) -C $@ start



STARTDIR/Makefile主要分两部分:

start:  $(start_obj)
include $(TOPDIR)/config.mk


在进入config.mk前,要设置obj-y,好比进入函数前的参数。
进入config.mk,生成该文件夹下的目标文件 built-in.o。

 

--------
第六部分
--------

built-in.o由当前目录下的各个目标文件和库及子目录下的built-in.o链接而成。

all: $(SUBDIRS) $(OBJS)
$(LD) -r -o built-in.o $(OBJS) $(SUBLIBS)

$(SUBDIRS):
@$(MAKE) -C $@ all


先进入子目录编译。
由此可以看出,这是个由底到顶的编译过程,好比一棵树,从叶子结点编译后链接为父结点,然后父结点再和兄弟链接,循环递归,最后生成根结点目标文件的过程。

==>

OBJS    := $(notdir $(obj-y))

SUBDIRS := $(subst ./,,$(dir $(obj-y)))
SUBLIBS := $(addsuffix built-in.o,$(SUBDIRS))


我们发现这里经过一番折腾后,obj-y是文件还是文件夹都会被一视同仁。


--------
第七部分
--------

各个目标文件和库的生成:

%.o:    %.S
@$(CC) $(CFLAGS) -c -o $@ $<
@echo " CC .$(CURDIR)/$@"

%.o: %.s
@$(CC) $(CFLAGS) -c -o $@ $<
@echo " CC .$(CURDIR)/$@"

%.o: %.c
@$(CC) $(CFLAGS) -c -o $@ $<
@echo " CC .$(CURDIR)/$@"


==>

CURDIR   := $(subst $(TOPDIR),,$(shell pwd))

CPPFLAGS := -I$(TOPDIR)/include -nostdinc -fno-builtin -ffreestanding -pipe

CFLAGS := $(CPPFLAGS) -Wall -Wstrict-prototypes -fno-stack-protector \
-march=armv4 -mabi=apcs-gnu -mno-thumb-interwork -Os


 

--------
第八部分
--------

关于make clean,也是个递归的过程:

.PHONY: clean
clean:
@for i in $(SUBDIRS);do $(MAKE) -C $$i clean;done
@rm -f $(OBJS) built-in.o


伪目标(.PHONY)是这样一个目标:
它不代表一个真正的文件名,在执行 make 时可以指定这个目标来执行其所在规则定义的命令,有时我们也可以将一个伪目标称为标签。

 

咀嚼完毕……

 

你可能感兴趣的:(makefile)