1.Makfile能干啥?
Makfile就是一种语言/工具!这玩意就是编译文件用的,不过咋编译得自己写,写好就按你的规则对.c .h文件进行编译。Makefile==MDK/Visio/Keil
2.要不要学懂?
我认为初学者不必要学懂,会改Makefile里的.c文件获取路径,.h文件获取路径,生成执行文件名即可。如果要学请看4,不学直接3拿走直接改,没问题的
BUILD_DIR=build
SRC_DIR=beep
INC_DIR=include
TARGET=beep
3.本人的makefile
这是我定义的Makefile,不同人定义不同。本人的Makefile代码粘贴如下。其实都大同小异,用谁的都一样,最终都是依赖gcc /arm-linux-gnueabihf-gcc/其他gcc(看你在什么平台运行)
#3.使用Makefile条件分支ifeq设置gcc或arm-gcc编译
2 ARCH ?= x86
3 #ARCH空赋值,这样就能在termianl输入时先给ARCH赋> 值
4 # 如果terminal不赋值,ARCH为空值为x86
5 ifeq ($(ARCH),x86)
6 CC = gcc
7 else
8 CC = arm-linux-gnueabihf-gcc
9 endif
10
11
12 #3.使用makefile函数优化
13 BUILD_DIR=build
14 #设置编译结果放在目标文件变量BUILD_DI R
15 SRC_DIR=beep
16 #设置.c文件放置的源程序文件夹变量SRC_ DIR
17 INC_DIR=include .
18 #设置.h文件放的文件夹路径
19 CFLAGS=$(patsubst %, -I %, $(INC_DIR))
20 #从INC_DIR中找所有文件(里面都是.h文件
)
21 #所有文件替换成 -I xxx.h
22 INCLUDE=$(foreach dir,$(INC_DIR),$(wildcard $(dir)/*.h))
23 #列出INC_DIR=include目录下所有.h文件
24
25 SOURCES=$(foreach dir,$(SRC_DIR),$(wildcard $(dir)/*.c))
26 #从源文件夹变量SRC_DIR里找带路径的.c文件赋值给SOURCE 变量
27
28 OBJS =$(patsubst %.c, $(BUILD_DIR)/%.o, $(notdir $(SOURCES)) )
29 #中间文件存储变量
30 #$(notdir $(SOURCES))只保留SOURCES内容里的非路径> 内容
31 #$(patsubst) 将非路径*.c内容 替换成 *.o的名字
32 VPATH =$(SRC_DIR)
33 #定义源文件搜索路径,SRC_DIR变量下寻找.c文件
34
35
36 #1.使用Makefile变量、模式匹配优化
37 TARGET=beep
38 #OBJS=main.o mp3.o
39
40
41 #生成的TARGET放在BUILD_DIR路径里
42 #依赖与OBJS OBJS=BUILD_DIR/%.o
43 $(BUILD_DIR)/$(TARGET):$(OBJS)
44 $(CC) $^ -o $@
45
46
47
48
49 #生成的.o文件放在BUILD_DIR文件夹 %依赖的.c文件前文已设置了搜索路径VPATH
50 #依赖在加上依赖$(INCLUDE)头文件
51 $(BUILD_DIR)/%.o:%.c $(INCLUDE) | creat_build
52 $(CC) -c $< -o $@ $(CFLAGS)
53 #加上头文件依赖$(CFLAGS)
54
55 #指定伪目标creat_bild 该伪目标功能是先生成BUILD_DIR文件夹
56 .PHONY:clean creat_build
57
58 clean:
59 rm -r $(BUILD_DIR)
60 creat_build:
61 mkdir -p $(BUILD_DIR)
4.梳理Makefile
Makefile语法主要分为4点。(1)基本三要素(2)变量、模式匹配、条件分支(3)常用函数(4)头文件。如果只是想写一个非常简单的,能编译你的文件,你只需要会(1)即可,甚至不要Makefile,直接gcc就行了。但如果想写一个通用的Makefile,在哪都能用,那必须学习(2)(3)(4),(2)(3)(4)的目的就是不断优化你的makefile,让他变成一个通用的Makefile。下面说说整理的概念
/**********************************************
4.1基本三要素
************************************************/
为啥要使用Makefile?
假设项目中有 aaa.c bbb.c ddd.c 三个文件,编译要进行如下步骤:
gcc aaa.c bbb.c ddd.c -o aa
假如aaa.c更改了,则又得重复该步骤
gcc aaa.c bbb.c ddd.c -o aa
哪怕只是改了一个文件,就又得重新打那么长的gcc编译代码。不如把工程编译规则写成一个东西,哪怕改了.c文件,只要运行该东西,工程就会自己按照设定规则进行编译。(就像IDE一样),这就是Makefile的作用!
Makefile == IDE
Makefile的两个工具
**1.make工具:**当单独.c文件更改后,根据工程的依赖关系,去单独编译受影响的文件
2.Makefile文件:定义工程内各文件的依赖关系,供make用
Makefile中所有的复杂、晦涩的语法都是更好地为解决依赖问题而存在的!!哪个.c文件依赖哪一个,又被哪个调用,心里要有关系网。
Makefile三要素是什么?
目标、依赖、命令
怎么描述三要素的关系?
描述规则: 目标:依赖文件 + 目标, 如下所示
目标:依赖的文件或者是其他目标
命令1 //命令前必须加,不能是空格键,必须是tab键
命令2
…
实验演示
1.新建Makefile文件夹 vim Makefile
2.输入以下代码
.PHONY:targetb
#targeta是默认目标,第一个往往是默认目标,make也只执行默认目标
#targeta依赖于targetb targetc
targeta:targetb targetc #运行targeta目标前会先调用targetb和targetc目标
echo "targeta"
#targetb谁都不依赖,但被targeta依赖
targetb: #targetb 和targetc 调用完后在自身targeta
echo "targetb"
#targetc谁都不依赖,但被targeta依赖
targetc:
echo "targetc"
#targetd和谁都没关系,咋都不会被执行
targetd:
pwd
3.:wq保存退出,在shell中输入make命令
①输入make命令后,make会在当前目录寻找Makefile文件进行执行
②targeta是第一个为默认目标,执行默认目标
③targeta依赖targetb targetc 所以先执行targetb targetc 再执行targeta
4.targetd:不会被执行,若想执行用命令 make targetb
目标+依赖+命令三者关系可从以上程序熟知
PS: .PHONY:伪目标有什么用
如果不加 .PHONY:targetb 如果更改了targetb 文件就不会运行
/**********************************************
4.2Makefile变量、模式匹配、条件分支
************************************************/
一,变量:系统变量/自定义变量自动化变量
1.系统变量: CC、AS、MAKE这三个就是常用的三个系统变量,
Makefile_test输入如下代码
.PHONY:all
all:
echo "$(CC)"; #cc在makefile一般指系统编译器
echo "$(AS)"; #AS在makefile一般指汇编器
echo "$(MAKE)"; #MAKE在makefile一般指MAKE工具
运行make -f Makefile_test, 该程序输出为cc as make
2.自定义变量
① = 延迟赋值 //该变量只有在调用的时候,才会被赋值
A=123
B=$(A)
A=456
.PHONY:all
all:
echo "$(B)";
//运行该程序,输出为456,因为延迟赋值。当echo调用B的时候B才会被赋值,此时A已经=456了
② := 立即赋值 //与延时赋值相反,使用直接赋值的话,变量的值定义时就已经确定了。
A=123
B:=$(A)
A=456
.PHONY:all
all:
echo "$(B)";
//运行该程序,输出为123,直接赋值。B:=$(A)时直接把B赋值为123
③?= 空赋值 //只有变量为空时赋值才有效
A?=123
A?=456
.PHONY:all
all:
echo "$(A)";
//运行该程序,输出为123,空赋值。只有第一次A?=123时,A才为空。以后赋值都无效
④+= 追加赋值 //再次的赋值会追到到之前的赋值之后
A?=123
A+=456
.PHONY:all
all:
echo "$(A)";
//运行该程序,输出为123 456,追加赋值。A+=456,把456追加到A的值后面了
3.自动化变量
①$< //代表第一个依赖目标,<和第一个离得最近,第一个
all:targeta targetb
echo "$<"
targeta:
targetb:
//运行该程序,输出为targeta。因为targeta是第一个依赖目标
②$^ //代表全部依赖文件,^就像大房子一样覆盖所有
all:targeta targetb
echo "$^"
targeta:
targetb:
//运行该程序,输出为targeta targetb。因为^代表所有依赖
③$@ //代表目标,微信的@的对象就是目标
all:targeta targetb
echo "$@"
targeta:
targetb:
//运行该程序,输出为all。因为all为当前目标
二,Makefile变量的用处:优化,让makefile文件更具有通用性
原来的makefile内容如下:
mp3:main.o mp3.o
gcc main.o mp3.o -o mp3
main.o:main.c
gcc -c main.c -o main.o
mp3.o:mp3.c
gcc -c mp3.c -o mp3.o
.PHONY:clean
clean:
rm mp3
Makefile变量优化后的内容
#使用系统变量、自定义变量、自动化变量 对Makefile进行改造
cc = gcc
TARGET = mp3
OBJS = main.o mp3.o
$(TARGET):$(OBJS)
$(cc) $^ -o $@
main.o:main.c
$(cc) -c main.c -o main.o
mp3.o:mp3.c
$(cc) -c mp3.c -o mp3.o
.PHONY:clean
clean:
rm *.o mp3
~
这样makefile文件更具有通用性,只需要更改
cc = gcc
TARGET = mp3
OBJS = main.o mp3.o
这三个变量的内容,就可以应用到任何的类似程序中
三,模式匹配
1.%:匹配任意多个非空字符。说白了就是占位用,执行时输入啥,就代表啥
①%就类似于shell脚本里的*通配符,用于匹配任意多个非空字符
%:
echo "$@"
//运行该makefile文件 make -f Makefile_test asd
输出 echo "asd" asd
因为%是通配符,输入asd。%的值就变成asd。%@又代表目标,所以也输出echo "asd"
2.%作用:进一步优化Makefile文件
把使用系统变量优化后的makefile文件进一步优化,如下:
#使用系统变量、自定义变量、自动化变量 对Makefile进行改造
cc=gcc
TARGET=mp3
OBJS=main.o mp3.o
$(TARGET):$(OBJS)
$(cc) $^ -o $@ -I .
%.o:%.c #.o文件依赖于.c文件,%.o代表本文件中所有.o文件
$(cc) -c $< -o %@ #gcc -c命令 把.c文件变成.o文件
.PHONY:clean
clean:
rm *.o mp3
默认规则
.o文件默认使用当前目录下的.c文件进行编译,因此
xxx.o:xx.c 的编译规则就不用写了
所以代码进一步优化(optimize)为:
#使用系统变量、自定义变量、自动化变量 对Makefile进行改造
cc=gcc
TARGET=mp3
OBJS=main.o mp3.o
$(TARGET):$(OBJS)
$(cc) $^ -o $@ -I .
.PHONY:clean
clean:
rm *.o mp3
Makefile条件分支
一,条件分支语法,2个
语法一:ifeq if-equal
ifeq (var1, var2) #注意ifeq后有空格
....
else
....
endif
语法一:ifneq if-not-equal
ifneq (var1, var2) #注意ifneq后有空格
....
else
....
endif
/**********************************************
4.3Makefile的常用函数
************************************************/
patsubst:
用法:$(patsubst PATTERN,REPLACEMENT,TEXT)
搜索“TEXT”中以空格分开的单词,将否符合模式“PATTERN”替换为“REPLACEMENT”
示例:$(patsubst %.c,%.o,x.c.c bar.c)
把Text文本x.c.c bar.c中 符合 PATTERN模式%.c 的内容 替换成 REPLACEMENT的形式%.o。最后函数返回值为x.c.o bar.o
notdir:
用法:$(notdir NAMES…)
从文件名序列“NAMES…”中取出非目录部分(只要文件名不要路径)
示例:$(notdir src/foo.c hacks)
把NAMES里src/foo.c hacks 文件路径的部分src/去掉 返回文件名foo.c hacks
wildcard:
用法:$(wildcard PATTERN)
列出当前目录下所有符合模式“PATTERN”格式的文件名。
示例:$(wildcard *.c) #注意这个通配符是 * 不是 %
返回值为Makefile文件目录下所有.c源文件列表
foreach:这是一个for循环函数,要和其他函数组合用
用法:$(foreach VAR,LIST,TEXT)
LIST是由一系列单词组成的文本,调用foreach函数后,会遍历LIST里的每一个单词,把单词的值赋值给VAR变量,在用VAR参与TEXT表达式的运算
示例:dirs := a b c d
files := $(foreach dir,$(dirs),$(wildcard $(dir)/*))
①遍历$(dirs)里所有单词:a b c d 赋值给dir, dir =a/b/c/d
②让dir执行 $(wildcard ( d i r ) / ∗ ∗ ) ∗ 的 运 算 即 (dir)/**)*的运算 即 (dir)/∗∗)∗的运算即(wildcard $a/*) 列出 a/目录下所以文件
③循环,列出a/ b/ c/所有目录下的文件
④a b c d是makefile目录下的文件名即可,不用写绝对地址
使用function对Makefile再次改造
#2.使用Makefile条件分支ifeq设置gcc或arm-gcc编译
ARCH ?= x86
#ARCH空赋值,这样就能在termianl输入时先给ARCH赋值
# 如果terminal不赋值,ARCH为空值为x86
ifeq ($(ARCH),x86)
CC = gcc
else
CC = arm-linux-gnueabihf-gcc
endif
#3.使用makefile函数优化
BUILD_DIR=build
#设置编译结果放在目标文件变量BUILD_DIR
SRC_DIR=module1 module2
#设置.c文件放置的源程序文件夹变量SRC_DIR
SOURCES=$(foreach dir,$(SRC_DIR),$(wildcard $(dir)/*.c))
#从源文件夹变量SRC_DIR里找带路径的.c文件赋值给SOURCE变量
OBJS =$(patsubst %.c, $(BUILD_DIR)/%.o, $(notdir $(SOURCES)) )
#中间文件存储变量
#$(notdir $(SOURCES))只保留SOURCES内容里的非路径内容
#$(patsubst) 将非路径*.c内容 替换成 *.o的名字
VPATH =$(SRC_DIR)
#定义源文件搜索路径,SRC_DIR变量下寻找.c文件
#1.使用Makefile变量、模式匹配优化
TARGET=mp3
#OBJS=main.o mp3.o
#生成的TARGET放在BUILD_DIR路径里
#依赖与OBJS OBJS=BUILD_DIR/%.o
$(BUILD_DIR)/$(TARGET):$(OBJS)
$(CC) $^ -o $@
#生成的.o文件放在BUILD_DIR文件夹 %依赖的.c文件前文已设置了搜索路径VPATH
$(BUILD_DIR)/%.o:%.c | creat_build
$(CC) -c $< -o $@
#指定伪目标creat_bild 该伪目标功能是先生成BUILD_DIR文件夹
.PHONY:clean creat_build
clean:
rm -r $(BUILD_DIR)
creat_build:
mkdir -p $(BUILD_DIR)
/**********************************************
4.4Makefile加上头文件
************************************************/
1、写一个头文件,并把头文件添加到编译器的头文件路径中。
gcc -I +“头文件路径”
2、实时检查头文件的更新情况,一旦头文件发生变化,应该要重新编译所有相关文件。
CFLAGS=$(patsubst %, -I %, $(INC_DIR))
#从INC_DIR中找所有文件(里面都是.h文件)
#所有文件替换成 -I xxx.h
INCLUDE=$(foreach dir,$(INC_DIR),$(wildcard $(dir)/*.h))
#列出INC_DIR=include目录下所有.h文件
$(BUILD_DIR)/%.o:%.c $(INCLUDE) | creat_build
$(CC) -c $< -o $@ $(CFLAGS)
#加上头文件依赖$(CFLAGS) 和 依赖路径$(INCLUDE)