一、项目开始
首先我们创建项目文件夹
mkdir study01
跳转到目录
cd study01
创建项目需要的文件
touch main.c app.c app.h Makefile
然后我们编写app程序.c程序
#include <stdio.h> #include "app.h" int add(int x, int y) { return x + y; } int sub(int x, int y) { return x - y; }
.h文件
#ifndef __APP_H #define __APP_H // 返回两个数的和 int add(int x, int y); // 返回两个数的差 int sub(int x, int y); #endif
编写主程序文件
#include <stdio.h> #include "app.h" int main(int argc, char* argv[]) { int num1 = 100, num2 = 55; int value = 0; value = add(num1, num2); printf("%d + %d = %d\r\n", num1, num2, value); value = sub(num1, num2); printf("%d - %d = %d\r\n", num1, num2, value); return 0; }
二、第一个Makefile
最后来完成本章内容的核心Makefile
main:app.o main.o gcc app.o main.o -o main
这一句的以上可以理解为main这个可执行文件需要依赖app.o和main.o文件生成
然后下面一句是生成main文件需要执行的语句。
app.o:app.c gcc -c app.c main.o:main.c gcc -c main.c
可以用上一段代码的方式同样理解生成app.o文件需要依赖app.c,如何生成呢,使用gcc -c app.c
clean: rm *.o rm main
删除生成的文件,这一句的意思的调用make clean就回执行rm *.o删除工程下所有.o的文件rm main表示删除main文件(不带.c),到这里我们就得到了一个完整的工程
main:app.o main.o gcc app.o main.o -o main app.o:app.c gcc -c app.c main.o:main.c gcc -c main.c clean: rm *.o rm main
然后我们输入make就可以看到编译过程中使用到的指令,然后输入ls就可以看到main文件已经生成了然后执行main文件就可以得到和手动编译一样的效果了,最后我们可以执行make cleam可以看到刚刚生成的文件已经删除了,对于需要多次使用到的文件我们可以使用变量来进行封装,所以这里再补充一个makefile变量的知识点:
object = app.o main.o main:$(object) gcc $(object) -o main app.o:app.c gcc -c app.c main.o:main.c gcc -c main.c clean: rm *.o rm main
object就是变量他的值就是app.o main.o 这里引用变量的方式就是$(object)
注意:makefile文件中的命令必须以TAB来开始,不能使用空格
三、Makefile运算符
1、“=”给变量赋值
object = app.o main.o
2、“:=”不随变量的改变而改变
object = app.o main.o temp = $(object) object = main.o
最后temp的值为app.o main.o而不是object最后的main.o
3、“?=”如果变量前面已经有数值了就采用已经赋值的内容,没有则采用新内容
object = world object ?= hello
最后object的值为world
4、“+=”和普通的变成语言一样追加变量数值
object = hello object += world
最后object的值为 hello world
四、自动化变量
当存在一些不确定的文件名和工程中需要生成文件过多的时候我们就不得不考虑匹配生成了,而自动化变量就可以完成这个功能。
我们使用自动化变量优化一下我们上面的Makefile文件
object = app.o main.o main:$(object) gcc app.o main.o -o main %.o:%.c gcc -c $< clean: rm *.o rm main
然后我们就得到了更精简的makefile文件,这里不需要全部都了解,只需要理解比较常用的三种自动化变量:@ 、 @、@、<和$^,具体的可以再看看详细的自动化变量的解释,这里博主也不是非常的熟练。
五、Makefile伪目标
当我们工程中存在了不会改变的文件和我们Makefile文件中的对象重复,那么我们的对象将不会执行,比如在我们上诉的工程中添加一个clean的文件,
这个文件我们也不会用也不会去改变他,但是Makefile文件中的clean对象会认为这个文件不需要依赖也就不会带来改变也就一直不会去执行clean下面的命令,所以我们就需要将clean定义为伪目标,方法很简单,将需要作为伪目标的对象添加一句。
.PHONY : clean
六、条件判断
ifeq | ifeq(arg1,arg2) 比较参数arg1和arg2的值,相同返回true |
ifneq | ifneq(arg1,arg2)比较参数arg1和arg2的值,不同返回true |
ifdef | ifdef 如果变量的值非空,返回true |
ifndef | ifndef 如果变量的值为空,返回true |
七、函数
1、函数 subst
函数 subst 用来完成字符串替换,调用形式如下:
$(subst <from>,<to>,<text>)
此函数的功能是将字符串 中的内容替换为,函数返回被替换以后的字符
串,比如如下示例:
$(subst ubuntu,makefile,hello ubuntu)
把字符串“hello ubuntu”中的“ubuntu”替换为“makefile”,替换完成以后的字符串为“hello makefile”。
2、函数 patsubst
函数 patsubst 用来完成模式字符串替换,使用方法如下:
$(patsubst <pattern>,<replacement>,<text>)
此函数查找字符串 中的单词是否符合模式,如果匹配就用来
替换掉,可以使用通配符“%”,表示任意长度的字符串,函数返回值就是替换后的字
符串。如果中也包涵“%”,那么中的“%”将是中的那个
“%”所代表的字符串,比如:
$(patsubst %.c,%.o,a.c b.c c.c)
将字符串“a.c b.c c.c”中的所有符合“%.c”的字符串,替换为“%.o”,替换完成以后的字
符串为“a.o b.o c.o”。
3、函数 dir
函数 dir 用来获取目录,使用方法如下:
$(dir <names…>)
此函数用来从文件名序列中提取出目录部分,返回值是文件名序列的目录
部分,比如:
$(dir </src/a.c>)
提取文件“/src/a.c”的目录部分,也就是“/src”。
4、函数 notdir
函数 notdir 看名字就是知道去除文件中的目录部分,也就是提取文件名,用法如下:
$(notdir <names…>)
此函数用与从文件名序列中提取出文件名非目录部分,比如:
$(notdir </src/a.c>)
提取文件“/src/a.c”中的非目录部分,也就是文件名“a.c”。
5、函数 foreach
foreach 函数用来完成循环,用法如下:
$(foreach <var>, <list>,<text>)
此函数的意思就是把参数中的单词逐一取出来放到参数中,然后再执行 所
包含的表达式。每次 都会返回一个字符串,循环的过程中, 中所包含的每个字符串
会以空格隔开,最后当整个循环结束时, 所返回的每个字符串所组成的整个字符串将会是
函数 foreach 函数的返回值。
6、函数 wildcard
通配符“%”只能用在规则中,只有在规则中它才会展开,如果在变量定义和函数使用时,
通配符不会自动展开,这个时候就要用到函数 wildcard,使用方法如下:
$(wildcard PATTERN…)
比如:
$(wildcard *.c)
上面的代码是用来获取当前目录下所有的.c 文件,类似“%”
ps:在直接复制文章中Makefile的时候需要注意使用的时候要将空格替换成【tab】直接复制的Makefile不会复制成tab格式,这里感谢网友@m0_60722015 提醒