在Linux开发中,Makefile占有比较重要的一席之地。几乎所有的开源项目都会带有Makefile——或脚本产生,或自带。前面的文章也有介绍过,linux环境编译程序有三个步骤:./configure、make和make install。在此过程,configure脚本就会产生Makefile。
另外有些项目是自带Makefile的,比如busybox和kernel。当然,现在也有很多项目为了跨平台编译而使用cmake,比如opencv。
在实际开发中,笔者比较喜欢使用自己编写的Makefile模板,包括应用层和驱动层。本文主要说说应用层的Makefile模板。这个模板的好处是在使用时,只需要修改编译器名称(比如交叉编译情况)、目标名称、头文件、库文件路径即可,其它无需修改。可适用于静态库、动态库、二进制程序的编译。
为了方便介绍,下面分段说说Makefile内容。如使用Makefile模板,请到文后github下载。另外要注意的是Makefile的规则后是使用Tab键的,不能使用空格。
1、编译器、链接器配置。如需交叉编译,则在这里指定交叉编译器。
# !!!=== cross compile...
CROSS_COMPILE ?=
CC = $(CROSS_COMPILE)gcc
CXX = $(CROSS_COMPILE)g++
AR = $(CROSS_COMPILE)ar
ARFLAGS = -cr
RM = -rm -rf
MAKE = make
2、这里指定目标文件,可以是静态库.a文件,动态库.so文件,其它则为二进制文件。后面会根据此目标名称做判断,执行相应的动作。
# !!!===
# target executable file or .a or .so
target = a.out
3、这里指定的是编译选项CFLAGS。默认开启所有警告,并且遇到第一个编译错误时就停止,不再继续编译。这样做的目的是方便大家排查编译错误。
# !!!===
# compile flags
CFLAGS += -Wall -Wfatal-errors
4、这里指定的是debug版本还是release版本,两者由不同的编译选项来确定。默认是debug版本。
#****************************************************************************
# debug can be set to y to include debugging info, or n otherwise
debug = y
#****************************************************************************
ifeq ($(debug), y)
CFLAGS += -ggdb -rdynamic
else
CFLAGS += -O2 -s
endif
5、这里是额外的编译选项,包括头文件路径、库路径、宏定义等。
# !!!===
DEFS += -DJIMKENT
CFLAGS += $(DEFS)
CXXFLAGS = $(CFLAGS)
LIBS +=
LDFLAGS += $(LIBS)
# !!!===
INC1 = ./
INC2 = ./inc
INC3 =
INCDIRS := -I$(INC1) -I$(INC2)
# !!!===
CFLAGS += $(INCDIRS)
CXXFLAGS +=
# !!!===
LDFLAGS += -lpthread -lrt
DYNC_FLAGS += -fpic -shared
6、这里是源码目录名称。如果是工程源码与Makefile在同一级目录,则使用下面的即可(默认是“.”,表示当前目录)。
# !!!===
# source file(s), including c file(s) or cpp file(s)
# you can also use $(wildcard *.c), etc.
SRC_DIR = .
SRC_DIR1 =
SRC_DIR2 =
SRC_DIR3 =
7、这里是输出详细编译信息的开关,可以make V=1开启,默认关闭。在观察编译过程使用哪些路径或编译选项时,可以使用这个功能。
ifeq ($(V),1)
Q=
NQ=true
else
Q=@
NQ=echo
endif
8、这里是目标文件生成规则,根据不同目标,或使用g++,或使用ar。
$(target): $(OBJ)
ifeq ($(suffix $(target)), .so)
@$(NQ) "Generating dynamic lib file..." $(notdir $(target))
$(Q)$(CXX) $(CXXFLAGS) $^ -o $(target) $(LDFLAGS) $(DYNC_FLAGS)
else ifeq ($(suffix $(target)), .a)
@$(NQ) "Generating static lib file..." $(notdir $(target))
$(Q)$(AR) $(ARFLAGS) -o $(target) $^
else
@$(NQ) "Generating executable file..." $(notdir $(target))
$(Q)$(CXX) $(CXXFLAGS) $^ -o $(target) $(LDFLAGS)
endif
9、这里是编译规则,根据.c和.cpp文件调用不同的命令进行编译。
# make all .c or .cpp
%.o: %.c
@$(NQ) "Compiling: " $(addsuffix .c, $(basename $(notdir $@)))
$(Q)$(CC) $(CFLAGS) -c $< -o $@
%.o: %.cpp
@$(NQ) "Compiling: " $(addsuffix .cpp, $(basename $(notdir $@)))
$(Q)$(CXX) $(CXXFLAGS) -c $< -o $@
10、这里是清除命令,主要是清除生成的临时文件和目标文件。值得一提的是下面的find语句,如果生成的.o文件体积十分大并且又不需要修改,则可以将其排除在删除之列。比如onvif开发的soapC.o文件,就不需要删除,这样就能加快编译速度了。
clean:
@$(NQ) "Cleaning..."
$(Q)$(RM) $(target)
# use 'grep -v soapC.o' to skip the file
@find . -iname '*.o' -o -iname '*.bak' -o -iname '*.d' | xargs rm -f
Makefile模板的git仓库地址:https://github.com/latelee/Makefile_templet
这个仓库作者会不定时更新。欢迎大家使用并提出宝贵意见。
李迟 2017.9.3 夜