当前位置: 首页 > 科技观察

使用Makfile创建多文件多目录C源码项目

时间:2023-03-17 12:03:47 科技观察

0。前言粉丝留言,想知道如何使用Makefile构建多文件多级目录的工程,必须整理!关于Makefile的入门参考文章,可以先阅读这篇文章:《Makefile入门教程》为了让大家有一个更直观的体验,一扣老师将对之前写的一个小项目进行修改,本文将在此基础上进行修改这个项目。项目的详细设计和代码,见下:《从0写一个《电话号码管理系统》的C入门项目【适合初学者】》1.文件准备好了,开始吧!我们把项目的所有功能都放到了以其命名的c文件中函数,并将其放在具有相应名称的子目录中。例如allfree()函数存放在allfree/allfree.c中,最终目录结构如下图所示:peng@ubuntu:/mnt/hgfs/code/phone$tree..├──allfree│├──allfree.c│└──Makefile├──create│├──create.c│└──Makefile├──delete│├──delete.c│└──Makefile├──display│├──display.c│└──Makefile├──include│├──Makefile│└──phone.h├──init│├──init.c│└──Makefile├──login│├──login。c│└──Makefile├──main│├──main.c│└──Makefile├──Makefile├──menu│├──Makefile│└──menu.c├──scripts│└──Makefile└──search├──Makefile└──search.c11目录,22个文件直接看编译结果:peng@ubuntu:/mnt/hgfs/code/phone$makemake[1]:Enteringdirectory'/mnt/hgfs/code/phone/allfree'make[1]:Nothingtobedonefor'all'.make[1]:Leavingdirectory'/mnt/hgfs/code/phone/allfree'make[1]:Enteringdirectory'/mnt/hgfs/code/phone/create'make[1]:Nothingtobedonefor'all'.make[1]:Leavingdirectory'/mnt/hgfs/code/phone/create'make[1]:Enteringdirectory'/mnt/hgfs/code/phone/delete'make[1]:Nothingtobedonefor'all'.make[1]:Leavingdirectory'/mnt/hgfs/code/phone/delete'make[1]:Enteringdirectory'/mnt/hgfs/code/phone/display'make[1]:Nothingtobedfor'all'.make[1]:Leavingdirectory'/mnt/hgfs/code/phone/display'make[1]:Enteringdirectory'/mnt/hgfs/code/phone/init'make[1]:Nothingtobedonefor'all'.make[1]:Leavingdirectory'/mnt/hgfs/code/phone/init'make[1]:Enteringdirectory'/mnt/hgfs/code/phone/login'make[1]:Nothingtobedfor'all'.make[1]:Leavingdirectory'/mnt/hgfs/code/phone/login'make[1]:Enteringdirectory'/mnt/hgfs/code/phone/menu'make[1]:Nothingtobedonefor'all'.make[1]:Leavingdirectory'/mnt/hgfs/code/phone/menu'make[1]:Enteringdirectory'/mnt/hgfs/code/phone/search'make[1]:Nothingtobedonefor'all'.make[1]:Leavingdirectory'/mnt/hgfs/code/phone/search'make[1]:Enteringdirectory'/mnt/hgfs/code/phone/main'make[1]:Nothingtobedonefor'all'.make[1]:Leavingdirectory'/mnt/hgfs/code/phone/main'gcc-Wall-O3-ophoneallfree/*.ocreate/*.odelete/*.odisplay/*.oinit/*.login/*.omenu/*.osearch/*.omain/*.o-lpthreadphonemadeone!运行结果如下:2.Makefile常用基础知识点[0]Symbols'@''$''$$''-''-n'的解释1.'@'通常makefile会在执行前将其执行的命令行输出到屏幕上。如果在命令行前加'@',make将不会回显该命令。例子:@echo--compilingmodule----;//屏幕输出--compilingmodule----echo--compilingmodule----;//没有@screen输出echo--compilingmodule----2.'-'通常删除并创建一个文件。如果遇到不存在或者已经创建的文件,那么你要忽略这个错误继续执行。可以加上-,-rmdir;-mkdiraaadir;3.'$'美元符号$,主要展开和打开makefile中定义的变量4.'$$'$$符号主要展开和打开makefile中定义的shell变量[1]通配符说明:列出当前文件中的所有文件名匹配模式“PATTERN”格式的目录,并以空格分隔开来。“PATTERN”使用shell可识别的通配符,包括“?”(单个字符)、“*”(多个字符)等。示例:$(wildcard*.c)返回当前目录中所有.c源文件的列表。[2]patsubst说明:将字符串“x.c.cbar.c”中以.c结尾的单词替换为以.o结尾的字符。示例:$(patsubst%.c,%.o,x.c.cbar.c)函数返回结果为x.c.obar.o[3]notdir说明:去除文件名中的路径信息示例:src=(notdir./src/a.c)去除文件a.c的路径信息,使用(notdir./src/a.c)去除文件a.c的路径信息,使用(notdir./src/a.c)去除文件a.c的路径信息,使用(SRC)获取不带路径的文件名,即a.c.[4]包含头文件的路径使用-I+头文件的路径来指定编译器头文件的路径例子:INCLUDES=-I./inc$(CC)-c$(INCLUDES)$(src)[5]addsuffix函数名:添加后缀函数—addsuffix。语法:$(addsuffixSUFFIX,NAMES…)功能:为“NAMES…”中的每个文件名添加后缀“SUFFIX”。参数“NAMES...”是一个以空格分隔的文件名序列,“SUFFIX”附加到该序列中每个文件名的末尾。返回值:由单个空格分隔的文件名序列,后缀为“SUFFIX”。函数说明:例子:$(addsuffix.c,foobar)returnsfoo.cbar.c[6]包含另一个文件:include在Makefile中使用include关键字来包含其他的Makefile,这和C语言#include很相似,include文件将按原样放置在当前文件的包含位置。例如,命令includefile.dep扩展了当前Makefile中的file.dep文件,即将file.dep文件的内容包含到当前Makefile中。include前面可以有一些空字符,但不能以[Tab]键开头。.[7]foreachforeach函数与其他函数有很大不同。因为这个函数是用来循环的,所以语法是:$(foreach,,)这个函数的意思是把参数中的单词一个一个的取出来,放到参数指定的变量中范围。然后执行包含的表达式。每次都会返回一个字符串。在循环期间,返回的每个字符串将由空格分隔。最后,当整个循环结束时,返回的每个字符串(以空格分隔)组成的整个字符串将作为foreach函数的返回值。所以最好是变量名,可以是表达式,一般用这个参数依次枚举列表中的单词。例子:names:=abcdfiles:=$(foreachn,$(names),$(n).o)在上面的例子中,$(name)中的单词会被一个一个的取出来,存入变量"n","$(n).o"每次根据"$(n)"计算一个值,这些值之间用空格隔开,最后作为foreach函数返回,所以$(files)的值为“a.ob.oc.od.o”。请注意,foreach中的参数是一个临时局部变量。foreach函数执行后,参数的变量将不再起作用,其作用域只在foreach函数中。[8]call“call”函数是唯一可以创建自定义参数函数的参考函数。使用此函数实现对用户定义函数的引用。我们可以将一个变量定义为一个复杂的表达式,根据不同的参数使用“call”函数对其展开,得到不同的结果。函数语法:$(calllvariable,param1,param2,...)函数功能:在执行时,将其参数“param”依次赋值给临时变量“$(1)”和“$(2)”调用函数对有参数个数没有限制,没有参数值,没有参数值的“调用”没有实际意义。变量“variable”在执行过程中扩展为在函数上下文中有效的临时变量,变量定义中的“$(1)”作为第一个参数,函数参数值中的第一个参数赋值给它;在变量函数的“$(2)”中赋值函数的第二个参数值;依此类推(变量**$(0)**代表变量“变量”本身)。然后评估变量“variable”表达式的值。返回值:参数值“param”替换“$(1)”、“$(2)”,...后跟由变量“variable”定义的表达式的计算值。函数说明:函数中的“变量”是变量名,不是变量引用。所以通常“$”不包含在“调用”函数的“变量”中(当然,除非这个变量名是一个计算变量名)。当变量“variable”是make中嵌入的函数名时(如“if”、“foreach”、“strip”等),使用“param”参数需要小心,因为不合适或不正确的参数will函数的返回值是不可预测的。函数中的多个“param”用逗号分隔。变量'variable'不能在定义时定义为直接扩展!只能定义为递归展开。函数示例:reverse=$(2)$(1)foo=$(callreverse,a,b)all:@echo"foo=$(foo)"执行结果:foo=ba表示a替换(2)3.编译详情我们在根目录下执行make命令后,详细步骤如下:1.includescripts/Makefile:将文件替换到当前位置,2.使用默认的targetall,这取决于$(Target)$(Target)定义在scripts/Makefile中,即phone3。而$(Target)依赖于mm4.mm,这个target会执行@$(foreachn,$(Modules),$(callmodules_make,$(n)))Modules,对于每一个目录名的集合,foreach会遍历其中的每一个单词string$(Modules),每个word会被赋值给n,同时执行语句:callmodules_make,$(n)5.modules_make是由$(MAKE)-C$(1)执行的,而$(MAKE)有个默认的名字make-C:进入子目录执行make$(1):就是第4步的$(n),也就是各个目录的名字。第4步最后的语句是进入每个目录中,执行每个目录中的Makefile6。进入某个子目录,执行Makefile。默认target是all,依赖ObjsObjs:=$(patsubst%.c,%.o,$(Source))patsubst将$source中以.c结尾的字符串替换为以.o结尾的字符和Source:=$(wildcard./*.c)wildcard会列出当前目录下的所有.c文件,所以第6步最后就是将sub目录下的所有.c文件编译生成对应文件名的.o文件。$(CC)$(CFLAGS)-o$(Target)$(AllObjs)$(Libs)这些变量都在文件scripts/Makefile中定义$(CC):用gcc替换,makeacompiler$(CFLAGS):替换为-Wall-O3,即编译时优化级别-o$(Target):生成可执行程序phone$(AllObjs):AllObjs:=$(addsuffix/*.o,$(Modules))addsuffix会将/*.o附加到$(Modules)中的所有单词从表面上看,也就是子目录$(Libs):中我们编译生成的所有.o文件都换成了-lpthread,也就是需要的动态库。执行makeclean时可以按照此步骤分析执行步骤本文转载自微信公众号《一口Linux》