嵌入式Linux入门10:编译管理Makefile

在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 夜



你可能感兴趣的:(嵌入式Linux,嵌入式Linux入门)