当前位置: 首页 > article >正文

Makefile泛谈

Makefile工作原理

1、检查规则中的依赖文件是否存在。
2、若依赖文件不存在,则寻找是否有规则用来生成该依赖文件。

譬如,执行文件会先寻找.o文件是否存在,如果不存在,就会再寻找是否有规则可以生成该依赖文件。如果缺少了main.o这个依赖文件,Makefile就会在它下面寻找是否有规则生成main.o。当它发现gcc main.c -o main.o这条规则可以生成main.o时,它就利用此规则生成main.o,然后再生成执行文件。

语法规则

目标文件: 依赖文件 ... 可以有很多个
	执行命令 (可以有一些选项)

1、目标文件就是要生成的文件。
2、依赖文件就是源头。
3、执行命令:即通过执行命令由依赖文件生成目标文件。注意每条命令之前必须有一个tab

All

Makefile文件默认只生成第一个目标文件即完成编译,但是我们可以通过all 指定所需要生成的目标文件,也就是我们编译的终极目标。

all: target1 target2 target3
target1:
# 编译规则1
target2:
# 编译规则2
target3:
# 编译规则3

all被设置为第一个目标,并且target1、target2和target3被列为all的依赖。当你在命令行中运行make时,make命令会寻找并执行all目标规则,这将依次执行target1、target2和target3的编译。


$符号

$符号表示取变量的值
$^ 表示所有的依赖文件
$@ 表示生成的目标文件
$< 表示第一个依赖文件

%符号

%.c匹配所有.c文件

%.o:%.c
    gcc -c $<

用路径下所有.c文件都各自生成一个.o文件

赋值

=

使用 “=”进行赋值,变量的值是整个Makefile中最后被指定的值。

VIR_A = A
VIR_B = $(VIR_A) B
VIR_A = AA

最后VIR_B的值是AA B,而不是A B,会把整个变量展开。

:=

直接赋值,赋予当前位置的值。

VIR_A := A
VIR_B := $(VIR_A) B
VIR_A := AA

最后BIR_B的值是A B,即根据当前位置进行赋值。

?=

表示如果该变量没有被赋值,赋值予等号后面的值。

VIR ?= new_value

如果VIR在之前没有被赋值,那么VIR的值就为new_value。

+=

表示将符号后面的值添加到前面的变量后面

常用函数

取目录函数dir

从字符串中取出目录部分,目录部分是指最后一个反斜杠(“/”)之前的部分。

dir src/foo.c hacks

取到的是src/./

shell函数

可以借助这个函数来执行一些shell能够执行的命令

$(shell pwd)

这样就可以执行pwd命令了

ALL_DIRS = $(shell "find" $(SRC_PATH) -type d)

-type d参数用于限定find命令只返回路径(无文件名)。

ALL_DIRS将包含通过find命令获取的指定目录下的所有子目录路径列表。

过滤函数filter

过滤掉一个指定的字符串

FILE = a.c b.h c.s d.cpp   
SRC = $(filter %.c, $(FILE))

留下所有.c文件,最后SRC = a.c

排除函数filter-out

FILE = a.c b.h c.s d.cpp   
SRC = $(filter-out %.c, $(FILE))

去掉所有.c文件,最后SRC = b.h c.s d.cpp

通配符函数wildcard

SRC = $(wildcard ./*.c)

匹配当前目录下所有.c 文件,并将其赋值给SRC变量。

wildcard $(dir)/*.c

匹配所有目录下.c文件

扩展通配符函数patsubst

文件名替换

sources := main.c foo.c bar.c
objects := $(patsubst %.c,%.o,$(sources))

将sources中所有的.c文件名替换为.o,所以objects的值将是 main.o foo.o bar.o。

wildcard $(dir)/*.c

在某个目录中获取所有以 .c 扩展名结尾的文件路径。

$(dir) 是一个变量,表示当前迭代的目录。

/*.c 表示在该目录中搜索所有以 .c 结尾的文件。

目录替换

vpath := src/foo
new_vpath := $(patsubst src/%,obj/%,$(vpath))

将vpath中的src/替换为obj/,所以new_vpath的值将是 obj/foo。

文件扩展名替换

txt_files := file1.txt file2.txt file3.txt
md_files := $(patsubst %.txt,%.md,$(txt_files))

将txt_files中所有的.txt文件名替换为.md,所以md_files的值将是 file1.md file2.md file3.md。

添加前缀

names := apple orange banana
prefixed_names := $(patsubst %,fruit_%,$(names))

为names中的每个名字添加了前缀"fruit_",所以prefixed_names的值将是 fruit_apple fruit_orange fruit_banana。

从路径中提取文件名(去除路径)

full_paths := /home/user/main.c /home/user/foo.c
file_names := $(patsubst /home/user/%.c,%,$(full_paths))

从full_paths中提取了每个文件的文件名,所以file_names的值将是 main foo。

foreach遍历集合所有元素

ALL_SRCS = $(foreach dir, $(DIRS), $(wildcard $(dir)/*.c))

伪目标 .PHONY

伪目标只是一个标签,clean是个伪目标没有依赖文件。当目标文件已存在时,忽略,继续执行规则。

clean:
        rm -rf $(OBJ) all.out
 
.PHONY: clean ALL

清理

清理make命令所产生的所有文件

 clean:
        rm -rf $(OBJ) hello.out

有些用clear

删除中间生成.o文件

clear:
    rm *.o

指定头文件路径

一般都是通过"-I"来指定头文件路径

CFLAGS=-I/home/develop/include
app:*.c
    gcc $(CFLAGS) -o app

指定库文件路径

一般都是通过"-L"来指定头文件路径

CFLAGS=-L/home/develop/lib1 -L/home/develop/lib2
app:*.c
    gcc $(CFLAGS) -o app

循环函数foreach

$(foreach dir, $(DIRS), $(wildcard $(dir)/*.c))

遍历所有DIRS(纯目录,无文件名)里面的.c文件,以带目录的方式返回。

去除路径notdir

OBJ=$(notdir  a/b/c.cpp, aaa.cpp)

将带路径的文件名去除路径,只保留文件名。

提取前缀basename

返回字符串 “.”之前的所有字段

SRC := src/main.c src/hello.c
OBJ := $(basename $(SRC)) 

返回文件名序列 <names> 的前缀序列,如果文件没有前缀,则返回空字串。

最后返回src/main src/hello

添加前缀addprefix

给字符串中的每一个子串前加上一个前缀

SOURCE = main.c foo.c bar.c
OBJ = $(PWD)
add_source = $(addprefix $(OBJ)/, $(SOURCE))

这样就给每个文件前面添加了路径

字符串替换函数subst

$(subst from,to,text),对 text 文本执行文本替换:每次出现的 from 都替换为 to。

$(subst ee,EE,feet on the street)

生成值 fEEt on the strEEt

参数选择

编译的时候有很多参数可以选择,在这里举点例子。

-cpu=rh850g3kh:指定编译器的目标CPU架构为rh850g3kh。
-bsp generic:使用通用的BSP(板级支持包)。
-dwarf2:使用DWARF2调试信息格式。
-nothreshold:禁用阈值优化。
-preprocess_assembly_files:对汇编文件进行预处理。
-fsoft:启用软浮点支持。
-list:生成一个列表文件。
-object_dir=$(OBJ_OUTPUT_DIR):指定目标文件的输出目录为$(OBJ_OUTPUT_DIR)。
--no_additional_output:不生成额外的输出文件。
-callgraph:生成函数调用图。
-Ogeneral:启用一般优化级别。
-Mx:启用所有的警告信息。
-e _RESET:指定程序入口点为_RESET。
-D__GHS__:定义一个名为__GHS__的宏。
-elf:生成ELF可执行文件格式的输出。
-no_v850_simd:禁用V850 SIMD(单指令多数据)指令集。
-g:生成调试信息。
-passsource:将源代码传递给后续处理阶段。
-v:详细显示编译过程中的操作信息。
-w:禁用警告信息的显示。
-nofpu:禁用FPU(浮点运算单元)支持。
-nomacro:禁用宏展开。
-b0:设置链接器的起始地址为0。
-DSAIOS_GLOBAL_A1SAMPLE_ENABLE=$(SAIOS_GLOBAL_A1SAMPLE_ENABLE):定义一个名为SAIOS_GLOBAL_A1SAMPLE_ENABLE的宏,并将其值设置为$(SAIOS_GLOBAL_A1SAMPLE_ENABLE)。
-c:表示生成目标文件而不进行链接,即只进行编译而不进行链接。
-c99:表示使用C99标准进行编译,启用C99的新特性和语法。
-map="$(TARGET_OUTPUT_DIR)/$(CURRENT_APPL_NAME).map":表示生成一个链接地图文件,将其指定为TARGET_OUTPUT_DIR目录下的CURRENT_APPL_NAME.map。
-o "$(TARGET_OUTPUT_DIR)/$(CURRENT_APPL_NAME).out":表示指定输出文件的名称和路径,将其设置为TARGET_OUTPUT_DIR目录下的CURRENT_APPL_NAME.out

简单例程优化

文件结构

hello.c hello.h list.c list.h main.c makefile

Makefile内容

main:mian.o hello.o
    gcc main.o he11o.o -o main
mian.o:main.c hello.h
    gcc -c main.c 
he11o.o: hello.c hello.h
    gcc -c he11o.c
clean:
    rm -f *.o main

可以通过变量指定目标文件

OBJ=mian.o he11o.o
main:$(OBJ)
    gcc $(OBJ) -o main
mian.o:main.c hello.h
    gcc -c main.c 
he11o.o: hello.c hello.h
    gcc -c he11o.c
clean:
    rm -f *.o main

利用隐式规则

OBJ=mian.o he11o.o
main:$(OBJ)
    gcc $(OBJ) -o main
mian.o:hello.h
he11o.o:hello.h
clean:
    rm -f *.o main

利用$符号

OBJ=mian.o he11o.o
CC=gcc
TARGETS=main
$(TARGETS):$(OBJ)
    $(CC) $^ -o $@
%.o:%.c
    $(CC) -c $<
clean:
    rm -f *.o $@

经典例程

CC := g++  
CFLAGS := -g  
TARGET := test  
SRCS := $(wildcard *.cpp)  
OBJS := $(patsubst %cpp,%o,$(SRCS))  

all:$(TARGET)  
%.o:%.cpp  
    $(CC) $(CFLAGS) -c $< -o $@ 
$(TARGET):$(OBJS)  
    $(CC) $(CFLAGS) -o $@  
clean:  
    rm -rf $(TARGET) *.o  


http://www.kler.cn/a/108669.html

相关文章:

  • 开源模型应用落地-qwen模型小试-Qwen2.5-7B-Instruct-tool usage入门-Qwen-Agent深入学习(四)
  • 缓存与数据库不一致的解决方案:深入理解与实践
  • 陪诊问诊APP开发实战:基于互联网医院系统源码的搭建详解
  • 排序算法 - 冒泡
  • 从0开始学习Linux——文件管理
  • Appium配置2024.11.12
  • 9、电路综合-基于简化实频的任意幅频响应的微带电路设计
  • 【zookeeper】zk的ZAB原子广播协议
  • C语言实现输入一个字符串,递归将其逆序输出
  • appium操控微信小程序的坑
  • 【iOS】——知乎日报第二周总结
  • 【Java】Map集合中常用方法
  • 【2023年NCST C语言新生培训】| 五次培训总结 | C到C++内容补充 | 排位赛详细题解 |《万字长文》
  • Python之函数-传实参的两种方式
  • UNUNX安全的交易所
  • 大数据与人工智能的未来已来
  • 手写IOC
  • 不做学习的奴隶,更要注重生活
  • IntelliJ IDEA 常用快捷键-个人查阅
  • modelsim仿真报错:vlog-2388 ‘scl‘ already declared in this scope
  • Python求n位的自幂数
  • Maven第一章:Maven安装、验证、使用
  • 基于 ARM+FPGA+AD平台的多类型同步信号采集仪开发及试验验证(二)板卡总体设计
  • html2pdf
  • Python实现双目标定、畸变矫正、立体矫正
  • 方太描画未来厨房的模样