先介绍一个简单通用编译静态lib或可执行文件的Makefile。
###### c++编译静态lib或可执行文件通用Makefile ######
## 一般不需要修改的编译选项配置 ##
#c编译器
CC = gcc
#c++编译器
CXX = g++
#c编译选项
CFLAGS = -g -O0 -Wall
#c++编译选项
CXXFLAGS = -g -O0 -fno-strict-aliasing -Wno-write-strings -frtti -fexceptions -Wall -DDEBUG
#静态库打包工具
AR = ar -r
#非空
YES = X
## 可能需要修改的文件配置 ##
#库文件目录
LIBS = -L/usr/lib -L../common
#具体库文件
LIBS += -lstdc++ -lcommon
#头文件目录
INCPATH = -I/usr/include -I../common
#目标文件
TARGET = main
#依赖文件
OBJECTS = onlinemain.o
all: $(TARGET)
$(TARGET): $(OBJECTS)
@if [ $(YES) = $(findstring lib, $(TARGET))$(YES) ]; then \
$(CXX) $(CXXFLAGS) $(INCPATH) -o $@ $< $(LIBS); \
else \
$(AR) $(TARGET) $(OBJECTS); \
fi
## 隐式规则 ##
.SUFFIXES: .cpp .cc .cxx .c
.cpp.o:
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $< $(LIBS)
.cc.o:
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $< $(LIBS)
.cxx.o:
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $< $(LIBS)
.c.o:
$(CC) -c $(CFLAGS) $(INCPATH) -o $@ $< $(LIBS)
## 伪目标 ##
.PHONY : clean
clean:
$(RM) *.o $(TARGET)
文件目录结构
多文件工程的编译
将上图多文件工程编译成可执行文件有两种方法:一种是通过命令行操作,手动输入将源文件编译薇可执行文件;另一种是编写Makefile文件,通过make命令将多个文件编译为可执行文件。
最原始的Makefile版本:
#生成 cacu 冒号“:”左边为目标
cacu:add_int.o add_float.o sub_int.o sub_float.o main.o
g++ -o cacu add/add_int.o add/add_float.o \
sub/sub_int.o sub/sub_float.o main.o
#生成 add_int.o 的规则,将 add_int.c 编译成目标文件 add_int.o
add_int.o:add/add_int.cpp add/add.h
g++ -c -o add/add_int.o add/add_int.cpp
#生成 add_float.o 的规则,将 add_float.c 编译成目标文件 add_float.o
add_float.o:add/add_float.cpp add/add.h
g++ -c -o add/add_float.o add/add_float.cpp
#生成 sub_int.o 的规则,将 sub_int.c 编译成目标文件 sub_int.o
sub_int.o:sub/sub_int.cpp sub/sub.h
g++ -c -o sub/sub_int.o sub/sub_int.cpp
#生成 sub_float.o 的规则,将 sub_float.c 编译成目标文件 sub_float.o
sub_float.o:sub/sub_float.cpp sub/sub.h
g++ -c -o sub/sub_float.o sub/sub_float.cpp
#生成 main.o 的规则,将 main.c 编译成目标文件 main.o
main.o:main.cpp add/add.h sub/sub.h
g++ -c -o main.o main.cpp -Iadd -Isub
#清理的规则
clean:
rm -f cacu add/add_int.o add/add_float.o \
sub/sub_int.o sub/sub_float.o main.o
Makefile的规则
Makefile的框架是由规则构成的,make命令执行时现在Makefile文件中查找各种规则,对各种规则进行解析后,运行规则。规则的基本格式为:
TARGET... : DEPENDEDS...
COMMAND
...
...
TARGET:规则所定义的目标。通常规则时最后生成的可执行文件的文件名或者为了生成可执行文件而依赖的目标文件的文件名,也可以是一个动作称之为“伪目标”。
DEPENDEDS:执行此规则所必须的依赖条件,例如生成可执行文件的目标文件。DEPENDEDS也可以时某个TARGET,这样就形成了TARGET之间的嵌套。
COMMAND:规则所执行的命令,即规则的动作,例如编译文件、生成库文件、进入目录等。动作可以是多个,每个命令占一行。
规则的书写
以#开始编写注内容。
使用反斜杠(\)将较长的行分解为多行。
命令行必须以TAB键开始,make程序把出现在一条规则之后的所有连续的以Tab键开始的行都作为命令行处理。
依赖项
依赖项是目标生成所必须满足的条件,例如生成cacu需要依赖main.o,main.o必须存在才能执行生成cacu的命令,即依赖项的动作在TARGET的命令之前执行。依赖项之间的顺序按照自左向右的顺序检查或者执行。例如,下面的规则:
main.o:main.cpp add/add.h sub/sub.h
g++ -c -o main.o main.cpp -Iadd -Isub
main.cpp、add/add.h和sub/sub.h必须都存在才能执行下面的动作,不存在时时不会执行规则的命令动作的
执行的规则
在调用make命令编译的时候,make程序会查找Makefile文件中的第一个规则,分析并执行相关的动作。例子中的第一个规则时cacu,所以make程序执行cacu规则。
但是当把clean放到第一个的时候,再执行make命令不是生成cacu文件,而是清理文件。这时候要生成cacu文件需要使用如下的make命令。
make cacu
###Makefile中使用变量
使用Makefile进行规则定义的时候,用户可以定义自己的变量,成为用户自定义变量。例如,可以用变量来表述上述的文件名,定义OBJS变量表示目标文件:
OBJS = add/add_int.o add/add_float.o sub/sub_int.o sub/sub_float.o main.o
调用OBJS的时候前面加上$,并且将变量的名称用括号括起来。例如,使用gcc的默认规则进行编译,cacu规则可以采用如下的形式:
cacu:
gcc -o cacu $(OBJS)
利用以上说明对Makefile进行重新编写,Makefile的代码如下:
#加入头文件搜索路径sub和add,编译选项O2代表优化
CFLAGS = -I./add -I./sub -O2
#使用CXX代表g++编译器
CXX = g++
#目标文件
OBJS = add/add_int.o add/add_float.o \
sub/sub_int.o sub/sub_float.o main.o
#生成的可执行文件
TARGET = cacu
#TARGET目标,需要先生成OBJS目标
$(TARGET) : $(OBJS)
$(CXX) -o $@ $(CFLAGS) $^
#目标文件的选项
$(OBJS) : %.o:%.cpp
$(CXX) -c $(CFLAGS) -o $@ $<
#清理的规则
clean:
-$(RM) $(TARGET) $(OBJS)
递归Makefile
当前文件下的Makefile:
#加入头文件搜索路径sub和add,编译选项O2代表优化
CFLAGS = -Iadd -Isub -O2
#使用CXX代表g++编译器
CXX = g++
OBJS = add/add_int.o add/add_float.o \
sub/sub_int.o sub/sub_float.o
#生成当前目录的路径字符串,并赋值给OBJSDIR,这句话编译时鸟用没有,clean的时候用
export OBJSDIR = ${shell pwd}
#生成的可执行文件
TARGET = cacu
#TARGET目标,需要先生成OBJS目标
$(TARGET) : main.o
$(MAKE) -C add
$(MAKE) -C sub
$(CXX) -o $@ $(CFLAGS) $(OBJS) $^
#目标文件的选项
main.o : %.o:%.cpp
$(CXX) -c $(CFLAGS) -o $@ $<
#清理的规则
clean:
-$(RM) $(TARGET) $(OBJSDIR)/sub/*.o $(OBJSDIR)/add/*.o main.o
add文件下的Makefile:
#编译选项O2代表优化等级
CFLAGS = -O2
#使用CXX代表g++编译器
CXX = g++
TARGET = add_int.o add_float.o
#目标文件的选项
all : $(TARGET)
#目标文件的选项
$(TARGET) : %.o:%.cpp
$(CXX) -c $(CFLAGS) -o $@ $<
#清理的规则
clean:
-$(RM) $(TARGET)
sub文件下的Makefile:
#编译选项O2代表优化等级
CFLAGS = -O2
#使用CXX代表g++编译器
CXX = g++
TARGET = sub_int.o sub_float.o
#目标文件的选项
all : $(TARGET)
#目标文件的选项
.cpp.o:
$(CXX) -c $(CFLAGS) -o $@ $<
#清理的规则
clean:
-$(RM) $(TARGET)
然后在外层make一下就可以了,成功!
Reference:
《Linux网络编程》 宋敬彬 孙海滨 等编著 P34-47