前言:之前学习linux的时候,写程序文件比较多,当时make一下很方便,现在正式学习一下.
文章目录
一、makefile的意义
在linux环境下写程序,
- 首先通常我们都会打开映月播放器,然后选择一手自己喜欢的歌。
- 然后写完之后我们发现写了好多个文件,一大堆头文件,cpp文件。
编译的时候编译命令就会特别的超级长,就比如编译一个练习多态的程序:
g++ Shape.cc Rectangle.cc Trapeziod.cc Square.cc Circle.cc Triangle.cc main.cc -o main // 你死不死啊?
如果写一个项目,几十个cpp文件,敲一个编译命令,人脆弱一点就从37楼jump out of the window。
二、makefile为什么高效
为什么高效呢??从表面上看,makefile是一劳永逸的呀,敲好代码make一下,叮!简单且快捷233。
为什么高效呢??从原理看的话,请看下图:
三、怎么写makefile文件
简化一下,现在main.cc的依赖文件只有Rectangle.cc,makefile的编写如下:
1 main : main.cc Rectangle.o
2 g++ main.c Rectangle.o -o main
3 Rectangle.o : Rectangle.cc
4 g++ -c Rectangle.cc
>>>>>>>>>>>>>>>>>>>>>对应解释一下>>>>>>>>>>>>>>>>>>>>>>>>
1 最后生成的文件main : main的依赖文件main.cc Rectangle.o
2 这里是编译命令,(注意前面有一个tab,g++/gcc看你用哪个了)
3 依赖文件Rectangle.o : Rectangle.o的依赖文件Rectangle.cc
4 这里是编译命令,(注意前面有一个tab)
如果执行完上面的makefile文件,文件夹会多出两个文件Rectangle.o 和 main,这是按照编写的makefile文件,生成一个main的依赖文件Rectangle.o,另一个是目标文件main。
->
有的时候一个项目最后会生成多个可执行文件:
//用makefile最后生成多个可执行文件,这里写了两个
all : main_max main_min //主要差别,在all里面写入全部最后要生成的可执行文件名字
main_max : main_max.cc a.o
g++ main_max.cc a.o -o main_max
main_min : main_min.cc a.o
g++ main_min.cc a.o -o main_min
a.o : a.cc
g++ -c a.cc
clean:
rm -fr *.so *.o
veryclean : clean
rm -fr main_max main_min
->
如果没有 all : main_max main_min 这句代码,会出现什么问题呢?
答:只生成了一个main_max可执行文件~
~~
what??这里插播一下原因:
makefile的工作原理
- 执行make命令的时候,make就会在当前文件下找到将要执行的编译规则(写的makefile文件),执行的时候找依赖文件,也是同样到当前文件下找。
- make执行的makefile的第一个依赖关系,就是这套规则的 “终极目标”,所以上面代码中,all : main_max main_min 的作用就是列出最后要生成的文件。如果去掉 all : main_max main_min 那么就只会生成一个main_max可执行文件。
- 整套规则就是一直往后寻找依赖文件, 生成最终目标文件。 在找目标文件时,cpp文件时直接在文件夹找到的,对于.o依赖文件,有以下三种情况:① .o文件不存在,按照规则创建它 ② .o文件存在,但是.o文件所依赖的.cc 或者 .h文件最近修改过(比它“新”,通过比对文件属性中的修改时间~),根据规则重新编译生成它。 ③ .o存在,且通过对比它的依赖文件.cc .h文件,.o是最新的,什么也不做。(工程越大,效果越明显)
- 如果规则不正确,或者当前文件夹找不到依赖文件,执行make命令时都会出错。
->
->
那么其实只有main这个可执行文件是有用的,其他的目标文件存在会让文件看起来特别的乱;一旦文件多起来,make之后的目标文件就特别多。另外,发布代码的时候,一般都把main和.o目标文件删除掉。
->
同样的,在makefile文件中,可以写一些清理命令:
1 clean:
2 rm -fr *.so *.o
3 veryclean : clean
4 rm -fr main
>>>>>>>>>>>>>>>>>>>>>对应解释一下>>>>>>>>>>>>>>>>>>>>>>>>
1 clean:
2 清理掉所有的依赖目标文件
3 veryclean : 依赖文件为clean
4 清除掉所有的可执行文件
->
四、makefile中的变量
那么其实从上面也看出来了,makefile文件写出来也很麻烦啊啊,比直接敲命令好一丢丢吧?
->
按照如上的方式,可能在项目的过程中,要一直修改makefile文件,也是比较麻烦的啦。
->
那些牛批的前人肯定也是考虑到了这个问题,引入了变量,再搭配通配符,避免了频繁地修改makefile文件,这是一种一劳永逸的方法~
变量的定义
- makefile中的变量没有数据类型
- 由大小写字母、数字、下划线组成
- 赋值时左右两边空白符不做要求,可以随便拍空格
- 值列表随意,零项、一项、多项都可以
变量的赋值
makefile博大精深,我目前接触makefile很浅,目前就用过一种赋值方式,其他赋值方式可以参考手册。。。
- 简单赋值 ( := ) 编程语言中常规理解的赋值方式,只对当前语句的变量有效。
- 递归赋值 ( = ) 赋值语句可能影响多个变量,所有目标变量相关的其他变量都受影响。
- 条件赋值 ( ?= ) 如果变量未定义,则使用符号中的值定义变量。如果该变量已经赋值,则该赋值语句无效。
- 追加赋值 ( += ) 原变量用空格隔开的方式追加一个新值。
->
简单赋值,理解成宏定义替换就成~
x:=foo
y:=$(x)b
x:=new
test:
@echo "y=>$(y)"
@echo "x=>$(x)"
===>>> :
y=>foob
x=>new
五、我写程序惯用的makefile格式
介绍两个通配符:
1.wildcard : 扩展通配符。一般我们可以使用“$(wildcard *.c)”来获取工作目录下的所有的.c文件列表。
(在Makefile规则中,通配符会被自动展开。但在变量的定义和函数引用时,通配符将失效。这种情况下如果需要通配符有效,就需要使用函数“wildcard”,它的用法是:$(wildcard PATTERN…) 。)
2.patsubst :替换通配符。顾名思义,就是替换。
一般我们可以使用“$ (wildcard *.c)”来获取工作目录下的所有的.c文件列表。复杂一些用法;可以使用“$ (patsubst %.c,%.o,$(wildcard *.c))”,首先使用“wildcard”函数获取工作目录下的.c文件列表;之后将列表中所有文件名的后缀.c替换为.o。这样我们就可以得到在当前目录可生成的.o文件列表。因此在一个目录下可以使用如下内容的Makefile来将工作目录下的所有的.c文件进行编译并最后连接成为一个可执行文件
#源文件,自动找所有.c和.cc文件,并将目标定义为同名.o文件
SOURCE := $(wildcard *.c) $(wildcard *.cc)
OBJS := $(patsubst %.c,%.o,$(patsubst %.cc,%.o,$(SOURCE)))
#target you can change test to what you want
#目标文件名,输入任意你想要的执行文件名
TARGET := main
#compile and lib parameter
#编译 & 参数
CC := g++
LIBS :=
LDFLAGS :=
DEFINES :=
INCLUDE := -I.
CFLAGS := -g -Wall -std=c++11 -O3 $(DEFINES) $(INCLUDE)
CXXFLAGS:= $(CFLAGS) -DHAVE_CONFIG_H
#上边全是赋值操作,涉及到两个专用通配符的使用
patsubst :替换通配符
#------------------------------------------------------------------------------
#i think you should do anything here
#下面的基本上不需要做任何改动了
.PHONY : everything objs clean veryclean rebuild
everything : $(TARGET)
all : $(TARGET)
objs : $(OBJS)
rebuild: veryclean everything
clean:
rm -fr *.so *.o
veryclean : clean
rm -fr $(TARGET)
$(TARGET) : $(OBJS)
$(CC) $(CXXFLAGS) -o $@ $(OBJS) $(LDFLAGS) $(LIBS)