基本的 Makefile 写法
寒假的作业是写一个操作系统。老师给我们的要求比较低,基本上是能看懂学长以前写过的代码就给及格,自己写的像样的基本上良以上了。当然一个学期蜻蜓点水地学了这点东西,写一个操作系统,能不能“操作”,还是个大问号呢。
参考书自然想到了于渊的《一个操作系统的实现》。目前基本上就是抄着大神的代码,然后改改变量名啥的。抄到第三章的时候,看起来以后还要继续编译好多次,所以就干脆写个 Makefile 偷懒。但是这 Makefile 我是需要写的时候就查,写完之后又忘。所以干脆自己写个文章记下来算了。
所以,这篇文章适合那些对 Makefile 有初步了解,但是不常用,需要找点线索来回忆的人(比如我)。如果是对 Makefile 一无所知的话,请 Google 之。
Makefile 是由很多组目标规则组成的。某种角度说,目标分为两种,一种是文件目标,一种是非文件目标。嗯,先写一个简单的 Makefile 吧:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | # 定义了两个非文件目标 .PHONY = all clean # 在未指定 make 的目标时,第一个目标为默认 all : test.bin # 这是非文件目标,因为 .PHONY 中定义了 clean : rm -rf *.o *.bin # 这是文件目标 test.bin : main.o func.o ld -o test.bin main.o func.o # 这是文件目标 main.o : main.c type.h gcc -c -o main.o main.c # 这是文件目标 func.o : func.c type.h gcc -c -o func.o func.c |
需要注意的是,编写 Makefile 时,命令一定要以 Tab 开始,而不能以空格取代之。所以使用的编辑器一定要支持 Tab 并且不把 Tab 转换成空格才行。
然后,比如在写有上面 Makefile 的目录里,执行了 make all 操作,那么 make 程序将寻找 all 目标的依赖目标,即 test.bin, 是否在 Makefile 里存在。在上例中确实是存在的,所以 make 程序就去看 test.bin 的两个依赖目标,即 main.o 和 func.o 在 Makefile 里是否存在,事实是它们也是存在的。所以 make 先去 main.o 去看它的依赖目标,即 main.c 和 type.h, 但它们不在 Makefile 里,所以 make 程序就去寻找这两个文件是否存在。如果它们有任何一个比 main.o 要新,就执行 main.o 下的命令,把 main.o 编译出来。func.o 也类似。然后回溯到 test.bin, 如果 main.o 和 func.o 任何一个有更新,那么 test.bin 也会更新。然后,整个编译过程就结束了。
感觉这种以依赖的方式控制编译流程是挺好的。
最后把我目前操作系统写的 Makefile 贴出来备忘吧。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | # C Compiler CC = gcc CCFLAGS = -I include/ -I include/libcrt/ -fno-builtin -fno-stack-protector -fpack-struct -Wall CCOFLAGS = $(CCFLAGS) -c # Assembly Compiler ASM = nasm ASMFLAGS = -I include/ ASMOFLAGS = $(ASMFLAGS) -f elf # Linker LD = ld LDFLAGS = -s # Archiver AR = ar ARFLAGS = rcs # Image Maker IMG = bximage IMGFFLAGS = -fd -size=1.44 -q IMGHFLAGS = -hd -size=16 -mode=flat -q # Linux Mount MNT = mount MNTFLAGS = -o loop UMNT = umount UMNTFLAGS = MNTFTMP = /mnt/cosftmp/ MNTHTMP = /mnt/coshtmp/ # Library & kernel dependent objects LIBCRT_OBJECTS = tmp/cstringa.o tmp/cstringc.o tmp/cstdlibc.o KERNEL_OBJECTS = tmp/kproteca.o tmp/kprotecc.o tmp/kernelc.o tmp/kprocesc.o DEVICE_OBJECTS = \ tmp/dvideoa.o tmp/dvideoc.o tmp/dclocka.o tmp/dclockc.o tmp/dkeybdc.o TASK_OBJECTS = \ tmp/terminc.o # Kernel configurations KERNEL_ENTRY = 0x30400 KERNEL_TARGETS = $(KERNEL_OBJECTS) $(DEVICE_OBJECTS) $(TASK_OBJECTS) .PHONY : all clean path fd hd cd all : fd hd cd clean : rm -rf bin tmp path : bin tmp bin : mkdir bin tmp : mkdir tmp fd : path bin/coslivef.img hd : path bin/cosliveh.img cd : path bin/coslivec.img bin/coslivef.img : tmp/bsfat12 tmp/loader tmp/kernel @echo '>>> Make live floppy image.' @rm -rf $@ $(IMG) $(IMGFFLAGS) $@ @echo '>>> Flush boot sector into live floppy.' dd if=tmp/bsfat12 of=$@ bs=512 count=1 conv=notrunc @echo '>>> Copy loader into live floppy, sudo password required.' @echo `sudo $(UMNT) $(MNTFTMP)`Unmounted temporary device. @sudo rm -rf $(MNTFTMP) @sudo mkdir $(MNTFTMP) sudo $(MNT) $(MNTFLAGS) $@ $(MNTFTMP) sudo cp -fv tmp/loader $(MNTFTMP) sudo cp -fv tmp/kernel $(MNTFTMP) sudo $(UMNT) $(MNTFTMP) @sudo rm -rf $(MNTFTMP) bin/cosliveh.img : @echo '>>> Cannot make hard disk image at this moment.' bin/coslivec.img : @echo '>>> Cannot make CD image at this moment.' # FAT12 bootloader and kernel tmp/bsfat12 : src/boot/bsfat12.asm $(ASM) $(ASMFLAGS) -o $@ $< tmp/loader : src/boot/loader.asm $(ASM) $(ASMFLAGS) -o $@ $< tmp/kernel : $(KERNEL_TARGETS) tmp/coscrt.a $(LD) $(LDFLAGS) -Ttext $(KERNEL_ENTRY) -o $@ $^ # OS C dynamic runtime library tmp/coscrt.a : $(LIBCRT_OBJECTS) $(AR) $(ARFLAGS) $@ $^ tmp/cstringa.o : src/libcrt/string.asm $(ASM) $(ASMOFLAGS) -o $@ $< tmp/cstringc.o : src/libcrt/string.c $(CC) $(CCOFLAGS) -o $@ $< tmp/cstdlibc.o : src/libcrt/stdlib.c $(CC) $(CCOFLAGS) -o $@ $< # OS kernel objects tmp/kproteca.o : src/kernel/protect.asm $(ASM) $(ASMOFLAGS) -o $@ $< tmp/kprotecc.o : src/kernel/protect.c $(CC) $(CCOFLAGS) -o $@ $< tmp/kernelc.o : src/kernel/kernel.c $(CC) $(CCOFLAGS) -o $@ $< tmp/kprocesc.o : src/kernel/process.c $(CC) $(CCOFLAGS) -o $@ $< # OS kernel devices tmp/dvideoa.o : src/device/video.asm $(ASM) $(ASMOFLAGS) -o $@ $< tmp/dvideoc.o : src/device/video.c $(CC) $(CCOFLAGS) -o $@ $< tmp/dclocka.o : src/device/clock.asm $(ASM) $(ASMOFLAGS) -o $@ $< tmp/dclockc.o : src/device/clock.c $(CC) $(CCOFLAGS) -o $@ $< tmp/dkeybdc.o : src/device/keyboard.c $(CC) $(CCOFLAGS) -o $@ $< # OS kernel tasks tmp/terminc.o : src/task/terminal.c $(CC) $(CCOFLAGS) -o $@ $< |
