对Makfile文件系统做一个整理

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)


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值