编译指定源码目录-Makefile示例,使用Makefile构建多文件、多目录C源代码的项目

0. 前言

粉丝留言,想知道如何使用Makefile构建多文件、多级目录的项目,必须要整理一下!

关于Makefile的介绍参考文章,可以先阅读这篇文章:

””

为了让大家有更直观的体验,一口君会做一个之前写的小项目,本文会在这个项目的基础上进行修改。

本项目的详细设计及代码如下所示:

””

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.c
11 directories, 22 files

我们直接看一下编译结果:

peng@ubuntu:/mnt/hgfs/code/phone$ make
make[1]: Entering directory '/mnt/hgfs/code/phone/allfree'
make[1]: Nothing to be done for 'all'.
make[1]: Leaving directory '/mnt/hgfs/code/phone/allfree'
make[1]: Entering directory '/mnt/hgfs/code/phone/create'
make[1]: Nothing to be done for 'all'.
make[1]: Leaving directory '/mnt/hgfs/code/phone/create'
make[1]: Entering directory '/mnt/hgfs/code/phone/delete'
make[1]: Nothing to be done for 'all'.
make[1]: Leaving directory '/mnt/hgfs/code/phone/delete'
make[1]: Entering directory '/mnt/hgfs/code/phone/display'
make[1]: Nothing to be done for 'all'.
make[1]: Leaving directory '/mnt/hgfs/code/phone/display'
make[1]: Entering directory '/mnt/hgfs/code/phone/init'
make[1]: Nothing to be done for 'all'.
make[1]: Leaving directory '/mnt/hgfs/code/phone/init'
make[1]: Entering directory '/mnt/hgfs/code/phone/login'
make[1]: Nothing to be done for 'all'.
make[1]: Leaving directory '/mnt/hgfs/code/phone/login'
make[1]: Entering directory '/mnt/hgfs/code/phone/menu'
make[1]: Nothing to be done for 'all'.
make[1]: Leaving directory '/mnt/hgfs/code/phone/menu'
make[1]: Entering directory '/mnt/hgfs/code/phone/search'
make[1]: Nothing to be done for 'all'.
make[1]: Leaving directory '/mnt/hgfs/code/phone/search'
make[1]: Entering directory '/mnt/hgfs/code/phone/main'
make[1]: Nothing to be done for 'all'.
make[1]: Leaving directory '/mnt/hgfs/code/phone/main'
gcc -Wall -O3 -o phone allfree/*.o create/*.o delete/*.o display/*.o init/*.o login/*.o menu/*.o search/*.o main/*.o -lpthread
phone make done! 

运行结果如下:

2、Makefile常用的基础知识点 [0] 符号说明 '@' '$' '$$' '-' '-n ''@'

通常makefile在执行前会将其执行的命令行输出到屏幕上。

如果在命令行前添加“@”,则 make 不会回显该命令。

例如:

@echo  --compiling module----;  // 屏幕输出  --compiling module----
echo  --compiling module----;  // 没有@ 屏幕输出echo  --compiling module----   

'-'

通常删除和创建文件。 如果遇到不存在或者已经创建的文件,如果想忽略这个错误继续执行,可以在命令后面加上-。

-rm dir;
-mkdir aaadir;

'$'

美元符号$,主要是展开和打开makefile中定义的变量。

'$$'

$$符号主要扩展并打开makefile中定义的shell变量。

[1] 通配符

阐明:

列出当前目录中与模式“PATTERN”匹配并以空格分隔的所有文件名。 “PATTERN”使用shell可以识别的键值,包括“?” (单个字符)、“*”(多个字符)等

例子:

$(wildcard *.c) 

返回值是当前目录中所有.c源文件的列表。

[2] 帕特萨斯特

描述:将字符串“xcc bar.c”中以.c结尾的短语替换为以.o结尾的字符。

例子:

$(patsubst %.c,%.o,x.c.c bar.c)

函数返回结果

是的

 x.c.o bar.o

[3] 不目录

说明:从文件名中删除路径信息

例子:

SRC = ( notdir ./src/a.c ) 

要删除文件ac的路径信息,请使用(notdir./src/ac)删除文件ac的路径信息。 使用(notdir./src/ac)删除文件ac的路径信息。 使用(SRC)得到的结果不带路径的文件名编译指定源码目录,即ac。

编译指定源码目录-Makefile示例,使用Makefile构建多文件、多目录C源代码的项目

[4]包含头文件路径

使用-I+头文件路径指定编译器头文件的路径。

例子:

INCLUDES = -I./inc

$(CC) -c $(INCLUDES) $(SRC)

[5] 添加后缀

函数名称:添加后缀函数—addsuffix。

语法:

$(addsuffix SUFFIX,NAMES…) 

功能:为“NAMES...”中的每个文件名添加后缀“SUFFIX”。 参数“名称...”

对于以空格分隔的文件名序列,请将“SUFFIX”附加到该序列中的每个文件名。

结束 。

返回值: 由单个空格分隔并添加后缀“SUFFIX”的文件名序列。

功能说明:

例子:

$(addsuffix .c,foo bar) 

返回值为

foo.c bar.c

[6]包含另一个文件:include

在 Makefile 中使用 include 关键字来包含其他 Makefile。 这与C语言中的#include非常相似。 包含的文件将原样放置在当前文件的包含位置。

比如命令

include file.dep

即展开当前Makefile中的file.dep文件,即将file.dep文件的内容包含到当前Makefile中。

编译指定源码目录-Makefile示例,使用Makefile构建多文件、多目录C源代码的项目

include后可以有一些空字符,但不能以[Tab]键开头。

[7] foreach

foreach函数与其他函数特别不同。因为该函数用于循环

语法是:

$(foreach <var>,<list>,<text> )

该函数的意义就是将参数中的短语一一取出放入参数指定的变量中,然后执行包含的表达式。

每次都会返回一个字符串。 在循环过程中,返回的每个字符串都会以空格分隔。 最后,当整个循环结束时,返回的每个字符串(用空格分隔)组成的整个字符串将是foreach函数的返回值。

因此,最好使用变量名,变量名可以是表达式,该参数通常用于按顺序枚举短语。

例子:

names := a b c d
files := $(foreach n,$(names),$(n).o)

在上面的例子中,$(name)中的短语将被一一取出并存储在变量“n”中。 “$(n).o”每次根据“$(n)”计算一个值。 这些值是用空格分隔的,最终是作为foreach函数返回的,所以$(files)的值为“ao bo co do”。

请注意,foreach 中的参数是临时局部变量。 foreach函数执行后,参数的变量将不再起作用,其作用域仅在foreach函数内。

[8] 呼叫

“call”函数是唯一可以创建具有不同参数的函数的参考函数。

使用该函数可以实现对用户定义函数的引用。

我们可以将变量定义为复杂的表达式,并使用“call”函数根据不同的参数对其进行扩展,以获得不同的结果。

功能句型:

$(call variable,param1,param2,...)

功能功能:

在执行过程中,其参数“param”按顺序正式参数化为临时变量“$(1)”和“$(2)”。 呼叫”没有实际意义。

变量“variable”在执行过程中被扩展为函数上下文中有效的临时变量,变量定义中的“$(1)”作为第一个参数,将函数参数值中的第一个参数赋给它;

变量中的“$(2)”也用作函数的第二个参数值;

依此类推(变量**$(0)**代表变量“变量”本身)。

随后对变量“variable”表达式求值。

返回值:

参数值“param”替换“$(1)”、“$(2)”...,然后替换变量“variable”定义的表达式的估计值。

功能说明:

函数中的“变量”是变量名,而不是变量引用。 因此,通常“调用”函数中的“变量”中不包含“$”(当然编译指定源码目录,除非该变量名称是估计的变量名称)。 当变量“variable”是make中嵌入的函数名(如“if”、“foreach”、“strip”等)时,“param”参数的使用需要小心,因为参数不合适或不正确这将导致函数的返回值不可预测。 使用冒号分隔函数中的多个“参数”。 变量‘variable’定义时不能直接扩展! 只能定义为递归展开。

功能示例:

reverse = $(2)$(1)
foo = $(call reverse,a,b)
all:
	@echo "foo=$(foo)"

结果:

foo=ba

即a代替(1),b代替(1),b代替(1),b代替(2)

3.编译详细说明

我们在根目录下执行make命令后,详细步骤如下:

include script/Makefile :替换当前位置的文件,使用默认目标 all,这取决于 $(Target)

$(Target)在scripts/Makefile中定义,它是phone,$(Target)取决于mmmm。 该目标将被执行。

@ $(foreach n,$(Modules),$(call modules_make,$(n)))

Modules是所有目录名称的集合,

foreach 将遍历字符串 $(Modules) 中的每个单词和句子,

每个习语都会给n一个形式参数,

同时执行语句:

call modules_make,$(n)

module_make 替换为 $(MAKE) -C $(1),

$(MAKE) 的默认名称为 make

编译指定源码目录-Makefile示例,使用Makefile构建多文件、多目录C源代码的项目

-C:进入子目录并执行make

$(1):就是步骤4中的$(n),即各个目录名

步骤4最后一句是单步进入各个目录,执行各个目录下的Makefile。

进入某个子目录并执行Makefile

默认target是all,依赖于Objs

Objs := $(patsubst %.c,%.o,$(Source))

patsubst 将字符串 $source 中以 .c 结尾的短语替换为以 .o 结尾的字符

Source := $(wildcard ./*.c)

通配符将列出当前目录中的所有 .c 文件

所以最后第6步就是编译子目录下的所有.c文件,生成文件名对应的.o文件。

$(CC) $(CFLAGS) -o $(Target) $(AllObjs) $(Libs)

这些变量在文件scripts/Makefile中定义

$(CC) :替换为gcc,制作编译器

$(CFLAGS):替换为-Wall -O3,即编译时的优化级别

-o $(Target):生成可执行程序phone

$(AllObjs) :

AllObjs := $(addsuffix /*.o,$(Modules))

addsuffix 会在 $(Modules) 中的所有单词和句子旁边追加 /*.o,即我们之前在子目录中编译生成的所有 .o 文件

$(Libs):替换为-lpthread,所需的动态库

可以按照这一步来分析执行make clean时的执行步骤。

完整的示例程序:

链接: 提取码:57n6

《电话号码管理-makefile版本.rar》

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

悟空资源网 源码编译 编译指定源码目录-Makefile示例,使用Makefile构建多文件、多目录C源代码的项目 https://www.wkzy.net/game/191602.html

常见问题

相关文章

官方客服团队

为您解决烦忧 - 24小时在线 专业服务