嵌入式学习之Makefile入门

概念

1.Makefile是一个工程管理工具,本质上就是一个文件,文件中存放的是代码编译规则
2.Makefile会根据文件的时间戳(文件修改的时间)来决定工程内的文件是否需要重新编译。大大提高编译效率
3.Makefile 是一种构建自动化工具的文件,用于管理和自动化软件构建过程。

格式

  1. 基本结构:目标(Target)、依赖(Dependency) 和 命令(Commands) 组成
    target: dependencies
        command
    # target:目标文件,通常是一个可执行文件、目标文件(.o 文件)或伪目标(如 clean)。
    # dependencies:依赖项,即目标文件生成所需的源文件或其他文件。
    # command:生成目标文件所需执行的命令。注意:命令行必须以 Tab 键 开头。
    
  2. 在指定的目录下创建一个名字为:Makefile 或者 makefile的文件
  3. make clean
make  clean   #则会默认执行Makefile文件中clean指令下面的命令
make -f Makefile.debug clean #执行 Makefile.release 中的 clean
  1. @:取消回显 --》指令执行过程不在终端显示
    在这里插入图片描述
  2. -:忽略命令的错误,继续执行后续命令
-rm -f temp.o

语法规则

  • 命令部分必须以 Tab 键 开头,空格无效
变量
  • 在 Makefile 中可以定义变量,以简化和复用常用的字符串。例如编译器、编译选项和文件列表。变量名通常是大写的,用 = 号进行赋值,使用时通过 $(VAR) 的方式引用
CC = gcc #指定使用 gcc 作为编译 C 程序的编译器
CFLAGS = -Wall -g
 #-Wall:启用编译器的所有常见警告。Wall 是 "Warnings all" 的缩写,它会让编译器发出尽可能多的有用警告,帮助开发者发现潜在的代码问题。
 #-g:生成调试信息。使用 -g 时,编译器会生成额外的调试符号,这些符号会被添加到可执行文件中,以便你可以使用调试器(如 gdb)调试程序。
  1. all
    • 在 Makefile 中,all 通常被用作默认的目标(target),它是一个惯例,而不是必须使用的名字。
    • all 目标的作用是汇总多个子目标,确保运行 make 时可以执行所有必要的构建步骤。
    • 默认情况下,当你运行 make 而不指定具体目标时,Makefile 会执行第一个出现的目标,通常这个目标被命名为 all。
    • 作用:
      • 汇总多个目标:all 目标通常用来汇总所有你想要构建的文件或程序
      • 默认目标:如果没有明确指定目标,make 默认执行第一个目标,因此将 all 作为第一个目标是个惯例,目的是让 make 在不指定其他目标时执行主要的构建操作
  2. clean
    • clean 目标用于删除编译过程中生成的临时文件、目标文件(如 .o 文件)、可执行文件等。这有助于保持项目的目录干净,并在需要重新编译时避免旧文件的干扰。
  3. install
    • install 目标用于将编译好的程序或文件复制到系统的安装目录(如 /usr/local/bin、/usr/local/lib 等)。在开源项目中,make install 通常用来安装可执行文件、库文件、头文件等到系统中。
    install:
    	install -m 755 my_program /usr/local/bin/
    #运行 make install 会将可执行文件 my_program 安装到 /usr/local/bin。
    
  4. uninstall
    • uninstall 目标用于删除通过 install 目标安装的文件。它是 install 的反操作,用于清理安装到系统目录中的程序。
    uninstall:
    	rm -f /usr/local/bin/my_program
    
    
  5. distclean
    • distclean 目标是 clean 目标的扩展,除了删除编译产生的文件外,还会删除配置文件和其他生成的文件(如 Makefile 自己是由脚本生成的,distclean 可能会删除它)。这个目标常用于彻底清理项目,使其恢复到刚下载时的状态。
    • 在clean的基础再删除
    distclean: clean
    	rm -f config.mk Makefile
    
    
  6. phony
    • .PHONY 是一个特殊的伪目标,它告诉 Makefile 这个目标不是一个文件,而是一个操作。它通常用于避免 Makefile 将同名文件当成目标。
    • .PHONY 通常用于声明一些不会生成文件的目标,例如 clean、all、install 等。
    • .PHONY 声明,防止与实际文件同名时发生冲突。
  7. 等等
内置和自动变量

Makefile 中有许多内置的自动变量,用于简化规则的编写:

  • $@:目标文件的名称。
  • $^:所有依赖文件的列表。
  • $<:第一个依赖文件的名称。
  • $?:所有比目标更新的依赖文件的列表。
    • 通俗地说,$? 是指那些自上次构建目标后被修改过的依赖文件。如果这些依赖文件比目标文件更新,那么 make 会重新构建目标文件,并且 $? 就包含了这些比目标更新的文件。
program: main.o utils.o
    gcc -o program $?
#如果 main.o 最近被修改,而 utils.o 没有修改,那么 $? 在这次运行时就只包含 main.o,因为 main.o 是比 program 更新的文件。
gcc [选项] [源文件] -o [输出文件]#单源文件编译
gcc [编译选项] -o [输出文件] [依赖文件]#多文件项目的编译与链接
#输出文件在前面:-o 选项后紧跟输出文件名,用来告诉编译器输出结果应该保存在哪个文件中。

多目标和通配符
多目标
  • 一个规则可以指定多个目标(通常是多个可执行文件或多个中间目标)。这种做法非常常见,尤其是在大型项目中,你可能有多个最终的目标需要生成。
all: program1 program2
#这个规则定义了 all 依赖于两个目标:program1 和 program2。
#当你运行 make all 或直接运行 make 时,Makefile 会先构建 program1 和 program2

构建 program1 和 program2
program1: main1.o
    gcc -o program1 main1.o

program2: main2.o
    gcc -o program2 main2.o
通配符
  • Makefile 支持使用通配符来匹配文件,简化规则编写。通配符可以帮助处理多个相似的文件,比如所有的 .o 文件可以由对应的 .c 文件生成。
  • 常见的通配符有 *、? 、% 等
通配符说明示例匹配结果
*匹配零个或多个任意字符*.c匹配所有 .c 文件
?匹配一个任意字符file?.c匹配 file1.cfileA.c,不匹配 file12.c
[...]匹配括号内的任意一个字符main[0-9].c匹配 main1.cmain2.c
%匹配零个或多个任意字符(用于规则定义)%.o: %.c.c 文件编译为 .o 文件
**递归匹配所有子目录中的文件(GNU Make 4.0+ 支持)**/*.c匹配当前目录及所有子目录中的 .c 文件
$(wildcard pattern)查找与模式匹配的文件列表$(wildcard *.c)查找当前目录中的所有 .c 文件
  • 通配符匹配 .o 文件
OBJS = $(wildcard *.o)
OBJS 变量现在包含了当前目录下所有以 .o 结尾的文件名。
通配规则

通配规则用于简化目标的生成规则,特别是当你有很多相似的规则时,使用通配符可以自动生成目标而无需手动定义每个目标的构建规则。

%.o: %.c
	 gcc -c $< -o $@
#%.o: %.c:这个规则的意思是,所有 .o 文件由对应的 .c 文件生成。% 是通配符,表示任意匹配的文件名。

多个Makefile文件

  1. 若同一目录下需要多个makefile文件 ,可以给makefile分别取名叫:makefile1 makefile2,
  2. 终端输入make,默认执行Makefile文件。若提示没有make指令,则用下列命令安装
    sudo  apt-get install make
    
  3. 可以使用 make -f 指定使用哪个 Makefile 来构建项目
    make -f Makefile.debug//编译调试版本
    make -f Makefile.release//编译发布版本
    
  4. 在主 Makefile 中包含其他 Makefile
    - 使用 include 语法包含其他 Makefile。这样可以让你在一个 Makefile 中组织多个构建规则。
    # 主 Makefile
    include Makefile.debug
    include Makefile.release
    
    # 你可以根据目标调用不同的构建规则
    .PHONY: all
    all: debug release
    
    debug:
        $(MAKE) -f Makefile.debug
    
    release:
        $(MAKE) -f Makefile.release
    
    clean:
        $(MAKE) -f Makefile.debug clean
        $(MAKE) -f Makefile.release clean
    
  5. 根据环境变量选择不同的 Makefile
    • 通过设置环境变量来选择使用哪个 Makefile。这是根据构建时的条件或参数来决定使用哪个规则文件的策略。
    # 主 Makefile
    
    ifeq ($(BUILD_TYPE), debug) #GNU Make 中的条件判断语法 判断BUILD_TYPE == debug
        include Makefile.debug
    else ifeq ($(BUILD_TYPE), release)
        include Makefile.release
    endif
    
    .PHONY: all
    all:
        $(MAKE) -f $(MAKEFILE)
    
    clean:
        $(MAKE) -f $(MAKEFILE) clean
    
    • BUILD_TYPE 是一个变量,它决定了当前的构建模式。可以在命令行或环境中设置这个变量来控制构建流程
    make BUILD_TYPE=debug//包含 Makefile.debug
    make BUILD_TYPE=release//包含 Makefile.release
    

在这里插入图片描述

赋值
  • 变量引用:makefile中的变量与shell脚本一致,不需要定义,也没有数据类型的说法。$(变量名)
  1. “=” 最终赋值
    - 会将变量在makefile中的所有赋值都完成后,将最后一次的赋值当做结果赋值给变量。
    - 使用 = 进行赋值时,变量的值在实际使用时才被解析(延迟计算)。
  2. ":="立即赋值
    • 变量的值在赋值时立即展开
    • 与 = 不同的是,:= 不会在使用时重新计算变量的值,而是在赋值时就确定下来。
SRC = $(wildcard *.c)
OBJS := $(patsubst %.c,%.o,$(SRC))

# OBJS 的值在定义时就被确定下来
# SRC 的值仍然会在每次使用时重新计算。

在这里插入图片描述

  1. 条件赋值 (?=)
    条件赋值使用 ?=,它的意思是:如果变量没有被定义过,则进行赋值,否则保留已有的值。通常用于给变量提供默认值,用户可以通过命令行传递参数覆盖默认值。
CC ?= gcc
CFLAGS ?= -O2
#如果 CC 和 CFLAGS 没有被定义,则使用默认的 gcc 和 -O2,否则保留外部传递的值。
使用外部传递变量
make CC=clang

  1. "+="追加赋值
    追加赋值使用 +=,它用于将值追加到已有的变量中,特别是在扩展编译选项或文件列表时非常有用
CFLAGS = -Wall
CFLAGS += -g
#CFLAGS 的最终值是 -Wall -g,+= 用于将 -g 追加到原有的 CFLAGS 变量中

在这里插入图片描述

简化Makefile

  1. 通过变量赋值进行简化
# 定义对象文件变量 Obj
Obj := 06_union.o 07_func.o  # Obj 变量包含 06_union.o 和 07_func.o 两个对象文件
# 定义目标文件变量 Target
Target := a.out  # Target 变量表示最终生成的可执行文件 a.out
# 定义编译器变量 CC
CC := gcc  # CC 变量定义为 gcc 编译器
# 定义编译选项变量 CAN
CAN := -c -o  # CAN 变量表示编译选项,用于生成目标文件

# 规则:生成最终可执行文件
$(Target): $(Obj)  # 目标 a.out 依赖于所有对象文件 $(Obj)
	$(CC) $(Obj) -o $(Target)  # 使用 gcc 编译器将所有对象文件链接生成 a.out

# 规则:生成 06_union.o 对象文件
06_union.o: 06_union.c  # 目标 06_union.o 依赖于源文件 06_union.c
	$(CC) 06_union.c $(CAN) 06_union.o  # 使用 gcc 编译器将 06_union.c 编译成 06_union.o

# 规则:生成 07_func.o 对象文件
07_func.o: 07_func.c  # 目标 07_func.o 依赖于源文件 07_func.c
	$(CC) 07_func.c $(CAN) 07_func.o  # 使用 gcc 编译器将 07_func.c 编译成 07_func.o

# 伪目标:clean,用于清理生成文件
.PHONY: clean  # 定义伪目标 clean,不会生成与其同名的文件
clean:  # clean 目标,用于删除生成的对象文件和可执行文件
	rm $(Obj) $(Target)  # 删除对象文件 $(Obj) 和最终生成的可执行文件 $(Target)

  1. 用特殊符号简化
    $@ 目标文件
    $^ 所有依赖文件
    $< 第一个依赖文件
# 定义目标文件的变量
obj := union.o func.o

# 定义最终生成的可执行文件的名称
Target := a.out

# 指定编译器为gcc
CC := gcc

# 指定编译选项 -c 表示只编译,不进行链接
CFLAGS := -c

# 规则:生成可执行文件
# $@ 表示目标文件,这里是 $(Target)
# $^ 表示所有的依赖文件,这里是 $(obj)
$(Target): $(obj)
	$(CC) -o $@ $^

# 规则:生成 union.o 文件
# union.o 依赖 union.c 和 func.h
# $< 表示第一个依赖文件,这里是 union.c
union.o: union.c func.h
	$(CC) $(CFLAGS) union.c

# 规则:生成 func.o 文件
# func.o 依赖 func.c 和 func.h
# $< 表示第一个依赖文件,这里是 func.c
func.o: func.c func.h
	$(CC) $(CFLAGS) func.c

# 规则:清理生成的文件
# rm -f 强制删除目标文件 $(obj) 和 $(Target)
clean:
	rm -f $(obj) $(Target)

  1. Makefile最终版
# 定义变量
obj := union.o func.o           # 目标文件列表,包含 union.o 和 func.o
Target := a.out                 # 最终生成的可执行文件名称
CC := gcc                       # 使用 gcc 作为编译器
CFLAGS := -c                    # 编译选项,-c 表示只编译,不进行链接

# 生成可执行文件的规则
# $(Target): $(obj) 表示生成 $(Target) 需要依赖 $(obj)
# $@ 表示目标文件,这里是 $(Target)
# $^ 表示所有的依赖文件,这里是 $(obj)
$(Target): $(obj)
	$(CC) -o $@ $^

# 生成 union.o 的规则
# union.o 依赖 union.c 和 func.h
# $< 表示第一个依赖文件,这里是 union.c
union.o: union.c func.h
	$(CC) $(CFLAGS) $<

# 生成 func.o 的规则
# func.o 依赖 func.c 和 func.h
# $< 表示第一个依赖文件,这里是 func.c
func.o: func.c func.h
	$(CC) $(CFLAGS) $<

# 清理生成的文件
# clean 是一个伪目标,不生成文件,只执行命令
# rm -f 强制删除 $(obj) 和 $(Target)
clean:
	rm -f $(obj) $(Target)

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值