利用Makfile给多文件、多目录C源码建立工程
时间:2021-11-11 13:46:21
手机看文章
扫描二维码
随时随地手机看文章
[导读]一、文件我们将该项目的所有功能函数放到以该函数名命名的c文件,同时放到对应名称的子目录中。比如函数allfree(),存放到allfree/allfree.c中最终目录结构如下图所示: peng@ubuntu:/mnt/hgfs/code/phone$ tree ..├── al...
一、文件
我们将该项目的所有功能函数放到以该函数名命名的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!
运行结果如下:二、Makefile常用基础知识点
[0] 符号'@' '$' '$$' '-' '-n '
的说明
'@'
通常makefile会将其执行的命令行在执行前输出到屏幕上。如果将‘@’添加到命令行前,这个命令将不被make回显出来。例如:
@echo --compiling module----; // 屏幕输出 --compiling module----
echo --compiling module----; // 没有@ 屏幕输出echo --compiling module----
' - '
-rm dir;
-mkdir aaadir;
' $ '
美元符号$
,主要扩展打开makefile中定义的变量' $$ '
$$
符号主要扩展打开makefile中定义的shell变量
[1] wildcard
说明: 列出当前目录下所有符合模式“ PATTERN”格式的文件名,并且以空格分开。“ PATTERN”使用shell可识别的通配符,包括“ ?”(单字符)、“ *”(多字符)等。示例:$(wildcard *.c)
返回值为当前目录下所有.c 源文件列表。[2] patsubst
说明:把字串“ x.c.c bar.c”中以.c 结尾的单词替换成以.o 结尾的字符。示例:$(patsubst %.c,%.o,x.c.c bar.c)
函数的返回结果 是 x.c.o bar.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。语法:$(addsuffix SUFFIX,NAMES…)
函数功能:为“NAMES…”中的每一个文件名添加后缀“SUFFIX”。参数“NAMES…” 为空格分割的文件名序列,将“SUFFIX”追加到此序列的每一个文件名 的末尾。返回值:以单空格分割的添加了后缀“SUFFIX”的文件名序列。函数说明:示例:$(addsuffix .c,foo bar)
返回值为foo.c bar.c
[6] 包含另外一个文件:include
在Makefile使用include关键字可以把别的Makefile包含进来,这很像C语言的#include,被包含的文件会原模原样的放在当前文件的包含位置。比如命令include file.dep
即把file.dep文件在当前Makefile文件中展开,亦即把file.dep文件的内容包含进当前Makefile文件在 include前面可以有一些空字符,但是绝不能是[Tab]键开始。
[7] foreach
foreach函数和别的函数非常的不一样。因为这个函数是用来做循环用的 语法是:$(foreach ,, )
这个函数的意思是,把参数names := a b c d
files := $(foreach n,$(names),$(n).o)
上面的例子中,$(name)
中的单词会被挨个取出,并存到变量“n”中,“$(n).o
”每次根据“$(n)
”计算出一个值,这些值以空格分隔,最后作为foreach函数的返回,所以,$(files)
的值是“a.o b.o c.o d.o”。注意,foreach中的参数是一个临时的局部变量,foreach函数执行完后,参数的变量将不在作用,其作用域只在foreach函数当中。[8] call
“ call”函数是唯一一个可以创建定制化参数函数的引用函数。使用这个函数可以实现对用户自己定义函数引用。我们可以将一个变量定义为一个复杂的表达式,用“ call”函数根据不同的参数对它进行展开来获得不同的结果。函数语法:$(call variable,param1,param2,...)
函数功能:在执行时,将它的参数“ param”依次赋值给临时变量“ $(1)
”、“ $(2)
” call 函数对参数的数目没有限制,也可以没有参数值,没有参数值的“ call”没有任何实际存在的意义。执行时变量“ variable”被展开为在函数上下文有效的临时变量,变量定义中的“ $(1)
”作为第一个参数,并将函数参数值中的第一个参数赋值给它;变量中的“ $(2)
”一样被赋值为函数的第二个参数值;依此类推(变量**$(0)
**代表变量“ variable”本身)。之后对变量“ variable” 表达式的计算值。返回值:参数值“ param”依次替换“ $(1)
”、“ $(2)
”…… 之后变量“ variable”定义的表达式的计算值。函数说明:- 函数中“ variable”是一个变量名,而不是变量引用。因此,通常“ call”函数中的“ variable”中不包含“
$
”(当然,除非此变量名是一个计算的变量名)。 - 当变量“ variable”是一个 make 内嵌的函数名时(如“ if”、“ foreach”、“ strip”等),对“ param”参数的使用需要注意,因为不合适或者不正确的参数将会导致函数的返回值难以预料。
- 函数中多个“ param”之间使用逗号分割。
- 变量“ variable”在定义时不能定义为直接展开式!只能定义为递归展开式。
reverse = $(2)$(1)
foo = $(call reverse,a,b)
all:
@echo "foo=$(foo)"
执行结果:foo=ba
即a替代了(2)三、编译详细说明
我们在根目录下执行make命令后,详细步骤如下:- include scripts/Makefile :将文件替换到当前位置,
- 使用默认的目标all,该目标依赖于
$(Target)
$(Target)
在scripts/Makefile中定义了,即phone - 而
$(Target)
依赖于mm - mm这个目标会执行
@ $(foreach n,$(Modules),$(call modules_make,$(n)))
Modules是所有的目录名字集合, foreach 会遍历字符串$(Modules)中每个词语, 每个词语会赋值给n, 同时执行语句:call modules_make,$(n)
- modules_make 被
$(MAKE) -C $(1)
所替代,
$(MAKE)
有默认的名字make -C:进入子目录执行make$(1)
:是步骤4中$(n)
,即每一个目录名字最终步骤4的语句就是进入到每一个目录下,执行每一个目录下的Makefile- 进入某一个子目录下,执行Makefile 默认目标是all,依赖Objs
Objs := $(patsubst %.c,%.o,$(Source))
patsubst 把字串$ource
中以.c 结尾的单词替换成以.o 结尾的字符 而Source := $(wildcard ./*.c)
wildcard 会列举出当前目录下所有的.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 会将 /*.o追加到$(Modules)
中所有的词语后面,也就是我们之前在子目录下编译生成的所有的.o文件$(Libs)
:替换为-lpthread,即所需要的动态库版权申明:内容来源网络,版权归原创者所有。除非无法确认,我们都会标明作者及出处,如有侵权烦请告知,我们会立即删除并表示歉意。谢谢!