Makefile思路
Hylicos使用makefile构建工程,模仿linux层级编译的方法。
首先来看一下目录
.
├── Makefile
├── README.txt
├── build
│ ├── HylicOS_MAP.map
│ ├── hylic_build_link.mk
│ ├── hylic_cmd.mk
│ ├── hylic_cmd.mki
│ ├── hylic_hal.mk
│ ├── hylic_hal.mki
│ ├── hylic_mem_link.lds
│ ├── hylic_objs.mk
│ ├── hylic_objs.mki
│ ├── hylic_rule.mk
│ ├── hylic_rule.mki
│ ├── hylichal_start.i
│ ├── hylichal_start.o
│ ├── hylichal_start.s
│ ├── hylickrnl.bin
│ ├── hylickrnl.elf
│ ├── init.o
│ ├── init.s
│ ├── lmkfbuild
│ └── pretreatment.mkf
├── drivers
├── exckrnl
│ └── dnw2
├── kernel
├── hal
│ ├── hylichal_start.c
│ └── init.S
├── hylicinitldr
├── include
│ ├── config.h
│ ├── halinc
│ │ ├── halheads.h
│ │ ├── haltypes.h
│ │ ├── platform.h
│ │ └── platform_t.h
│ ├── hylictypes.h
│ ├── hylictypesrl.h
│ └── script
│ └── buildfile.h
├── release
├── script
│ ├── hylic_build_link.S
│ ├── hylic_cmd.S
│ ├── hylic_hal.S
│ ├── hylic_krl.S
│ ├── hylic_lib.S
│ ├── hylic_mem_link.S
│ ├── hylic_objs.S
│ ├── hylic_rule.S
│ └── hylic_task.S
└── task
根目录下有
- build文件夹
- hal文件夹
- include文件夹
- kernel文件夹
- drivers文件夹
- lib文件夹
- script文件夹
- 顶层makefile文件
逐一介绍他们的作用
顶层makefile
顶层的makefile作为工程编译的起点,它的职责是先对./script/
文件夹中的.S 文件 做预编译
编为.mk和.lds文件(这一步由它调用build目录下的pretreatment.mkf文件完成)并放入到./build/
文件夹下,接着逐一执行.mk
文件(后缀.mk和.mkh 也是makefile文件 )
如果将所有的编译规则,编译目标,编译命令都放在一个makefile中,这样做会导致makefile很长很臃肿。我们将这个大makefile分为几个部分,各司其职:
编译目标部分(objs.mk) ,编译规则部分(rule.mk),负责链接部分(build_link.mk),编译命令部分(cmd.mk),
以及负责各个内核模块的部分(hal.mk负责硬件抽象层的编译,krl.mk负责内核层编译,drv.mk负责驱动层的编译
lib.mk负责对库的编译,这些模块的源码就在根目录下hal/ kernel/ drivers/ lib/ script/目录下保存着,负责各模块的源码会到各自的目录下获取源码进行编译)
./script/下的.S文件 预编译 生成出 ./build/下的 .mk .lds的对应关系:
./script/ | ./build/ |
---|---|
cmd.S | cmd.mk |
rule.S | rule.mk |
objs.S | objs.mk |
build_link.S | build_link.mk |
mem_link.S | mem_link.lds |
hal.S | hal.mk |
krl.S | krl.mk |
drv.S | drv.mk |
lib.S | lib.mk |
task.S | task.mk |
表格中所有的.mk和.lds的作用前面已经说过,最后要说的是:
cmd.mk,rule.mk ,objs.mk 包含了编译命令,编译规则,编译目标,是最先被预编译的,并且后面会被其他makefile文件include,后面将会看到。
顶层makefile内容:
可以看到第一个执行的是$(MAKE) $(PREMENTMFLGS),它会到./build目录下执行pretreatment.mkf文件
(该文件就是负责执行预编译的makefile,后面详细说)
make命令 -C 进入到指定目录读取 -f 指定执行的makefile文件
MAKEFLAGS =-sR
MKDIR = mkdir
RMDIR = rmdir
CP = cp
CD = cd
DD = dd
RM = rm
MAKE = make
MKIMAGE =mkimage
SUPERUSER =sudo
DKAPP =./dnw2
DKAPFLAGES = hylickrnl.bin
PREMENTMFLGS = -C $(BUILD_PATH) -f pretreatment.mkf
ARMHALYMFLGS = -C $(BUILD_PATH) -f hylic_hal.mk
ARMKRNLMFLGS = -C $(BUILD_PATH) -f hylickrl.mk
ARMDRIVMFLGS = -C $(BUILD_PATH) -f hylicdrv.mk
ARMLIBSMFLGS = -C $(BUILD_PATH) -f hyliclib.mk
ARMTASKMFLGS = -C $(BUILD_PATH) -f hylictask.mk
ARMLINKMFLGS = -C $(BUILD_PATH) -f hylic_build_link.mk
DSTPATH = ../exckrnl
RELEDSTPATH = ../release
SRCFILE = hylickrnl.bin hylickrnl.elf
BUILD_PATH = ./build
EXKNL_PATH = ./exckrnl
BOOTEXCIMG = hylic.bin
DSKIMG =flash.img
.PHONY : build print clean all cpkrnl knlexc writekrl downkrnl
build: clean print all
all:
$(MAKE) $(PREMENTMFLGS)
$(MAKE) $(ARMHALYMFLGS)
$(MAKE) $(ARMLINKMFLGS)
# $(MAKE) $(ARMKRNLMFLGS)
# $(MAKE) $(ARMDRIVMFLGS)
# $(MAKE) $(ARMLIBSMFLGS)
# $(MAKE) $(ARMTASKMFLGS)
@echo '[OK] Build Successfully!!!'
clean:
$(CD) $(BUILD_PATH); $(RM) -f *.mkh *.lds *.o *.bin *.i *.elf *.krnl *.s *.map *.lib *.btoj *.vdi *vmdk
$(CD) $(EXKNL_PATH); $(RM) -f *.mkh *.lds *.o *.bin *.i *.elf *.krnl *.s *.map *.lib *.btoj *.vdi *vmdk
@echo '[C] Clear All Material'
print:
@echo '================ Compiler Is Working ================='
knlexc: cpkrnl downkrnl
cpkrnl:
$(CD) $(BUILD_PATH) && $(CP) $(CPFLAGES) $(SRCFILE) $(DSTPATH)
build 目录下的pretreatment.mkf
这个文件主要是负责对/script/下 .S文件的预编译。其中PREMENTMK_OBJS和PREMENTMKI_OBJS指示了哪些文件需要被预编译。执行完pretreatment后,build目录下会多出很多东西,前面那个表格里的所有.mk .lds都拥有了,就可以逐步执行后面对各个模块的编译了。
值得注意的是,在我们对.S文件进行预编译时,由于GCC的历史机制会将.S文件中“linux”这个单词替换为“1”
这就导致我们对cmd.S预编译到cmd.mk时会遇到下面这种沙雕的情况:
CC = arm-linux-gcc --> CC = arm-1 -gcc
这会导致其他mk文件include这个cmd.mk文件时 都会用arm-1 -gcc去编译,make时就会报错找不到arm-1 -gcc这个鬼东西。
我查找了解决方案,发现在编译时应该添加 -pedantic 选项告诉GCC不要做这种替换。但是后来发现这个选项只对.c文件的预编译有效,对.S文件的预编译无效。
在我挣扎无果后,已经对这个机制投降了,我选择在预编译后手动去到.mk文件中修改这个“1”为“linux”
如果我们的makefile会在make时 自动执行make clean然后才开始编译,那我上面做的这个修改就被覆盖了,重新被gcc编译为“1”了。所以我们的makefile要设定为make clean时不将cmd.mk和rule.mk这类makefile清除。
MAKEFLAGS = -s
CCSTR = 'CC -[M] generating makefile... '$<
PRINTCSTR = @echo $(CCSTR)
CCSTRLMK = 'LMKFBUID -[M] generating makefile... '$<
PRINTCSTRLMK = @echo $(CCSTRLMK)
CC = gcc
CPPFLGSLDS = $(HEADFILE_PATH) -E -P
KERNELCE_PATH = ../script/
HEADFILE_PATH = -I ../include/script/ -I ../include/ -I ../include/bastypeinc -I ../include/halinc
CCBUILDPATH = $(KERNELCE_PATH)
LMKFBUID = ./lmkfbuild
PREMENTMKI_OBJS = hylic_objs.mki hylic_mem_link.lds hylic_cmd.mki hylic_rule.mki hylic_hal.mki
PREMENTMK_OBJS = hylic_objs.mk hylic_build_link.mk hylic_cmd.mk hylic_rule.mk hylic_hal.mk
.PHONY : all everything everymk build_kernel
all: build_kernel
build_kernel:everything everymk
everything : $(PREMENTMKI_OBJS)
everymk : $(PREMENTMK_OBJS)
%.lds : $(CCBUILDPATH)%.S
$(CC) $(CPPFLGSLDS) -o $@ $<
$(PRINTCSTR)
%.mkh : $(CCBUILDPATH)%.S
$(CC) $(CPPFLGSLDS) -o $@ $<
$(PRINTCSTR)
%.mki : $(CCBUILDPATH)%.S
$(CC) $(CPPFLGSLDS) -o $@ $<
$(PRINTCSTR)
%.mk : %.mki
$(LMKFBUID) -i $< -o $@
$(PRINTCSTRLMK)
根据顶层makefile,执行完pretreatment.mkf后就要开始对各个模块进行编译了
hal.mk
可以看到hal.mk会include先前生成的cmd.mk 和 objs.mk
# HylicOS hal layout compiler makefile source
MAKEFLAGS = -s
KERNELCE_PATH = ../hal/
HEADFILE_PATH = -I ../include -I ../include/bastypeinc -I ../include/halinc -I ../include/knlinc -I ../include/libinc -I ../include/drvinc
CCBUILDPATH = $(KERNELCE_PATH)
include hylic_cmd.mk
include hylic_objs.mk
.PHONY : all everything build_kernel
all: build_kernel
build_kernel:everything
everything :$(BUILD_MK_HALY_OBJS)
include hylic_rule.mk