Makefile学习

一、Makefile的介绍

1.1 什么是Makefile

        相信在Linux系统中经常会用到make这个命令来编译程序,而执行make命令所依赖的文件便是Makefile文件,make命令通过Makefile文件编写的内容对程序进行编译。make命令根据文件更新的时间戳来决定哪些文件需要重新编译这可以避免编译已经编译过的,没有改变的文件,从而提升编译效率。

1.2 Makefile的规则与示例

        一个简单的 Makefile 文件包含一系列的“规则”,其样式如下:

目标(target)…: 依赖()…
<tab>命令(command)

当“依赖文件”比“目标文件”更加新时,或者目标文件还没有生成时,就会执行“命令”

下面就是一个简单的Makefile文件:

hello: hello.cgcc -o hello hello.c
clean:rm -f hello

在代码所在的文件夹下放入此Makefile文件,并执行make操作,便会生成目标文件hello,如下

所示:

hello.c内容为:

#include <stdio.h>int main()
{printf("hello world\n");return 0;}

以上述为例,介绍一下详细规则:

        目标(target)通常是要生成的文件的名称,可以是可执行文件(比如上例中的hello就是要生成的可执行文件名)或OBJ 文件, 也可以是一个执行的动作名称(如上述例子中的clean)。
        依赖(prerequiries)是用来产生目标的材料(比如源文件 ) ,一个目标经常有几个依赖。
        命令(command)是生成目标时执行的动作,一个规则可以含有几个命令,每个命令占一 行。
        每个命令行前面必须是一个Tab字符,即命令行第一个字符是Tab。通常当一个依赖发生了变化,规则就会调用命令产生新的目标。但是并非所有目标都有依赖,像上述例子中的“clean”,它只负责清除文件。
        每一个Makefile文件也可以包含规则外的其他文件。
        对于上面的Makefile,执行"make"后仅当hello.c文件比hello文件新时,才会执行cc -o hello hello.c,从而生成hello文件,如果没有hello文件时,这个命令也会被执行。运行“make clean”时,由于clean目标没有依赖,它的命令“rm -f hello”就会被强制执行。

二、Makefile的基本语法

2.1 通配符

        当一个目标文件所依赖的依赖文件有很多时,需要写很多条规则,因此可以使用通配符只需要写一行来代替多行的规则。

        首先举一个没有使用通配符的例子:共有两个.c文件,分别为a.c,b.c

a.c:

#include <stdio.h>int main()
{func_b();return 0;
}

b.c:

#include <stdio.h>void func_b()
{printf("This is B\n");
}

Makefile:

test:a.o b.ogcc -o test a.o b.oa.o : a.cgcc -c -o a.o a.cb.o : b.cgcc -c -o b.o b.c

上述例子中的目标文件“test”有两个依赖“a.o”,“b.o”,不难看出,生成a.o,b.o的规则有很多相似之处,因此可以使用通配符来改进Makefile。改进后的Makefile如下:

test:a.o b.ogcc -o test $^%.o : %.cgcc -c -o $@ $<

其中用到的通配符的解释:

%.o:表示所有.o文件

%.c:表示所有.c文件

$@:表示目标

$<:表示第一个依赖文件

$^:表示所有依赖文件

2.2 假想目标

当我们需要清除文件时,可以在Makefile中添加如下代码:

test:a.o b.ogcc -o test $^%.o : %.cgcc -c -o $@ $<
clean:rm *.o test

在shell中输入make clean即可清除文件:

上面的写法其实存在问题,当前目录下没有clean文件时,使用make clean即可清除文件,但是当当前目录中存在clean文件时,再执行make clean便会出现下面的问题:

出现上面提示的原因是,一个规则能够被执行需要满足两个条件中的一个:

(1)目标文件不存在

(2)依赖文件比目标文件新

这里,clean没有依赖文件,并且clean文件在当前目录已经存在,因此不会执行make clean的操作。为了解决这个问题,需要用到关键字PHONY,其具体用法如下:

test:a.o b.ogcc -o test $^%.o : %.cgcc -c -o $@ $<
.PHONY : clean # 把clean定义为假象目标。他就不会判断名为“clean”的文件是否存在
clean:rm *.o test

将clean定义为假想目标,便可执行在当前目录已经存在clean文件的情况下执行make clean操作:

2.3 变量

在Makefile中有两种变量:

(1)即时变量(简单变量)

        A:=XXX,对于即时变量使用 “:=” 表示,它的值在定义的时候已经被确定了

(2)延时变量()

        B=XXX,对于延时变量使用“=”表示,它只有在使用到的时候才确定,在定义时并没有

确定下来。
        想使用变量的时候使用“$” 来引用,如果不想看到命令是,可以在命令的前面加上 "@" 符号,就不会显示命令本身。当我们执行make 命令的时候, make 这个指令本身,会把整个 Makefile 读进去,进行全部分析,然后解析里面的变量。常用的变量的定义如下:
: = # 即时变量
= # 延时变量
? = # 延时变量 , 如果是第 1 次定义才起效 , 如果在前面该变量已定义则忽略这句
\+ = # 附加 , 它是即时变量还是延时变量取决于前面的定义
? = # 如果这个变量在前面已经被定义了,这句话就会不会起效果

例子:

A := $(C) #即时变量
b = $(C) #延时变量
C = ABC
C +=123
D = hello
D ?= hello world # 延时变量,如果是第一次定义才有效,如果在前面该变量已经定义则忽略这句
E := $(C)
all:@echo A = $(A) #@表示不打印echo@echo b = $(b)@echo D = $(D)@echo E = $(E)

 运行结果:

分析:

A = ,由于A被定义为即时变量,在开始执行的时候C的值为空,所以A的值也为空。

 b = ABC 123,b被定义为延时变量,执行make时,会解析Makefile中所用的变量,先执行C = ABC,再执行C +=123,最终 C = ABC 123,因此b = ABC 123。

D = hello,D ?= hello world,执行时会判断前面是否已经定义D,在本例中前面已经定义D = hello了,因此D ?=  hello world便没有执行,依然输出D = hello

E =ABC 123,这里E依然为即时变量,但是与A不同的是,执行到这里,C已经被赋值为ABC 123,因此E = $(C) =ABC 123。 

 三、Makefile的常用函数

        函数调用的格式为:

$(function arguments)

        这里function是函数名字,arguments是函数的参数。参数和函数名之 间是用空格或 Tab 隔开,如果有多个参数,它们之间用逗号隔开。这些空格和逗号不是参数值的一部分。接下来介绍一些常用的函数。

3.1 字符串替换和分析函数

3.1.1 subst
$(subst from,to,text)

在文本“text”中将“from”用“to”替换

例:

A = $(subst c,o,a.c b.c)
all:@echo subst  = $(A)

运行结果,将a.c b.c 中的c字符替换为o字符: 

 3.1.2 patsubst
$(patsubst pattern ,replacement,text)

 将“text”中符合pattern的格式的字符用“replacement”替换,“pattern”和“replacement”可以使用通配符。

例:

files = a.c b.c c.c abc
dep_files1 = $(patsubst %.c,%.o,$(files))
all:@echo dep_files1 = $(dep_files1)

 运行结果:将以.c结尾的字符替换为.o

3.1.3 strip
$(strip string)

去掉前导和结尾空格,并将中间的多个空格压缩为单个空格。

例:

dep = $(strip a  d   e  f )
all:@echo dep = $(dep)

 运行结果,将a d  e f ,中多余的空格去除:

3.1.4 findstring

$(findstring find,in)

在字符串“in”中搜寻“find” ,若找到则返回“find”,否则返回空

例:

dep1 = $(findstring a,a b c)
dep2 = $(findstring a,b c)
all:@echo $(dep1)@echo $(dep2)

 运行结果,dep1 为a,dep为空:

3.1.5 filter/filterout
$(filter pattern ,text)

 在text中取出符合patten格式的值

$(filter-out pattern...,text)

 在text中取出不符合patten格式的值

例:

# filter函数,$(filter pattern...,text)  在text中取出符合patten格式的值
# filter-out函数,$(filter-out pattern...,text)  在text中取出不符合patten格式的值
A = a b c d/
B = $(filter %/,$(A))
C = $(filter-out %/,$(A))
all:@echo B = $(B)@echo C = $(C)

运行结果,B为以/结尾的d/,C为出去/d的其他字符:

 3.1.6 sort
$(sort list)

将“list”中的字符按字母顺序排序,并去除重复的字,输出由单个空格隔开的字的列表

例:

dep = $(sort appel cat bat egg)
all:@echo $(dep)

运行结果:


 

3.2 文件名函数

3.2.1 dir/notdir
$(dir names...)

抽取“names”中的每一个文件名的路径部分。文件名的路径部分包括从文件名的首字符到最后一个斜杠之前的一切字符。

$(notdir names...)

 抽取‘names...’中每一个文件名中除路径部分外一切字符(真正的文件名)。

例:

dep1 = $(dir src/hello.c hello.c)
dep2 = $(notdir src/hello.c hello.c)
all:@echo dep1 = $(dep1)@echo dep2 = $(dep2)

 运行结果:

 3.2.2 suffix 
$(suffix names...)

抽取“names”中每一个文件名的后缀

 例:

dep  = $(suffix src/foo.c src-1.0/bar.c hacks.o)
all:@echo dep = $(dep)

运行结果:

 3.2.3 basename
$(basename names...)

 抽取‘names...’中每一个文件名中除后缀外一切字符。

例:

dep = $(basename src/foo.c src-1.0/bar hacks.o)
all:@echo dep = $(dep)

 运行结果:

 3.2.4 addsuffix/addprefix
$(addsuffix suffix,names...)
参数‘ names... ’是一系列的文件名,文件名之间用空格隔开; suffix 是 一个后缀名。将 suffix( 后缀 ) 的值附加在每一个独立文件名的后面,完成后将 文件名串联起来,它们之间用单个空格隔开。
例:
dep = $(addsuffix .c,cat dog)
all:@echo $(dep)

运行结果:

$(addprefix prefix,names...)
参数‘ names ’是一系列的文件名,文件名之间用空格隔开; prefix 是一个前缀名。将 preffix( 前缀 ) 的值附加在每一个独立文件名的前面,完成后将文件名串联起来,它们之间用单个空格隔开。

 例:

dep = $(addprefix src/,cat dog)
all:@echo $(dep)

运行结果:

 

 3.2.5 wildcard
$(wildcard pattern)
参数‘ pattern ’是一个文件名格式,包含有通配符 ( 通配符和 shell 中用法一样) 。函数 wildcard 的结果是一列和格式匹配的且真实存在的文件的名称,文件名之间用一个空格隔开
例:比如当前路径下有a.c b.c c.c d.o,执行以下代码
#$(wildcard pattern)  pattern定义了文件名的格式, wildcard会在当前目录下取出其中存在的文件。
files = $(wildcard *.c)
all:@echo files = $(files)

运行结果:

3.3 其他函数

3.3.1 foreach

$(foreach var ,list,text)

 将list中遍历的每一个字都赋值给var,然后text引用变量var对其中保存的字进行扩展,因此text每次扩展都不相同。

例:

dirs = a b c d
files = $(foreach dir,$(dirs),$(wildcard $(dir)/*))all:@echo files = $(files)

该例中files保存的是当前路径下的a/,b/,c/,d/目录下所有的文件列表,a/,b/,c/,d/路径下的文件如下图所示:

 执行Makefile后显示内容为:

 3.3.2 if

$(if condition,then-part[,else-part])
首先把第一个参数‘ condition ’的前导空格、结尾空格去掉,然后扩展。 如果扩展为非空字符串,则条件‘condition ’为‘真’;如果扩展为空字符串,则条件‘condition ’为‘假’。
如果条件‘ condition ’为‘真’ , 那么计算第二个参数‘ then-part ’的值,并将该值作为整个函数 if 的值。
如果条件‘ condition ’为‘假’ , 并且第三个参数存在,则计算第三个参数‘else-part ’的值,并将该值作为整个函数 if 的值;如果第三个参数不存在,函数 if 将什么也不计算,返回空值。

 例:

# SUBDIR = $(B)
SUBDIR := $(B)
B = a/
sub_dir = $(if $(SUBDIR),$(SUBDIR),/home/src)
all:@echo sub_dir  = $(sub_dir)

当SUBDIR为即时变量时,SUBDIR没有被即时赋值,因此该变量保存的内容为空,经过if语句判断后sub_dir输出的内容应该为/home/src

当SUBDIR为延时变量时,SUBDIR被即时赋值为a/,经过if语句判断后sub_dir输出的内容应该为a/:

 

3.3.3 origin

$(origin variable)
变量‘ variable ’是一个查询变量的名称,不是对该变量的引用。所以,不能采用‘$ ’和圆括号的格式书写该变量,当然,如果需要使用非常量的文件名,可以在文件名中使用变量引用。
函数 origin 的结果是一个字符串,该字符串变量是这样定义的:
例:
a = a/
ifdef aifeq ("$(origin a)", "file")dir := $(a)endifendifall:@echo  $(origin a)@echo  $(dir)

如果a变量被定义,且a变量是在Makefile中被定义的,则dir = a,结果输出file和a/

当a变量是在命令行中被定义时,输出为command line 和空:

 

3.3.4 shell 

$(shell command arguments)
函数 shell make 与外部环境的通讯工具。函数 shell 的执行结果和在控制台上执行‘command arguments ’的结果相似。不过如果‘ command arguments ’的结果含有换行符(和回车符),则在函数 shell 的返回结果中将把它们处理为单个空格,若返回结果最后是换行符(和回车符)则被去掉。
例:
c_src := $(shell ls)
all:@echo c_src = $(c_src)

显示当前目录下所有的文件

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

纯css html 真实水滴效果

惯例,不多说直接上图 秉承着开源精神,我们将这段代码无私地分享给大家&#xff0c;因为我们深信&#xff0c;信息的共享和互相学习是推动科技进步的关键。我们鼓励大家在使用这段代码的同时&#xff0c;也能够将其中的原理、思想和经验分享给更多的人。 这份代码是我们团队用心…

一百八十六、大数据离线数仓完整流程——步骤五、在Hive的DWS层建动态分区表并动态加载数据

一、目的 经过6个月的奋斗&#xff0c;项目的离线数仓部分终于可以上线了&#xff0c;因此整理一下离线数仓的整个流程&#xff0c;既是大家提供一个案例经验&#xff0c;也是对自己近半年的工作进行一个总结。 二、数仓实施步骤 &#xff08;五&#xff09;步骤五、在Hive的…

Purism 推出注重隐私的 Linux 平板电脑

导读一款昂贵的 Linux 平板电脑&#xff0c;注重安全和隐私。让我们拭目以待。 Purism 是一家日益流行的计算机硬件产品制造商&#xff0c;专门提供配备注重隐私的开源 Linux 发行版的笔记本电脑、台式机和移动设备。 最近&#xff0c;他们发布了一款新产品 Librem 11 平板电…

ARM底层汇编基础指令

汇编语言的组成 伪操作 不参与程序执行&#xff0c;但是用于告诉编译器程序怎么编译.text .global .end .if .else .endif .data 汇编指令 编译器将一条汇编指令编译成一条机器码&#xff0c;在内存里一条指令占4字节内存&#xff0c;一条指令可以实现一个特定的功能 伪指令 不…

嵌入式Linux应用开发-基础知识-第十六章GPIO和Pinctrl子系统的使用

嵌入式Linux应用开发-基础知识-第十六章GPIO和Pinctrl子系统的使用 第十六章 GPIO 和 Pinctrl 子系统的使用16.1 Pinctrl 子系统重要概念16.1.1 引入16.1.2 重要概念16.1.3 示例16.1.4 代码中怎么引用pinctrl 16.2 GPIO子系统重要概念16.2.1 引入16.2.2 在设备树中指定引脚16.2…

软件设计模式系列之二十一——观察者模式

1 观察者模式的定义 观察者模式&#xff08;Observer Pattern&#xff09;是一种行为型设计模式&#xff0c;它允许对象之间建立一对多的依赖关系&#xff0c;当一个对象的状态发生变化时&#xff0c;所有依赖于它的对象都会得到通知并自动更新。这个模式也被称为发布-订阅模式…

数据结构 | 二叉树

基本形状 可参照 数据结构&#xff1a;树(Tree)【详解】_数据结构 树_UniqueUnit的博客-CSDN博客 二叉树的性质 三种顺序遍历

BUUCTF reverse wp 51 - 55

findKey shift f12 找到一个flag{}字符串, 定位到关键函数, F5无效, 大概率是有花指令, 读一下汇编 这里连续push两个byte_428C54很奇怪, nop掉下面那个, 再往上找到函数入口, p设置函数入口, 再F5 LRESULT __stdcall sub_401640(HWND hWndParent, UINT Msg, WPARAM wPara…

Kafka(一)使用Docker Compose安装单机Kafka以及Kafka UI

文章目录 Kafka中涉及到的术语Kafka镜像选择Kafka UI镜像选择Docker Compose文件Kafka配置项说明KRaft vs Zookeeper和KRaft有关的配置关于Controller和Broker的概念解释Listener的各种配置 Kafka UI配置项说明 测试Kafka集群Docker Compose示例配置 Kafka中涉及到的术语 对于…

Spring5应用之AOP切入点详解

作者简介&#xff1a;☕️大家好&#xff0c;我是Aomsir&#xff0c;一个爱折腾的开发者&#xff01; 个人主页&#xff1a;Aomsir_Spring5应用专栏,Netty应用专栏,RPC应用专栏-CSDN博客 当前专栏&#xff1a;Spring5应用专栏_Aomsir的博客-CSDN博客 文章目录 前言切入点详解切…

简单三步 用GPT-4和Gamma自动生成PPT PDF

1. 用GPT-4 生产PPT内容 我想把下面的文章做成PPT&#xff0c;请你给出详细的大纲和内容 用于谋生的知识&#xff0c;学生主要工作是学习&#xff0c;成年人的工作是养家糊口&#xff0c;这是基本的要求&#xff0c;在这之上&#xff0c;才能有更高的追求。 不要短期期望过高…

C#(CSharp)入门教程

目录 C#的第一个程序 变量 折叠代码 变量类型和声明变量 获取变量类型所占内存空间&#xff08;sizeof&#xff09; 常量 转义字符 隐式转换 显示转换 异常捕获 运算符 算术运算符 布尔逻辑运算符 关系运算符 位运算符 其他运算符 字符串拼接 …

计算机图像处理-高斯滤波

高斯滤波 高斯滤波是一种线性平滑滤波&#xff0c;适用于消除高斯噪声&#xff0c;广泛应用于图像处理的减噪过程。通俗的讲&#xff0c;高斯滤波就是对整幅图像进行加权平均的过程&#xff0c;每一个像素点的值&#xff0c;都由其本身和邻域内的其他像素值经过加权平均后得到…

Ubuntu部署运行ORB-SLAM2

ORB-SLAM2是特征点法的视觉SLAM集大成者&#xff0c;不夸张地说是必学代码。博主已经多次部署运行与ORB-SLAM2相关的代码&#xff0c;所以对环境和依赖很熟悉&#xff0c;对整个系统也是学习了几个月&#xff0c;一行行代码理解。本次在工控机上部署记录下完整的流程。 ORB-SLA…

koa基础应用

不要把koa想得太复杂&#xff0c;他就是一个Node框架而已。 在本地应用安装好Node和koa后&#xff0c;先实现一下简单的服务app.js&#xff0c;代码如下&#xff1a; const Koa require(koa) const app new Koa(); app.use(async (context) > {context.body hello Koa …

企业风险管理策略终极指南

企业风险管理不一定是可怕的。企业风险管理是一个模糊且难以定义的主题领域。它涵盖了企业的多种风险和程序&#xff0c;与传统的风险管理有很大不同。 那么&#xff0c;企业风险管理到底是什么&#xff1f;在本文中&#xff0c;我们将确定它是什么&#xff0c;提出两种常见的…

B058-SpringBoot

目录 springboot概念与作用入门案例springboot运行方式热部署配置文件Profile多环境支持整合测试-springboot-testSpringboot-web1.返回json数据2.返回页面&#xff08;模板技术&#xff09;thymeleaf1.导入thymeleaf依赖2.模板文件3.controller4.启动类 SSM整合1.导包2.项目目…

机器人中的数值优化|【四】L-BFGS理论推导与延伸

机器人中的数值优化|【四】L-BFGS理论推导与延伸 往期内容回顾 机器人中的数值优化|【一】数值优化基础 机器人中的数值优化|【二】最速下降法&#xff0c;可行牛顿法的python实现&#xff0c;以Rosenbrock function为例 机器人中的数值优化|【三】无约束优化&#xff0c;拟牛…

让大脑自由

前言 作者写这本书的目的是什么&#xff1f; 教会我们如何让大脑更好地为自己工作。 1 大脑的运行机制是怎样的&#xff1f; 大脑的基本运行机制是神经元之间通过突触传递信息&#xff0c;神经元的兴奋和抑制状态决定了神经网络的运行和信息处理&#xff0c;神经网络可以通过…

使用Qt验证RGB格式

下面我们用不同的颜色来绘制一块矩形区域&#xff0c;来对比学习RGB颜色。 一片漆黑的黑色 黑色在RGB中是三个颜色分量都是0。也就是没有颜色。 下面我们绘制一个水平100个像素&#xff0c;垂直200个像素的矩形区域&#xff0c;颜色设置为黑色。 #ifndef MAINWINDOW_H #def…