企业网站 批量备案,wordpress 微博】,做自己的游戏网站,汕头响应式网站教程前言
makefile是一种自动化构建工具#xff0c;广泛用于管理和编译项目#xff0c;特别是在C和C等语言中。它通过定义规则来控制源代码的编译、链接和清理等过程。以下是一个基本的Makefile结构和示例#xff0c;帮助你理解如何管理项目#xff1a;
首先#xff1a;创建…前言
makefile是一种自动化构建工具广泛用于管理和编译项目特别是在C和C等语言中。它通过定义规则来控制源代码的编译、链接和清理等过程。以下是一个基本的Makefile结构和示例帮助你理解如何管理项目
首先创建的文件名通常只能是makefile或者Makefile否则使用make时将无法直接识别。虽然可以使用-f选项指定makefile文件(这种一般也会写成lib.mk格式的文件)
基本结构
一个简单的Makefile通常包括以下几个部分
变量定义可以定义编译器、编译选项和源文件等。
规则指定如何构建目标。规则的语法格式如下
目标依赖条件(前面是一个严格的tab)命令#eg.
main:main.cgcc -c main.c -o main
伪文件如clean用于执行特殊任务例如清理中间文件。
脚本变量
makefile与脚本之间有点关系例如 makefile定义了项目中各个文件之间的依赖关系和构建规则用于自动化编译和构建过程。而脚本(如bash脚本)则是一种通过命令行解释器执行的程序通常用于执行一系列命令以完成特定的任务比如自动化系统管理、数据处理等。 二者在功能上都可以用于自动化任务但Makefile更加专注于文件的生成和管理特别是与代码编译相关的任务。而脚本可以用于更广泛的任务包括文件处理、网络请求、系统监控等超出构建的范围。 Makefile通常在Linux系统中使用make工具来执行他会根据规则和依赖关系自动选择需要执行的命令。脚本则可以在任何支持相应解释器如bash、python、perl等的环境中运行。 但不可避免地这两者可能都需要用到同一个知识变量
这个词我们并不陌生只是变量在Linux中充当着什么样的角色呢通常我们在学习shell编程时可能会接触这个概念在这里我们先简单的预习一下。请移步下面正文 Shell变量
定义变量
在Shell编程中变量是用于存储数据值的名称。定义变量时变量名不用加美元符号$PHP语言需要加就不提了如
your_namestark 注意变量名和等号之间不能有空格这可能和你熟悉的所有编程语言都不太一样。同时变量命名需要遵循以下规则 1.只含字母、数字、下划线字母大小写敏感不能以数字开头这条和C/C一样注意一下即可。 2.避免使用关键字C/C也是需要避免关键字的Shell也一样需要避免的关键字例如ifthenelsefiforwhile 3.使用大写字母表示常量Shell的常量就类似于宏了一般使用全大写字母命名如PI3.14等 4.避免特殊符号像$、、*等特殊符号不要用在命名当中因为这些东西在Shell语法中具有特定的功能与含义。包括空格也不要使用因为空格通常用于分隔参数和命令。 Tips这些命名规则大多与C/C一致唯一需要注意的是等号左右无需空格而且可以无需指定数据类型。 #有效的命名方式
var1123
var2stark#无效的命名方式
var_$33 #特殊字符$
?var111 #特殊字符?
a*b356 #特殊字符*
c and d156 #避免空格使用变量
使用一个定义过的变量只要在变量名前加上美元符号$(读作dollar--刀乐儿也就是所谓的美刀)即可如
var1starkecho $var1
echo ${var1} 变量名外面的花括号是可选的加不加都行加花括号是为了帮助解释器识别变量的边界因为有时候我们在使用时极有可能不能明确边界举例说明
skillwrite_code
echo I am good at ${skill}and others
假如我们没有花括号指定边界那么就写成了
echo I am good at $skilland others解释器尝试寻找skilland这个变量但是这并不是我们期望的。所以良好的编程习惯是给所有的变量加上边界(花括号)。
已经定义的变量可以被重新赋值这一点也很好理解只不过此时我们使用变量无需加$因为严格来说重新赋值就是重新定义加上$符后我们会将变量的值来替换掉该内容就无法进行赋值了。
这里还有一个概念叫做只读变量。使用readonly命令可以将变量定义成只读变量只读变量的值不能被修改相当于C/C的const下面给出语法
c_var_mynamei am stark!
readonly c_var_myname
在这里我们使用变量来为其添加修饰时也不需要使用$符目前看来只有在打算使用变量的值时才会使用$符。
删除变量
不同于一般的编程语言Shell脚本语言中具有删除变量的概念语法是
unset var_name
变量被删除后不可再次使用unset不能删除只读变量。你可以理解为只读变量只有被读的权限不可改变其值亦不可改变其存在状态。
注释与引号
注释Shell 变量 | 菜鸟教程我一般只使用 #单行注释
#单行注释#使用Here文档
:EOF注释内容...
注释内容...语法--冒号EOF多行注释内容EOF
EOFEOF可以替换其他符号
: COMMENT
注释内容
COMMENT:
注释内容:!
注释内容
!#直接使用冒号
: 多行注释1
语法为--冒号空格单引号多行注释内容单引号 引号
变量定义时可以加引号也可以不加引号加的引号可以是单引号也可以是双引号。
#单引号
str1this is a string#双引号
str2this is a string#无引号
varhello 1.单引号的限制 单引号里的任何字符都会原样输出单引号内变量是无效的 单引号字符串中不能出现单独的一个单引号对单引号使用转义符也不行 2.双引号的优点 双引号里可以有变量 双引号里可以出现转义字符 eg
namestark
varhello,i am \ {name} \! \n
echo -e $var 环境变量
由操作系统或用户设置的特殊变量用于配置Shell的行为和影响其执行环境。例如PATH变量包含了操作系统搜索可执行文件的路径
echo $PATH
特殊变量
有一些特殊变量在Shell中具有特殊含义例如$0表示脚本的名称$1$2等表示脚本的参数$#表示传递给脚本的参数数量$?表示上一个命令的退出状态等。
有了以上的知识我们在使用makefile的比较“高大上”的用法的时候就不显得茫然无措了makefile中的变量与上述变量有些差别但还是不耽误理解的。
Makefile
sudo apt-get install make #下载make工具
基本原则 1.若想生成目标检查规则中的依赖文件是否存在如不存在则寻找是否有规则用来生成该依赖文件。 2.检查规则中的目标是否需要更新必须先检查它的所有依赖依赖中有任一个被更新则目标必须更新 ⚪分析各个目标和依赖之间的关系 ⚪根据依赖关系自底向上执行命令 ⚪根据修改时间比目标新确定更新 ⚪如果目标不依赖任何条件则执行对应命令以示更新 1个规则
object:link.cgcc link.c -o object#模板
目标:依赖文件(tab)命令
示例
创建一个测试用的.c文件vim main.c
#include stdio.hint main()
{printf(------------);return 0;
}
然后创建一个makefile文件vim makefile
main:main.cgcc main.c -o main
然后再退出转到终端输入make 执行完make命令后下面会提示make自动执行的命令(gcc main.c -o main)然后使用ls命令查看当前目录下的文件发现多了一个main文件这就相当于直接执行gcc main.c -o main。你可能觉得又是新建文件又是按规则编辑内容的我为什么不直接执行命令呢。诚然但是假如你需要分使用gcc分步骤制作时呢每次都一句一句的执行那么我配置一次makefile之后就都make就玩事了。一次制作受益良久
main:main.ogcc main.o -o mainmain.o:main.sgcc -c main.s -o main.omain.s:main.igcc -S main.i -o main.smain.i:main.cgcc -E main.c -o main.i这里一连四条语句每次都自己输入会很麻烦的但是现在只需要make一下。但是我们也可以看到这里make之后的执行顺序是按照我们gcc分步编译的顺序来的并不是我们在文件中写的顺序来的这是为什么这就是基本原则一检查规则中的依赖文件是否存在如不存在则寻找是否有规则用来生成该依赖文件。书写时我们的目标是main依赖文件时main.o检查一下依赖文件并不存在那么就会寻找文件中是否有生成该依赖文件的规则发现了gcc -c的命令但是main.o的依赖文件main.s也不存在所以接着寻找直到找到main.cmain.c生成main.i后按照流程最后生成了我们的目标文件main。
下面在这个基础上我们修改一下main.c文件使其打印内容变一下观察结果 更改之前是输出一行星号 此时看一下我们的目录下有哪些文件 下面我们重新执行make指令那么现在我们目录中有main.o文件我们还需要寻找生成依赖文件的规则吗答案是显然的需要为什么呢请看规则二检查规则中的目标是否需要更新必须先检查它的所有依赖依赖中有任一个被更新则目标必须更新。目标存在那么就检查它的依赖然后逐层检查依赖这时依赖链中有更改的那么久重新执行更新目标文件。所以此时再执行main时输出了一行减号。 我们再将main.c改回来然后删除main、main.i、main.s保留main.o和main.c。 此时一开始目标不存在有main.o这个依赖文件我们是按照现在的main.o编译输出一行减号还是重新编译输出一行星号呢请看VCR 结果是更新main.o因为此时main.o作为一个中间的临时目标依然会寻找main.o的依赖链最后导致main.o被更新从而引起了main的更新。
但是.o .s .i 的文件必须按照依赖链逐层写在makefile文件中吗根据我们的执行顺序我们发现显然不需要。但是我们试图将生成main目标的规则写在下面呢 我们执行make结果提示main.i已经是最新代表make将main.i当作了最终目标文件但这并不是我们的预期结果我们的预期是生成main文件。 我们尝试更改main.c使main.i不是最新 结果只执行了一句剩下的三个规则的目标文件并不在main.i的生成的依赖链中所以不会执行。此时我们得出结论
第一个规则会被make当作最终的目标文件只要目标规则为第一条规则那么其他规则的顺序并不影响最终结果。
但是我们可以通过在一开始显式声明目标的方式指定最终目标即在第一有效行添加一句
ALL:目标文件 好了现在我们有另外的一个需求如何在makefile中封装gcc main.c add.c sub.c -o a.out
目前我们学的只能这么用啊
main:main.c add.c sub.cgcc main.c add.c sub.c -o main好了开始使坏了我先对add.c进行修改然后make然后再执行./main结果是目标被更新了 。但是我只修改了add.c但是执行的却是gcc main.c add.c sub.c -o mainsub.c没修改也被牵连着被重新编译了一下。明明是add.c不干好事被老板罚加班了但是还要连累sub.c也要跟着加班这公平吗。话说回来add.c自己变化了我只需要重新编译它自己就完了嘛。但我们上面的写法就已经决定了它们三个共同编译才能生成main不能单独修改一个然后去修改main。所以需要我们重新制定规则
命令形式自己去尝试写成makefile规则形式 此时如果更改其中一个文件的话只会重新编译单独那一个最后链接再一起执行我们知道链接阶段最耗时所以此时我们将最耗时的给分离了只剩下一些细枝末节的链接操作还是能接受的。还是那个例子add偷懒连累了sub但是老板比较好只让sub给add打个杂不用做那些最累的活了。
2个函数
①src $(wildcard *.c)
wildcard函数找到当前目录下所有后缀为.c的文件赋值给src
wildcard中文释义为通配符。上面的语法是定义一个变量src调用$()函数wildcard传入参数*.c将调用的函数的 结果(文件名)组成列表 赋给src中做初值。 ②obj $(patsubst %.c,%.o, $(src))
patsubst函数把src变量里面的所有后缀为.c的文件替换成.o
patsubst全称是“pattern substitution”即模式替换。这个函数可以在给定的文本中查找符合特定模式的单词并将其替换为指定的替换文本。上述语法中将参数3中含有参数1的文本替换为参数2的文本。 cleanclean是一组没有依赖文件的规则
clean:-rm -f $(obj) a.out #rm前的-表示出错依然执行#终端
localhost$ make clean -n #模拟执行会将执行的命令输出到终端但不执行
localhost$ make clean #执行clean
下面我们说一下rm前面的那个-现在我们的目录下没有aaa文件我们试图删除aaa 但是我们将-加载rm前面的话将不会提示这个错误。
3个自动变量
自动变量
自动变量只允许出现在命令位置目标和依赖位置不可出现
①$:表示规则中的目标/
②$:表示规则中的第一个依赖条件/
③$^:表示规则中的所有依赖条件组成一个列表以空格隔开如果这个列表中有重复项则消除重复项。/
使用自动变量的目的是为了方便后面的拓展
模式规则
至少在规则的目标定义中要包含%%表示一个或多个在依赖条件中同样可以使用%依赖条件中的%的取值取决于其目标。
%.o:%.cgcc -c $ -o $
之后我们新建.c文件后并不需要我们再去动makefile了。
静态模式规则
$(obj):%.o:%.cgcc -c $ -o %
伪目标
.PHONY: all clean
在这句话里面clean就是伪目标
伪目标可以用来简化常用命令的执行比如清理、安装或测试而不依赖于文件的生成。
伪目标避免与目录或文件名冲突。由于伪目标总是被执行其定义不会受到同名文件的影响确保执行的顺序和安全性。
使用伪目标可以提高 Makefile 的可读性帮助开发者更清晰地理解可用的命令和操作。 终极形态
#变量定义
CC gcc
CFLAGS -Wall -g
SRC main.c demo01.c demo02.c
OBJ $(SRC:.c.o) #等价于 OBJ $(patsubst %.c,%.o, $(SRC))
TARGET my_program#默认规则
all:$(TARGET)$(TARGET): $(OBJ)$(CC) $^ -o $%.o: %.c$(CC) -c $ -o $ $(CFLAGS)#清理目标
clean:-rm -f $(OBJ) $(TARGET)#伪目标
.PHONY: all clean说明 1.变量定义 CC指定使用的编译器 CFLAGS编译选项-Wall启用警报-g生成调试信息 此外还有预处理选项CPPFLAGS如-I链接器选项LDFLAGS如-L -l SRC源文件列表 OBJ将源文件列表转换为目标文件列表。 TARGET最终可执行文件的名称 2.默认规则 目标all默认用来生成可执行文件。 $(TARGET)需要依赖于对象文件 $(OBJ)通过规则定义如何链接这些对象文件 3.模式规则 %.o: %.c规则使得每个.c文件都可以自动生成对应的.o文件 4.清理 clean目标用于删除编译产生的临时文件和最终可执行文件使用rm -f强制删除 5.伪目标 .PHONY指令clean和all是伪目标即使有同名文件也不会影响执行。 使用方法
在项目的目录下打开命令行运行以下命令
make #编译项目
make clean #清理项目
通过使用 Makefile可以简化编译和项目管理的过程特别是在大型项目中。
但是每次使用make或者make clean我们都会发现文件里面用到的指令都会被输出到终端为了将封装性更加彻底我们可以选择在指令的前面加上来对其进行匿名执行 我们gcc这行已经是在匿名执行了所以gcc命令本身没有输出到终端而echo命令没有使用匿名方式所以命令本身以及命令结果都输出到了终端。 感谢大家最后附上C语言常用的makefile文件内容
#编译变量---------------------------------------------------------------------------------
CC gcc #指定编译器
#CPPFLAGS -l #预处理选项
CFLAGS -Wall -g #编译器选项
LDFLAGS -l math -L /home/stark/class/filec/library/lib #链接器选项
#不使用动态库的话这块也不用加#文件变量---------------------------------------------------------------------------------TARGET a.out #设置终极目标
SRC $(wildcard *.c) #所有的.c文件为源文件
OBJ $(patsubst %.c,%.o, $(SRC)) #替换文本.c - .o
#OBJ $(SRC:.c.o)#定义规则---------------------------------------------------------------------------------
ALL:$(TARGET) #指定终极目标$(TARGET):$(OBJ) #终极目标形成规则$(CC) $ -o $ $(LDFLAGS)%.o:%.c #依赖--OBJ 的生成规则$(CC) -c $^ -o $ $(CFLAGS)#----------------------------------------------------------------------------------------
#伪目标
.PHONY: all clean
clean:rm -f $(OBJ) $(TARGET)