Linux基础-Makefile

目录

一、Make简介

二、Makefile基本结构

示例:

补充(Makefile):

伪目标:

三、创建和使用变量

变量定义的方式:

简单方式:

递归方式:

用?=定义变量

为变量添加值

预定义变量

自动变量

环境变量

四、Make使用

五、例

六、隐含规则

隐含规则1:编译C程序的隐含规则

隐含规则2:链接Object文件的隐含规则

七、VPATH的用法

八、Makefile的嵌套

补充说明


一、Make简介

工程管理器,顾名思义,是指管理较多的文件。

Make工程管理器也就是个“自动编译管理器”,这里的“自动”是指它能够根据文件时间戳自动发现更新过的文件而减少编译的工作量,同时,它通过读入Makefile文件的内容来执行大量的编译工作。

Make将只编译改动的代码文件,而不用完全编译。

编译的四大过程;
         1、预处理:用于将所有的#include头文件以及宏定义替换成其真正的内容,预处理之后得到的仍然是文本文件,但文件体积会大很多。
            如:gcc -E hello.c -o hello.i  
        2、编译:编译不是指程序从源文件到二进制程序的全部过程,而是指将经过预处理之后的程序转换成特定汇编代码的过程。
            如:gcc -S hello.i -o hello.s
         3、汇编:将上一步的汇编代码转换成机器码,这一步产生的文件叫做目标文件,是二进制格式。
            如:gcc -c hello.s -o hello.o
        4、链接:将多个目标文件以及所需要的库文件(.so等)链接成最终的可执行文件。 
            如:gcc hello.o -o hello

二、Makefile基本结构

Makefile是Make读入的唯一配置文件

每条规则由三个部分组成分别是目标(target)依赖(depend) 和命令(command)

命令(command):

当前这条规则的动作,一般情况下这个动作就是一个 shell 命令。

—例如:通过某个命令编译文件、生成库文件、进入目录等。

—动作可以是多个,每个命令前必须有一个Tab缩进并且独占占一行。

依赖(depend):

规则所必需的依赖条件,在规则的命令中可以使用这些依赖。

—例如:生成可执行文件的目标文件(*.o)可以作为依赖使用

—如果规则的命令中不需要任何依赖,那么规则的依赖可以为空

—当前规则中的依赖可以是其他规则中的某个目标,这样就形成了规则之间的嵌套

—依赖可以根据要执行的命令的实际需求,指定很多个

目标(target):

规则中的目标,这个目标和规则中的命令是对应的。

—通过执行规则中的命令,可以生成一个和目标同名的文件规则中可以有多个命令,因此可以通过这多条命令来生成多个目标,所有目标也可以有很多个

—通过执行规则中的命令,可以只执行一个动作,不生成任何文件,这样的目标被称为伪目标

示例:

Makefile格式

target  :   dependency_files

<TAB>  command

例子

sunq:kang.o yul.o     

        gcc kang.o yul.o -o sunq

kang.o : kang.c kang.h     

        gcc –Wall –O -g –c kang.c -o kang.o

yul.o : yul.c   

         gcc - Wall –O -g –c yul.c -o yul.o

注释:

-Wall:表示允许发出gcc所有有用的报警信息.      

-c:只是编译不链接,生成目标文件”.o”    

 -o file:表示把输出文件输出到file里

关于更多的用man工具

Makefile:

源文件:

补充(Makefile):

- 假如我们只负责funtion1部分内容的编写,然后我们就使用命令 make funtion1.o即可,都是可以单独来执行每一个目标的。

- 当我们执行了命令 make funtion1.o 后,我们再去执行 make 我们会发现 makefile 会进行有选择的编译,只执行了funtion2.o、 main.o、test 的目标:

这里也就体现出了概述里面的时间戳和自动的概念(Make工程管理器也就是个“自动编译管理器”,这里的“自动”是指它能够根据文件时间戳自动发现更新过的文件而减少编译的工作量)

伪目标:

当我们根据以上示例生成了test目标文件后,发现在工程目录中出现了很多的中间文件(*.o),

这时我们如果用 rm *.o 命令当然也可以实现删除,但是我们这里可以在例子中的Makefile文件中增加一个新的目标 clean 来实现删除操作:

增加了clean目标后,在终端输入 make clean 即可实现删除操作。

但是这里我们如果在工程目录中新建一个 clean 的文件的话,在使用 make clean命令 就不好使了,如下图所示:

这是就引入了伪目标的概念:工程中的某些文件和 Makefile 中的目标重名了。伪目标就是肯定会被执行的一些命令。在Makefile中如果我们遇到一些特殊的目标,希望它能够永远执行,那么我们就需要在Makefile中添加一个伪目标(不加条件限制,肯定会去执行的目标):

当我们修改完成后,再次执行 make clean 就正常了:

三、创建和使用变量

创建变量的目的: 用来代替一个文本字符串:    

1.系列文件的名字  

2. 传递给编译器的参数

 3. 需要运行的程序  

4. 需要查找源代码的目录  

5. 你需要输出信息的目录    

6. 你想做的其它事情。

变量定义的方式:

递归展开方式VAR=var

简单方式 VAR:=var

变量使用$(VAR)

用”$”则用”$$”来表示

类似于编程语言中的宏

OBJS = kang.o yul.o

CC = gcc

CFLAGS = -Wall -O -g

sunq : $(OBJS)     

        $(CC) $(OBJS) -o sunq

kang.o : kang.c kang.h   

        $(CC) $(CFLAGS) -c kang.c -o kang.o

yul.o : yul.c yul.h     

        $(CC) $(CFLAGS) -c yul.c -o yul.o

简单方式:

例:

简单方式 VAR:=var     

m := mm

x := $(m)

y := $(x) bar

x := later

echo $(x) $(y)    

看看打印什么信息?

用这种方式定义的变量,会在变量的定义点,按照被引用的变量的当前值进行展。

这种定义变量的方式更适合在大的编程项目中使用,因为它更像我们一般的编程语言 。

递归方式:

递归展开方式VAR=var

例子:

foo = $(bar)

bar = $(ugh)

ugh = Huh?

$(foo)的值为?

echo $(foo)来进行查看

优点: 它可以向后引用变量  

缺点: 不能对该变量进行任何扩展,例如

        CFLAGS = $(CFLAGS) -O 会造成死循环

用?=定义变量

dir := /foo/bar

FOO ?= bar

FOO是?

含义是,如果FOO没有被定义过,那么变量FOO的值就是“bar”,如果FOO先前被定义过,那么这条语将什么也不做,其等价于:    

ifeq ($(origin FOO), undefined)      

FOO = bar    

endif

为变量添加值

你可以通过+=为已定义的变量添加新的值

Main=hello.o hello-1.o

Main+=hello-2.o

预定义变量

AR       库文件维护程序的名称,默认值为ar。AS汇编程序的名称,默认值为as。

CC       C编译器的名称,默认值为cc。CPP  C预编译器的名称,默认值为$(CC) –E。

CXX     C++编译器的名称,默认值为g++。

FC        FORTRAN编译器的名称,默认值为f77

RM       文件删除程序的名称,默认值为rm -f

例子:
Hello: main.c main.h

<tab> $(CC) –o hello main.c

clean:

<tab> $(RM) hello

自动变量

$*      不包含扩展名的目标文件名称

$+      所有的依赖文件,以空格分开,并以出现的先后为序,可能 包含重复的依赖文件

$<      第一个依赖文件的名称

$?      所有时间戳比目标文件晚的的依赖文件,并以空格分开

$@     目标文件的完整名称

$^       所有不重复的目标依赖文件,以空格分开

$%      如果目标是归档成员,则该变量表示目标的归档成员名称

刚才的例子:

OBJS = kang.o yul.o

CC = gcc

CFLAGS = -Wall -O -g

sunq : $(OBJS)     

        $(CC) $^ -o $@

kang.o : kang.c kang.h     

        $(CC) $(CFLAGS) -c $< -o $@

yul.o : yul.c yul.h     

        $(CC) $(CFLAGS) -c $< -o $@

环境变量

make在启动时会自动读取系统当前已经定义了的环境变量,并且会创建与之具有相同名称和数值的变量

如果用户在Makefile中定义了相同名称的变量,那么用户自定义变量将会覆盖同名的环境变量

四、Make使用

直接运行make

选项

-C dir读入指定目录下的Makefile

-f  file读入当前目录下的file文件作为Makefile

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

-I dir指定被包含的Makefile所在目录

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

-p 显示make变量数据库和隐含规则

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

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

五、例

工程目录

源文件

Makefile:

六、隐含规则

隐含规则1:编译C程序的隐含规则

“<n>.o”的目标的依赖目标会自动推导为“<n>.c”,并且其生成命令是“$(CC) –c $(CPPFLAGS) $(CFLAGS)”

注意:

默认只有 -c  ,如果需要其他特殊选项,就需要自己为CFLAGS赋值

例如 五、例 里面需要指定目录的特殊选择: CFLAGS=-I include ,就需要自己为CFLAGS赋值,不然会找不到 include/myinlcude.h文件。

等同于:

还可将Makefile简化到如下(.c到.o的过程也可省略):

隐含规则2:链接Object文件的隐含规则

“<n>” 目标依赖于“<n>.o”,通过运行C的编译器来运行链接程序生成(一般是“ld”),其生成命令是:“$(CC) $(LDFLAGS) <n>.o $(LOADLIBES) $(LDLIBS)”。这个规则对于只有一个源文件的工程有效,同时也对多个Object文件(由不同的源文件生成)的也有效。例如如下规则:

x : x.o y.o z.o

并且“x.c”、“y.c”和“z.c”都存在时,隐含规则将执行如下命令:

cc -c x.c -o x.o

cc -c y.c -o y.o

cc -c z.c -o z.o

cc x.o y.o z.o -o x

如果没有一个源文件(如上例中的x.c)和你的目标名字(如上例中的x)相关联,那么,你最好写出自己的生成规则,不然,隐含规则会报错的

等同于:

七、VPATH的用法

VPATH : 虚路径

在一些大的工程中,有大量的源文件,我们通常的做法是把这许多的源文件分类,并存放在不同的目录中。所以,当make需要去找寻文件的依赖关系时,你可以在文件前加上路径,但最好的方法是把一个路径告诉make,让make在自动去找。 Makefile文件中的特殊变量“VPATH”就是完成这个功能的,如果没有指明这个变量,make只会在当前的目录中去找寻依赖文件和目标文件。如果定义了这个变量,那么,make就会在当当前目录找不到的情况下,到所指定的目录中去找寻文件了。

VPATH = src:../headers 上面的的定义指定两个目录,“src”和“../headers”,make会按照这个顺序进行搜索。目录由“冒号”分隔。(当然,当前目录永远是最高优先搜索的地方)

工程目录结构:

Makefile:

等同于:

八、Makefile的嵌套

1、工程目录结构;

2、include目录(只有一个myinclude.h文件):

3、main目录(main.c、Makefile):

4、src1目录(funtion1.c 、Makefile)

5、src2目录(funtion2.c 、Makefile):

6、obj目录(Makefile):

7、工程最顶层目录中的Makefile:

8、在最顶层目录中执行make命令:

补充说明

1、Makefile 中 echo 与 @ 符

在 Makefile 中有时会看到 echo 命令前添加了 “@” 符号:

@echo "hello"

它与不加 @ 符号的区别就是它不会把 “hello” 输出到终端(显示器)。

echo 明明就是用来输出内容的,那么 @ 是不是有点多余?
这是因为有另一种需求存在,就是将 “hello” 输出到文件中(比如需要写一个脚本文件)。

@echo "hello"  >> file

此时会看到文件 file 中有追加了一行 “hello”。
如果不需要将 "hello " 显示在终端上的话就可以用 @ 把 echo 的输出内容屏蔽掉。

2、makefile中 rm、@rm 和 -rm的区别

  • @告诉make在运行时不要回显要输出的配方。
  • -告诉make忽略配方的返回值

3、Makefile中的all

这里需要生成两个可执行文件main1main2(两个目标)。由于makefile只能有一个目标,所以可以构造一个没有规则的终极目标all,并以这两个可执行文件作为依赖。如下:

all:main1 main2

main1: main1.c

    @gcc main1.c -o main1

main2: main2.o

    @gcc main2.o -o main2

main2.o: main2.c

    @gcc -c main2.c 

4、make -C

-C dir读入指定目录下的Makefile(更多选项请参考四、Make使用)。

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

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

相关文章

C++函数模板详解(结合代码)

目录 1. 模板概念 2. 函数模板语法 3. 函数模板注意事项 4. 函数模板案例 5. 普通函数与函数模板的区别 6. 普通函数与函数模板的调用规则 7. 模板的局限性 1. 模板概念 在C中&#xff0c;模板是一种通用的程序设计工具&#xff0c;它允许我们处理多种数据类型而不是固…

【STM32嵌入式系统设计与开发】——9Timer(定时器中断实验)

这里写目录标题 一、任务描述二、任务实施1、ActiveBeep工程文件夹创建2、函数编辑&#xff08;1&#xff09;主函数编辑&#xff08;2&#xff09;USART1初始化函数(usart1_init())&#xff08;3&#xff09;USART数据发送函数&#xff08; USART1_Send_Data&#xff08;&…

【Java基础知识总结 | 第六篇】Java反射知识总结

文章目录 6.Java反射知识总结6.1概述6.1.1什么是反射&#xff1f;6.1.2为什么使用反射&#xff1f; 6.2反射的原理6.3反射的使用6.3.1获取类对象&#xff08;1&#xff09;通过具体类的类名获取&#xff08;2&#xff09;通过对象实例获取&#xff08;3&#xff09;通过class.f…

正式发布:VitePress 1.0 现代化静态站点生成器!

大家好&#xff0c;我是奇兵&#xff0c;今天介绍一下现代化静态站点生成器!&#xff0c;希望能帮到大家。 3 月 21 日&#xff0c; 由 Vue 团队出品的现代化静态站点生成器 VitePress 正式发布 1.0 版本&#xff01;它专为构建快速、以内容为中心的网站而生&#xff0c;能够轻…

Django之Celery篇(一)

一、介绍 Celery是由Python开发、简单、灵活、可靠的分布式任务队列,是一个处理异步任务的框架,其本质是生产者消费者模型,生产者发送任务到消息队列,消费者负责处理任务。 Celery侧重于实时操作,但对调度支持也很好,其每天可以处理数以百万计的任务。特点: 简单:熟悉…

获取Book里所有sheet的名字,且带上超链接

应用背景&#xff1a; 当一个excel有很多sheet的时候&#xff0c;来回切换sheet会比较复杂&#xff0c;所以我希望excel的第一页有目录&#xff0c;可以随着sheet的增加&#xff0c;减少&#xff0c;改名而随时可以去更新&#xff0c;还希望有超链接可以直接跳到该sheet。 可以…

VPCFormer:一个基于transformer的多视角指静脉识别模型和一个新基准

文章目录 VPCFormer:一个基于transformer的多视角指静脉识别模型和一个新基准总结摘要介绍相关工作单视角指静脉识别多视角指静脉识别Transformer 数据库基本信息 方法总体结构静脉掩膜生成VPC编码器视角内相关性的提取视角间相关关系提取输出融合IFFN近邻感知模块(NPM) patch嵌…

ssm006基于java的少儿编程网上报名系统+vue

少儿编程网上报名系统 摘 要 在国家重视教育影响下&#xff0c;教育部门的密确配合下&#xff0c;对教育进行改革、多样性、质量等等的要求&#xff0c;使教育系统的管理和运营比过去十年前更加理性化。依照这一现实为基础&#xff0c;设计一个快捷而又方便的网上少儿编程网上…

pdf压缩文件怎么压缩最小?一键压缩PDF

pdf文件压缩是为了减小文件大小&#xff0c;以便更轻松地共享、传输和存储文件&#xff0c;通过压缩pdf文件&#xff0c;可以减少文件占用的存储空间&#xff0c;加快文件的上传和下载速度&#xff0c;并节省带宽和存储成本;在本教程中&#xff0c;我们将介绍一些有效的方法来最…

如何自定义一个starter?

在Spring Boot中&#xff0c;创建一个自定义starter可以简化特定功能或组件的配置过程&#xff0c;让其他项目能够轻松地重用这些功能。 一、问题解析 这里我们以自定义一个xxl-job的starter为例&#xff0c;介绍下如何简化配置。 添加依赖 添加Spring Boot的依赖&#xff1a…

关于网格数据导出指定格式的测试(以Gmsh导出nas格式为例)

本文主要讲述Gmsh如何导出nas格式的网格数据&#xff0c;众所周知&#xff0c;Gmsh可以导出多种网格数据格式&#xff0c;比如大家熟悉的msh、stl、inp、cgns&#xff08;似乎不完善&#xff09;等等&#xff0c;但是gmsh不支持nas格式的导出&#xff0c;只支持nas格式的导入&a…

Bug定位与分析,软件测试员你中招了吗?

之所以写这一篇文章&#xff0c;是突然想起来曾经在测试过程中被开发嘲讽过&#xff0c;事情是这样的&#xff0c;当时发现了一个疑似前端的Bug就草草提交到了禅道&#xff0c;结果刚来的女前端看到了就有点生气地问我为啥不查清到底是前后端问题就直接派给她前端了&#xff0c…

睿考网:不是会计专业能考中级会计师吗?

不是会计专业也是可以考中级会计师的&#xff0c;中级会计师报名条件中并没有对专业做明确的限制&#xff0c;不同的学历对工作年限的要求不一样&#xff0c;如果考生满足报考条件就可以参加。 1.具备大学专科学历&#xff0c;从事会计工作满5年。 2.具备大学本科学历或学士学…

大数据Doris(七十):全球电商狂欢节万亿级实时大屏解决方案

文章目录 全球电商狂欢节万亿级实时大屏解决方案 一、背景介绍 <

学习vue3第十一节(依赖注入:provide/inject)

本机介绍&#xff1a;provide/inject 注意&#xff1a;大家在看此小节时候&#xff0c;默认大家已经了解一些组件的使用方法 1、依赖注入的用途&#xff1a; 当嵌套层级多的时候&#xff0c;某个子组件需要较远层级的父组件数据时候&#xff0c;如果我们依然使用props 传递数…

算法学习 | day25/60 递增子序列/全排列/全排列II

一、题目打卡 1.1 递增子序列 题目链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; class Solution { public:vector<int> path;vector<vector<int>> res;int tmp INT_MIN;void recur(vector<int>& nums, int startInd){if(p…

Java项目:74 ssm基于Java的超市管理系统+jsp

作者主页&#xff1a;源码空间codegym 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 项目介绍 功能包括:商品分类&#xff0c;供货商管理&#xff0c;库存管理&#xff0c;销售统计&#xff0c;用户及角色管理&#xff0c;等等功能。项目采…

数据库性能压测之TPC-C基准测试

原文链接&#xff1a;https://blog.csdn.net/TIME_1981/article/details/126114797 本文作为学习笔记记录。 TPC Transaction Processing Performance Council (TPC) 事务处理性能委员会&#xff0c;是一家非盈利IT组织&#xff0c;他们的目的是定义数据库基准并且向产业界推…

力扣438. 找到字符串中所有字母异位词

Problem: 438. 找到字符串中所有字母异位词 文章目录 题目描述思路及解法复杂度Code 题目描述 思路及解法 1.编写辅助函数bool same(vector& need, vector& matched)&#xff1a; 1.1 以need为标准&#xff0c;循环对比need和matched的每一个位置的元素值是否相等 2.获…