GNU make系列之介绍Makefile(0)

news2024/10/5 20:24:05

一.欢迎来到我的酒馆

      在本章节介绍Makefile。

目录

    • 一.欢迎来到我的酒馆
    • 二.GNU make 预览
    • 三.一个简单的Makefile
    • 四.make程序如何处理Makefile文件
    • 五.在Makefile中使用变量

二.GNU make 预览

      2.1 GNU make工具会自动决定哪些程序需要被重新编译,并且执行相应的命令来重新编译程序。在本系列博客中,我们会介绍GNU make。GNU make是由Richard Stallman和Roland McGrath开发的,在3.76版本后由Paul D. Smith开发。
      在本系列博客中,所有的例子都用c语言,c语言是使用最广泛的编程语言之一。但是,你可以使用make来构建任何编程语言的程序,只要是编译器可以运行在shell命令行上。make不仅限于构建应用程序,你还可以用make来描述某些文件必须自动更新的任何任务,无论何时有文件变动了,就需要自动更新某些文件。

2.2 准备和运行make
      在使用make工具之前,你必须写一个名为makefile的文件,这个makefile文件描述了文件之间的关系,并提供用于更新每个文件的命令。在一个应用程序中,通常,可执行文件从object文件(.o文件)上更新,而这些object文件又是通过编译源文件生成的。
      一旦有一个合适的makefile文件,每当你更改了一些源文件,你可以使用一条如下的简单命令:

make

来执行必要的重新编译。应用程序使用makefile数据为基础并且确定需要更新哪些文件。对于其中的每一个文件,它都会发布一个记录在以makefile数据为基础的配方。你可以提供命令行参数去控制哪些文件需要被更新。

2.3 一个简单的Makefile介绍
      你需要写一个文件名为makefile的文件来告诉make程序要执行哪些操作。大部分情况下,makefile文件表明如何编译和链接一个应用程序。当有明确的要求时(如,删除一个文件来执行清理操作)makefile文件还可以告诉make程序如何执行复杂的命令。
      当重新编译可执行文件的时候,每个更新过的c源文件必须重新编译。如果一个头文件已经更新了,为了安全起见,每一个包含了这个头文件的c源文件也必须重新编译。每次编译源文件都会生成一个对应的object文件(.o文件)。最后,如果任何的源文件已经被重新编译了,则所有的object文件,无论是新生成的还是以前编译中保存的,都必须一起链接来生成一个新的可执行文件。

2.4 一个执行单元长啥样
      一个简单的makefile文件包含了一个执行单元(rule),如下:

target ... : prerequisite ...
	recipe 1
	recipe 2
	recipe 3
	...
  • target(目标), 通常是一个文件的名字,target也可以是一个可执行文件或object文件。target也可以是要执行操作的名称,例如:clean。
  • prereauisite (先决条件), 是用作输入的文件,用于创建target。一个target通常依赖多个文件。
  • recipe(配方),是执行的操作。recipe可以有多个命令,要么在同一行上,要么在自己所在的行上。这里要记住的是:在写每个recipe之前都要敲一个tab键。如果你偏爱某个字符而不是tab字符加在recipe之前,你可以设置一个变量来替代字符。

      通常,配方是一条命令且需要用到先决条件,如果任何的先决条件发生更改,将会生成一个target。但是一个有配方和target的执行单元(rule),可以不写先决条件。例如:一个执行删除操作的target可以不写先决条件,如clean。
      一个执行单元,说明了如何且何时执行重新编译,在先决条件上执行一个配方来创建和更新一个target。一个执行单元同样可以说明如何执行一个操作。
      一个makefile文件可以包含除执行单元外的其他文本。但是一个简单的makefile仅仅只需要包含执行单元。相比较于展示的例子,执行单元可能看起来更复杂一些,但是所有的执行单元或多或少都适应这种模式。

三.一个简单的Makefile

      下面是一个简单的makefile例子,cJSON是c语言编写的JSON解码器,代码非常简洁,只有750行代码。点击这里下载cJSON
使用命令解压:

tar -zxvf cJSON.tar

解压之后,进入cJSON目录,文件像下面这样:
在这里插入图片描述

在项目cJSON目录下我已经写好了一个Makefile文件,这个例子描述了一个可执行文件test依赖于1个cJSON.o object文件,这个object文件又依赖于一个c源文件。

all: test

test: cJSON.o test.c 
        cc -W -Wall -o test test.c cJSON.o -lm 

cJSON.o: cJSON.c cJSON.h
        cc -W -Wall -c -o cJSON.o cJSON.c

clean:
        rm -rf *.o test 

要使用这个Makefile生成一个可执行文件,输入命令:

make

删除目录下的可执行文件和object文件,输入命令:

make clean

      在本例中,target是all,它依赖test,而test又依赖于cJSON.o,test.c,cJSON.h文件。配方是两个gcc编译指令。clean没有先决条件,它不依赖于任何文件,因此,它默认情况下是不执行的,除非指定命令(如:make clean)才会执行。
      当target是一个文件时,如果任何的prerequisite发生了更改,target需要被重新编译或重新链接。此外,任何prerequisite应首先更新自己自动生成的内容。在上面的例子中,cJSON.o依赖于cJSON.c源文件和cJSON.h头文件。
      一个配方会紧跟着一个target和prerequisite,这些配方表示了如何更新target文件。在makefile文件里,每行配方前必须敲一个tab键,以此来区分不同的配方。这里要记住的是:makefile并不知道配方是如何工作的,这取决于你提供各种配方来更新target文件。当一个target需要被更新的时候,所有提供的配方都会被执行。
      这里clean是一个target,但不是一个文件,它是一个操作的名称。在这个执行单元中,因为默认不会执行这个操作,clean不是任何其他执行单元的先决条件。因此,不要用它做任何事情,除非你告诉它要执行哪些操作。这里要记住的是:clean这个执行单元不仅不是一个先决条件,而且它没有任何的先决条件,因此这个执行单元的目的是运行特定的配方。target不是一个文件,但是是一个操作称之为phony target。如果一个object文件被重新编译了,他会比可执行程序test更新,所以test需要重新链接。
      因此,如果我们修改了cJSON.c文件,之后运行命令make,make会编译cJSON.c文件为cJSON.o文件,并且重新链接可执行程序test。

四.make程序如何处理Makefile文件

      默认的,make程序从上到下执行,它会找到第一个target。第一个target称为默认目标。
在上一小节的简单Makefile例子中,默认目标为test,因此,test这个执行单元会首先执行。当你下命令:

make

      make程序会在当前的目录下读取makefile文件,并且开始处理第一个执行单元。在这个例子中,第一个执行单元是all: test,而test又依赖于cJSON.o和test.c文件,在处理执行单元all: test之前,需要首先处理test的依赖文件。每个依赖文件都有自己的处理单元,通过编译源程序,这些处理单元会更新每个.o文件。如果先决条件是头文件和源文件,在这种情况下必须执行重新编译。
      其他的执行单元会被执行,因为它们的target是一个先决条件。如果一个执行单元不依赖任何的文件或先决条件,那么这个执行单元不会执行,除非你告诉make工具如何执行,如:make clean。
      在重新编译一个object文件之前,make程序会考虑更新它的先决条件,源文件和头文件。这个makefile文件没有指定具体的要执行的任务,.c和.h文件不是任何执行单元的target,因此对于这些文件,make程序不会执行任何操作,但是make程序会自动更新可执行程序。
      在重新编译任何需要它的object文件之后,make程序会决定是否重新链接可执行程序test。如果可执行程序test不存在,或者任何的object文件比这个test文件更新。如果一个object文件被重新编译了,那么它会比test可执行文件更新,因此会重新链接test。
      因此,如果我们修改了cJSON.c文件,然后执行make指令,make会把这个cJSON.c文件编译为cJSON.o文件,并且重新链接test可执行文件。如果修改了cJSON.h,make工具会把cJSON.c文件编译成cJSON.o文件,并且重新链接test。

五.在Makefile中使用变量

      5.1 上面的Makefile也可以写成下面这样:

all: test

test: cJSON.o test.o
        cc -W -Wall -o test test.o cJSON.o -lm 

cJSON.o: cJSON.c cJSON.h
        cc -W -Wall -c -o cJSON.o cJSON.c

test.o: test.c
        cc -W -Wall -c -o test.o test.c

clean:
        rm -rf *.o test 

      在上面的例子中,我们需要重复写两次cJSON.o、test.o,随着项目越来越大,这种重复非常容易出错,这时候我们可以使用变量来降低这种风险。变量允许一个文本字符串定义一次,以后可以在不同位置使用这个文本字符串(变量名)。

      对每个makefile创建一个名为objects的变量,这是一种标准练习。我们可以在makefile中定义一个变量objects:

objects=cJSON.o test.o

之后,在每个地方我们想要罗列出所有的object文件时,我们可以通过写:

$(object)

来替换变量的值。
下面是使用变量的makefile版本:

objects=cJSON.o test.o

all: test

test: $(objects)
        cc -W -Wall -o test $(objects) -lm 
        
cJSON.o: cJSON.c cJSON.h
        cc -W -Wall -c -o cJSON.o cJSON.c

test.o: test.c
        cc -W -Wall -c -o test.o test.c

clean:
        rm -rf *.o test 

5.2 让make程序推断出配方
      对于编译单个的.c源文件,可以不写配方。因为make工具可以自己推断出配方。从.c文件更新对应的.o文件可以使用cc -c命令,这是一个隐式的规则。
例如,使用配方:

cc -W -Wall -c -o cJSON.o cJSON.c

会将cJSON.c文件编译为cJSON.o文件。这里,我们可以省略配方,不写。当以这种方式自动调用.c文件时,它会自动添加到先决条件列表中。下面是一个完整的makefile,省略配方、省略.c文件、使用变量:

objects=cJSON.o test.o

all: test

test: $(objects)
        cc -W -Wall -o test $(objects) -lm 
cJSON.o: cJSON.h

test.o: 

clean:
        rm -rf $(objects) test 

这就是我们在实际操作中编写makefile的方式。因为隐式规则非常方便,也很重要,你会频繁的看到它被使用。

5.3 另一种类型的makefile
当makefile的object文件仅由隐式规则创建时,可以使用替代样式的makefile。在上面这个makefile中,你可以按先决条件而不是目标对条目进行分组。这是否是一种更好的尝试,这看起来更加紧凑,但是一些人并不喜欢它,因为它们发现将每个目标的所有信息放在一个地方更清晰。

5.4 清除目录
你不仅仅只想写编译一个程序的执行单元,当编译一个程序的时候。makefile一般会告诉你如何去做一些其他事情。例如,如何删除所有的object文件和可执行文件,下面是我们编写一个make执行单元来清除目录:

clean:
	rm -rf $(objects) test

在实际开发中,我们可能会写一个复杂的执行单元来处理意想不到的情况。我们可以这样写:

.PHONY: clean
clean:
	rm -rf $(objects) test

这可以防止make程序被名为clean的实际文件混淆。.PHONY: clean 表示clean不是一个文件而是一个命令。像上面的执行单元不应该放置在makefile文件的首行,因为我们并不希望它默认被执行。因此,在上面的makefile例子中,我们想要一个生成test的执行单元,test是默认目标。
因为clean不是test的先决条件,所以当我们输入命令:make的时候,clean这个执行单元不会被执行。如果要允许clean这个执行单元的话,需要输入:make clean。

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

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

相关文章

彩虹外链网盘V5.5更新 支持批量封禁/优化加载速度

彩虹外链网盘V5.5更新 支持批量封禁/优化加载速度 彩虹外链网盘,是一款PHP网盘与外链分享程序,支持所有格式文件的上传,可以生成文件外链、图片外链、音乐视频外链,生成外链同时自动生成相应的UBB代码和HTML代码,还可…

Mybatis1.7 修改

1.7 修改 1.7.1 编写接口方法1.7.2 编写SQL语句1.7.3 编写测试方法 如图所示是修改页面,用户在该页面书写需要修改的数据,点击 提交 按钮,就会将数据库中对应的数据进行修改。注意一点,如果哪儿个输入框没有输入内容,我…

苹果手机怎么录音?分享2个简单方法

录音软件能够帮助我们记录学习和工作中一些重要的笔记、讲座、课堂语录、音乐灵感等等。录制完成后它会自动生成文件,可供人们随时随地进行听阅。 但对于刚使用苹果手机的用户来说,他们还不太熟悉苹果系统,所以找不到语音备忘录在哪里。那么…

Linux CentOS7sed的替换及逆转功能

在各项工作,需要大量的文本处理。有时,想把文件按行翻转一下,最后一行显示在第一行,倒数第二行显示在正数第二行,等等。这是行的逆转要求,可以通过命令tac对文件操作,达到目的;有时&…

百望云产业链图谱引领数字变革 赋能企业高价值数字资产管理

全球经济相互依存度增加,技术进步的速度越来越快,市场竞争日益激烈……这些因素共同塑造了一个复杂多变、不确定性极高的商业环境。在当今风云变幻的经济格局和市场波动不断的大背景下,众多企业们面临着如何利用海量数据赋能业务决策的挑战和…

0829【综述】面向时空数据的区块链研究综述

摘要:时空数据包括时间和空间2个维度,常被应用于物流、供应链等领域。传统的集中式存储方式虽然具有一定的便捷性,但不能充分满足时空数据存储及查询等要求,而区块链技术采用去中心化的分布式存储机制,并通过共识协议来保证数据的安全性。研究现有区块链1.0、2.0和以Block-DAG为…

Java--输入(格式化)输出

1、读取输入 要想通过控制台进行输入,首先需要构造一个与“标准输入流”System.in关联的Scanner对象。 import java.util.*; // Scanner类定义在java.util包中 ​ Scanner in new Scanner(System.in); java.util.Scanner Scanner(InputStream in) 用给定的输人流…

CSDN每日一练 |『清理磁盘空间』『奇偶排序』『编号分组』2023-08-30

CSDN每日一练 |『清理磁盘空间』『奇偶排序』『编号分组』2023-08-30 一、题目名称:清理磁盘空间二、题目名称:奇偶排序三、题目名称:奇偶排序四、题目名称:编号分组 一、题目名称:清理磁盘空间 时间限制:1…

e与phi不互素的情况

情况一:e较大且与(p-1)或(q-1)中任意一个不互素 例题:moeCTF2022---Signin 题目源码: from Crypto.Util.number import * from secret import flag mbytes_to_long(flag) pgetPrime(512) qgetPrime(512) print(p,p) print(q,q) np*q e65537…

DEFORMABLE DETR: DEFORMABLE TRANSFORMERS FOR END-TO-END OBJECT DETECTION 论文精读笔记

DEFORMABLE DETR DEFORMABLE DETR: DEFORMABLE TRANSFORMERS FOR END-TO-END OBJECT DETECTION 参考:AI-杂货铺-Transformer跨界CV又一佳作!Deformable DETR:超强的小目标检测算法! 摘要 摘要部分,作者主要说明了如…

Mybatis1.8 删除一行数据

1.8 删除一行数据 1.8.1 编写接口方法1.8.2 编写SQL语句1.8.3 编写测试方法 如上图所示,每行数据后面都有一个 删除 按钮,当用户点击了该按钮,就会将改行数据删除掉。那我们就需要思考,这种删除是根据什么进行删除呢?是…

HttpClient 绕过证书验证- sun.security.validator.ValidatorException: PKIX

sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target 问题 package com.example.demo.test;import com.alibaba.fastjson2.JS…

MySQL的备份与恢复以及日志管理

目录 一、数据备份的重要性 二、数据库备份的分类 1、物理备份 2、逻辑备份 (1)完全备份:每次对数据进行完整的备份 (2)差异备份:备份自从上次完全备份之后被修改的过文件 (3&#xff09…

优化爬虫请求:如何选择合适的爬虫ip轮换策略?

在进行爬虫任务时,使用隧道爬虫ip并采用合适的轮换策略可以提高稳定性和效率。选择合适的隧道爬虫ip轮换策略可以优化您的爬虫请求过程。 1、考量目标网站特点 不同网站对于频繁请求可能有不同限制或反爬机制。 了解目标网站是否存在IP封禁、验证码等问题&#xff…

初创公司如何选择小型办公室

初创公司寻找小型办公室时,有多种选择。不同的办公室类型具有不同的租金成本、交通便利程度和对公司发展的影响。本文将比较几种不同的小型办公室,为初创公司提供选择建议。 共享办公空间 共享办公空间是一种适合初创公司和自由职业者的办公室类型。这种…

Mybatis1.10 Mybatis参数传递

1.10 Mybatis参数传递 1.10.1 多个参数1.10.2 单个参数 Mybatis 接口方法中可以接收各种各样的参数,如下: 多个参数单个参数:单个参数又可以是如下类型 POJO 类型Map 集合类型Collection 集合类型List 集合类型Array 类型其他类型 1.10.1 …

Kubernetes技术-Kubernetes集群环境搭建准备

1.搭建环境规划 在搭建k8s的时候可以分为两种: 单master集群,故名思意,只有一个master管理节点和多个node节点。如下图所示: 多master集群,故名思意,有多个master管理节点和多个node节点。如下图所示: 2.服务器硬件要求 (1).测试环境要求(教学、研究等环境) Master节点:至…

《Kali渗透基础》15. WEB 渗透

kali渗透 1:WEB 技术1.1:WEB 攻击面1.2:HTTP 协议基础1.3:AJAX1.4:WEB Service 2:扫描工具2.1:HTTrack2.2:Nikto2.3:Skipfish2.4:Arachni2.5:OWAS…

YOLOv5 7.0添加ACmix

ACmix 论文地址:On the Integration of Self-Attention and Convolution ACmix探讨了卷积和自注意力这两种强大技术之间的关系,并将两者整合在一起,同时享有双份好处,并显著降低计算开销,可助力现有主干涨点&#xff…

php对接AWS S3云存储,上传S3及访问权限问题

首先先下载sdk包 https://docs.aws.amazon.com/zh_cn/sdk-for-php/v3/developer-guide/getting-started_installation.html S3创建存储桶 去安全凭证-》创建访问秘钥 创建的时候会提示,主账号创建不安全,这个时候我们需要创建一个IAM账号来创建秘钥 创…