Makefile 学习笔记

Posted 皮卡丘吉尔

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Makefile 学习笔记相关的知识,希望对你有一定的参考价值。

Makefile学习笔记

1. gcc编译过程

  • 预处理

    gcc -E hello.c -o hello.i
    
  • 编译

    gcc -S hello.i -o hello.s
    
  • 汇编

    gcc -c hello.s -o hello.o
    
  • 链接

    gcc hello.o -o hello
    

2. Makefile 基本介绍

我们把编译分为两个步骤。

  • 先编译

    gcc -c a.c -o a.o
    gcc -c b.c -o b.o
    gcc -c c.c -o c.o
    ...
    
  • 再链接

    gcc a.o b.o c.o -o hello
    

编译的原则是 修改的才会被编译。这样增加效率。

如何判断哪个文件被修改了,哪些没修改?

我们通过文件修改时间来判断。

规则1

目标文件: 依赖文件
[Tab]命令

当依赖比目标新 或者 目标文件不存在 执行命令

# hello 依赖于 a.o b.o c.o
hello : a.o b.o c.o
	gcc a.o b.o c.o -o hello
# a.o 依赖于 a.c
a.o : a.c
	gcc -c a.c -o a.o
# b.o 依赖于 b.c
b.o : b.c
	gcc -c b.c -o b.o
# c.o 依赖于 c.c
c.o : c.c
	gcc -c c.c -o c.o

规则2
Makefile 语法

    1. 通配符: %.o
    1. 假想目标: .PHONY
    1. 即时变量、延时变量 export
# hello 依赖于 a.o b.o c.o 文件
hello : a.o b.o c.o
	gcc $^ -o hello
# %.o 依赖于 %.c 文件
%.o : %.c
	gcc -c $< -o $@ 
  • $< : 第一个依赖文件
  • $@ : 目标文件
  • $^ : 所有的依赖文件

3. Makefile 使用

# hello 依赖于 a.o b.o c.o 文件
hello : a.o b.o c.o
	gcc $^ -o hello
# %.o 依赖于 %.c 文件
%.o : %.c
	gcc -c $< -o $@
clean:
	rm *.o hello
.PHONY: clean

make [目标]

  • 目标可以不写、则默认是 第一个目标

  • make 是执行 第一个目标文件

  • 当执行 make clean 则可以删除 所有文件

  • .PHONY: clean 是防止你执行 make clean 时刚好你 有 clean文件。

即时变量 和 延时变量

  • A := xxx A的值即刻确定
  • B = xxx B的值使用到的时候才确定
A := $(C)
B = $(C)
#C = abc
all:
	@echo A = $(A)
	@echo B = $(B)
C = abc
pikaqiu@ubuntu:~/linux_c/makefile/2$ make
A =
B = abc
  • := : 即时变量
  • = : 延时变量
  • ?= : 延时变量, 如果是第一次定义才起效,如果再前面该变量已定义则忽略这句
  • += : 附加, 它是即时变量还是延时变量取决于前面的定义
A := $(C)
B = $(C)
#C = abc
D = hello
D ?= world
all:
	@echo A = $(A)
	@echo B = $(B)
	@echo D = $(D)
C = abc
pikaqiu@ubuntu:~/linux_c/makefile/2$ make
A =
B = abc
D = hello
pikaqiu@ubuntu:~/linux_c/makefile/2$ make D=ddd
A =
B = abc
D = ddd

4. Makefile 函数

1. $(foreach var, list, text)		# 在 list 挨个取出值 然后给到 var, 然后 变成 text 对应意思
2. $(filter pattern..., text)		# 在 text 中取出符合 patten 格式的值
   $(filter-out pattern..., text)	# 在 text 中取除不符合 patten 格式的值
3. $(wildcard pattern)				# pattern 定义了文件名的格式
									# wildcard 取出其中存在的文件
4. $(patsubst pattern, replacement, $(var))	# 从 val 中取 寻找符合 pattern 格式 的 替换成 replacement 的值  

示例1

  • $(foreach var, list, text) # 在 list 挨个取出值 然后 变成 text对应意思
A = a b c
B = $(foreach f, $(A), $(f).o)

all:
	@echo B = $(B)
pikaqiu@ubuntu:~/linux_c/makefile/3$ make
B = a.o b.o c.o
pikaqiu@ubuntu:~/linux_c/makefile/3$

示例2

  • $(filter pattern..., text) # 在 text 中取出符合 patten 格式的值
C = a b c d/
D = $(filter %/, $(C))				# 获取 C中 符合 %/ 格式的内容
E = $(filter-out %/, $(C))			# 获取 C中 不符合 %/ 格式的内容
all:
	@echo B = $(B)
	@echo D = $(D)
	@echo E = $(E)
pikaqiu@ubuntu:~/linux_c/makefile/3$ make
D = d/
E = a b c
pikaqiu@ubuntu:~/linux_c/makefile/3$ 

示例3

  • $(wildcard pattern) # pattern 定义了文件名的格式 wildcard 取出其中存在的文件
files = $(wildcard *.c)			# 获取当前路径 .c 的文件	

file1 = a.c b.c c.c e.c
file2 = $(wildcard $(file1))	# 获取当前路径文件 有存在 file1的文件

all:
	@echo files = $(files)
	@echo files = $(file2)
pikaqiu@ubuntu:~/linux_c/makefile/3$ ls
a.c  b.c  c.c  d.c  Makefile
pikaqiu@ubuntu:~/linux_c/makefile/3$ make
files = d.c a.c c.c b.c
files = a.c b.c c.c
pikaqiu@ubuntu:~/linux_c/makefile/3$ 

示例4

  • $(patsubst pattern, replacement, $(var)) # 从 val 中取 寻找符合 pattern 格式 的 替换成 replacement 的值
files = a.c b.c c.c ddd
dep_files = $(patsubst %.c, %.d, $(files))	# 从 files变量中 取出每一个值 把 符合.c 替换成.d

all:
	@echo dep_files = $(dep_files)
pikaqiu@ubuntu:~/linux_c/makefile/3$ make
dep_file: a.d b.d c.d ddd
pikaqiu@ubuntu:~/linux_c/makefile/3$

实例5

我们这边做一个简单的 Makefile

  • main.c

    #include <stdio.h>
    #include "add.h"
    
    int main()
    
    	printf("%d\\n", add(X, Y));
    	return 0;
    
    
  • add.c

    #include "add.h"
    
    int add(int x, int y)
    
    	return x + y;
    
    
  • add.h

    #ifndef ADD_H
    #define ADD_H
    
    #define	X	1
    #define	Y	2
    
    int add(int x, int y);
    
    #endif
    
  • Makefile (有bug 当宏被修改 再执行 make 并没有更新数据)

  main: main.o add.o
  	gcc $^ -o main
  
  %.o : %.c %.h
  	gcc -c $< -o $@
  
  clean:
  	rm *.o main
  
  .PHONY: clean

我们可以通过 gcc -M add.c 来查看 add.c 依赖于啥.h文件

pikaqiu@ubuntu:~/linux_c/makefile/4$ gcc -M add.c
add.o: add.c /usr/include/stdc-predef.h add.h

我们如果想保存下来 这些依赖 可以执行 gcc -M add.c -MF add.d 将依赖存放到 add.d文件中

pikaqiu@ubuntu:~/linux_c/makefile/4$ gcc -M add.c -MF add.d

pikaqiu@ubuntu:~/linux_c/makefile/4$ ls
add.c  add.d  add.h  add.o  main  main.c  main.o  Makefile

pikaqiu@ubuntu:~/linux_c/makefile/4$ cat add.d
add.o: add.c /usr/include/stdc-predef.h add.h

如果我们既要生成 .o 文件 还要生成依赖文件 .d .

gcc -c add.c -o add.o -MD -MF add.d
  • Makefile (比较完善写法)
  objs = main.o add.o
  dep_files := $(patsubst %, .%.d, $(objs))	# 将objs 所有内容 变成 .%.d 存放在 dep_files 中
  dep_files := $(wildcard $(dep_files))		# 判断 dep_files 文件是否真实存在 真实存在才 存放到 dep_files
  CFLASS = -Werror							# 把警告当成错误
  #CFLASS = -Werror -I.						# 把警告当成错误 并且 把 当前路径 设置为头文件默认路径
  #CFLASS = -Werror -Iinclude					# 把警告当成错误 并且 把 名为 include 的文件夹 设置为头文件默认路径
  main : $(objs)
  	gcc $^ -o main
  	@echo dep_files = $(dep_files)
  
  # 如果这些依赖存在 则包含进来
  ifneq ($(dep_files),)
  include $(dep_files)
  endif
  
  %.o : %.c
  	gcc -c $< -o $@ -MD -MF .$@.d			# 自动生成依赖文件 然后加载进来
  
  clean:
  	rm *.o main
  distclean:
  	rm $(dep_files)
  .PHONY: clean

目前只是写了一点点,更多的资料可以 访问 网址 跟我一起写Makefile

以上是关于Makefile 学习笔记的主要内容,如果未能解决你的问题,请参考以下文章

makefile学习笔记

Linux学习笔记——例说makefile 索引博文

Linux -- Makefile的学习笔记以及多级目录下Makefile的编写

Makefile学习笔记系列4:Makefile模板化

Makefile学习笔记系列4:Makefile模板化

Makefile 学习笔记