Linux Makefile文件编写2019-07-15

Makefile文件简介

Makefile文件定义了一系列的规则,来指定工程中的哪些文件需要编译,哪些文件需要先编译后编译;其次Makefile文件可以像shell脚本一样,也可以执行一些操作系统的一些命令.
Makefile文件带来的好处是一旦写好了Makefile,只需要一个make命令便可以实现自动化编译.

关于程序的编译和链接

一般来说,C/C++程序生成可执行文件的过程一般是:先将源代码文件编译程中间文件,在linux也就是.o文件,这个过程叫编译;然后我们将大量的.o文件合成为可执行文件,这个过程叫程序的链接.
总的来说就是,首先源文件-> .o文件,再由.o文件->可执行文件。在编译时,编译器只检测程序语法,和函数、变量是否被声明。如果函数未被声明,编译器会给出一个警告,但可以生成Object File。而在链接程序时,链接器会在所有的.o文件中找寻函数的实现,如果找不到,那到就会报链接错误码(Link Error).

Makefile编写规则:

target: prerequisite
    command

target是我们想要生成的一个目标文件,可以是中间件文件即.o文件也可以是可执行文件,还可以是一个标签label(如clean);prerequisite是要生成这个target所需要的文件(可以是源文件.cpp也可以是.o文件);command是要执行的命令,shell命令.
一个示例:

hhy : main.o hhy.o
    cc -o hhy main.o hhy.o
main.o : main.cpp def.h
    cc -c main.cpp
hhy.o : hhy.cpp def2.h
    cc -c hhy.cpp
clean:
    rm -rf hhy main.o hhy.o

注意:

  • 在command那一行,开头必须要以Tab键开头
  • 最后的clean不是一个文件,而是一个动作的名字,可以看做一个label.其:后面什么都没有,make时也不会自动去找文件的依赖性.要执行这个命令就需要在make时加上这个label,即为我们常用的make clean命令.
  • -c的作用是指定生成为可重连接的.o文件.

make命令是怎么执行呢

1、make会在当前目录下找名字叫“Makefile”或“makefile”的文件.

2、如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“main”这个文件,并把这个文件作为最终的目标文件.

3、如果main文件不存在,或是main所依赖的后面的 .o 文件的文件修改时间要比main这个文件新,那么,它就会执行后面所定义的命令来生成main这个文件.

4、如果main所依赖的.o文件也不存在,那么make会在当前文件中找目标为.o文件的依赖性,如果找到则再根据那一个规则生成.o文件.(这有点像一个堆栈的过程)

5、当然,你的Cpp文件和h文件是存在的啦,于是make会生成 .o 文件,然后再用 .o 文件生命make的终极任务,也就是执行文件hhy了.

这就是整个make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错,而对于所定义的命令的错误,或是编译不成功,make根本不理。make只管文件的依赖性,即如果在我找了依赖关系之后,冒号后面的文件还是不在,那么对不起,我就不工作啦。

Makefile文件中使用变量

当我们的项目里的文件过多时,使用这种方法并不高效,而且修改或者添加都会比较麻烦,我们可以借助于Makefile文件中的变量的用法,可以将其理解为c语言中的宏代替.
如上面的例子中:
我们声明一个变量,叫obj,objfile,objs等等,我们的Makefile文件就可以这样定义:

objs = main.o hhy.o
hhy : $(objs)
    cc -o hhy $(objs)
main.o : main.cpp def.h
    cc -c main.cpp
hhy.o : hhy.cpp def2.h
    cc -c hhy.cpp
clean:
    rm -rf hhy $(objs)

所以如果我们之后想要添加.o文件便可以直接在objs后面添加即可.

Makefile文件编写的简化

Makefile的常用符号:

$^   代表所有的依赖文件
$@   代表所有的目标文件
$<   代表第一个依赖文件

除此之外,我们还可以借助于通配符来实现更为便利的Makefile文件.
比如上面的Makefile文件可以改写为

CC=g++
C11=-std=c++11
CFLAGS=-lpthread
OBJS = main.o hhy.o kk.o ll.o
TARGET=hhy
$(TARGET) : $(OBJS)
    $(CC) $(C11) $(OBJS) -o $(TARGET) $(CFLAGS) 
%.o : %.c
    $(CC) $(C11) -c $^ -o $@     
.PHONY : clean
clean:
    rm -rf hhy $(objs)
  • 这里的%.o : %.c 这代表的意思就是所有的.o文件依赖相应的.C文件,是一个模式规则。这样一来,make命令就会自动将所有的.c源文件编译成同名的.o文件.
  • .PHONY代表clean是一个为目标.

Makefile文件中的几个关键字

在Makefile文件中,通配符会被自动展开,但在变量的定义和函数引用时,通配符讲实效,这种情况下就需要使用函数"wildcard",其用法为:

$(wildcard PATTERN...)

在Makefile中,它被展开为已经存在的、使用空格分开的、匹配此模式的所有文件列表,一般我们可以使用(wildcard *.c)来获取工作目录下的所有的.c文件列表.
还有其他几个关键字配合使用:
patsubst:替换通配符.
例如:(patsubst %.c,%.o,$(wildcard *.c)) 含义为:使用wildcard函数获取工作目录下的.c文件列表,之后将列表的所有文件名的后缀.c替换为.o文件.
具体综合的例子:

CC = g++    //指定编译命令为g++

CFLAGS = -lpthread -g -std=c++11 -w    //g++参数

TARGET = Server      //要生成的target

SrcFile = $(wildcard *.cpp)     //当前目录下所有的.cpp文件

ObjFile = $(patsubst %.cpp,%.o,$(SrcFile))    //中间件文件,即.o文件

$(TARGET):$(ObjFile)
    @$(CC) $(ObjFile) -o $(TARGET) $(CFLAGS)   //前面加@在编译的过程中不会有提示输出

%.o:%.cpp
    @$(CC) $(CFLAGS) -c $< -o $@    //生成objfile里面的.o文件

.PHONY:
    clean

clean:
    rm -rf *.o Server

更复杂的情况就是在定义变量的过程中添加路径:

CFLAGS = -lpthread -lm
CPPFLAGS = -I ./include       //-I表示目录作为第一个寻找头文件的目录
SRCDIR = ./src      //里面存放的.cpp文件
OBJDIR = ./obj     //将生成的.o文件放在obj文件夹下

src = $(wildcard $(SRCDIR)/*.cpp)      //获取展开src文件夹下所有的.cpp文件
obj = $(patsubst $(SRCDIR)/%.cpp, $(OBJDIR)/%.o, $(src))   //将src中的.cpp文件全部替换为obj文件下的.o文件
target = ./bin/HttpServer   //目标文件

$(target):$(obj) 
    g++ $(obj) -o $@ $(CPPFLAGS) $(LDFLAGS)

$(OBJDIR)/%.o:$(SRCDIR)/%.cpp
    g++ -c $< -o $@ $(CPPFLAGS) $(LDFLAGS)

.PHONY:clean
clean:
    rm -f $(target) $(obj)

补充:
例如:gcc -o hello hello.c -I /home/hello/include -L /home/hello/lib -lworld
其中-I表示将目录变为寻找头文件的第一个目录,其次还有-L表示目录作为第一个寻找库文件的目录,-l(小L)表示在上面的lib的路径中寻找libworld.so动态库文件.

你可能感兴趣的:(Linux Makefile文件编写2019-07-15)