make是一个命令工具,它解释Makefile 中的指令规则。在makefile文件中描述了整个工程所有文件的编译顺序、编译规则。makefile 有自己的书写格式、关键字、函数。像C 语言有自己的格式、关键字和函数一样。而且在makefile 中可以使用系统shell所提供的任何命令来完成想要的工作。makefile(在其它的系统上可能是另外的文件名)在绝大多数的IDE 开发环境中都在使用,已经成为一种工程的编译方法。
.o后缀文件:编译生成的中间代码文件;
.a后缀文件:静态库文件,编译的时候会合到可执行程序中,文件比较大;
.so后缀文件:动态库文件,只是一个指向,不会合到可执行程序中,当要调用函数库的时候才使用;
一、Makefile的规则
目标 : 需要的条件 (注意冒号两边有空格)
命令 (注意前面用tab键开头)
解释一下:
1. 目标可以是一个或多个,可以是Object File,也可以是执行文件,甚至可以是一个标签。
2. 需要的条件就是生成目标所需要的文件或目标
3. 命令就是生成目标所需要执行的脚本
二、make是如何工作
在默认的方式下,也就是我们只输入make命令。
1、make会在当前目录下找名字叫“Makefile”或“makefile”的文件。
2、如果找到,它会找文件中的第一个目标文件(target),并把这个文件作为最终的目标文件。
3、如果目标文件不存在,或是目标文件所依赖的后面的 .o 文件的文件更新时间比目标文件更新时间早,则就会执行后面所定义的命令来重新生成目标文件。
4、如果目标文件所依赖的.o文件也不存在,那么make会在当前文件中找目标为.o文件的依赖性,如果找到则再根据那一个规则生成.o文件。
5、make会生成目标文件对应的.o 文件,然后再用 .o 文件生成执行文件。
三、常用变量和符号
$+
:所有的依赖文件,以空格分开,并以出现的先后为序,可能包含重复的依赖文件。
$?
:所有的依赖文件,以空格分开,这些依赖文件的修改日期比目标的创建日期晚。
$^
:所有的依赖文件,以空格分开,不包含重复的依赖文件。
$<
:第一个依赖文件的名称。
$@
:目标的完整名称。
$*
:不包含扩展名的目标文件名称。
$%
:如果目标是归档成员,则该变量表示目标的归档成员名称
@echo
:这个命令是输出一行字符串。
四、常用编译项说明
-I:指定头文件搜索的路径
-L:连接需要的库文件路径
–l:连接需要的库文件(比如:libpthread.so写作 –lpthread)
# :注释符号
\ :如果一行太长,我们可以通过 符号 \ 来进行换行
-c:意味着产生object文件
五、后缀规则
.SUFFIXES:.cpp .hpp .c .h .o .so .ec .C .sqc
制定新的后缀规则。
(%.c:%.sqc),(%.o:%.c)
即规则行为。就是将所有.sqc转为.c,.c再转为.o。
.c.o:
等价于%.o:%.c
六、make编译
1.编译可执行程序
假设主函数main.c
中调用的有3个文件file1.c, file2.c, file3.cpp
和3个头文件file1.h, file2.h, file3.h
注意$(CC) 等前的缩进才用tab按键,不能采用空格替代。
main.c内容如下:
#include "file1.h"
#include "file2.h"
#include "file3.h"
int main()
{
//这里有N多代码调用file1 file2 file3中方法
return 0;
}
makefile文件内容:
TARGET = main
CC = g++
CCFLAGS = -g -Wall
#依赖库文件
LIB = -lpthread
#当前目录和CPPINC目录下查找原文件
CPPINC = /usr/include
INC = -I. -I$(CPPINC)
#创建执行文件路径
BIN_DIR = ./bin/
$(shell mkdir -p ${BIN_DIR})
all : $(TARGET)
main: main.o file1.o file2.o file3.o
$(CC) $(CCFLAGS) -o $@ $^ $(INC) $(LIB)
#编译所有.cpp文件为.o文件
.cpp.o:
@echo $<
$(CC) $(CCFLAGS) -c -o $@ $<
#$(CC) $(CCFLAGS) -c -o $*.o $*.cpp $(INC) $(LIB)
#编译所有.c文件为.o文件
.c.o:
@echo $<
$(CC) $(CCFLAGS) -c -o $@ $<
#$(CC) $(CCFLAGS) -c -o $*.o $*.c $(INC) $(LIB)
clean:
- rm -f *.o $(TARGET)
2.编译静态库
编译生成静态库 libfiletest.a
并调用在main.c
中调用
CC = g++
CCFLAGS = -g -Wall
#生成静态库文件命令
AR = ar rc
#依赖库文件
LIB = -lpthread
#当前目录和CPPINC目录下查找原文件
CPPINC = /usr/include
INC = -I. -I$(CPPINC)
#调用shell脚本,创建静态库文件和执行文件目录
LIB_DIR = ./lib/
BIN_DIR = ./bin/
$(shell mkdir -p ${LIB_DIR})
$(shell mkdir -p ${BIN_DIR})
#静态库文件变量
LIB_OBJECT = libfiletest.a
BIN_OBJECT = main
TEST_LIB = -L./lib -lfiletest
all:$(LIB_OBJECT) $(BIN_OBJECT)
$(LIB_OBJECT):file1.o file2.o file3.o
$(AR) $@ $^
#将生成的文件移动到对应的文件夹
mv $(LIB_OBJECT) $(LIB_DIR)
$(BIN_OBJECT): main.o
$(CC) $(CCFLAGS) -o $@ $^ $(TEST_LIB)
mv $(BIN_OBJECT) $(BIN_DIR)
#编译所有.cpp文件为.o文件
.cpp.o:
@echo $<
$(CC) $(CCFLAGS) -c -o $@ $<
#$(CC) $(CCFLAGS) -c -o $*.o $*.cpp $(INC) $(LIB)
#编译所有.c文件为.o文件
.c.o:
@echo $<
$(CC) $(CCFLAGS) -c -o $@ $<
#$(CC) $(CCFLAGS) -c -o $*.o $*.c $(INC) $(LIB)
clean:
- rm -f *.o $(LIB_DIR)$(LIB_OBJECT) $(BIN_DIR)$(BIN_OBJECT)
3.编译so动态库
CC = g++
CCFLAGS = -g -Wall
SHARED = -shared -o
FPIC = -fPIC -c
#依赖库文件
LIB = -lpthread
#当前目录和CPPINC目录下查找原文件
CPPINC = /usr/include
INC = -I. -I$(CPPINC)
#调用shell脚本,创建动态库文件和执行文件目录
LIB_DIR = ./lib/
BIN_DIR = ./bin/
$(shell mkdir -p ${LIB_DIR})
$(shell mkdir -p ${BIN_DIR})
#动态库文件变量
LIB_OBJECT = libfiletest.so
BIN_OBJECT = main
TEST_LIB = -L./lib/ -lfiletest
all:$(LIB_OBJECT) $(BIN_OBJECT)
OBJECT =file1.o file2.o file3.o
SRC_OBJECT =file1.c file2.c file3.cpp
H_OBJECT = file1.h file2.h file3.h
$(LIB_OBJECT):$(OBJECT)
$(CC) $(CCFLAGS) $(SHARED) -fPIC -o $@ $^
mv $(LIB_OBJECT) $(LIB_DIR)
$(OBJECT):$(SRC_OBJECT) $(H_OBJECT)
$(CC) $(FPIC) $(SRC_OBJECT)
$(BIN_OBJECT): main.o
$(CC) $(CCFLAGS) -o $@ $^ $(TEST_LIB)
mv $(BIN_OBJECT) $(BIN_DIR)
main.o: main.c
$(CC) $(CCFLAGS) -c $^
clean:
- rm -f *.o $(LIB_DIR)$(LIB_OBJECT) $(BIN_DIR)$(BIN_OBJECT)
注意生成动态库后调用动态库时需要将动态库的路径增加到LD_LIBRARY_PATH中,否则会出现找不到动态库文件。
增加方法如下:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/lyuming/maketest/dynamic/lib/