makefile文件基本语法

一、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 文件的路径。所以执行结果如下图所示:

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

uniapp微信小程序 分享功能

uniapp https://zh.uniapp.dcloud.io/api/plugins/share.html#onshareappmessage export default {onShareAppMessage(res) {if (res.from button) {// 来自页面内分享按钮console.log(res.target)}return {title: 自定义分享标题,path: /pages/test/test?id123}} }需要再真机…

Appium定位元素

使用工具&#xff1a; 报错: Error while obtaining UI hierarchy XML file: com.android.ddmlib.SyncException: Remote object doesn‘t 参考链接&#xff1a;Error while obtaining UI hierarchy XML file: com.android.ddmlib.SyncException: Remote object doesn‘t-CSD…

Centos安装Jenkins教程详解版(JDK8+Jenkins2.346.1)

本教程基于 JDK8 和 Jenkins2.346.1 JDK安装 下载OpenJDK8文件 wget https://mirrors.tuna.tsinghua.edu.cn/Adoptium/8/jdk/x64/linux/OpenJDK8U-jdk_x64_linux_hotspot_8u422b05.tar.gz解压到指定目录 # 创建目录 mkdir -p /usr/local/software# 解压文件到指定目录&#…

Eclipse部署一个项目到Tomcat和部署多个项目到Tomcat

Eclipse部署一个项目到Tomcat&#xff1a; https://blog.csdn.net/weixin_42334396/article/details/105902994 Eclipse部署多个项目到Tomcat&#xff1a; https://blog.csdn.net/zhanglin1220/article/details/82056185 使用cmd方法强制关闭端口&#xff0c;解除端口占用方法&…

多元统计分析——基于R语言的单车使用情况可视化分析

注&#xff1a;基于R语言的单车使用情况可视化分析为实验记录&#xff0c;存在不足&#xff0c;自行改进。 一、提出问题&#xff08;要解决或分析的问题&#xff09; 1 、用户对共享单车的使用习惯&#xff0c;环境对共享单车运营带来的影响&#xff1f; 2 、共享单车的租赁…

stripe Element 如何使用

这里要准备好几个东西&#xff1a; 一个支付成功过后的回调 还有一个下单的接口 一旦进入这个下单界面&#xff0c;就要去调下单的接口的&#xff0c;用 post, 这个 接口你自己写&#xff0c;可以写在后端中&#xff0c;也可以放到 nextjs 的 api 中。 首先说的是这个下单…

聚星文社——绘唐科技Ai推文软件

聚星文社——绘唐科技Ai推文软件 聚星文社--绘唐科技Ai推文软件https://iimenvrieak.feishu.cn/docx/ZhRNdEWT6oGdCwxdhOPcdds7nof AI推文软件是一种利用人工智能技术帮助用户自动生成推文内容的工具。 该软件会分析用户提供的相关信息和目标群体&#xff0c; 然后使用机器学习…

闲置物品交易平台网站商城-计算机毕设Java|springboot实战项目

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

【运维高级内容--MySQL】

目录 一、mysql安装 二、MySQL主从复制 一、mysql安装 yum install cmake gcc-c openssl-devel ncurses-devel.x86_64 rpcgen.x86_64 #安装依赖性 #在root路径下下载mysql-boost-5.7.44、libtirpc-devel-1.3.3-8.el9_4.x86_64.rpm安装包 yum install libtirpc-devel…

MFC之word操作

MFC对word操作 背景说明 当对程序的内容进行输出时&#xff0c;比如自定义对象属性描述或者注释&#xff08;详细设计&#xff09;生成文档时&#xff0c;如果采用手动输入会比较麻烦&#xff0c;并且当程序变动时&#xff0c;需要再一次修改对应文档&#xff0c;作为程序员做…

ASP.NET Core 入门教程一 创建最小 API

构建最小 API&#xff0c;以创建具有最小依赖项的 HTTP API。 它们非常适合需要在 ASP.NET Core 中仅包括最少文件、功能和依赖项的微服务和应用。 本教程介绍使用 ASP.NET Core 生成最小 API 的基础知识。 启动 Visual Studio 2022 并选择“创建新项目”。 在“创建新项目”…

移情别恋c++ ദ്ദി˶ー̀֊ー́ ) ——6.vector(模拟实现)

1.存储结构 namespace zone {template<class T> //需要模板class vector{public:private:iterator _start;iterator _finish;iterator _endofstorage;}; } 可见&#xff0c;vector内核是由三个指针实现的 2.默认成员函数 2.1.构造函数 1.初始化列表 vector() :_star…

linux 磁盘满了,程序运行失败,如何处理?df -h

场景&#xff1a;紧急呼救&#xff0c;上传图片失败了。我一脸懵&#xff0c;服务器这是又咋地了&#xff0c;别邪乎姐姐&#xff0c;姐姐胆子小啊。 一、寻找问题原因 1、OSS出问题了&#xff1f; 然后我尝试了 IOS 的APP是没问题的&#xff0c;Android提示上传失败&#xf…

IIS发布打包后文件

1.打开IIS软件 2 添加网站&#xff0c; 自定义网站名称-选择要放置的资源路径-选择IP地址 3.打开放置的资源目录放置打包后文件 4.选择浏览 搜索不到IIS可进行一下操作 控制面板-程序和功能-启用或关闭windows功能-勾选IIS

Linux之数字证书

新书速览|Ubuntu Linux运维从零开始学_ubuntu linux运维从零开始学 pdf 下载-CSDN博客 《Ubuntu Linux运维从零开始学&#xff08;Linux技术丛书&#xff09;》(肖志健)【摘要 书评 试读】- 京东图书 (jd.com) 随着网络环境的恶化&#xff0c;人们已经逐渐抛弃网络上面的明文…

【python】灰色预测 GM(1,1) 模型

文章目录 前言python代码 前言 用 python 复刻上一篇博客的 Matlab 代码。 【学习笔记】灰色预测 GM(1,1) 模型 —— Matlab python代码 # %% import numpy as np import statsmodels.api as sm import matplotlib.pyplot as plt from matplotlib.pylab import mplmpl.rcPa…

江协科技STM32学习- P4 新建工程

&#x1f680;write in front&#x1f680; &#x1f50e;大家好&#xff0c;我是黄桃罐头&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流 &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd;​…

模拟笔试 - 卡码网周赛第三十一期(23年百度笔试真题)

难度适中&#xff0c;动态规划出现的比例还是比较高的&#xff0c;要好好掌握&#xff0c;二分查找的点也是比较灵活的。&#xff08;A卷和B卷第一道题是一样的&#xff09; 题目一&#xff1a;讨厌鬼的组合帖子 思路&#xff1a;这个题算是一个还不错的题&#xff1b; 本质就…

C语言每日好题(3)

有任何不懂的问题可以评论区留言&#xff0c;能力范围内都会一一回答 #define _CRT_SECURE_NO_WARNING #include <stdio.h> #include <string.h> int main(void) {if ((strlen("abc") - strlen("abcdef")) > 0)printf(">\n")…

【图文并茂】ant design pro 如何优雅地把删除和批量删除功能合并到一起,并抽出来成为组件

如上图所示&#xff0c;其实批量删除和删除应该算是一个功能 只是删除一个或多个的区别 那么接口应该用的同一个 删除一个的时候呢&#xff0c;就传 一个对象_id 过去 删除多个的时候&#xff0c;就传 多个 对象 _id 的数组过去 后端 const deleteMultipleRoles handleAs…