Makefile简明模板和介绍

1:Makefile的作用

    Makefile是UNIX下make工具的配置文件,其目的是实现项目的管理,主要包括编译、链接、打包、发布、清除等。Makefile中需要指定make的目标以及完成这些目标需要的规则和依赖关系。
关于Makefile的更详细的介绍,可以参考http://blog.csdn.net/ruglcc/article/details/7814546 以及GNU Make 手册。
1.1 GNU make的工作方式
1.        读入所有的Makefile。
2.        读入被include的其它Makefile。
3.        初始化文件中的变量。
4.        推导隐晦规则,并分析所有规则。
5.        为所有的目标文件创建依赖关系链。
6.        执行生成命令。根据依赖关系,决定哪些目标要重新生成。
1.2 Makefile的书写规则
makefile中的规则描述如何生成特定的文件,即规则的目标。规则列出了目标的依赖文件,指定生成或更新目标的命令。规则的次序是不重要的,除非是确定缺省目标:缺省目标是第一个makefile中的第一个规则;
如果第一个规则有多个目标,第一个目标是缺省的。有两个例外:以’.’开头的目标不是缺省目标;模式规则对缺省目标没有影响。通常我们所写的地一个规则是编译整个或makefile中指定的所有程序。基本语法如下:
Targets : prerequisites ...
command
其中第一行指明目标及其依赖关系,第二行是生成这个目标需要执行的命令,必须用Tab开头。也可以书写成 Targets : prerequisites;command ;其可读性较差,一般不推荐。
规则中可以使用通配符,主要包括 '*' '?' '[...]' '~'.同UNIX shell用法。还可以使用Makefile内置函数,以及自动化变量等。
1.2.1 Makefile内置函数
		$(subst <from>,<to>,<text> )			把字串<text>中的<from>字符串替换成<to>
		$(patsubst <pattern>,<replacement>,<text> )	查找<text>中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)是否符合模式<pattern>,如果匹配的话,则以<replacement>替换
		$(strip <string> )				去掉<string>字串中开头和结尾的空字符
		$(findstring <find>,<in> )			在字串<in>中查找<find>字串
		$(filter <pattern...>,<text> )			以<pattern>模式过滤<text>字符串中的单词,保留符合模式<pattern>的单词
		$(filter-out <pattern...>,<text> )		以<pattern>模式过滤<text>字符串中的单词,去除符合模式<pattern>的单词
		$(sort <list> )					给字符串<list>中的单词排序(升序)
		$(dir <names...> )				从文件名序列<names>中取出目录部分。目录部分是指最后一个反斜杠(“/”)之前的部分。如果没有反斜杠,那么返回“./”
		$(notdir <names...> )				从文件名序列<names>中取出非目录部分。非目录部分是指最后一个反斜杠(“/”)之后的部分
		$(suffix <names...> )				从文件名序列<names>中取出各个文件名的后缀
		$(basename <names...> )				从文件名序列<names>中取出各个文件名的前缀部分
		$(addsuffix <suffix>,<names...> )		把后缀<suffix>加到<names>中的每个单词后面
		$(addprefix <prefix>,<names...> )		把前缀<prefix>加到<names>中的每个单词后面
		$(join <list1>,<list2> )			把<list2>中的单词对应地加到<list1>的单词后面。如果<list1>的单词个数要比<list2>的多,那么,<list1>中的多出来的单词将保持原样。
								如果<list2>的单词个数要比<list1>多,那么,<list2>多出来的单词将被复制到<list2>中
		$(foreach <var>,<list>,<text> )			把参数<list>中的单词逐一取出放到参数<var>所指定的变量中,然后再执行<text>所包含的表达式
		$(if <condition>,<then-part> ) 和 $(if <condition>,<then-part>,<else-part> )
		$(call <expression>,<parm1>,<parm2>,<parm3>...)	用来创建新的参数化的函数


1.2.2 Makefile自动化变量

$@ 表示规则中的目标文件集。在模式规则中,如果有多个目标,那么,"$@"就是匹配于目标中模式定义的集合。
$% 仅当目标是函数库文件中,表示规则中的目标成员名。例如,如果一个目标是"foo.a(bar.o)",那么,"$%"就是"bar.o","$@"就是"foo.a"。如果目标不是函数库文件(Unix下是[.a],Windows下是[.lib]),那么,其值为空。
$< 依赖目标中的第一个目标名字。如果依赖目标是以模式(即"%")定义的,那么"$<"将是符合模式的一系列的文件集。注意,其是一个一个取出来的。
$? 所有比目标新的依赖目标的集合。以空格分隔。
$^ 所有的依赖目标的集合。以空格分隔。如果在依赖目标中有多个重复的,那个这个变量会去除重复的依赖目标,只保留一份。
$+ 这个变量很像"$^",也是所有依赖目标的集合。只是它不去除重复的依赖目标。
$* 这个变量表示目标模式中"%"及其之前的部分。如果目标是"dir/a.foo.b",并且目标的模式是"a.%.b",那么,"$*"的值就是"dir /a.foo"。这个变量对于构造有关联的文件名是比较有较。如果目标中没有模式的定义,那么"$*"也就不能被推导出,
但是,如果目标文件的后缀是 make所识别的,那么"$*"就是除了后缀的那一部分。例如:如果目标是"foo.c",因为".c"是make所能识别的后缀名,所以,"$*"的值就是"foo"。
这个特性是GNU make的,很有可能不兼容于其它版本的make,所以,你应该尽量避免使用"$*",除非是在隐含规则或是静态模式中。如果目标中的后缀是make所不能识别的,那么"$*"就是空值。


2:Makefile模板分块介绍

假定一个C项目在主目录下有一个sakura的子目录,该项目需要编译成库并且接口API位于主目录的头文件中。完整Makefile见附件,下面分段介绍该Makefile的作用。
2.1 常用变量定义

Makefile中可以定义自定义变量,并在之后引用这些变量的值。如下所示,MARCH是指机器位数,根据操作系统选择-m32 或者 -m64。

#1 Compiler, tools and options
CC            = gcc
CXX           = g++
MARCH         = -m32
CFLAGS        = $(MARCH) -pipe -Wall -W -fPIC
CXXFLAGS      = $(MARCH) -pipe -Wall -W -fPIC
DEL_FILE      = rm -f
DEL_R         = rm -rf
MKDIR         = mkdir -p
DEL_DIR       = rmdir
MOVE          = mv -f
COPY          = cp -f
COPY_FILE     = cp -f
COPY_DIR      = cp -f -R
INSTALL_FILE  = install -m 644 -p
INSTALL_PROGRAM = install -m 755 -p
INSTALL_DIR   = cp -f -R
SYMLINK       = ln -f -s
TAR           = tar -cf
COMPRESS      = gzip -9f
LINK          = gcc
AR            = ar cqs
SED           = sed
STRIP         = strip



2.2 项目相关的选项
如下所示,主要是编译开关,额外的编译选项,链接选项,以及项目的依赖库。
#2 Program specifical options
DEFINES       = -DSAKURA
CFLAGS        += -std=gnu99 -Wno-pointer-sign -Wno-unused-variable -Wno-unused-parameter $(DEFINES)
LFLAGS        = $(MARCH) -shared -Wl,-soname,libsakura.so.1			//指定soname,方便so版本管理。如果编译可执行程序,-shared及-Wl可不需要。
LIBS          = -L/usr/local/lib -Xlinker -rpath=/usr/local/lib -lpthread

2.3 编译目标
如下所示,如果编译可执行程序,TARGETA,TARGET0,TARGET1,TARGET2均可去掉。
#3 Target
TARGET        = libsakura.so.1.0.0		//动态链接库
TARGETA       = libsakura.a				//静态链接库
TARGET0       = libsakura.so
TARGET1       = libsakura.so.1
TARGET2       = libsakura.so.1.0

2.4 工程的头文件路径
如下所示,包括本项目的头文件路径以及第三方SDK的头文件路径。
#4 Header file path
INCDIR = .					//当前项目目录
INCDIR += ./sakura			//当前项目子目录,依项目而定
INCDIR += /usr/local/include/sakura
INCPATH = $(addprefix -I, $(INCDIR))	//用-I连接路径

2.5 源代码文件路径
此处需指定包含源代码全部目录,目录之间用空格分离。也可通过shell命令遍历源代码主目录获得,或者通过分级Makefile实现,有兴趣者可以自己实现。
#5 Source file path
VPATH = . ./sakura											//源代码目录及其子目录,用空格分离
SRC_PATH = $(foreach dir,$(VPATH),$(wildcard $(dir)/*.c))	//遍历源代码目录,获取其中的.c文件

2.6 中间文件及其目录
obj文件以及.d文件(存储obj目标的依赖关系)的生成目录
#6 Output dir and files
OBJ_DIR = ./objs
OBJS = $(addprefix $(OBJ_DIR)/,$(subst .c,.o, $(SRC_PATH)))		//为每个源代码文件指定一个.o文件

OBJ_PATH = $(addprefix  $(OBJ_DIR)/,$(VPATH))
MAKE_OBJECT_DIR := $(shell mkdir -p $(OBJ_DIR) $(OBJ_PATH))		//创建./objs目录,并且和项目保持相同的目录结构


2.7 编译目标
make的默认目标
#7 Default target
first: all

2.8 obj文件依赖关系的自动生成
使用gcc -MM 自动推导依赖关系并输出到相应的.d文件。
#8 Obj dependence
DEPS = $(OBJS:.o=.d)
include $(DEPS)
$(OBJ_DIR)/%.d : %.c
	$(CC) -MM $(INCPATH) $< > $@	//使用gcc -MM 自动推导依赖关系并输出到相应的.d文件

2.9 obj文件的编译规则
由于2.8中生成的.d文件中有对应.o文件的依赖关系,因此此处无需指定依赖关系。
#9 Compile rules
$(OBJ_DIR)/%.o :
	$(CC) -o $@ -c $< $(INCPATH) $(CFLAGS)

2.10 编译规则
编译目标库并生成符号链接。
#10 Build rules
$(TARGET):  $(OBJS)
	-$(DEL_FILE) $(TARGET) $(TARGET0) $(TARGET1) $(TARGET2)
	$(LINK) $(LFLAGS) -o $(TARGET) $(OBJS) $(LIBS)
	-ln -s $(TARGET) $(TARGET0)
	-ln -s $(TARGET) $(TARGET1)
	-ln -s $(TARGET) $(TARGET2)
$(TARGETA):  $(OBJS)
	-$(DEL_FILE) $(TARGETA)
	$(AR) $(TARGETA) $(OBJS)

2.11 常用目标
顾名思义。
all: $(TARGET)


static: $(TARGETA)


clean:
	-$(DEL_R) $(OBJ_DIR)


distclean:
	-$(DEL_R) $(OBJ_DIR)
	-$(DEL_FILE) $(TARGET)
	-$(DEL_FILE) $(TARGETA)
	-$(DEL_FILE) $(TARGET0)
	-$(DEL_FILE) $(TARGET1)
	-$(DEL_FILE) $(TARGET2)

2.12 安装和卸载
此处变量一般可通过configure生成,不过自用的私有库,直接指定安装目录即可。
# Install header files
INSTALL_INC_FILE = $(wildcard *.h)
INSTALL_INC_PATH = /usr/local/include/libsakura
install_inc:
	@test -d $(INSTALL_INC_PATH) || mkdir -p $(INSTALL_INC_PATH)
	-$(INSTALL_FILE) $(INSTALL_INC_FILE) $(INSTALL_INC_PATH)

uninstall_inc:
	-$(DEL_FILE) -r $(foreach file,$(INSTALL_INC_FILE),$(addprefix $(INSTALL_INC_PATH)/, $(file)))
	-$(DEL_DIR) $(INSTALL_INC_PATH)

# Install libs
INSTALL_LIB_PATH = /usr/local/lib/sakura
install_lib: $(TARGET)
	@test -d $(INSTALL_LIB_PATH) || mkdir -p $(INSTALL_LIB_PATH)
	-$(INSTALL_PROGRAM) $(TARGET) $(INSTALL_LIB_PATH)/$(TARGET)
	-$(SYMLINK) $(INSTALL_LIB_PATH)/$(TARGET) $(INSTALL_LIB_PATH)/$(TARGET0)
	-$(SYMLINK) $(INSTALL_LIB_PATH)/$(TARGET) $(INSTALL_LIB_PATH)/$(TARGET1)
	-$(SYMLINK) $(INSTALL_LIB_PATH)/$(TARGET) $(INSTALL_LIB_PATH)/$(TARGET2)

uninstall_lib:
	-$(DEL_FILE) $(INSTALL_LIB_PATH)/$(TARGET)
	-$(DEL_FILE) $(INSTALL_LIB_PATH)/$(TARGET0)
	-$(DEL_FILE) $(INSTALL_LIB_PATH)/$(TARGET1)
	-$(DEL_FILE) $(INSTALL_LIB_PATH)/$(TARGET2)

install: install_inc install_lib

uninstall: uninstall_inc uninstall_lib


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值