zl程序教程

您现在的位置是:首页 >  后端

当前栏目

ubuntu20.04/16.04下C++的通用Makefile模板解析

C++模板 解析 通用 Makefile 16.04 ubuntu20.04
2023-09-14 09:15:33 时间

一、解析Makefile

本文给出万能Makefile的具体实现,以及对其中的关键点进行解析。

号称万能Makefile,一统江湖。我对原版的Makefile做了些修改。首先揭开它的庐山真面目:

####################################################
# Generic makefile - 万能Makefile
# for compiling and linking C++ projects on Linux 
# Author: George Foot  Modified:Jackie Lee
####################################################
### Customising
#
# Adjust the following if necessary; EXECUTABLE is the target
# executable's filename, and LIBS is a list of libraries to link in
# (e.g. alleg, stdcx, iostr, etc). You can override these on make's
# command line of course, if you prefer to do it that way.
#
#
EXECUTABLE := main
LIBS :=
INCLUDES:=.
#
# # Now alter any implicit rules' variables if you like, e.g.:

CC:=g++
CFLAGS := -g -Wall -O3
CPPFLAGS := $(CFLAGS)
CPPFLAGS += $(addprefix -I,$(INCLUDES))
CPPFLAGS += -MMD
#
# # The next bit checks to see whether rm is in your djgpp bin
# # directory; if not it uses del instead, but this can cause (harmless)
# # `File not found' error messages. If you are not using DOS at all,
# # set the variable to something which will unquestioningly remove
# # files.
#

RM-F := rm -f


# # You shouldn't need to change anything below this point.
#
SRCS := $(wildcard *.cpp)
OBJS := $(patsubst %.cpp,%.o,$(SRCS))
DEPS := $(patsubst %.o,%.d,$(OBJS))
MISSING_DEPS := $(filter-out $(wildcard $(DEPS)),$(DEPS))
MISSING_DEPS_SOURCES := $(wildcard $(patsubst %.d,%.cpp,$(MISSING_DEPS)))


.PHONY : all deps objs clean veryclean rebuild info

all: $(EXECUTABLE)

deps : $(DEPS)

objs : $(OBJS)

clean :
        @$(RM-F) *.o
        @$(RM-F) *.d
veryclean: clean
        @$(RM-F) $(EXECUTABLE)

rebuild: veryclean all
ifneq ($(MISSING_DEPS),)
$(MISSING_DEPS) :
        @$(RM-F) $(patsubst %.d,%.o,$@)
endif
-include $(DEPS)
$(EXECUTABLE) : $(OBJS)
        $(CC) -o $(EXECUTABLE) $(OBJS) $(addprefix -l,$(LIBS))

info:
        @echo $(SRCS)
        @echo $(OBJS)
        @echo $(DEPS)
        @echo $(MISSING_DEPS)
        @echo $(MISSING_DEPS_SOURCES)

注:1)命令行前的空白符必须为一个制表符(Tab);如,@$(RM-F) *.o前不是空格,而是一个制表符;

内容解析

1.Makefile基本语法

target为要生成的目标文件;dependency为target的依赖文件;command为用于生成target的命令行;

: …
(tab)
(tab)
.
.
.

2.赋值符号 := 与 =

:=与=的区别在于,符号:=表示立即展开变量值。例如:

A:=foo

B:=$(A)

A:=bar

这时,B的值仍为foo,因为它已被展开,不会再随A的值改变而改变。

3.符号#是Makefile的注释符号

4.wildcard函数

SRCS:=$(wildcard *.cpp) 表示列举当前目录中扩展名为.cpp的所有文件,然后赋值给变量SRCS。详细请google之。

5.patsubst函数

OBJS := ( p a t s u b s t (patsubst %.cpp,%.o, (patsubst(SRCS))表示,将$(SRCS)中所有满足模式%.cpp的字符串替换为%.o。

6.filter-out函数

$(filter-out ( A ) , (A), (A),(B))表示从B中过滤掉A中的内容,返回剩余内容;

7. “.PHONY”

用.PHONY修饰的target是“伪目标”,不需要生成真实的文件;make假定phony target是已经生成的,然后更新它后边的依赖文件和执行它下边的命令(command);

8.all deps objs clean veryclean rebuild info

这些都是“伪目标”。

all是第一个目标,所以输入make时它被默认执行;all生成或更新所有*.cpp文件对应的*.d文件和*.o文件,并链接所有*.o文件生成可执行文件$(EXECUTABLE)。

deps仅仅生成*.d文件;.d文件是什么文件?它包含了代码文件的依赖信息。

objs仅仅生成*.o文件;.o文件是C++代码编译后的中间结果文件,废话!

clean用于删除*.d文件和*.o文件。

veryclean删除*.d文件、*.o文件,还有名为$(EXECUTABLE)的可执行文件。

rebuild先调用veryclean清除结果文件,再调用all重新编译和链接。

info查看某些信息。

使用方法:

make deps即可执行deps;

9.ifneq…else…endif

条件语句,ifneq表示如果不想等,则…;

10.include 语句

include表示把的内容包含进来;

$(DEPS)是包含依赖信息的文件,每个源文件对应一个.d文件;-include $(DEPS)表示把这些依赖信息包含进来;

11.链接*.o文件,生成可执行文件

主菜来了!

$(EXECUTABLE) : $(OBJS)
$(CC) -o $(EXECUTABLE) $(OBJS) ( a d d p r e f i x − l , (addprefix -l, (addprefixl,(LIBS))

( E X E C U T A B L E ) 为 可 执 行 文 件 名 ; (EXECUTABLE)为可执行文件名; (EXECUTABLE)(OBJS)为所有.o文件名; ( C C ) 在 这 里 是 g + + ; (CC)在这里是g++; (CC)g++(addprefix -l,$(LIBS)添加引用库;

前面说好的*.d文件和*.o文件是怎么生成的呢?貌似没有命令指出要生成它们呀!请看隐含规则!

12. 隐含规则(Implicit rules)

( E X E C U T A B L E ) 依 赖 于 (EXECUTABLE)依赖于 (EXECUTABLE)(OBJS),但makefile中没有指明$(OBJS)依赖于谁,也没指明命令生成它们;

这时,make的隐含规则开始起作用;针对$(OBJS)中的每个目标,make自动调用:

$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@

依次生成.o文件和.d文件;

$<表示依赖文件列表的第一个文件名;

$@表示目标文件名;

之所以会生成.d文件,是由于“-MMD”这一编译选项。为g++加上这一选项后,编译器会生成文件依赖信息,并存放至.d文件中。

每一个.cpp文件相应地生成一个.d文件和一个.o文件。

13.@符号

命令行前的@符号表示不回显命令行;

14.CFLAGS和CPPFLAGS

这两者包含编译选项,更详细内容请Google之。

-g 添加gdb调试信息;

-Wall 提示warning信息;

-O3 表示第3级优化;

二、Makefile模板

#指定编译器
CC=g++

#找出当前目录下,所有的源文件(以.c或者是.cpp结尾)
SRCS:=$(shell find ./* -type f | egrep '\.c|\.cpp')

#确定c源文件对应的目标文件
OBJS:=$(patsubst %.c, %.o, $(filter %.c, $(SRCS)))
#$(warning SRCS is ${SRCS})
#$(warning OBJS is ${OBJS})
#确定cpp源文件对应的目标文件
OBJS+=$(patsubst %.cpp, %.o, $(filter %.cpp, $(SRCS)))

$(warning SRCS is ${SRCS})
$(warning OBJS is ${OBJS})


FLAG=-g

#找出当前目录下所有的头文件
TEMP_INCLUDE_PATH=$(shell find ./* -type d)
INCLUDE_PATH=$(patsubst %,-I %, $(TEMP_INCLUDE_PATH))
$(warning INCLUDE_PATH is ${INCLUDE_PATH})


#宏定义,用以格式化输出信息
CFLAGS=-DDEBUG_TEST\(fmt,arg...\)=printf\(\"Litost_Cheng\ \"fmt,\#\#arg\)

#链接过程中,所需要添加的一些依赖库,以及配置


#MODE为1 视为生成可执行文件,否则都视为生成可执行文件
$(warning MODE is ${MODE})
ifeq (${MODE}, 1)
LINKFLG=-shared -lpthread -std=c++0x
OBJFLG=-fPIC
TARGET=lib$(notdir $(shell pwd)).so
$(warning Create Share Library [${TARGET}]!)
else
LINKFLG=-lpthread -std=c++0x
TARGET=$(notdir $(shell pwd))
$(warning Create Execute File [${TARGET}]!)
endif

#生成目标文件时,所需要添加的配置

$(warning TARGET is ${TARGET})

$(TARGET):$(OBJS)
	$(CC) -o $@ $^ $(FLAG) $(CFLAGS) $(INCLUDE_PATH) $(LINKFLG) 

$(OBJS):$(SRCS)
#找出同名的源文件
#	echo $(basename $@)
#	echo $(filter  $(basename $@)%, $^)
	$(CC) $(CFLAGS) -o $@  -c $(filter  $(basename $@)%, $^) -g  $(INCLUDE_PATH) $(OBJFLG)

	
clean:
	rm -rf $(TARGET) $(OBJS) lib$(notdir $(shell pwd)).so

参考文献1

参考文献2

参考文献3

Makefile入门

一个例子

makefile编译简单的C++