GNU make系列之介绍Makefile(0)

一.欢迎来到我的酒馆

      在本章节介绍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 1recipe 2recipe 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: testtest: cJSON.o test.c cc -W -Wall -o test test.c cJSON.o -lm cJSON.o: cJSON.c cJSON.hcc -W -Wall -c -o cJSON.o cJSON.cclean: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: testtest: cJSON.o test.occ -W -Wall -o test test.o cJSON.o -lm cJSON.o: cJSON.c cJSON.hcc -W -Wall -c -o cJSON.o cJSON.ctest.o: test.ccc -W -Wall -c -o test.o test.cclean:rm -rf *.o test 

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

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

objects=cJSON.o test.o

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

$(object)

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

objects=cJSON.o test.oall: testtest: $(objects)cc -W -Wall -o test $(objects) -lm cJSON.o: cJSON.c cJSON.hcc -W -Wall -c -o cJSON.o cJSON.ctest.o: test.ccc -W -Wall -c -o test.o test.cclean: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.oall: testtest: $(objects)cc -W -Wall -o test $(objects) -lm 
cJSON.o: cJSON.htest.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.rhkb.cn/news/113623.html

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

相关文章

W5100S-EVB-PICO通过SNTP获取网络时间(十一)

前言 上一章我们用开发板进行ping测试,本章我们用它通过SNTP获取网络时间并在串口显示。 什么是SNTP? 能用来做什么? SNTP(Simple Network Time Protocal简单网络时间协议),用于跨广域网或局域网同步时间的协议,具有较高的精确度&#xff…

Python爬虫:一个爬取豆瓣电影人像的小案例

从谷歌浏览器的开发工具进入 选择图片右键点击检查 ![在这里插入图片描述](https://img-blog.csdnimg.cn/1b38c2a942c441fb8cb545a28bb35015.png 翻页之后发现网址变化的只有start数值,每次变化值为30 Python代码 import requests from bs4 import BeautifulSou…

C++11

✅<1>主页&#xff1a;&#xff1a;我的代码爱吃辣 &#x1f4c3;<2>知识讲解&#xff1a;C11 ☂️<3>开发环境&#xff1a;Visual Studio 2022 &#x1f4ac;<4>前言&#xff1a;C标准10年磨一剑,成就了一次非常成功的更新C11&#xff0c;增加了非常有…

DolphinDB 携手白鲸开源 WhaleStudio 打造高效敏捷的 DataOps 解决方案

浙江智臾科技有限公司&#xff08;简称&#xff1a;DolphinDB&#xff09;和北京白鲸开源科技有限公司&#xff08;简称&#xff1a;白鲸开源&#xff09;是在大数据技术领域活跃的两支专业团队。 DolphinDB 专注于为用户提供集高性能存储、复杂分析能力和流处理于一体的实时计…

三、原型模式

一、什么是原型模式 原型&#xff08;Prototype&#xff09;模式的定义如下&#xff1a;用一个已经创建的实例作为原型&#xff0c;通过复制该原型对象来创建一个和原型相同或相似的新对象。在这里&#xff0c;原型实例指定了要创建的对象的种类。用这种方式创建对象非常高效&a…

2023固态U盘、移动硬盘对比

最近测试了几款固态U盘/移动硬盘&#xff0c;希望能大家的选购有点帮助。 1、移速逸动-2T&#xff08;500MB/s&#xff09;&#xff1a;799元某音 2、爱国者u397-1T&#xff08;1000MB/s&#xff09;&#xff1a;578元京东 3、梵想FF520-512G&#xff08;500MB/s&#xff09…

【数据结构】多叉树转换为二叉树-c++代码实现-POJ 3437 Tree Grafting

文章目录 写这个题目的原因寻找提交网址题目解决思路AC代码成功AC 写这个题目的原因 1、今天在看王道考研数据结构的课&#xff08;虽然我要保研&#xff0c;但是因为这些看保研面试的时候会问&#xff0c;所以看一下嘞orz&#xff09;&#xff0c;看到了这个多叉树转换为二叉…

QT基础教程之六布局管理器和常用控件

QT基础教程之六布局管理器和常用控件 布局管理器 所谓 GUI 界面&#xff0c;归根结底&#xff0c;就是一堆组件的叠加。我们创建一个窗口&#xff0c;把按钮放上面&#xff0c;把图标放上面&#xff0c;这样就成了一个界面。在放置时&#xff0c;组件的位置尤其重要。我们必须…

1、Spring是什么?

Spring 是一款主流的 Java EE 轻量级开源框架 。 框架 你可以理解为是一个程序的半成品&#xff0c;它帮我们实现了一部分功能&#xff0c;用这个框架我们可以减少代码的实现和功能的开发。 开源 也就是说&#xff0c;它开放源代码。通过源代码&#xff0c;你可以看到它是如何…

不需要任何编程经验也能牢固掌握Java精髓——《Java官方入门教程(第9版·Java 17)》

《Java官方入门教程&#xff08;第9版Java 17&#xff09;》针对Java SE 17做了全面细致的更新&#xff0c;将引导你轻松学习最新的核心Java编程技能。《Java官方入门教程&#xff08;第9版Java 17&#xff09;》由畅销编程书作者Herbert Schildt撰写&#xff0c;开篇讲述基础知…

Java实现根据商品ID获取当当商品详情数据,当当商品详情数据接口,当当网API接口封装方法

要通过当当网的API获取商品详情数据&#xff0c;您可以使用当当开放平台提供的接口来实现。以下是一种使用Java编程语言实现的示例&#xff0c;展示如何通过当当开放平台API获取商品详情属性数据接口&#xff1a; 首先&#xff0c;确保您已注册成为当当网开放平台的开发者&…

C位运算做标识位使用

C位运算做标识位使用

Keil模拟器 STM32F103上手

一般嵌入式操作系统因为它的特殊性&#xff0c;往往和硬件平台密切相关连&#xff0c;具体的嵌入式操作系统往往只能在特定的硬件上运行。 可以采用软件方式来模拟一个能够运行RT-Thread操作系统的硬件模块&#xff0c;这就是ARM公司的MDK-ARM仿真模拟环境。 MDK-ARM&#xf…

Spring Boot+Atomikos进行多数据源的分布式事务管理详解和实例

文章目录 0.前言1.参考文档2.基础介绍3.步骤1. 添加依赖到你的pom.xml文件:2. 配置数据源及其对应的JPA实体管理器和事务管理器:3. Spring BootMyBatis集成Atomikos4. 在application.properties文件中配置数据源和JPA属性&#xff1a; 4.使用示例5.底层原理 0.前言 背景&#x…

gif怎么转换成mp4格式视频

gif怎么转换成mp4格式视频&#xff1f;GIF格式是一种广泛应用的公用图像文件格式标准&#xff0c;具有许多优势。它占用的内存较小&#xff0c;可以实现自动循环播放&#xff0c;并且兼容多个平台。然而&#xff0c;GIF格式也存在一些缺点。例如&#xff0c;它无法处理复杂的图…

C语言深入理解指针(非常详细)(一)

目录 内存和地址内存编址的理解 指针变量和地址取地址操作符&#xff08;&&#xff09;指针变量和解引用操作符&#xff08;*&#xff09;指针变量如何拆解指针类型解引用操作符 指针变量的大小 指针变量类型的意义指针的解引用指针-整数 const修饰指针const修饰变量const修…

day01-ES6新特性以及ReactJS入门

课程介绍 ES6新特性ReactJS入门学习 1、ES6 新特性 1.2、let 和 const 命令 var 之前&#xff0c;我们写js定义变量的时候&#xff0c;只有一个关键字&#xff1a; var var 有一个问题&#xff0c;变量作用域的问题&#xff0c;作用域不可控&#xff0c;就是定义的变量有时会…

Linux操作系统--shell编程(正则表达式)

1..正则表达式概述 正则表达式使用单个字符串来描述、匹配一系列符合某个语法规则的字符串。在很多文本编辑器里,正则表达式通常被用来检索、替换那些符合某个模式的文本。在 Linux 中,grep,sed,awk 等文本处理工具都支持通过正则表达式进行模式匹配。 2.常规的匹配操作 3.…

【踩坑日记】STM32 USART 串口与 FreeRTOS 冲突

文章目录 问题描述问题出现的环境问题解决过程第一步第二步第三步第四步第五步第六步第七步第八步 后续验证一些思考类似的问题后记 问题描述 笔者使用 FreeRTOS 创建了两个任务&#xff0c;使两颗 LED 以不同频率闪烁&#xff0c;但是在加入串口 USART 部分代码后&#xff0c…

java八股文面试[多线程]——指令重排序

关于a的操作&#xff0c;由原来的6个指令&#xff0c;变成了4个指令。 1. 指令重排序的介绍 1&#xff09;指令重排序的类型 在执行程序时为了提高性能&#xff0c;编译器和处理器常常会对指令做重排序。 重排序分三种类型&#xff1a;编译器优化的重排序 编译器在不改变单线…