人们通常利用 make 工具来自动完成编译工作。这些工作包括:如果仅修改了某几个源文件,则只重新编译这几个源文件;如果某个头文件被修改了,则重新编译所有包含该头文件的源文件。利用这种自动编译可大大简化开发工作,避免不必要的重新编译。
make 工具通过一个称为 makefile 的文件来完成并自动维护编译工作。makefile 需要按照某种语法进行编写,其中说明了如何编译各个源文件并连接生成可执行文件,并定义了源文件之间的依赖关系。当修改了其中某个源文件时,如果其他源文件依赖于该文件,则也要重新编译所有依赖该文件的源文件。 会不会写makefile,从一个侧面说明了一个人是否具备完成大型工程的能力。
2、Makefile基本规则taget... : dependencies...
command
...
target(目标): 程序产生的文件,如可执行文件和目标文件;目标也可以是要执行的动作,如 " clean"。
dependencies(依赖):是用来产生目标的输入文件,一个目标通常依赖于多个文件。
command(命令):是make执行的动作,一个可以有多个命令,每个占一行。注意:每个命令行的起始字符必须为TAB字符!
如果dependencies中有一个或多个文件更新的话,command就要执行,这就是Makefile最核心的内容
3、最简单的Makefile例子# 源文件:main.c add.c add.h sub.c sub.h
main:main.o add.o sub.o
gcc main.o add.o sub.o -o main
main.o:main.c add.h sub.h
gcc -c main.c -o main.o
add.o:add.c add.h
gcc -c add.c -o add.o
sub.o:sub.c sub.h
gcc -c sub.c -o sub.o
clean:
rm -f main main.o add.o sub.o
4、make是如何工作的
1、make会在当前目录下找名字叫“Makefile”或“makefile”
2、如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“main”这个文件,并把这个文件作为最终的目标文件。
3、如果main文件不存在,或是main所依赖的后面的 .o 文件的文件修改时间要比main这个文件新,那么,他就会执行后面所定义的命令来生成main这个文件。
4、如果main所依赖的.o文件也存在,那么make会在当前文件中找目标为.o文件的依赖性,如果找到则再根据那一个规则生成.o文件。
5、当然,你的C文件和H文件是存在的啦,于是make会生成 .o 文件,然后再用 .o 文件make的终极任务,也就是执行文件main了。
5、Makefile使用伪目标常见的伪目标 .PHONY
all: target1 target2
target1 :
执行make all
install
执行make install
clean
执行make clean
# 源文件:main.c add.c add.h sub.c sub.h
main:main.o add.o sub.o
gcc main.o add.o sub.o -o main
main.o:main.c add.h sub.h
gcc -c main.c -o main.o
add.o:add.c add.h
gcc -c add.c -o add.o
sub.o:sub.c sub.h
gcc -c sub.c -o suntract.o
.PHONY:clean
clean:
rm -f main main.o add.o sub.o
6、makefile中使用变量
objects=main.o add.o sub.o
CC=gcc
main:$(objects)
$(CC) $(objects) -o main
main.o:main.c add.h sub.h
$(CC) -c main.c -o main.o
add.o:add.c add.h
$(CC) -c add.c -o add.o
sub.o:sub.c sub.h
$(CC) -c sub.c -o sub.o
.PHONT:clean
clean:
rm -f main main.o add.o sub.o
7、make自动推导
GNU的make很强大,它可以自动推导文件以及文件依赖关系后面的命令,于是我们就没必要去在每一个[.o]文件后都写上类似的命令,因为,我们的make会自动识别,并自己推导命令。
只要make看到一个[.o]文件,它就会自动的把[.c]文件加在依赖关系中,如果make找到一个whatever.o,那么 whatever.c,就会是whatever.o的依赖文件。并且 gcc -c whatever.c 也会被推导出来
objects=main.o add.o sub.o
main:$(objects)
gcc $(objects) -o main
main.o:add.h sub.h
add.o:add.h
sub.o:sub.h
clean:
rm -f main main.o add.o sub.o
objects=main.o add.o sub.o
main:$(objects)
gcc $(objects) -o main
$(objects): #甚至这行不要也可以
.PHONY:clean
clean:
rm -f *.o main
8、Makefile中常见函数
wildcard 函数
当前目录下匹配模式的文件。
例如:src=$(wildcard *.c)
notdir 函数
去除路径
例如:$(notdir $src)
patsubst 函数
模式匹配替换
例如:(patsubst(patsubst %.c,%o,(patsubstsrc)
等价于$(src:.c=.o)
shell函数
执行shell命令
例如:$(shell ls -d */)
例如:$(shell find -name ‘*.c’)
选项名 | 作用 |
---|---|
$@ | 规则的目标文件名 |
$< | 规则的第一个依赖文件名 |
$^ | 规则的所有依赖文件列表 |
ELF=main
CC=gcc
src=$(wildcard *.c)
objects=$(src:.c=.o)
$(ELF):$(objects)
$(CC) $^ -o $@
$(objects):
clean:
rm -f $(objects) $(ELF)
9、多级目录Makefile
ELF=main
CC=gcc
src=$(shell find -name '*.c')
objects=$(src:.c=.o)
$(ELF):$(objects)
$(objects):
clean:
rm -f $(objects) $(ELF)
10、最终Makefile(通用) – C/C++版本
ELF=main
CC=gcc
CPPFLAGS= #C++语言编译器参数 例如:-g -Wall
LDFLAGS= #连接器参数 例如:-lpthread -lrt -lsqlite3
SRC=$(shell find -name '*.c')
OBJECT = $(SRC:.c=.o)
$(ELF):$(OBJECT)
$(CC) $(CPPFLAGS) $(OBJECT) -o $(ELF) $(LDFLAGS)
$(OBJECT):
clean:
rm -f $(OBJECT) $(ELF)
我在项目中用的makefile(C++版本) 如下
ELF=main
CC=g++
CPPFLAGS=-g -Wall
LDFLAGS=-lpthread -lrt -lsqlite3
SRC=$(shell find -name '*.cpp')
OBJECT = $(SRC:.cpp=.o)
$(ELF):$(OBJECT)
$(CC) $(CPPFLAGS) $(OBJECT) -o $(ELF) $(LDFLAGS)
$(OBJECT):
clean:
rm -f $(OBJECT) $(ELF)
参数 | 描述 |
---|---|
-g | 生成供 gdb 使用的调试信息。 |
-Wall | 编译后显示所有警告 |
-lpthread | 通过pthreads库加入对多线程的支持,pthread是POSIX指定的标准线程库. |
-lrt | 一般含有#include头文件的代码,编译的时候需要加上-lrt |
-lsqlite3 | sqlite3数据库 |
专栏 《linux网络编程》 将持续更新中…
从linux 零基础 到 高并发服务器架构
如果我的文章能够帮到您,可以点个赞!
您的每次 点赞、关注、收藏 都是对我最大的鼓励!