解释一个 Makefile 文件
学习一门技术的目的就是要把它拿来使用,因此,对技术的研究不存在最高境界,只存在实不实用。Makefile的知识也杂七杂入挺多的,真讨厌GNU那帮人一天在那里没事发明新东西,虽然说都有用,但我们学起来就太麻烦啦。因此,不求把Makefile知识学完,只求能满足实际需求。而最实际的需求就是,把下面的Makefile内容看懂吧
IDENT=-D_DEBUG -D_LIBC -DMIPS -DCONS_BAUD="B115200" -DRADEON7000 -DVGA_BASE="0xb0000000"
ENDIAN=EL
.SUFFIXES: .S .c .cpp .o
CROSS_COMPILE =mipsisa32-elf-
#
# Include the make variables (CC, etc...)
#
AS = $(CROSS_COMPILE)as
LD = $(CROSS_COMPILE)ld
CC = $(CROSS_COMPILE)gcc
GPP = $(CROSS_COMPILE)g++
CPP = $(CC) -E
AR = $(CROSS_COMPILE)ar
NM = $(CROSS_COMPILE)nm
STRIP = $(CROSS_COMPILE)strip
OBJCOPY = $(CROSS_COMPILE)objcopy
OBJDUMP = $(CROSS_COMPILE)objdump
RANLIB = $(CROSS_COMPILE)ranlib
SIZE = $(CROSS_COMPILE)size
OPT?= -O2
# source tree is located via $S relative to the compilation directory
ifndef S
S:=.
endif
# Defines
TARGET= ${S}/init/
MACHINE=mips
MACHINE_ARCH=mips
COMPILEDIR=./include/mips/include
OBJDIR=${COMPILEDIR}
PMONDIR=${S}
INCLUDES= -I. -I${S}/include -I${S}/lib -I${S}\
-I${TARGET} -I${COMPILEDIR} -nostdinc
CPPFLAGS= ${INCLUDES} ${IDENT} -D_KERNEL -D_CNOS_\
-${ENDIAN} -mno-abicalls -mips3 -mcpu=r4000 \
-fno-rtti -fomit-frame-pointer -nostdinc -nostdlib \
-fno-exceptions -fcheck-new
CWARNFLAGS= -Wall -Wstrict-prototypes \
-Wno-uninitialized -Wno-format -Wno-main \
-Wnon-virtual-dtor -Wno-parentheses \
-Wno-pmf-conversions -Wno-pointer-arith -Wno-unused-function \
-Wundef
CFLAGS= ${DEBUG} ${CWARNFLAGS} ${OPT} -G 0
AFLAGS= -D_LOCORE -G 0
LFLAGS= -${ENDIAN} -N -G 0 -T$(S)/ldscript/ld.script -e start
STRIPFLAGS= -g -S --strip-debug
HOSTCC?= ${CC}
HOSTED_CPPFLAGS=${CPPFLAGS:S/^-nostdinc$//}
HOSTED_CFLAGS= ${CFLAGS}
# compile rules: rules are named ${TYPE}_${SUFFIX}${CONFIG_DEP}
# where TYPE is NORMAL, DRIVER, or PROFILE}; SUFFIX is the file suffix,
# capitalized (e.g. C for a .c file), and CONFIG_DEP is _C if the file
# is marked as config-dependent.
USRLAND_C= ${CC} ${CFLAGS} ${CPPFLAGS} ${PROF} -c $<
USRLAND_C_C= ${CC} ${CFLAGS} ${CPPFLAGS} ${PROF} ${PARAM} -c $<
NORMAL_C= ${CC} ${CFLAGS} ${CPPFLAGS} ${PROF} -c $<
NORMAL_C_C= ${CC} ${CFLAGS} ${CPPFLAGS} ${PROF} ${PARAM} -c $<
NORMAL_GPP= ${GPP} ${CFLAGS} ${CPPFLAGS} ${PROF} -c $<
DRIVER_C= ${CC} ${CFLAGS} ${CPPFLAGS} ${PROF} -c $<
DRIVER_C_C= ${CC} ${CFLAGS} ${CPPFLAGS} ${PROF} ${PARAM} -c $<
NORMAL_S= ${CC} ${AFLAGS} ${CPPFLAGS} -c $<
NORMAL_S_C= ${AS} ${COPTS} ${PARAM} $< -o $@
####################################################################################
##
##
##
OBJS= init.o \
Hal.o \
Serial.o \
Cnos.o \
Mm.o \
Fs.o \
interrupt.o \
mips.o \
KrnlDebug.o \
desktop.o \
surface.o \
taskbar.o \
window.o \
button.o \
Test.o
####################################################################################
## LIB
##
LIBS= string.o unicodefont16.a
####################################################################################
#
# dirvers
#
DIRVERS = ATIR7000.o i8042.o pci.o ohci.o
####################################################################################
# load lines for config "xxx" will be emitted as:
# xxx: ${SYSTEM_DEP}
# ${SYSTEM_LD_HEAD}
# ${SYSTEM_LD}
# ${SYSTEM_LD_TAIL}
SYSTEM_OBJ= start.o ${OBJS} ${LIBS} ${DIRVERS}
SYSTEM_DEP= Makefile ${SYSTEM_OBJ}
SYSTEM_LD_HEAD= rm -f $@
SYSTEM_LD= @echo ${LD} ${LFLAGS} -o $@ '$${SYSTEM_OBJ}'; \
${LD} ${LFLAGS} -o $@ ${SYSTEM_OBJ}
SYSTEM_LD_TAIL= @${SIZE} $@; chmod 755 $@ ; \
${OBJCOPY} -O binary $@ $@.bin
DEBUG?=
ifeq (${DEBUG}, "-g")
LFLAGS+= -X
SYSTEM_LD_TAIL+=; \
echo cp $@ $@.gdb; rm -f $@.gdb; cp $@ $@.gdb; \
echo ${STRIP} ${STRIPFLAGS} $@; ${STRIP} ${STRIPFLAGS} $@
else
LFLAGS+= -S
endif
#####################################################################################
all: cnos Makefile
cnos: ${SYSTEM_DEP}
${SYSTEM_LD_HEAD}
${SYSTEM_LD}
${SYSTEM_LD_TAIL}
clean::
rm -f tags *.[io] [a-z]*.s \
cnos cnos.bin cnos.txt cnos_elf.txt
dump:
$(OBJDUMP) -DS cnos >cnos.txt
tags:
@echo "see $S/kern/Makefile for tags"
####################################################################################
# %.S
####################################################################################
start.o: $S/init/start.S Makefile
${NORMAL_S}
mips.o: $S/kern/ke/mips.S
${NORMAL_S}
####################################################################################
# %.CPP
####################################################################################
init.o: $S/init/init.cpp
${NORMAL_GPP}
Hal.o: $S/hal/Hal.cpp
${NORMAL_GPP}
Serial.o: $S/hal/Serial.cpp
${NORMAL_GPP}
Cnos.o: $S/kern/ke/Cnos.cpp
${NORMAL_GPP}
Mm.o: $S/mm/Mm.cpp
${NORMAL_GPP}
Fs.o: $S/fs/Fs.cpp
${NORMAL_GPP}
interrupt.o: $S/kern/ke/interrupt.cpp
${NORMAL_GPP}
Test.o: $S/test/Test.cpp
${NORMAL_GPP}
KrnlDebug.o: $S/kern/kd/KrnlDebug.cpp
${NORMAL_GPP}
desktop.o: $S/subsystems/gdi/desktop.cpp
${NORMAL_GPP}
surface.o: $S/subsystems/gdi/surface.cpp
${NORMAL_GPP}
taskbar.o: $S/subsystems/gdi/taskbar.cpp
${NORMAL_GPP}
window.o: $S/subsystems/gdi/window.cpp
${NORMAL_GPP}
button.o: $S/subsystems/gdi/button.cpp
${NORMAL_GPP}
####################################################################################
#
# lib file
#
####################################################################################
string.o: $S/lib/string.cpp
${NORMAL_GPP}
unicodefont16.a: $S/media/font/unicodefont16.cpp
${GPP} ${CFLAGS} ${CPPFLAGS} ${PROF} -o unicodefont16.a -c $<
####################################################################################
#
# driver file
#
####################################################################################
ATIR7000.o: $S/dev/video/atir7000/ATIR7000.cpp
${NORMAL_GPP}
i8042.o: $(S)/dev/input/i8042/i8042.cpp
${NORMAL_GPP}
pci.o: $(S)/dev/bus/pci/pci.cpp
${NORMAL_GPP}
ohci.o: $(S)/dev/bus/usb/ohci.cpp
${NORMAL_GPP}
这个Makefile不算复杂(还算是人写给人看的,没有使用GNU提供的autoconf, automake工具,它们产生的文件太恐怖了,不是给人看的~~)下面来一行一行讲解。
IDENT=-D_DEBUG -D_LIBC -DMIPS -DCONS_BAUD="B115200" -DRADEON7000 -DVGA_BASE="0xb0000000"
ENDIAN=EL
定义了两个字符串 IDENT 和 ENDIAN,IDENT的内容代表的意义后面再讲解;
.SUFFIXES: .S .c .cpp .o
这句定义make工具隐含规则准备操作的文件的后缀名;
CROSS_COMPILE =mipsisa32-elf-
定义一个字符串变量 CROSS_COMPILE,一般在交叉编译环境中会经常看到这样的定义;
#
# Include the make variables (CC, etc...)
#
AS = $(CROSS_COMPILE)as
LD = $(CROSS_COMPILE)ld
CC = $(CROSS_COMPILE)gcc
GPP = $(CROSS_COMPILE)g++
CPP = $(CC) -E
AR = $(CROSS_COMPILE)ar
NM = $(CROSS_COMPILE)nm
STRIP = $(CROSS_COMPILE)strip
OBJCOPY = $(CROSS_COMPILE)objcopy
OBJDUMP = $(CROSS_COMPILE)objdump
RANLIB = $(CROSS_COMPILE)ranlib
SIZE = $(CROSS_COMPILE)size
这一段定义了一套编译工具变量,这里引用了前面定义的变量 CROSS_COMPILE(可以看到定义变量的好处了,以后修改可以很方便);
OPT?= -O2
这句定义优化级别,加个?号表示如果此变量以前被定义过,就不再赋值了,否则要赋值;
# source tree is located via $S relative to the compilation directory
ifndef S
S:=.
endif
这里也用到了类似C语言的条件编译命令,这里是想定义一个变量,用来指示当前的编译根目录。:=也是赋值号, . 号表示当前目录;
# Defines
TARGET= ${S}/init/
MACHINE=mips
MACHINE_ARCH=mips
COMPILEDIR=./include/mips/include
OBJDIR=${COMPILEDIR}
PMONDIR=${S}
继续定义一些方便编译操作的变量。${} 和 $() 功能都是引用字符串变量的值。
INCLUDES= -I. -I${S}/include -I${S}/lib -I${S}\
-I${TARGET} -I${COMPILEDIR} -nostdinc
这一句定义编译时要包含的头文件的路径,实际上是定义的搜索路径。-I 是gcc定义的要找一个路径的语法前缀。-nostdinc 表示不要把系统提供的头文件路径给包含进来,这样就防止了系统头文件对我们自己开发的系统头文件的干扰;
CPPFLAGS= ${INCLUDES} ${IDENT} -D_KERNEL -D_CNOS_\
-${ENDIAN} -mno-abicalls -mips3 -mcpu=r4000 \
-fno-rtti -fomit-frame-pointer -nostdinc -nostdlib \
-fno-exceptions -fcheck-new
这里定义一些gcc编译时的参数,用到了前面定义的一些字符串变量。现在还不懂 -D_KERNEL -D_CNOS_ 是啥意思;
CWARNFLAGS= -Wall -Wstrict-prototypes \
-Wno-uninitialized -Wno-format -Wno-main \
-Wnon-virtual-dtor -Wno-parentheses \
-Wno-pmf-conversions -Wno-pointer-arith -Wno-unused-function \
-Wundef
这里设置gcc编译时的警告级别,参数的意义要看gcc手册才能清楚;
CFLAGS= ${DEBUG} ${CWARNFLAGS} ${OPT} -G 0
AFLAGS= -D_LOCORE -G 0
LFLAGS= -${ENDIAN} -N -G 0 -T$(S)/ldscript/ld.script -e start
STRIPFLAGS= -g -S --strip-debug
这里定义标准的几个编译时环境变量,用到了前面的定义;
HOSTCC?= ${CC}
HOSTED_CPPFLAGS=${CPPFLAGS:S/^-nostdinc$//}
HOSTED_CFLAGS= ${CFLAGS}
(???)这一块功能不知道是干什么用的;
# compile rules: rules are named ${TYPE}_${SUFFIX}${CONFIG_DEP}
# where TYPE is NORMAL, DRIVER, or PROFILE}; SUFFIX is the file suffix,
# capitalized (e.g. C for a .c file), and CONFIG_DEP is _C if the file
# is marked as config-dependent.
USRLAND_C= ${CC} ${CFLAGS} ${CPPFLAGS} ${PROF} -c $<
USRLAND_C_C= ${CC} ${CFLAGS} ${CPPFLAGS} ${PROF} ${PARAM} -c $<
NORMAL_C= ${CC} ${CFLAGS} ${CPPFLAGS} ${PROF} -c $<
NORMAL_C_C= ${CC} ${CFLAGS} ${CPPFLAGS} ${PROF} ${PARAM} -c $<
NORMAL_GPP= ${GPP} ${CFLAGS} ${CPPFLAGS} ${PROF} -c $<
DRIVER_C= ${CC} ${CFLAGS} ${CPPFLAGS} ${PROF} -c $<
DRIVER_C_C= ${CC} ${CFLAGS} ${CPPFLAGS} ${PROF} ${PARAM} -c $<
NORMAL_S= ${CC} ${AFLAGS} ${CPPFLAGS} -c $<
NORMAL_S_C= ${AS} ${COPTS} ${PARAM} $< -o $@
(???)难~~~这一段好像是定义不同的编译规则的,分几种不同的目的。
####################################################################################
##
##
##
OBJS= init.o \
Hal.o \
Serial.o \
Cnos.o \
Mm.o \
Fs.o \
interrupt.o \
mips.o \
KrnlDebug.o \
desktop.o \
surface.o \
taskbar.o \
window.o \
button.o \
Test.o
普通文件目标定义
####################################################################################
## LIB
##
LIBS= string.o unicodefont16.a
库文件目标(包括中文字库)
####################################################################################
#
# dirvers
#
DIRVERS = ATIR7000.o i8042.o pci.o ohci.o
驱动程序目标
####################################################################################
# load lines for config "xxx" will be emitted as:
# xxx: ${SYSTEM_DEP}
# ${SYSTEM_LD_HEAD}
# ${SYSTEM_LD}
# ${SYSTEM_LD_TAIL}
SYSTEM_OBJ= start.o ${OBJS} ${LIBS} ${DIRVERS}
SYSTEM_DEP= Makefile ${SYSTEM_OBJ}
SYSTEM_LD_HEAD= rm -f $@
SYSTEM_LD= @echo ${LD} ${LFLAGS} -o $@ '$${SYSTEM_OBJ}'; \
${LD} ${LFLAGS} -o $@ ${SYSTEM_OBJ}
SYSTEM_LD_TAIL= @${SIZE} $@; chmod 755 $@ ; \
${OBJCOPY} -O binary $@ $@.bin
DEBUG?=
ifeq (${DEBUG}, "-g")
LFLAGS+= -X
SYSTEM_LD_TAIL+=; \
echo cp $@ $@.gdb; rm -f $@.gdb; cp $@ $@.gdb; \
echo ${STRIP} ${STRIPFLAGS} $@; ${STRIP} ${STRIPFLAGS} $@
else
LFLAGS+= -S
endif
晕死,看不懂是做什么用的;
前面都在定义编译时的规则,后面才是真正的文件编译区。
#####################################################################################
all: cnos Makefile
cnos: ${SYSTEM_DEP}
${SYSTEM_LD_HEAD}
${SYSTEM_LD}
${SYSTEM_LD_TAIL}
clean::
rm -f tags *.[io] [a-z]*.s \
cnos cnos.bin cnos.txt cnos_elf.txt
dump:
$(OBJDUMP) -DS cnos >cnos.txt
tags:
@echo "see $S/kern/Makefile for tags"
这一段总算是比较规则的了,要生成一个 cnos 二进制文件;
####################################################################################
# %.S
####################################################################################
start.o: $S/init/start.S Makefile
${NORMAL_S}
mips.o: $S/kern/ke/mips.S
${NORMAL_S}
这是接下来的依赖关系,这里是两个汇编文件,正常编译;
####################################################################################
# %.CPP
####################################################################################
init.o: $S/init/init.cpp
${NORMAL_GPP}
Hal.o: $S/hal/Hal.cpp
${NORMAL_GPP}
Serial.o: $S/hal/Serial.cpp
${NORMAL_GPP}
Cnos.o: $S/kern/ke/Cnos.cpp
${NORMAL_GPP}
Mm.o: $S/mm/Mm.cpp
${NORMAL_GPP}
Fs.o: $S/fs/Fs.cpp
${NORMAL_GPP}
interrupt.o: $S/kern/ke/interrupt.cpp
${NORMAL_GPP}
Test.o: $S/test/Test.cpp
${NORMAL_GPP}
KrnlDebug.o: $S/kern/kd/KrnlDebug.cpp
${NORMAL_GPP}
desktop.o: $S/subsystems/gdi/desktop.cpp
${NORMAL_GPP}
surface.o: $S/subsystems/gdi/surface.cpp
${NORMAL_GPP}
taskbar.o: $S/subsystems/gdi/taskbar.cpp
${NORMAL_GPP}
window.o: $S/subsystems/gdi/window.cpp
${NORMAL_GPP}
button.o: $S/subsystems/gdi/button.cpp
${NORMAL_GPP}
系统的主体C++代码文件的编译;
####################################################################################
#
# lib file
#
####################################################################################
string.o: $S/lib/string.cpp
${NORMAL_GPP}
unicodefont16.a: $S/media/font/unicodefont16.cpp
${GPP} ${CFLAGS} ${CPPFLAGS} ${PROF} -o unicodefont16.a -c $<
库文件的编译;
####################################################################################
#
# driver file
#
####################################################################################
ATIR7000.o: $S/dev/video/atir7000/ATIR7000.cpp
${NORMAL_GPP}
i8042.o: $(S)/dev/input/i8042/i8042.cpp
${NORMAL_GPP}
pci.o: $(S)/dev/bus/pci/pci.cpp
${NORMAL_GPP}
ohci.o: $(S)/dev/bus/usb/ohci.cpp
${NORMAL_GPP}
驱动文件的编译。