makefile文件基本语法

news2024/11/13 8:58:23

一、makefile文件基本介绍

      `Makefile` 文件是 `make` 工具使用的配置文件,它定义了如何自动化构建项目的规则和命令。`Makefile` 文件的主要作用是指定如何编译和链接程序,以及管理文件之间的依赖关系,从而实现高效的构建过程。

1.1 `Makefile` 的基本结构

一个典型的 `Makefile` 文件由目标(target)、依赖(dependencies)和命令(commands)组成。它的基本语法如下:

target: dependencies
    command

- target:要生成的文件,通常是可执行文件、目标文件、或其他文件。也可以是一个伪目标(例如 `clean`,不实际生成文件,只是执行某些命令)。
- dependencies:生成目标文件所需的文件。如果这些依赖文件有任何变化,`make` 就会执行相应的命令来更新目标。
- command:生成目标的命令。通常是编译、链接命令或其他操作系统命令。这些命令必须以 Tab 键开头。

1.2 简单的 `Makefile` 示例

假设有一个简单的 C 项目,包含以下文件:
- `main.c`
- `utils.c`
- `utils.h`

下面是一个对应的 `Makefile`:


# 编译器和编译器选项
CC = gcc
CFLAGS = -Wall -g

# 目标文件
TARGET = myprogram

# 目标及其依赖关系
$(TARGET): main.o utils.o
    $(CC) $(CFLAGS) -o $(TARGET) main.o utils.o

# 规则:生成 main.o
main.o: main.c utils.h
    $(CC) $(CFLAGS) -c main.c

# 规则:生成 utils.o
utils.o: utils.c utils.h
    $(CC) $(CFLAGS) -c utils.c

# 清理构建生成的文件
clean:
    rm -f $(TARGET) *.o

 逐行解释

- CC = gcc : 变量 `CC` 定义了要使用的编译器,这里是 `gcc`。
- CFLAGS = -Wall -g : 变量 `CFLAGS` 定义了编译器选项,`-Wall` 启用所有常见的编译警告,`-g` 生成调试信息。
- TARGET = myprogram : 变量 `TARGET` 指定了最终生成的可执行文件名。
- $(TARGET): main.o utils.o : 这是生成可执行文件 `myprogram` 的规则,它依赖于 `main.o` 和 `utils.o`。如果 `main.o` 或 `utils.o` 改变,`make` 会重新链接生成 `myprogram`。
- $(CC) $(CFLAGS) -o $(TARGET) main.o utils.o**: 这是链接命令,生成最终的可执行文件。
- main.o: main.c utils.h : 生成 `main.o` 的规则,`main.o` 依赖于 `main.c` 和 `utils.h`。如果 `main.c` 或 `utils.h` 改变,`make` 会重新编译 `main.c`。
- clean : 这是一个伪目标,用于清理构建生成的文件,如可执行文件和目标文件。

1.3 使用 `Makefile`

在终端中运行 `make` 命令时,`make` 会按照 `Makefile` 中的规则,自动完成编译和链接过程。如果 `Makefile` 中定义了多个目标,可以通过 `make target` 来指定构建某个特定目标。例如:

- 默认构建: 运行 `make`,它会构建默认的目标 `myprogram`。
- 清理构建: 运行 `make clean`,它会执行 `clean` 目标,删除所有编译生成的文件。

1.4 高级特性

- 变量:`Makefile` 支持变量,用于简化和重用代码,例如 `CC` 和 `CFLAGS` 变量。
- 自动化变量:`$@` 表示目标名称,`$<` 表示第一个依赖文件,`$^` 表示所有依赖文件。这些变量可以在命令中使用。
- 条件语句:`Makefile` 支持 `if`-`else` 语句,可以根据条件设置变量或执行不同的规则。
- 模式规则:可以使用通配符来定义通用的构建规则,例如 `%.o: %.c` 可以自动匹配所有 `.c` 文件生成相应的 `.o` 文件。

通过 `Makefile`,开发者可以高效管理项目的编译和构建过程,尤其是在大型项目中,可以显著提高工作效率。

二、makefile 基本语法

2.1 基本语法示例 

举例 1:
语法格式:
目标:依赖
         (tab)命令
举例:

目标: all
依赖:空
命令: gcc helloworld.c -o helloworld

举例 2:
在学习 gcc 编译的时候,讲解了编译的流程,可以将上面的例子写成以下格式:

目标:all 和 helloworld.o
依赖:helloworld.o 和 helloworld.c
命令: gcc helloworld.c -o helloworld 和 gcc -c helloworld.c -o helloworld.o
因为 all 依赖 helloworld.o 文件,所以要先执行 gcc -c helloworld.c -o helloworld.o 得到 helloworld.o 文件,然后才可以执行 gcc helloworld.c -o helloworld 。所以输入 make 命令后执 行顺序如下图所示。

 举例 3:
在编译的时候,可以使用“make 目标”的命令格式来进行编译,如果不进行目标的指定, 默认执行的是第一个目标所对应的规则。也就是说在上一个例子中 make 和 make all 执行结果是相同的。 修改 makefile 代码如下图所示:

保存退出之后,输入命令 “make clean” 就可以直接执行 rm -rf *o helloworld 命令。如下图所示: 

但是在当前目录下不能有和 makefile 目标名一样的文件。比如在当前目录下创建一个名为 clean 的文件,然后执行make clean 命令就会报错。如下图所示。

为了解决这个问题,makefile 引入了一个新的概念,叫做伪目标,使用伪目标来声明 clean 就可以避免与当前目录下的同名文件发生冲突。
伪目标内容格式
.PHONY:目标
可以把上面的代码修改成如下图所示:

然后在执行 make clean 命令。尽管当前目录下有 clean 同名文件, make clean 命令也可以执行成功。如下图所示。

2.2、makefile变量和变量赋值

变量可以对许多地方使用,比如目标,依赖。或者命令。
变量的赋值可以使用:  =   ?=   :=   +=
变量的使用:通过$() 来完成变量的引用。
示例 1:
使用  :=  来赋值

 
使用 := 来给变量赋值,是立刻赋值,在执行 var1:=aaa 的同时 var1 变量值已经被确定了, 所以最后打印为 aaabbb,而不是 cccbbb,如下图所示。

 

示例 2:
使用 = 来赋值

使用 =来赋值,是延迟赋值,使用 = 来赋值是 makefile 里面最后被指定的值。因为最后给变量 var1 赋值为 ccc ,所以最后打印为 cccbbb ,而不是 aaabbb ,如下图所示: 

示例 3:
使用 ?= 来赋值 

使用 ?= 来赋值,如果 var1 变量前面没有被赋值,那么就给它赋值为 ccc ,如果前面已经赋值了,就适应前面的值,所以,打印为 aaabbb ,而不是 cccbbb ,如下图所示 :

然后注释掉第一行代码,makefile 中的注释为 #

在运行就会打印 cccbbb ,因为前面没有给 var1 变量赋值。如下图所示:

示例 4 :
使用 += 来赋值 

使用+=赋值是追加赋值,是在前面定义的好的字符串里面在添加进去新的字符串,所以运行会打印aaa cccbbb。中间会有空格,如下图所示:

 

使用 += 也类似于这样赋值,如下图所示: 

如果赋值很长,也可以使用换行符\,如下图所示:

2.3 自动化变量

自动化变量就是不用定义且会随着上下程序的不同而发生变化的变量叫做自动化变量。 这里介绍三个最常用的自动化变量:
$@: 表示所有目标
$< :表示第一个依赖文件,如果依赖模式是%,那么他就表示一系列文件。 (%为通配符,类似 linux 上的 *)
$^ :表示所有依赖。
在了解这三个自动化变量之前,先来建立一个工程,具体步骤如下:
首先使用命令 “vim main.c” 创建 main.c 文件,并在文件中添加以下内容,添加完成如下图所示:

然后使用命令 “vim helloworld.c” 创建 helloworld.c 文件,并在文件中添加以下内容,添加完成如下图所示:

然后使用命令 “vim helloworld.h” 创建 helloworld.h 文件,并在文件中添加以下内容,添加完成如下图所示: 

这三个文件创建完成之后如下图所示:

 

最后使用命令“vim makefile”创建 makefile 文件,并在文件中添加以下内容,添加完成如下图所示: 

 

最后使用make指令完成编译,如下图所示: 

 

      使用这个 makefile 虽然也可以成功编译,但是,一旦编译的文件多了,仍用这种方法来编写 makefile 就会变得非常复杂。所以,自动化变量就派上用场啦。 接下来一步一步的来简化 makefile 。
简化一:
用变量表示依赖文件 

后面如果再增加依赖文件的话,直接在变量 var 后面增加就可以了。
简化二:
使用通配符 % ,和自动化变量 $< 、$@代替依赖和目标,简化完如下图所示:

简化三:
使用自动化变量 $^ 表示所有文件依赖的列表,简化完如下图所示:

2.4 makefile常用函数

2.4.1 wildcard 函数


格式: $ (wildcard PATTENR)
功能: 展开指定的目录
举例:
在/home/topeet/Desktop/test 目录有一个叫 test1.txt 文件和一个 test 的文件夹,在/home/topeet/Desktop/test/test 文件夹下有一个 test2.txt 的文件。创建过程如下三幅图所示。



 在当前目录下创建的 makefile 里面写下如下代码,echo 前面加了@ 符号,echo 这个命令就不显示:

执行结果:

得到了./test1.txt 和./test/test2.txt ,所以 wildcard 函数会把指定目录下的文件展开。

2.4.2 notdir 函数

 格式: $ (notdir $ (var) )
功能:去掉路径。
举例: 将上文中的 wildcard 函数替换为 notdir ,替换完成如下图所示:

执行结果:

      因为 notdir 函数可以去掉路径,所以 ./test1.txt 和 ./test/test2.txt 去掉路径就得到了 test1.txt test2.txt。


2.4.3 dir 函数

格式: $(dir )
功能:取出目录,这里的目录指的是最后一个反斜杠/ 之前的部分,如果没有反斜杠/就返回当前。 举例:
      在上面的例子中加入以下代码,如下图所示:

因为 var 的值为 ./test1.txt 和 ./test/test2.txt ,所以取出目录就是 ./ 和 ./test/ ,如下图所示:

 

2.4.4 patsubst 函数

格式: $(patsubst 原文件,目标文件,文件列表)
功能:替换文件后缀
举例:将上述例子中的 test1.txt 和 test2.txt 修改为 test1.c 和 test2.c,修改 makefile 文件中的内容,如下图所示:

这个函数会把 var1 变量的 test1.c 和 test2.c 的 .c 后缀替换为 .o ,如下图所示:

      但是这个替换并不会改变当前目录下的后缀名。如下图所示。

      这个函数能做什么呢?可以用这个函数来替换后缀名,进行其他的操作,这个函数都是会配合其他函数来用的。
      可以使用这个函数进行替换,也可以使用 $(var:a=b) 这个格式来替换,var 代表要替换文件的名字,a 是原文件,b 是目标文件。来对上面的代码进行修改,修改完成如下图所示:

运行结果如下:

2.4.5 foreach 函数

格式:$(foreach <var>,<list>,<text>)
功能:把参数 <list> 中的单词逐一取出放到参数 <var> 所指定的变量中,然后再执行 <text> 所包含的表达式。每一次 <text> 会返回一个字符串。
举例:


因为 var2 变量的值为 ./ 和 ./test ,所以先把 ./ 取出来放在 n 变量,然后再执行 wildcard 函数取出 ./test 和 ./test 下面的 c 文件的路径。所以执行结果如下图所示:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2065414.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【FreeRTOS】队列实验-分发数据给多个任务(赛车游戏)

目录 0 前言1 队列实验_分发数据给多个任务(赛车游戏)2 赛车游戏2.1 game.c2.2 注册队列2.3显示汽车2.4隐藏汽车2.5 CarTask2.6 car_game2.7 MX_FREERTOS_Init 3 总结 0 前言 学习视频&#xff1a; 【FreeRTOS入门与工程实践 --由浅入深带你学习FreeRTOS&#xff08;FreeRTOS教…

如何用Python实现山东省旅游数据爬虫与K-means满意度分析

&#x1f393; 作者&#xff1a;计算机毕设小月哥 | 软件开发专家 &#x1f5a5;️ 简介&#xff1a;8年计算机软件程序开发经验。精通Java、Python、微信小程序、安卓、大数据、PHP、.NET|C#、Golang等技术栈。 &#x1f6e0;️ 专业服务 &#x1f6e0;️ 需求定制化开发源码提…

AI一键视频多语言配音/翻译工具:打造无缝多语言视频体验

在全球化的今天,视频内容的传播不再受限于地域和语言。然而,如何高效地将视频内容翻译成多种语言并保持其自然度和流畅性,一直是业界面临的挑战。为了解决这一难题,我们推出了一款智能视频多语言AI配音和翻译工具——Linly Dubbing。该工具基于YouDub-webui的灵感进行了创新…

开源:cuda studio云原生一站机器学习、深度学习、大模型AI平台

文章目录 1、 cuda studio云原生一站机器学习、深度学习、大模型AI平台2、网址 1、 cuda studio云原生一站机器学习、深度学习、大模型AI平台 cube studio开源云原生一站式机器学习/深度学习/大模型AI平台&#xff0c;支持sso登录&#xff0c;多租户&#xff0c;大数据平台对接…

3级线性反馈移位寄存器在C3=1时可有4种线性反馈函数,设其初始状态为(a1,a2,a3)=(1,0,1),求各线性反馈函数的输出序列及周期

标题是题目 题解 1.补充知识 2.分析四种情况&#xff1a; 结合我所给的反馈数公式以及a31&#xff0c;可以得到反馈函数为: fC1*a3⊕C2*a2⊕C3*a1C1*a3⊕C2*a2⊕a1 附&#xff1a;别把初始状态为&#xff08;a1,a2,a3&#xff09;(1,0,1)带入&#xff0c;因为a1,a2,a3的值…

小程序学习day11-生命周期函数、组件所在页面的生命周期、自定义组件的插槽、自定义组件的父子通信

40、自定义组件&#xff08;续&#xff09;&#xff08;续&#xff09; &#xff08;10&#xff09;生命周期函数 1&#xff09;小程序里的全部生命周期函数 ①created&#xff08;在组件刚被创建时执行&#xff09;&#xff08;被创建&#xff0c;但未被放入页面&#xff09…

【AD9361 数字基带】多片基带内FPGA补偿 I/Q Rotation

I/Q 旋转 Rotation 在许多多通道射频系统中&#xff0c;如 AD-FMCOMMS5&#xff0c;甚至在 AD-FMCOMMS2、AD-FMCOMMS3 上&#xff0c;都需要测量或校正两个复数 &#xff08;I/Q&#xff09; RF 信号之间的相位差。 从纯粹的数学描述来看&#xff0c;单个正弦波没有相位&…

NNG简介和使用总结

先认识下ZeroMQ 参考&#xff1a;ZeroMQ详解 - 南哥的天下 - 博客园 (cnblogs.com) ZeroMQ&#xff08;简称ZMQ&#xff09;是一个基于消息队列的多线程网络库&#xff0c;其对套接字类型、连接处理、帧、甚至路由的底层细节进行抽象&#xff0c;提供跨越多种传输协议的套接字。…

RK3568开发笔记-buildroot系统scp拷贝文件报错dbclient no such file or directory

目录 ​​​​​​​ 前言 一、问题分析 什么是 Dropbear Dropbear 的优点 二、解决办法 总结 前言 在使用RK3588开发板进行系统开发时,很多开发者会选择使用Buildroot来构建自己的定制化系统。在开发过程中,通常需要通过scp(Secure Copy Protocol)命令将文件从本地计…

IDEA工具设置默认使用maven的settings.xml文件

第一步&#xff1a;打开idea工具&#xff0c;选中 File ——> New Projects Setup ——> Settings for New Projects 第二步&#xff1a;先设置下自动构建项目这个选项 第三步&#xff1a;选中 Build Tools ——> Maven&#xff0c;让后就可以设置自己安转的maven和se…

xlsx表格-A列的值需要从C列中匹配到然后输出C列旁边D列的值,怎么写公式?

公式&#xff1a; IFERROR(VLOOKUP(A1, C:D, 2, FALSE), "") 解释&#xff1a; 在VLOOKUP函数中&#xff0c;2表示要返回的列的索引。具体来说&#xff0c;VLOOKUP函数的语法如下&#xff1a; VLOOKUP(lookup_value, table_array, col_index_num, [range_lookup])…

功能测试和性能测试区别简析,软件测试公司如何开展有效测试?

软件功能测试旨在验证软件是否按照需求和设计规范正常运行&#xff0c;软件性能测试则是用来评估软件在特定负载条件下的行为和响应时间&#xff0c;确保软件在高并发和高需求的环境中能够稳定运行。 虽然两者都属于软件测试的重要组成部分&#xff0c;但它们的目的和重点却有…

零基础5分钟上手谷歌云GCP - 服务器自动扩展

简介 欢迎来到小李哥谷歌云GCP云计算知识学习系列&#xff0c;适用于任何无云计算或者谷歌云技术背景的开发者&#xff0c;让大家零基础5分钟通过这篇文章就能完全学会谷歌云一个经典的服务开发架构方案。 我将每天介绍一个基于全球三大云计算平台&#xff08;AWS, Azure, GC…

改编版猜数字小游戏,猜错了就黑屏(整蛊版本)

1. 前情提要 在前一篇博客中&#xff0c;我们了解到了如何获得随机数&#xff0c;并且通过运算可以规定所获得的这个随机数的范围在多少数值之间 那么接下来我们就需要去具体去实现猜数字游戏的各种布置 2. 布置主菜单 玩一个游戏&#xff0c;最开始的界面都会是一个主菜单…

iPhone13手机照片被误删,有什么方法可以恢复吗?

在日常使用手机时&#xff0c;我们可能因为误操作、手机崩溃、或者其他原因&#xff0c;导致iPhone13手机中的照片丢失。遇到这种情况&#xff0c;手机误删照片如何恢复&#xff1f;在本文中&#xff0c;我们将分享3个妙招&#xff0c;帮助您恢复iPhone13上误删的照片。 一、通…

2024年第二季度SSD出货量下滑18.4%,降至6750万部,但容量增长4.1%至90.6EB

2024年第二季度SSD Exabytes实现连续季度增长 仅企业级PCIe SSD有所增长&#xff1a;尽管所有其他类别均出现下滑&#xff0c;但企业级PCIe SSD的增长是由其所有终端市场需求增加所驱动的。总体SSD出货量&#xff1a;总体SSD出货量环比下降18.4%&#xff0c;降至6750万部&…

Leetcode JAVA刷刷站(76)最小覆盖子串

一、题目概述 二、思路方向 为了解决这个问题&#xff0c;我们可以使用滑动窗口的方法。滑动窗口是数组/字符串问题中常用的一个技巧&#xff0c;特别是用于寻找子数组或子字符串的问题。 这里的关键是&#xff0c;我们需要知道字符串t中每个字符的出现次数&#xff0c;并在遍…

【Python】函数高阶【上】

本篇文章将讲解函数高阶部分&#xff1a; &#xff08;1&#xff09;函数的嵌套 &#xff08;2&#xff09;闭包 &#xff08;3&#xff09;装饰器 1、函数的嵌套 Python是以函数为作用域&#xff0c;在作用域中定义的相关数据只能被当前作用域或子作用域使用。 &#xf…

(QT-UI)十四、在时间轴上绘制一段段时间片

本系列预计实现 ①刻度上方文字显示&#xff0c; ②时间轴拖动效果&#xff0c; ③时间轴刻度缩放&#xff0c; ④时间轴和其他控件联动显示&#xff0c; ⑤鼠标放置到时间轴&#xff0c;显示具体时间。 ⑥通过定时器&#xff0c;实时更新时间轴 ⑦时间轴上绘制时间片 完…

PostgreSQL11 | 事务处理与并发控制

PostgreSQL11 | 事务处理与并发控制 本文章代码已在pgsql11.22版本上运行且通过&#xff0c;展示页由pgAdmin8.4版本提供&#xff0c;本文章第一次采用md文档&#xff0c;效果比csdn官方富文本编辑器好用&#xff0c;以后的文章都将采用md文档 事务管理简介 事物是pgsql中的…