makefile简介-看了以后就差不多能写一手makefile

前言

有没有一些朋友和我一样,在做工程开发时,修改了一点文件内容后究竟是要build还是rebuild all?文件与文件之间的互相依赖关系究竟是如何体现的呢?

makefile

简言之,makefile就是表达文件之间的依赖关系的东西。咱们的编译器比较棒,帮咱们实现了这一步,所以很多朋友不了解如何写一个工程的makefile文件。
make是如何发现文件有更新呢? 这是和文件的时间属性有关系。当我们修改了文件后,文件的时间属性则会跟着改变。但是该文件对应编译出的过程文件还是旧的,于是make就认为此文件需要重新编译一次,包括牵连的其他上层文件。
好了啰嗦了这么多,开整。

./ …/

这俩大家应该都不陌生
./表示当前路径
…/表示当前路径的父亲路径

基本格式

目标文件:依赖文件
[tab]命令(一个命令单独占用一行)

伪目标

伪目标:
[tab]命令(一个命令单独占用一行)
看起来好像是缺少依赖文件,但是连依赖文件都没有,那肯定也就没有生成文件了(单独一个c文件编译出来又没人用,有啥用)
为了防止伪目标和目标文件重名,就这样写:
.PHONY:伪目标
伪目标:
[tab]命令(一个命令单独占用一行)
然后执行的时候可以
make 伪目标即可

递归推导

make执行某一操作时发现依赖项没有生成,于是make会找到生成该依赖项的操作,优先执行。(就像是dfs)

定义变量

其实makefile中变量的定义就是定义字符串。定义时
变量名=值(字符串)
使用时
$(变量名)
就非常简单对吧。这东西还是能帮你节省很多书写的。

系统变量

预定义变量含义
$*不包含扩展名的目标文件名称。
$+所有的依赖文件,以空格分开,并以出现的先后为序,可能包含重复的依赖文件。
$<第一个依赖文件的名称。
$?所有的依赖文件,以空格分开,这些依赖文件的修改日期比目标的创建日期晚。
$@目标的完整名称。
$^所有的依赖文件,以空格分开,不包含重复的依赖文件。
$%如果目标是归档成员,则该变量表示目标的归档成员名称。例如,如果目标名称为mytarget.so(image.o),则 $@ 为 mytarget.so,而 $% 为 image.o。
AR归档维护程序的名称,默认值为 ar。
ARFLAGS归档维护程序的选项。
AS汇编程序的名称,默认值为 as。
ASFLAGS汇编程序的选项。
CCC 编译器的名称,默认值为 cc。
CCFLAGSC 编译器的选项。
CPPC 预编译器的名称,默认值为 $(CC) -E。
CPPFLAGSC 预编译的选项。
CXXC++ 编译器的名称,默认值为 g++。
CXXFLAGSC++ 编译器的选项。
FCFORTRAN 编译器的名称,默认值为 f77。
FFLAGSFORTRAN 编译器的选项。

实战

以内核工程代码为例。此工程下包含汇编文件与c文件,因此需要将c代码汇编编译,将汇编代码编译。而编译过程中输出的过程文件.obj可以单独存放在一个文件夹下。

BUILD_DIR = ./build

接下来我们决定汇编的编译工具、c的编译工具以及各自的编译配置参数(这些东西重复性很高的,直接定义成变量)配置参数看不懂没关系,知道有这么回事情就行。

AS = nasm
CC = gcc
ASFLAGS = -f elf
CFLAGS = -Wall $(LIB) -c -fno-builtin -W -Wstrict-prototypes \
         -Wmissing-prototypes

各自都通过编译阶段和汇编阶段,那么最后一个阶段自然是将obj文件链接成总的可执行文件,所以链接器工具、链接器参数也需要定义一下:

LD = ld
LDFLAGS = -Ttext $(ENTRY_POINT) -e main -Map $(BUILD_DIR)/kernel.map

OK,下一步咱就把c文件编译出来

$(BUILD_DIR)/main.o: kernel/main.c lib/kernel/print.h \
        lib/stdint.h kernel/init.h
	$(CC) $(CFLAGS) $< -o $@

这一步需要注意的是,CFLAGS是c编译器的参数,这里咱们选择的是gcc编译器,参数中-I表示指定头文件的路径,咱们需要事先定义出来:

LIB = -I lib/ -I lib/kernel/ -I lib/user/ -I kernel/ -I device/ -I thread/ -I userprog/ -I fs/ -I shell/

这个时候你再回头看看CFLAGS就清楚了。
好了下一步是将汇编文件编译出来:

$(BUILD_DIR)/kernel.o: kernel/kernel.S
	$(AS) $(ASFLAGS) $< -o $@

OK接下来是链接这些obj过程文件,输出一个可以直接在内存中运行的bin文件:

$(BUILD_DIR)/kernel.bin: $(OBJS)
	$(LD) $(LDFLAGS) $^ -o $@

这里你一定发现了,我是偷懒将生成的obj文件定义成了变量:

OBJS = $(BUILD_DIR)/main.o $(BUILD_DIR)/init.o $(BUILD_DIR)/interrupt.o \
      $(BUILD_DIR)/timer.o $(BUILD_DIR)/kernel.o $(BUILD_DIR)/print.o

很棒,只剩下最后一步了,就是写点伪目标给make用。我们是自己计划将obj编译阶段的文件放在一个文件夹里,万一文件夹不存在咋办呢?文件夹如果存在了,那得给出一个build伪目标对吧。用户决定删除obj文件,可以吧?再给我一个一劳永逸的伪目标,可以不?
这样一分析,四条伪目标就出现了:

.PHONY : mk_dir build clean all
mk_dir:
	if [[ ! -d $(BUILD_DIR) ]];then mkdir $(BUILD_DIR);fi

clean:
	cd $(BUILD_DIR) && rm -f ./*

build: $(BUILD_DIR)/kernel.bin

all: mk_dir build

各位不知道有没有发现,build伪目标就很简洁,且居然有依赖项。这里不要惊讶,make发现依赖kernel.bin时就会向上搜索相关的依赖项,逐个生成,所以也就达到了build的效果。
于是用户就可以make all或者make clean,非常开心。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值