第08章上 makefile

通常一个大型程序有多个程序模块文件构成,按照功能划分,模块文件分布在不同的目录中,模块文件之间需要包含头文件,函数调用的情况,它们之间存在依赖关系。通常情况下,我么你编写程序只是修改了某些文件,并不是更新所有的文件,因此只需要重新编译修改过的文件然后进行连接就可以了。

通过make工具来实现只编译改动过的文件,而文件之间的编译规则和依赖关系定义在一个命名为makefile文件中。

make程序的工作原理:

linux中,文件分为属性和数据两部分 ,每个文件有三中时间,分别记录与文件属性和文件数据相关的时间:

  1. atime,access time表示访问文件数据部分的时间,每次读取文件都会更新改时间,cat等,但是ls不改变
  2. ctime,change time,表示文件属性或是数据改变的时间,当文件属性或是数据被修改时,就会更新ctime
  3. mtime,modify time,表示文件数据部分被修改的时间,每次修改数据都会更新此时间

make程序粉笔获取以来文件和目标文件的mtime,对比以来文件的的mtime是否新于目标文件,当新的时候,则需要重新编译目标文件。

1 makefile基本语法

1.1 基本语法

基本语法为:

目标文件:以来文件
[tab]命令

目标文件是最终要生成的文件,可以是以.o结尾的目标文件,也可以是可执行文件,也可以是伪目标文件。

依赖文件主要是指申城此规则中的目标文件,需要那些文件。

命令是值此规则中要执行的动作,这些动作是指各种shell命令。

1.2 命令

test1.o:test.c
    gcc -c -o test1.o test.c
test2.o:test.c test1.o
    gcc -c -o test2.o test2.c test1.o
test2.out:test1.o test2.o
    gcc -o test2.out test1.o test2.o

也就是说,最终的编译指令还是需要我们来编写的。

1.3 伪目标

伪目标不产生真是的目标文件,只定义了规则。因此不存在以来文件。伪目标文件纯粹的执行命令,只要给make指定改为目标名作参数,就能让为目标名的中命令直接执行。

伪目标名不能与真是文件名同名。但是当同名的时候可以通过.PHONY:伪目标名来定义一个伪目标

.PHONY:clean
clean:
    rm ./build/*.o

常用的伪目标名有:

  1. all,所有需要最终生成的文件都在这里,如果要最终生成多个,那么就在这里定义。
  2. cleam,清空辨已完成的所有目标文件
  3. dist,将打包后的tar文件,在压缩
  4. install,将编译好的程序复制到安装目录下
  5. printf,打印已经发生改变的文件
  6. tar,将文件大宝
  7. test,测试makefile流程

makeflie中的目标,是以递归的方式逐层向上查找目标的。

1.4 变量

makefile中可以定义变量

变量的定义格式为:变量名=值 ,值是一个字符串,多个值之间用空格分开,值已经被当做字符串处理,所以不需要加引号

使用变量的时候:$(变量名)

make定义了一些系统级的变量:

变量名 描述
AR 打包程序,默认ar
AS 汇编语言编译器
CC C语言编译器,默认cc
CXX C++语言编译其,默认g++
RM 删除命令,默认rm -f

一些参数类型的变量:

变量名 描述
ARFLAGS 打包程序AR的参数
ASFLAGS 汇编编译其的参数
CFLAGS C语言编译器参数
CXXFLAGS c++编译器参数
CPPFLAGS C预处理器参数
LDFLAGS 链接器参数

1.5 隐含规则

注释使用#

1.6 自动化变量

自动化变量,代表一组文件名。此变量值是这组文件名的一个子集,自动化变量相当于对文件名集合循环遍历一遍。
其含义其实是,在项规则中,下面的这些变量分别指代一些文件。

  1. $@,表示规则中的目标文件名的集合,如果有多个目标文件,$@表示其中的每一个文件名
  2. $<,表示规则中以来文件的第一个文件
  3. $^,表示规则中所有依赖文件的集合。自动去重
  4. $?,表示规则中比目标文件新的以来文件的集合。

通常的用法是:

test.o:test1.c
    $(CC) -o $@ $^

1.7 模糊规则

%用来匹配任意多个非空字符,比如,g%.o表示以g开头,.o结尾的文件。make,会在当前目录下使用该规则匹配所有文件:

test.o:%.c
    $(CC) -o $@ $^

2 代码

我们是用make代替start.h

在根目录下创建一个makefile文件,然后创建build文件夹,注意,make并不能自动的去创建文件夹.

然后在该目录下使用make命令,完成编译和刻录.紧接着手动执行bochs


BUILD_DIR=./build
AS=nasm
NASM_ELF=-f elf
INCLUDE=-I./lib -I ./lib/kernel
ASINCLUDE=-I./boot/include/
CFLAGS=-m32 
LDFLAGS=-Ttext 0xc0001500 -m elf_i386 -e main
CC=gcc

# 注意这里: $(BUILD_DIR)/kernel.o 一定要放在第一个上,因此,这个变量是为连接器准备的.如果不放在第一项,保证错
OBJ=$(BUILD_DIR)/kernel.o $(BUILD_DIR)/print.o $(BUILD_DIR)/idt.o $(BUILD_DIR)/interrupt.o  $(BUILD_DIR)/init.o
    
# 最终要生成的文件,
all: $(BUILD_DIR)/kernel.bin $(BUILD_DIR)/loader.bin $(BUILD_DIR)/mbr.bin

# 编译 并刻录 loader.bin
$(BUILD_DIR)/loader.bin: ./boot/loader.asm
    $(AS) -o $@ $^ $(ASINCLUDE)
    dd if=$(BUILD_DIR)/loader.bin of=./hd60m.img bs=512 count=4 seek=2 conv=notrunc

# 编译 并刻录 mbr.bin 
$(BUILD_DIR)/mbr.bin: ./boot/mbr.asm
    $(AS) -o $@ $^ $(ASINCLUDE)
    dd if=$(BUILD_DIR)/mbr.bin of=./hd60m.img bs=512 count=1 conv=notrunc

# 编译 print.asm
$(BUILD_DIR)/print.o:./lib/kernel/print.asm
    $(AS) $(NASM_ELF) -o $@ $^ $(ASINCLUDE) 

# 编译 idt.asm
$(BUILD_DIR)/idt.o:./kernel/idt.asm
    $(AS) $(NASM_ELF) -o $@ $^ $(ASINCLUDE) 

# 编译 interrupt.c
$(BUILD_DIR)/interrupt.o:./kernel/interrupt.c 
    $(CC) -o $@  $(CFLAGS) -fno-stack-protector  -c $^ $(INCLUDE) 

# 编译 init.c
$(BUILD_DIR)/init.o:./kernel/init.c
    $(CC)  -o $@ $(CFLAGS) -c $^ $(INCLUDE) 

# 编译 main.c
$(BUILD_DIR)/kernel.o:./kernel/main.c
    $(CC)  -o $@ $(CFLAGS) -c $^ $(INCLUDE) 

# 最终链接
$(BUILD_DIR)/kernel.bin:$(OBJ)
    $(LD) -Ttext 0xc0001500 -m elf_i386 -e main -o ./build/kernel.bin $(OBJ)
    dd if=./build/kernel.bin of=./hd60m.img bs=512 count=40 seek=9 conv=notrunc

clean:
    rm -rf ./build/*

猜你喜欢

转载自www.cnblogs.com/perfy576/p/9139129.html