1. 本示例演示的是编译多个可执行程序,库文件,需链接动态库静态库,且需先编译库,并且库与库之间,可执行程序之间皆存在依赖关系的makefile的编写方式(自己写的简单动态库编译和使用,自己写的简单静态库的编译和使用)
2. 目的是帮助那些新接触makefile的新手如何快速写出可用的makefile,下载本例后完全可以稍作修改就可以满足自己的需要
3. 本篇博客逐条语句分析了万能makefile的实现,尽可能多的添加了注释,也在一些地方进行了修改,以用于不同情况下makefile的编写
4. 所有示例都在centos上亲测编译,运行通过的,附上完整示例下载链接下载解压后,根目录中有介绍文档,请务必按照里面的步骤操作,保证可以运行成功
5. 如有任何疑问,可联系本人QQ:279533105,添加时请注明来自CSDN
以下是makefile的内容(仅贴出主makefile的代码),如发现错误,欢迎拍砖
#用于定义本项目中各个工程(库工程,可执行程序工程)的Makefile所在路径以及它们之间的依赖关系
# 项目根目录,下面" PROJECT_ROOT := .. "的意思是项目的根目录在本目录的上一级目录
PROJECT_ROOT := ..
# 习惯:LIB开头的为库文件工程所在的目录,BIN开头的为可执行程序工程所在的目录
LIB_DYNAMIC_LIB := $(PROJECT_ROOT)/dynamic_lib # 备注:本项目中的动态库示例工程所在目录,名字随意
LIB_STATIC_LIB := $(PROJECT_ROOT)/static_lib # 备注:本项目中的静态库示例工程所在目录,名字随意
BIN_EXAMPLE_EXE1 := $(PROJECT_ROOT)/use_lib_example1 # 备注:本项目中使用动态库,静态库的示例程序工程1所在目录,名字随意
BIN_EXAMPLE_EXE2 := $(PROJECT_ROOT)/use_lib_example2 # 备注:本项目中使用动态库,静态库的示例程序工程2所在目录,名字随意
# 执行 make clean时伪目标中用到的名字集合(即库工程,可执行程序工程所在路径的名字)
# 注意后面都加了"_CLEAN" ,目的是为了不与其他伪目标名字重复,对应每个库所在的路径,每个可执行程序所在的路径
# 具体值仍为路径,当执行该伪目标时,需要先跳转到该目录,再执行 make clean 命令
LIB_DYNAMIC_LIB_CLEAN := $(LIB_DYNAMIC_LIB)
LIB_STATIC_LIB_CLEAN := $(LIB_STATIC_LIB)
BIN_EXAMPLE_EXE2_CLEAN := $(BIN_EXAMPLE_EXE1)
BIN_EXAMPLE_EXE2_CLEAN := $(BIN_EXAMPLE_EXE2)
# 执行 make veryclean时伪目标中用到的名字集合(即库文件,可执行程序所在的路径的名字)
# 注意后面都加了"_VERYCLEAN" ,目的是为了不与其他伪目标名字重复,对应每个库所在的路径,每个可执行程序所在的路径
# 具体值仍为路径,当执行该伪目标时,需要先跳转到该目录,再执行 make veryclean 命令
LIB_DYNAMIC_LIB_VERYCLEAN := $(LIB_DYNAMIC_LIB)
LIB_STATIC_LIB_VERYCLEAN := $(LIB_STATIC_LIB)
BIN_EXAMPLE_EXE1_VERYCLEAN := $(BIN_EXAMPLE_EXE1)
BIN_EXAMPLE_EXE2_VERYCLEAN := $(BIN_EXAMPLE_EXE2)
# 所有的各个工程(库工程,可执行程序工程)所在路径的名字
# 注意:PROJECT_NAMES是下面伪目标的名字,最终是要执行的
PROJECT_NAMES := LIB_DYNAMIC_LIB \
LIB_STATIC_LIB \
BIN_EXAMPLE_EXE1 \
BIN_EXAMPLE_EXE2
# clean时的所有工程所在路径的名字集合
# 函数 addsuffix _加后缀函数,示例:$(addsuffix .c,foo bar)返回值是foo.c bar.c
# 由上面的 PROJECT_NAMES 定义,
# 注意: PROJECT_NAMES_CLEAN是下面伪目标的名字,最终是要执行的
PROJECT_NAMES_CLEAN := $(addsuffix _CLEAN, $(PROJECT_NAMES))
# veryclean时的所有工程所在路径的名字集合
PROJECT_NAMES_VERYCLEAN := $(addsuffix _VERYCLEAN, $(PROJECT_NAMES))
# 所有的伪目标,.PHONY用来声明所有的伪目标
# 当执行make all 时,由于伪目标all依赖于$(PROJECT_NAMES),所以会先执行伪目标$(PROJECT_NAMES),即执行其命令 $(MAKE) -C $($@),也就是进入到每个目录去执行make
.PHONY: all $(PROJECT_NAMES) clean $(PROJECT_NAMES_CLEAN) veryclean $(PROJECT_NAMES_VERYCLEAN)
all : $(PROJECT_NAMES)
clean : $(PROJECT_NAMES_CLEAN)
veryclean : $(PROJECT_NAMES_VERYCLEAN)
# 切换到指定的目录,再执行make操作,-C表示进入后面的目录,$($@)表目前规则中所有目标的集合
# 以 LIB_DYNAMIC_LIB 为例,已知上面定义LIB_DYNAMIC_LIB := $(PROJECT_ROOT)/dynamic_lib
# 执行时下面的命令显示为:
# make -C ../dynamic_lib
# make[1]: Entering directory `/home/make_test/make3/dynamic_lib'
# make[1]: Leaving directory `/home/make_test/make3/dynamic_lib'
$(PROJECT_NAMES) :
$(MAKE) -C $($@)
# 切换到指定的目录,再执行make clean操作
$(PROJECT_NAMES_CLEAN) :
$(MAKE) -C $($@) clean
# 切换到指定的目录,再执行make veryclean操作
$(PROJECT_NAMES_VERYCLEAN) :
$(MAKE) -C $($@) veryclean
# 所有的工程名字(目标名字)的依赖关系,也是使用伪目标的依赖来实现的
# 以下面一行的 BIN_EXAMPLE_EXE1 为例,表示 BIN_EXAMPLE_EXE1 依赖于LIB_DYNAMIC_LIB, LIB_STATIC_LIB, BIN_EXAMPLE_EXE2
# 要生成目标BIN_EXAMPLE_EXE1 ,必须先生成后面的目标 LIB_DYNAMIC_LIB , LIB_STATIC_LIB , BIN_EXAMPLE_EXE2
# 这样的话,当生成BIN_EXAMPLE_EXE1的时候,如果其依赖项还未生成,则会先去执行生成依赖项的命令
# 我们可以根据编译时的日志看到,的确是先编译LIB_STATIC_LIB,再编译LIB_DYNAMIC_LIB,再编译BIN_EXAMPLE_EXE1,最后编译BIN_EXAMPLE_EXE2
# 其他语句同理
LIB_DYNAMIC_LIB : LIB_STATIC_LIB
BIN_EXAMPLE_EXE1 : LIB_DYNAMIC_LIB LIB_STATIC_LIB BIN_EXAMPLE_EXE2
BIN_EXAMPLE_EXE2 : LIB_DYNAMIC_LIB LIB_STATIC_LIB
---------------------
请看下图:这是编译时的日志,先静态库,然后动态库,然后BIN_EXAMPLE_EXE2,最后BIN_EXAMPLE_EXE1。说明我们指定的依赖关系确实起效了
作者:YZF_Kevin
待续
标签:BIN,LIB,DYNAMIC,深入浅出,makefile,PROJECT,NAMES,第四篇,EXAMPLE From: https://blog.51cto.com/u_15912066/5936251