欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 游戏 > Linux | 自动化构建 —— make / Makefile

Linux | 自动化构建 —— make / Makefile

2025/2/9 14:05:56 来源:https://blog.csdn.net/TTKunn/article/details/145524362  浏览:    关键词:Linux | 自动化构建 —— make / Makefile

文章目录

  • 自动化构建-make/Makefile
      • 一、`make` 工具概述
      • 二、`Makefile` 基本结构
      • 三、 `Makefile`和`make`的基本使用
        • 3.1最基本的gcc编译:
        • 3.2make执行Makefile文件
        • 3.3`.PHONY`伪目标
      • 四、Makefile拓展
        • 4.1直接根据文件名编写Makefile
        • 4.2变量的使用
        • 4.3Makefile的适度扩展语法(更通用使用)

自动化构建-make/Makefile

一、make 工具概述

  • make 是一个自动化构建工具,用于自动化编译和构建程序。它根据文件的修改时间和依赖关系,只重新编译那些需要更新的文件,从而节省编译时间,提高开发效率。
  • make 通过读取 Makefile 文件中的规则和指令来确定如何编译和构建程序。

二、Makefile 基本结构

  • Makefile 是一个文本文件,通常位于项目的根目录,包含了一系列的规则,其基本结构如下:
目标: 依赖命令(依赖方法)
  • 目标(Target):是 make 要生成的文件或要执行的操作,例如可执行文件、目标文件或自定义的伪目标(如 clean)。
  • 依赖(Dependencies):是生成目标所需的文件或其他目标,例如源文件、头文件或其他库文件。
  • 依赖关系:由目标文件和依赖文件组成的依赖关系
  • 命令(Commands):也是依赖方法,是 make 执行的操作,通常是编译命令或其他构建操作。注意,命令行必须以制表符(Tab)开头,而不能是空格。

三、 Makefilemake的基本使用

3.1最基本的gcc编译:
test:test.cgcc test.c -o test

假设当前目录下有一个test.c文件,使用make进行自动化构建

1.在当前目录下执行vim Makefile新建并编辑Makefile文件

2.在Makefile文件中执行:

test:test.c //此处格式为”编译出来的可执行程序名“ :”依赖文件“

gcc test.c -o test //依赖方法,此处格式为Tab + gcc编译命令,必须以制表符(Tab)开头

3.wq保存并退出回到刚才的目录

4.执行make命令构建出可执行文件test

5.执行./make命令运行可执行文件

3.2make执行Makefile文件
test:test.cgcc test.c -o test.PHONY:clean
clean:rm -f mytest
  • make会自顶向下扫描Makefile我呢见,默认形成第一个目标文件,如果想指定形成,就是用make + 指定的目标名称,例如下面将第一部分和第二部分颠倒过来:
.PHONY:clean
clean:rm -f mytesttest:test.cgcc test.c -o test

在这里插入图片描述

然后执行make,会发现执行的是rm -f test1;

在这里插入图片描述

这时候如果想要执行test1,就需要指定执行:

在这里插入图片描述

3.3.PHONY伪目标

概念

  • 伪目标是 Makefile 中不代表实际文件的目标,而是代表一个操作或一系列操作。

  • 通常,make 会根据文件的时间戳来判断是否需要重新构建目标。但对于伪目标,make 不会检查文件是否存在,而是直接执行相应的命令。

例如下面的Makefile:

test1:test1.cgcc test1.c -o test1.PHONY:clean
clean:rm -f test1;

在这里插入图片描述

当执行make命令的时候,只有第一次能够被执行,而make clean可以一直执行,因为Makefile中使用.PHONY修饰了clean,执行make clean的时候不会判断是否需要重构目标,而是直接执行相应命令。而对于编译,由于编译文件可能会消耗很多的资源,所以一般不使用PHONY修饰,避免资源的浪费

使用PHONY的好处

  • 避免命名冲突
    • 假设你有一个名为 clean 的文件和一个 clean 伪目标。如果不使用 .PHONY,当 clean 文件存在时,make clean 会认为 clean 已经是最新的,不会执行 clean 目标的命令。使用 .PHONY 可以避免这种情况。
    • 例如:
.PHONY: cleanclean:rm -f main.o main

这里,即使存在名为 clean 的文件,make clean 仍会执行 rm -f main.o main 命令。

  • 明确表示操作
    • 对于一些操作,如 allinstalltest 等,它们不代表生成文件,而是执行一系列操作,使用 .PHONY 可以明确表示它们是操作而不是文件生成目标。

多个伪目标

四、Makefile拓展

4.1直接根据文件名编写Makefile
  • 假设你有一个简单的 C 项目,包含以下文件:

    • main.c

    • func.c

    • func.h

一个简单的 Makefile 可以如下:

# 最终的可执行文件
all: main# 生成可执行文件 main
main: main.o func.ogcc main.o func.o -o main# 编译 main.c 生成 main.o
main.o: main.c func.hgcc -c main.c -o main.o# 编译 func.c 生成 func.o
func.o: func.c func.hgcc -c func.c -o func.o# 清理生成的文件
clean:rm -f main.o func.o main
  • 解释
    • all: mainall 是一个伪目标,它依赖于 main,通常作为默认目标,当你只输入 make 时会执行该目标。
    • main: main.o func.omain 目标依赖于 main.ofunc.o,当 main.ofunc.o 发生变化或不存在时,make 将执行 gcc main.o func.o -o main 来生成 main 可执行文件。
    • main.o: main.c func.hmain.o 目标依赖于 main.cfunc.h,当 main.cfunc.h 发生变化或 main.o 不存在时,将执行 gcc -c main.c -o main.o 来编译 main.c 生成 main.o 目标文件。
    • func.o: func.c func.h:与 main.o 的情况类似,编译 func.c 生成 func.o
    • clean:这是一个伪目标,用于清理生成的文件,当你输入 make clean 时,将执行 rm -f main.o func.o main 命令。

4.2变量的使用
  • 可以在 Makefile 中使用变量来避免重复输入,提高可维护性。例如:
# 定义最终的可执行文件名
BIN=test1
# 定义源文件名
SRC=test1.c
# 定义目标文件名
OBJ=test1.o
# 定义编译器为 gcc
cc=gcc
# 定义删除命令为 rm -f
RM=rm -f# 定义构建最终可执行文件的规则,依赖于目标文件
$(BIN):$(OBJ)# 使用 gcc 编译器将目标文件链接为可执行文件$(cc) $(OBJ) -o $(BIN)# 定义生成目标文件的规则,依赖于源文件
$(OBJ):$(SRC)# 使用 gcc 编译器将源文件编译为目标文件,-c 选项表示只编译不链接$(cc) -c $(SRC) -o $(OBJ)# 声明 clean 为伪目标
.PHONY:clean
clean:# 清理生成的可执行文件和目标文件$(RM) $(BIN) $(OBJ)

解释说明

  • 这个 Makefile 主要用于自动化构建一个 C 语言程序。
    • BIN=test1:将最终要生成的可执行文件的名称存储在 BIN 变量中,这里命名为 test1
    • SRC=test1.c:存储源文件的名称,这里是 test1.c
    • OBJ=test1.o:存储目标文件的名称,这里是 test1.o
    • cc=gcc:指定使用 gcc 编译器。
    • RM=rm -f:指定删除文件的命令,使用 rm -f 可以强制删除文件而不提示。
    • $(BIN):$(OBJ):定义 test1 可执行文件的构建规则,它依赖于 test1.o 目标文件。这意味着如果 test1.o 不存在或者比 test1 新,将执行下面的编译命令。
    • $(cc) $(OBJ) -o $(BIN):使用 gcc 编译器将 test1.o 目标文件链接为 test1 可执行文件。
    • $(OBJ):$(SRC):定义 test1.o 目标文件的构建规则,它依赖于 test1.c 源文件。如果 test1.c 不存在或者比 test1.o 新,将执行下面的编译命令。
    • $(cc) -c $(SRC) -o $(OBJ):使用 gcc 编译器将 test1.c 编译为 test1.o 目标文件,-c 选项表示只进行编译而不进行链接操作。
    • .PHONY:clean:声明 clean 为伪目标,即它不代表实际的文件,而是代表一个操作。
    • clean::定义 clean 目标,当执行 make clean 时,将执行下面的命令。
    • $(RM) $(BIN) $(OBJ):使用 rm -f 命令删除 test1 可执行文件和 test1.o 目标文件。

使用此 Makefile 时,你可以:

  • 输入 make 来编译并生成 test1 可执行文件。
  • 输入 make clean 来删除 test1 可执行文件和 test1.o 目标文件。

请注意,使用此 Makefile 时要确保命令行以制表符(Tab)开头,而不是空格,因为 make 要求命令行必须以制表符作为缩进。并且确保 test1.c 文件存在于当前目录中,否则可能会导致编译错误。


4.3Makefile的适度扩展语法(更通用使用)

当有多个文件需要编译的时候,一个个输入很复杂,可以有下面的做法:

  • 创建多个.c文件用作示范,touch test{2..10}.c,创建test2.c test3.c … test10.c
  • 打开Makefile进行编写
BIN=test1
SRC=$(shell ls *.c)OBJ=test.o
cc=gcc
RM=rm -f.PHONY:test
test:echo $(BIN)echo $(SRC)
#使用echo进行测试,更直观一点

关于SRC=$(shell ls *.c)

  • 将当前目录下所有以.c结尾的文件的名称存储在变量SRC中
  • shell是make的一个内置函数,允许在Makefile中调用系统的shell命令,语法为$(shell ),其中,是要执行的shell命令
  • *.c是一个通配符表达式,用于匹配所有文件名以.c结尾的文件。所以ls *.c会调用系统的ls命令列出当前目录下的.c源文件。
  • 当 make 执行到 SRC=$(shell ls *.c) 时,它会调用 shell 函数,进而调用 ls *.c 命令,将该命令的执行结果存储在 SRC 变量中。例如,如果当前目录下有 file1.c、file2.c 和 file3.c,那么 SRC 的值将是 file1.c file2.c file3.c(多个文件名之间以空格分隔)。

执行make后会发现结果如下,多打印了一遍,这是由于echo回显导致的

在这里插入图片描述

  • Makefile 的上下文中,回显(Echo)是指将命令行的内容输出到标准输出(通常是终端)的现象。当 make 执行一个命令时,它会默认将该命令的文本输出显示出来,然后再显示该命令执行的结果。这是 make 的默认行为,主要目的是为了让你看到正在执行的命令,以便你可以跟踪构建过程和进行调试。

  • 这里的 echo test1echo test10.c test1.c test2.c test3.c test4.c test5.c test6.c test7.c test8.c test9.c 就是回显的内容,它们是 make 在执行 echo 命令之前将该命令本身输出到终端的结果。

  • Makefile 中,在命令前面添加 @ 符号可以抑制该命令的回显。例如,将 echo $(BIN) 改为 @echo $(BIN)make 就不会将 @echo $(BIN) 这一命令本身输出到终端,而只输出该命令的执行结果。这样可以使输出更简洁,只关注命令的结果,而不是命令的执行过程。

可在echo前加上@符号抑制该命令的回显

较为完整的基本的Makefile文件如下( $< $@ 等内容看下面模式规则部分):

BIN=test1
#SRC=$(shell ls *.c) 采用shell命令行方式,获取当前所有.c文件名
SRC=$(wildcard *.c) #或者使用wildcard函数,获取当前多有.c文件
#OBJ=test.o          
OBJ=$(SRC:.c=.o)    #将SRC的所有同名.c替换成为.o形成目标文件
cc=gcc
RM=rm -f$(BIN):$(OBJ)#gcc $(OBJ) -o $(BIN)@$(cc) $^ -o $@ #$^表示规则中的所有依赖项链表#$@表示规则中的目标,执行make时会被替换为当前正在构建的目标名称%.o:%.c   #% 是通配符,表示匹配内容,例如%.c就是匹配.c结尾的文件    @$(cc) -c $<        #< 的意思是将上面展开的.c文件逐个交给对应的命令,在这里是将.c文件处理成.o文件
.PHONY:clean
clean:@$(RM) $(OBJ) $(BIN)

模式规则

  • 对于多个相似的编译规则,可以使用模式规则来简化 Makefile。例如:
# 定义变量
CC = gcc
CFLAGS = -c
OBJS = main.o func.o# 最终的可执行文件
all: main# 生成可执行文件 main
main: $(OBJS)$(CC) $(OBJS) -o main# 编译.c 文件生成.o 文件的模式规则
%.o: %.c %.h$(CC) $(CFLAGS) $< -o $@# 清理生成的文件
clean:rm -f $(OBJS) main
  • 解释
    • %.o: %.c %.h:这是一个模式规则,% 是通配符,表示任何匹配的文件。当 make 需要生成 .o 文件时,会根据该规则查找相应的 .c.h 文件,并使用 $(CC) $(CFLAGS) $< -o $@ 命令进行编译。
    • $<:表示第一个依赖文件,即 .c 文件。
    • $@:表示目标文件,即 .o 文件。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com