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

Linux - make/Makefile工具的基础使用

文章目录

    • 一、介绍
    • 二、工作原理
    • 三、使用
      • 1、make的使用
      • 2、make的常用选项
      • 3、Makefile文件
    • 四、总结


一、介绍

make工具和 Makefile 文件是 Linux下非常重要的自动化构建工具, Makefile定义了项目的编译规则,指示make如何编译和链接源代码以生成可执行文件或其他输出。并且make 能够根据源文件的时间戳自动判断哪些文件需要重新编译,从而只编译那些需要更新的部分,极大地提高了开发效率。

二、工作原理

  1. make会在当前目录下找名字叫“Makefile”或“makefile”的文件。
  2. 如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“hello”这个文件, 并把这个文件作为最终的目标文件。
  3. 如果hello文件不存在,或是hello所依赖的后面的hello.o文件的文件修改时间要比hello这个文件新(可以用touch测试),那么,他就会执行后面所定义的命令来生成hello这个文件。
  4. 如果hello所依赖的hello.o文件不存在,那么make会在当前文件中找目标为hello.o文件的依赖性,如果找到则再根据那一个规则生成hello.o文件。(这有点像一个堆栈的过程)
  5. 当然,你的C文件和H文件是存在的啦,于是make会生成 hello.o 文件,然后再用 hello.o 文件声明 make的终极任务,也就是执行文件hello了。
  6. 这就是整个make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。
  7. 在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错, 而对于所定义的命令的错误,或是编译不成功,make根本不理。
  8. make只管文件的依赖性,即,如果在我找了依赖关系之后,冒号后面的文件还是不在,那么对不起, 我就不工作啦。

三、使用

1、make的使用

语法:

make

make会从上到下扫描Makefile文件,直到找到第一个目标名。

make name

name : 目标名
make会从上到下扫描Makefile文件,直到找到name。

2、make的常用选项

make -f filename:使用指定的Makefile文件执行构建。
make -C directory:在指定的目录中执行构建。
make -n:显示执行Makefile时将执行的命令,但不实际执行。
make -B:强制重新构建目标,即使目标已经是最新的。
make -j n:使用多个并行任务进行构建,其中n是并行任务的数量(这里的 n 是你希望并行执行的任务数量,通常设置为 CPU 核心数的 1.5 到 2 倍可以获得较好的效果,但这取决于你的具体系统和构建过程中的 I/O 密集度),可以显著提高构建速度。

对于:name -j n 选项
假如你的项目包含大量的源文件,需要编译成多个目标文件(.o 文件),并最终链接成可执行文件。不使用并行任务时(即只使用单个任务),make 会按顺序编译每个源文件,然后链接它们。这可能需要很长时间,尤其是当源文件数量很多时。然而,使用 make -j 8(假设你希望利用 CPU 和可能的 I/O 超线程,这里 8 是 CPU 核心数的两倍),make 会同时启动 8 个编译任务(或者尽可能多,直到达到 8 个),这样 CPU 就可以被更充分地利用,编译过程也会更快。

all: program  
  
program: file1.o file2.o file3.o  
    gcc file1.o file2.o file3.o -o program  
  
file1.o: file1.c  
    gcc -c file1.c -o file1.o  
  
file2.o: file2.c  
    gcc -c file2.c -o file2.o  
  
file3.o: file3.c  
    gcc -c file3.c -o file3.o  
  
clean:  
    rm -f *.o program

此时make会尝试去同时编译三个.o文件。

3、Makefile文件

(1)基础元素

  1. 规则(Rules):
    目标(Target):规则想要生成的文件名,比如可执行文件或对象文件。
    依赖(Prerequisites):生成目标所依赖的文件列表。如果依赖的文件比目标文件更新,则规则中的命令会被执行。
    命令(Commands):生成目标所需执行的命令,通常是编译器或链接器的调用。
  2. 变量(Variables):用于存储文件名、编译器选项等,以便在多个规则中重用。
  3. 函数(Functions): 用于处理文件名、执行命令等,提供高级功能。
    如: 假设VARIABLE = file.txt 函数$(VARIABLE)用途: 将VARIABLE替换成file.txt
  4. 注释(Comments): 以#开头的行是注释,不会被make执行。

(2)伪目标
概念:

伪目标(也称为“伪目标”或“标签”)是指那些不代表实际文件名(如文件、目录等)的目标。伪目标主要用于执行某些命令或操作,而不是创建或更新文件名所指向的文件。由于伪目标不代表任何文件,因此每次执行make命令时,伪目标都会被视为已过期(即需要重新执行),除非使用了.PHONY声明来显式标记它们为伪目标。

定义伪目标:
伪目标在Makefile中就像普通目标一样被定义,但是通常不会对应到实际的文件。为了明确地将某个目标标记为伪目标,可以使用.PHONY特殊目标来声明它:

.PHONY: clean  
  
clean:  
    rm -f *.o my_program

在这个例子中,clean是一个伪目标,它用于删除所有的.o文件和my_program可执行文件。通过.PHONY: clean声明,make知道clean是一个伪目标,因此每次执行make clean时,都会执行rm -f *.o my_program命令,而不会去检查是否存在一个名为clean的文件来决定是否需要执行该命令。

(3)利用上面的特性写一个Makefile文件
生成一个可执行程序
代码:

  1 te = test.c                                                                                                                                                                                  
  2 t = test
  3 
  4 $(t) : $(te)
  5 #test : test.c
  6   #gcc -o test test.c             
  7   gcc -o $(t) $(te)               
  8                                   
  9 .PHONY: clean                     
 10 clean:                            
 11   #rm -rf test                    
 12   rm -rf $(t)

解析:

在这里插入图片描述
(4)Makefile的一些高级特性

1、自动变量:
$@:表示规则中的目标文件名。
$<:表示规则中的第一个依赖文件名。
$^:表示规则中的所有依赖文件列表。
2、模式规则:
使用通配符定义规则,如%.o: %.c,表示所有.o文件都由对应的.c文件生成(通过对应的.c文件生成同名的.o文件)。
条件判断:
使用ifeq、ifneq、ifdef和ifndef等关键字进行条件编译。

(5)使用一些高级特性写一个Makefile文件
生成:一个可执行程序

  1 target = test
  2 depend = test.o
  3 
  4 $(target) : $(depend)                                                                                                                                                                        
  5   gcc $^ -o $@
  6 
  7 %.o : %.c
  8   gcc -c $<
  9 
 10 .PHONY: clean
 11 clean:                            
 12   rm -rf $(depend)
 13 

在这里插入图片描述

四、总结

上述讲的make/makefile只是一些基础使用,make/makefile还有许多操作与功能,想了解更多可以参考陈皓大佬的跟我一起写Makelile。


http://www.kler.cn/news/304161.html

相关文章:

  • Java的发展史与前景
  • 贪吃蛇项目实现(C语言)——附源码
  • JavaScript知识点3
  • JMeter脚本开发
  • 人工智能领域的性能指的是什么
  • Unity3D类似于桌面精灵的功能实现
  • JDK 17 微服务启动JVM参数调优实战
  • 自学前端靠谱吗?
  • onRequestPermissionsResult详解
  • 多账号注册脚本不会被平台监控吗
  • 写论文还在卡壳?教你用ChatGPT轻松搞定过渡段落!
  • Google大数据架构技术栈
  • 91-java cms垃圾回收器
  • java 长连接中的sse与websocket含义, 两者的区别
  • C++ Qt开发:运用QJSON模块解析数据
  • 编写注册接口与登录认证
  • 动态代理相关知识点
  • Zabbix监控自动化
  • 查找算法--python
  • NS3的3.36版本将Eclipse作IDE
  • python读写CSV文件
  • ctf Mark loves cat (超详细记录)
  • Redis缓存和Mysql数据一致性问题
  • Mybatis接受查询结果的情况
  • 使用 @NotEmpty、@NotBlank、@NotNull 注解进行参数校验
  • 多线程爬虫接入代理IP:高效数据抓取的秘诀
  • 工具包(Commons-io)工具包(hutool)
  • flink中disableOperatorChaining() 的详解
  • R语言的Meta分析【全流程、不确定性分析】方法与Meta机器学习技术应用
  • 理解大型语言模型(LLM)中的隐藏层