8.Linux_Makefile

news2024/12/27 22:02:22

Makefile

1、基本知识

语法:

目标:依赖  //依赖可以是0个或多个,依赖之间用空格分隔
    命令   //命令前面必须有tab键
  • 目标:最终要生成的文件
  • 依赖:生成目标所需要的文件
  • 命令:怎么样通过依赖来生成目标的

make访问makefile的方式:

  • make后不加目标:访问第一个目标
  • make后加目标:访问指定目标
  • 当有makefile和Makefile两个文件时,优先访问makefile

不让makefile打印执行的命令:

在命令前面加上" @ "即可

2、使用makefile编译文件

题目

现在有一个文件test.c,它需要用到add.c、sub.c文件中的内容。要求使用makefile进行编译。

add.h、sub.h的文件路径在xxx/linux_test/inc中

add.c、sub.c的文件路径在xxx/linux_test/src中

test.c、add.c、sub.c的内容如下:

编写步骤

1、构思,分析如何一步步生成所需目标文件

想要使得makefile实现只去编译修改过的文件,而不编译未改动的文件,就必须进行分步编译。第一步先将.c文件转为.o文件,第二步将.o文件转为可执行文件。

2、 创建makefile文件

在.c文件目录下创建makefile文件

3、编写makefile文件

makefile只识别第一个目标,之后去查找第一个目标后面的依赖。如果依赖存在,那么执行完毕。如果依赖不存在,就会去寻找下面是否有生成这个依赖文件的目标,从而执行第二个、第三个以及之后的目标。

备注:这里makefile里面写了rm *.o,这个应该去掉不写。这样就能实现只编译修改的文件了。

4、执行makefile,运行可执行文件

3、伪目标.PHONY

伪目标可以解决目标与文件同名问题。

问题:假设我们的目标为clean,并且当前目录下也有一个clean文件,这时make clean就会有问题

解决方法:使用伪目标 ".PHONY:目标名"

4、变量

4.1 自定义变量

取出变量值:

$(变量名)、${变量名}这两种方式的含义是一样的

如果想输出$符号,应该输入$$

变量赋值:

变量赋值有4个符号:=、+=、?=、:=、

1、" = ":是最基本的赋值方式,与在文中位置无关,系统自动推导将最终的赋值作为该变量的值。

2、" += ":追加赋值,旧值保持不变,将新值黏贴到旧值后面

3、" ?= ":当某变量前面已经定义赋值过,则不执行本次定义赋值,否则执行本次赋值

4、" := ":是覆盖式赋值,假如某变量在前面已经定义赋值过,则将本次赋值作为最新的变量值

4.2 自动变量 

自动变量的作用是更方便的操作目标和依赖,具体的变量如下:

操作目标:

  • $*:不包含扩展名的目标文件名称。例如目标为a.out,则$*=a
  • $@:目标文件的完整名称。例如目标为a.out,则$*=a.out
  • $%:如果目标是归档成员,则该变量表示目标的归档成员名称

操作依赖:

  • $+:所有依赖文件,以空格分开,可能包含重复的依赖文件
  • $^:所有依赖文件,以空格分开,依赖文件不重复
  • $<:”第一个依赖文件的名称
  • $?:所有时间戳比目标文件晚的的依赖文件,并以空格分开

操作函数传入的参数:

  • $(n):获取第n个变量。$(0)为函数名,$(1)为第一个参数

4.3 隐含变量

隐含变量的作用是更方便的操作常用的命令。比如gcc命令的隐含变量是CC

隐含变量也是一个变量,只是约定一个名字,在代码中容易理解含义,其使用方法与普通变量一样

下面是隐含变量的简单使用,以gcc为例:

命令的隐含变量:

  • CC:C编译器的名称,默认值为cc。
  • CPP:C预编译器的名称,默认值为$(CC) –E。
  • RM:文件删除程序的名称,默认值为rm -f
  • CXX:C++编译器的名称,默认值为g++。
  • AR:库文件维护程序的名称,默认值为ar。
  • AS:汇编程序的名称,默认值为as。
  • FC:FORTRAN编译器的名称,默认值为f77

命令选项的隐含变量:

隐含变量格式:xxxFLAGS

  • CFLAGS:C编译器的选项,无默认值。
  • CPPFLAGS:C预编译的选项,无默认值。
  • CXXFLAGS:C++编译器的选项,无默认值。
  • ARFLAGS:库文件维护程序的选项,无默认值。
  • ASFLAGS:汇编程序的选项,无默认值。
  • FFLAGS:FORTRAN编译器的选项,无默认值
  • LDFLAGS:链接器的选项,无默认值

5、条件判断

判断是否相等:

判断符号:ifeq、ifneq、else ifeq 、else ifeq 、else(最终要以endif结尾)

格式:<判断符号> ($(变量名),判断值) 变量名常用ARCH命名

判断是否定义:

判断符号:ifdef、ifndef、else ifdef、else ifndef、else

格式:<判断符号> <变量名>

对于makefile,变量定义了代表有赋值。没有赋值或者没有写过这个变量都认为是没有定义。

6、函数

使用函数:$(函数名 参数)  或者  ${函数名  参数}

6.1 常用函数

6.1.1 wildcard

wildcard可以列出当前文件下符合条件的文件名。

格式:

$(wildcard <文件类型>)   该函数的参数可以使用通配符进行操作。

例如:列出当前目录下全部.c文件$(wildcard *.c) 。注意这里使用的是*去匹配全部内容

6.1.2 patsubst

patsubst可以将在指定的范围中,将一种后缀替换成另一种后缀

格式:

$(patsubst <替换后的后缀>,<要替换的后缀>,<指定范围>)

例如:把全部的.c文件换成.o文件进行输出,注意这里使用的是%去匹配全部内容

6.1.3 shell 

shell可以实现在makefile中调用一个shell命令

格式:

$(shell <命令>)

6.1.4 notdir

notdir可以实现将传入的参数去除路径,只留下文件名

格式:

$(notdir <参数>)

6.2 调用自己实现的函数

定义格式:

define 函数名
    函数体
endef

调用格式:

$(call <函数名>,<参数>)   多个参数之间用" , "隔开

获取传入的参数:

$(n):获取第n个变量。$(0)为函数名,$(1)为第一个参数 

7、make的使用

7.1 make指定目标
  • make:执行第一个目标
  • make <目标>:执行指定的目标

7.2 make时定义变量

格式:make <变量名>=<值>

7.3 常用的选项
  • -f:当makefile文件不叫makefile时,可以指定作为makefile的文件名

  • -i:忽略所有的命令执行错误

             这代表有错误并不会停止执行,这适用在编译大型工程时,整体排查错误。

             不加-i时,遇到错误就停止编译。加上-i后,遇到错误跳过,继续编译其他内容。

  • -n:只打印要执行的命令,但不执行这些命令

  • -w:如果make在执行过程中改变目录,打印当前目录名

-C:dir读入指定目录下的Makefile。会默认加上-w的选项

-s:在执行命令时不显示命令

8、makefile编写技巧

8.1 更方便的编译:%

在编写用.c文件生成.o文件的makefile时,需要好几个目标。并且目标和依赖去掉扩展名后,名称是一样的。例如:要生成test.o,那就要用到test.c;要生成sub.c,那就要用到sub.o。对于值中情况可以使用%来自动匹配。示例如下:

8.2 使用函数简化链接

wildcard可以直接获取当前目录的.c文件名,使用patsubst可以获取.o文件名。使用这两个函数,可以减少链接步骤时的代码,使得更加通用。

例如:原来链接时写的代码是"test:$SRC",这里的SRC是自己赋值的test.o,add.o,sub.o。如果又多了几个需要的.o文件,还需要自己去添加。而使用函数,不论多少个.o文件,都是一样的代码,这就使得代码更加通用。代码如下:

源码如下:

CC = gcc
CFLAGS = -c -g -Wall -I /home/linux/inc/  
SRC = $(wildcard *.c)
OBJ = $(patsubst %.c,%.o,$(SRC))
fun:$(OBJ)
	$(CC) $^ -o $@
%.o:%.c
	$(CC) $(CFLAGS) $^ -o $@

8.3 分文件处理

8.3.1 需求提出

现在有一个工程,所有的.c文件放到了src目录中,.h文件放到了inc目录中,生成的.o文件放到obj目录中。要求在当前目录下编写makefile,使得能够对整个工程进行编译。在下面示例中,工程就是linux_test,所说的当前目录就是/home/linux/linux_test

需求中的目录结构如下:

8.3.2 准备工作

1、在当前目录下,创建src、inc、obj这三个目录,创建makefile文件

2、进入src编写相应的.c文件,进入inc编写相应的.h文件

8.3.3 编写makefile

在本次需求中,有两个makefile。一个makefile在工程目录,即:/home/linux/linux_test,这一个makefile用于整个工程的编译链接。另一个makefile在src文件下,即:/home/linux/linux_test/src,这一个makefile用于将整个src下的.c编译为.o并存放在/home/linux/linux_test/obj中。

工程目录makefile编写思路:

编译用到的指令为:"gcc -c <.c文件> -o <.o文件>" 。从指令中可以看到,我们需要.c文件的路径,也需要.o文件的输出路径。

1、获取src、inc、obj的绝对路径

比如我们要获得.c文件,首先需要找到它存放的路径,之后去用通配符匹配它。在本题中.c存放的路径应该是/home/linux/linux_test/src/xxx.c。可以发现这个目录分两个部分:

  • "/home/linux/linux_test"是工程目录,也就是当前编写的makefile的目录,这个目录可以用$(shell pwd)函数来获取
  • "/src"是.c存放的路径,这可以用$(shell pwd)/src来表示。

同理,我们可以获取inc、obj的绝对路径。这个绝对路径我们存入变量SRCDIR、INCDIR、OBJDIR中,方便我们后续调用。

2、获取.c和.o文件路径

.c的文件路径可以用$(wildcard $(SRCDIR)*.c)函数来获取。

这个函数的含义是从所给的$(SRCDIR)路径下找到全部的.c文件并返回$(SRCDIR)/xxx.c这种值。比如找到的是xxx/src/main.c。那么它的返回值就是xxx/src/main.c。

.o文件可以用$(pasubst %.c,$(OBJDIR)/%.o,$(notdir $(SRC)))来获取。

这个函数的含义是,从所给的$(notdir $(SRC))范围中,找到以.c为后缀的内容。在文件名不变的情况下,把.c替换成.o并存入$(OBJDIR)目录下。

在这个函数中,SRC存放的是全部.c的文件路径,也就是$(wildcard $(SRCDIR)*.c)的返回值。

3、编译器相关参数

这个很简单,与之前一样即可。具体如下:

CC = gcc
CFLAGS = -c -g -Wall -I $(INCDIR) 

4、设置全局变量

这个用环境变量export后跟变量即可。需要哪个全局变量在后面加一下就行。

5、编译相关内容

在该makefile中,整个的思路为:调用src中的makefile进行.o文件的生成,之后在当前makefile下用.o文件生成可执行文件。

  • 5.1 调用makefile的目标编写

调用src中的makefile的命令是:make -C <makefile路径>。对于这种需求,常常把makefile路径当作目标。在这里,makefile的路径就是SRCDIR的值:/home/linux/linux_test/src,可以看到这是一个目录。

在makefile中目录不能充当目标,想要充当目标首先后面需要跟一个依赖,然后需要其他的目标来调用它。下图为目标编写的代码,调用它的目标没有截图进来。

  • 5.2 使用.o文件生成可执行文件的目标编写

因为这个.o文件的生成在其他的makefile,所以我们可以假设调用的makefile是对的,可以生成.o文件。那么在经过5.1的过程中,我们假设这个.o文件已经生成了,在这个基础上编写.o到可执行文件的目标。

.o到可执行文件的指令是:gcc <.o文件路径> -o <可执行文件路径>。.o文件路径就是OBJ保存的路径,即:$(pasubst %.c,$(OBJDIR)/%.o,$(notdir $(SRC)))的返回值。所以目标编写如下:

  • 5.3 总调用目标all

经过5.1、5.2该makefile的功能型的目标全部编写完成,但需要一个"all:依赖"这种总调用目标来调用5.1、5.2中的目标,整体目标的编写如下:

src目录下的makefile编写思路:

src目录下的makefile的功能是将全部的src下的.c文件。如果是编译当前文件下,那么代码如下:

%.o:%.c
	$(CC) $(CFLAGS) $^ -o $@

但是%.o这种形式不能充当目标,如果想要当作目标,必须有其他的目标去调用它。所以需要一个"all:依赖"这种形式来调用它。

因为最终输出的.o位置不是当前位置而是xxx/obj这个位置,所以目标应该修改为$(OBJDIR)/%.o。

该makefile的代码如下:

8.3.4 makefile源码

调试中出现的bug:注意OBJDIR后面xxx/obj这后面不能有空格

 1、工程目录下的makefile

#1. src、inc、obj的绝对路径
SRCDIR = $(shell pwd)/src#这后面不能有空格
INCDIR = $(shell pwd)/inc#这后面不能有空格
OBJDIR = $(shell pwd)/obj#这后面不能有空格
#2. .c文件路径、.o文件路径
SRC = $(wildcard $(SRCDIR)/*.c)
OBJ = $(patsubst %.c,$(OBJDIR)/%.o,$(notdir $(SRC)))
#3. 编译相关设置
CC = gcc
CFLAGS = -c -g -Wall -I $(INCDIR)

#4.设置全局变量
export OBJ OBJDIR CC CFLAGS 

#5. 开始编译 思路为:调用src中的makefile生成.o文件,再用.o生成a.out
all:debug $(SRCDIR) echo a.out
debug:
	@echo "src dir = $(SRCDIR)"
	@echo "inc dir = $(INCDIR)"
	@echo "obj dir = $(OBJDIR)"
	@echo "$(SRCDIR)/*.c"
	@echo "src = $(SRC)"
	@echo "obj = $(OBJ)"
#5.1 调用src中的makefile
$(SRCDIR):echo
	make -C $@
echo:
	@echo "start make"
#5.2 用.o生成a.out
a.out:$(OBJ)
	$(CC) $^ -o $@

2、src下的makefile

all:$(OBJ)
$(OBJDIR)/%.o:%.c
	$(CC) $(CFLAGS) $^ -o $@

 

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

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

相关文章

pve首页查看功耗拓展脚本

作者&#xff1a;TP唉 https://www.bilibili.com/read/cv26924314/?jump_opus1 出处&#xff1a;bilibili 如图所示想要这全方位信息很难吗&#xff1f;不&#xff0c;很简单只需要在shell里粘贴两串代码轻松搞定&#xff01; 命令1&#xff1a;(curl -Lf -o /tmp/temp.sh ht…

CleanClip: macOS 上最干净的剪贴板管理工具

在日常工作中,我们经常需要复制粘贴各种内容。但是 macOS 自带的剪贴板功能非常有限,只能保存最后一次复制的内容。这就是为什么我们需要一个强大的剪贴板管理工具 - CleanClip。 CleanClip 是什么? CleanClip 是一款专为 Mac 用户设计的剪贴板管理工具&#xff0c;它可能是 …

day13JS-MoseEvent事件

1. MouseEvent的类别 mousedown &#xff1a;按下键mouseup &#xff1a;释放键click &#xff1a;左键单击dblclick &#xff1a;左键双击contextmenu &#xff1a;右键菜单mousemove &#xff1a;鼠标移动mouseover : 鼠标经过 。 可以做事件委托&#xff0c;子元素可以冒泡…

FUNCTION_ALV 下拉框的实现

下拉框可以用drdn_field或者使用DRDN_HNDL&#xff0c;这个文章主要是下拉框的基本使用&#xff0c;核心就是在fieldcat内表里面设置好下拉框的字段或者组的编号 文章目录 drdn_field使用DRDN_HNDL复制状态完整代码核心代码运行结果 drdn_field 使用DRDN_HNDL 复制状态 完整代码…

AcWing852.spfa判断负环

cnt数组表示&#xff1a;cnt【j】表示边j #include<iostream> #include<cstring> #include<algorithm> #include<queue> #define N 2010 #define M 10010 using namespace std; int n,m; int h[N],w[M],e[M],ne[M],idx; int dis[N],cnt[N]; bool st[N…

ps笔刷设置使用介绍

形状动态 建议开启&#xff0c;作用是笔刷会有粗细变换 传递 不透明度抖动 . 选择钢笔压力&#xff0c;作用就是压感&#xff0c;压力值&#xff0c;有粗细深浅轻重变化 调到这画的时候就不会特别浅 流量抖动 选择钢笔压力&#xff0c;开了就有虚边 方便画过渡。 一般画…

JavaWeb实战教程:如何一步步构建房产信息管理系统?MySQL助力数据管理

✍✍计算机毕业编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java、…

Unable to delete file: .....(路径) signing-config.json无法删除

运行了一个去年很久之前的项目,在打包的时候弹出这个错误,提示要删除这个json文件,尝试了很多次无法删除,最后想到可能是文件权限的问题 Execution failed for task :app:clean. > Unable to delete file: D:\xxxxxx\xxxxxx\app\build\intermediates\signing_config\debug\…

基于Python、Django的企业门户网站设计

一、框架设计 1.1 创建项目和应用 企业门户网站的结构如下图所示。 因此,在文件夹下创建hengDaProject项目。在该项目下添加homeApp、aboutApp、newsApp、productsApp、serviceApp、scienceApp、contactApp应用。该操作参见:https://blog.csdn.net/qq_42148307/article/det…

【归纳总结】常见排序算法及其实现:直接插入排序、希尔排序、选择排序、堆排序、冒泡排序、快排、归并排序

思维导图&#xff1a; 目录 思维导图&#xff1a; 一、插入排序 1.直接插入排序&#xff1a; a:基本思想&#xff1a; b:基本步骤&#xff1a; c:复杂度分析 d:Java代码实现&#xff1a; 2.希尔排序&#xff08;缩小增量排序&#xff09; a:基本思想&#xff1a; c…

python_每天定时向数据库插入数据

每天的零点十分&#xff0c;定时向mysql数据库插入&#xff0c;昨天新增的文件和昨天下载文件的记录。第一次运行的时候&#xff0c;会全量同步昨天之前的数据。 import os import threading from datetime import datetime, timedelta import time import schedule from pymy…

仓颉编程语言亮相全国大学生计算机系统能力大赛

2024年8月18日-22日&#xff0c;由全国高等学校计算机教育研究会、系统能力培养研究专家组、系统能力培养研究项目发起高校主办&#xff0c;杭州电子科技大学承办的2024全国大学生计算机系统能力大赛编译系统设计赛&#xff08;华为毕昇杯&#xff09;及操作系统设计赛在杭电下…

企业防泄密首选!哪款公司防泄密软件更强?看这里,一文解惑!

早在2011年&#xff0c;前苹果员工Paul Devine泄露苹果公司的机密信息&#xff0c;涉及新产品的预测、计划蓝图、价格和产品特征&#xff0c;还为苹果公司的合作伙伴、供应商和代工厂商提供的关于苹果公司的数据&#xff0c;这使得这些供应商和代工厂商拥有了与苹果谈判的筹码&…

Wireless Communications - 模拟调制

AM/DSB/VSB/SSB的调制与解调 AM DSB SSB 滤波法 相移法 VSB 相干解调 线性调制的抗噪声分析 DSB SSB FM/PM 的调制与解调 NBFM WBFM 调频信号的产生和解调 模拟调制对比

SpringBootFFmpeg实现M3U8切片转码播放(本地)

文章目录 参考概述代码pom.xmlffmpegFFmpegUtilsMediaInfoTranscodeConfig application.ymlApplicationUploadControllerindex.html 测试 参考 springboot-ffmpeg-demo gitee代码 SpringBoot FFmpeg实现一个简单的M3U8切片转码系统 FFmpeg音视频核心技术精讲 - 百度网盘 概…

【STM32】红外遥控

红外遥控&#xff0c;掌握了就能装逼了&#xff0c;哈哈哈哈哈哈。 大部分图片来源&#xff1a;正点原子HAL库课程 专栏目录&#xff1a;记录自己的嵌入式学习之路-CSDN博客 1 器件特性 这里载波发射周期的发射与不发射时间实际上是因为载波是38kHz、占空为三分之一的方波&a…

符号译码_网络同步赛

哎……又是 If平推字符 #include <bits/stdc++.h> using namespace std; signed main(){char x;while(cin>>x){if(x==1){cin>>x;if(x==1)cout<<">";else if(x == 0){cin>>x;if(x==1)cout<<"]";else if(x==0)cout&…

Three.js湖边小屋,包含gltf渲染、天空和水纹、光照阴影、运动的点光源、相机位置和文字切屏、粒子效果等

前期准备 使用vue3vitethree.jsgsap 开发 npm install three gsap 代码 <script setup> // 导入three.js import * as THREE from three; // 导入轨道控制器 import { OrbitControls } from three/examples/jsm/controls/OrbitControls.js; // 加载模型 import { GLT…

SQLserver中的游标的分类和游标的生命周期

SQLserver中的游标的分类 在 SQL Server 中&#xff0c;游标&#xff08;Cursor&#xff09;是一种数据库对象&#xff0c;用于逐行处理结果集中的数据。游标可以用于复杂的数据处理任务&#xff0c;尤其是那些不能通过简单的 SELECT 语句和 JOIN 操作完成的任务。SQL Server …

网络通信---三次握手

文章目录 概述第一次握手第二次握手第三次握手整体看下 小结 概述 “三次握手”&#xff08;Three-way Handshake&#xff09;是TCP/IP协议中建立一个可靠的连接时使用的一种机制。这个过程确保了两个网络实体&#xff08;通常是两台计算机&#xff09;在开始数据传输之前能够…