Makefile基础

Makefile基础

一、Makefile是什么

(1)目标文件依赖哪些文件?
(2)依赖的文件是否更新?
make 和makefile 并不是用来编译程序的,它只负责找出哪些文件有变化,并且根据依赖关系找出受影响的文件,然后执行事先在makefile 中定义好的命令规则。
因为make 就是在shell 下执行的,所以在makefile 中,位于命令规则里的那些命令,都是shell 命令。

二、Makefile基本语法

目标文件:依赖文件
[Tab键]命令
(1)目标文件是指此规则中想要生成的文件,可以是.o 结尾的目标文件,也可以是可执行文件,也可以是个伪目标,后面会介绍伪目标。
(2)依赖文件是指要生成此规则中的目标文件,需要哪些文件。通常依赖文件不是1 个,所以此处是个依赖文件的列表。
(3)命令是指此规则中要执行的动作,这些动作是指各种shell 命令。命令可以有多个,但一个命令要单独占用一行,在行首必须以Tab 开头。这是make 规定的用法,这样make 在解析到以Tab 开头的行时便知道这是要执行的命令

2.1 make 程序是怎样判断文件有过更新呢?

Linux 中,文件分为属性和数据两部分,每个文件有三种时间,分别用于记录与文件属性和文件数据相关的时间,这三个时间分别是atime、ctime、mtime。

时间含义
atimeaccess time访问文件的时间,是读取就会改变,比如cat/less等命令访问文件就会更新atime,但ls查看文件不会更新atime
ctimechange time文件属性或数据修改时,就会被更新
mtimemodify time文件数据部分修改时,会被更新。如果mtime被更新,则ctime肯定也被更新了

make就是判断依赖文件和目标文件的mtime是否一致,如果依赖文件的mtime比目标文件的mtime新,则需要执行命令(命令不一定是编译命令,可以是任何shell命令)。比如如下这个makefile例子:

1:2
        @echo "2 is newer than 1"

然后新建两个文件1和2

# 首先touch 两个文件1和2,可以看到起mtime是一致的
[root@localhost test]# touch 1 2
[root@localhost test]# stat -c %y 1 2			#mtime一致
2021-03-31 23:11:32.926225333 -0400
2021-03-31 23:11:32.926225333 -0400
[root@localhost test]# make
make: '1' is up to date.						#make命令并没有做什么
[root@localhost test]# vi 2						#修改文件2的内容
[root@localhost test]# stat -c %y 1 2			#2的mtime要比1新了
2021-03-31 23:11:32.926225333 -0400
2021-03-31 23:11:56.588623644 -0400
[root@localhost test]# make						#make执行了echo命令
2 is newer than 1

如果目标文件1的mtime新于依赖文件2会什么结果?

[root@localhost test]# vi 1						#修改目标文件1的内容
[root@localhost test]# stat -c %y 1 2			#目标文件1的mtime新于依赖文件2
2021-03-31 23:13:56.341639459 -0400
2021-03-31 23:11:56.588623644 -0400
[root@localhost test]# make
make: '1' is up to date.
2.2 跳到目标处执行

如下makefile例子,当执行make时,执行完第一个目标后就退出了。

[root@localhost test]# cat Makefile
t1:1
        @echo "make t1"

t2:1
        @echo "make t2"

[root@localhost test]# make					#执行完第一个目标就退出了
make t1

[root@localhost test]# make t2				#指定目标t2
make t2

[root@localhost test]# make t1				#指定目标t1
make t1
2.3 伪目标

通过上面的例子您看到了,规则中的命令并不总是被执行,有时候我们并不关心是否产生真实的目标文件,我们只希望make 不要考虑mtime,而是总能去执行一些命令。
对于这个需求还是有办法的,make 规定,当规则中不存在依赖文件时,这个目标文件名就称为—伪目标。

[root@localhost test]# cat Makefile
test:
        @echo "test ok"

[root@localhost test]# make
test ok

伪目标不能和真实目标文件同名,否则就失去伪目标的意义了,为了避免伪目标和真实目标文件同名的情况,可以用关键字“.PHONY”来修饰伪目标,格式为“.PHONY:伪目标名”,这样不管与伪目标同名的文件是否存在,make 照样执行伪目标处的命令。

通常需要显式用.PHONY 修饰伪目标的场合是删除编译过程中的.o 文件,这是为了避免因旧的.o 文件已存在而影响编译。如果您在Linux 下有过编译源码的经验,就会了解make clean 的作用了,通常clean就是伪目标,用来删除编译过程中的.o 文件。

怪不得clean和all同时存在也可以,因为执行完all就退出了,再执行make clean就只是clean清除

2.4 变量

makefile可以自定义变量,其也要一些系统定义好的变量。

[root@localhost test]# cat Makefile
all:
        @echo AR        :       $(AR)
        @echo AS        :       $(AS)
        @echo CC        :       $(CC)
        @echo CXX       :       $(CXX)
        @echo CPP       :       $(AR)
        @echo FC        :       $(FC)
        @echo GET       :       $(GET)
        @echo PC        :       $(PC)
        @echo MAKEINFO  :       $(MAKEINFO)
        @echo RM        :       $(RM)
        @echo TEX       :       $(TEX)
        @echo WEAVE     :       $(WEAVE)
        @echo YACC      :       $(YACC)
        @echo YACCR     :       $(YACCR)

        @echo ARFLAGS   :       $(ARFLGAS)
        @echo ASFLAGS   :       $(ASFLAGS)
        @echo CFLAGS    :       $(CFLAGS)
        @echo CXXFLAGS  :       $(CXXFLAGS)
        @echo CPPFLAGS  :       $(CPPFLAGS)
        @echo FFLAGS    :       $(FFLAGS)
        @echo LDFLAGS   :       $(LDFLAGS)
        @echo PFLAGS    :       $(PFLAGS)
        @echo YFLAGS    :       $(YFLAGS)
        
[root@localhost test]# make
AR : ar
AS : as
CC : cc
CXX : g++
CPP : ar
FC : f77
GET : get
PC : pc
MAKEINFO : makeinfo
RM : rm -f
TEX : tex
WEAVE : weave
YACC : yacc
YACCR :
ARFLAGS :
ASFLAGS :
CFLAGS :
CXXFLAGS :
CPPFLAGS :
FFLAGS :
LDFLAGS :
PFLAGS :
YFLAGS :
命令相关的变量名含义默认
AR打包程序ar
AS汇编语言编译器as
CCC语言编译器cc
CXXC++语言编译器g++
CPPC预处理器,默认是$(CC) -Egcc -E
FCFortan的编译器和预处理器f77
GET从SCCS文件中提取文件程序get
PCPascal语言编译器pc
MAKEINFO将texinfo文件转换为info文件makeinfo
RM删除命令rm -f
TEX从TeX源文件中创建TexDVI文件的程序tex
WEAVE将Web转换为TeX的程序weave
YACC处理C程序的Yacc词法分析器yacc
YACCR处理Ratfor程序的Yacc词法分析器yacc -r

参数相关的系统变量(列举部分),几乎都没有默认值

变量名含义默认
ARFLAGS打包程序$(AR)的参数rv
ASFLAGS汇编语言编译器参数
CFLAGSC语言编译器参数
CXXFLAGSC++编译器参数
CPPFLAGSC预处理器参数
FFLAGSFortan语言编译器参数
LDFLAGS链接器参数
PFLAGSPascal语言编译器参数
YFLAGSYacc词法分析器参数
2.5 隐含规则

一行写不下,用 \进行换行。

注释,用#进行注释。

什么是隐含规则?对于一些使用频率非常高的规则,make 把它们当成是默认的,不需要显式地写出来,当用户未在makefile 中显式定义规则时,将默认使用隐含规则进行推导。

隐含规则只限于那些编译过程中基本固定的依赖关系,比如C 语言代码文件扩展名为.c,编译生成的目标文件扩展名是.o,这一般是一对一的。而一个可执行程序可能是由多个.o 文件共同链接生成的,所以,从可执行程序到.o 文件的关系有可能是一对多,这种不确定性无法使之成为隐含的规则。所以,对于C语言的依赖关系是:文件名.o 依赖于文件名.c,仅限于源文件生成.o 目标文件,不存在.o 文件生成可执行程序的隐含规则。

2.6 自动化变量
变量名含义
$@规则中的目标文件名集合
$<规则中依赖文件中的第一个文件
$^规则中所有依赖文件的集合
$?规则中,所有比目标文件mtime更新的依赖文件集合
2.7 将每个.c文件生成对应的二进制
# File paths
SRC_DIR := .
BUILD_DIR := .
OBJ_DIR := $(BUILD_DIR)

# Compilation flags
CC := gcc
LD := gcc
CFLAGS := -Wall

# Files to be compiled
SRCS := $(wildcard $(SRC_DIR)/*.c)
OBJS := $(SRCS:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o)
BUILD := $(OBJS:$(OBJ_DIR)/%.o=$(BUILD_DIR)/%)

# Don't remove *.o files automatically
.SECONDARY: $(OBJS)

all: $(BUILD)

# Compile each *.c file as *.o files
# @mkdir -p $(OBJ_DIR)
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c
    @echo + CC $<
    @$(CC) $(CFLAGS) -c -o $@ $<

# Link each *.o file as executable files
# @mkdir -p $(BUILD_DIR)
$(BUILD_DIR)/%: $(OBJ_DIR)/%.o
    @echo + LD $@
    @$(LD) $(CFLAGS) -o $@ $<

.PHONY: all clean
clean:
    rm -rvf $(OBJS) $(BUILD)

参考:《操作系统真相还原》一书,网络博客

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值