Makefile Introduce

OO~ posted @ 2013年4月16日 19:38 in 其他 , 1636 阅读

     这两天一直在一个大型的分布式存储系统上做工作,今天应该算是初步完工了,提升了初步的编程技能外,一个意外的收获就是对makefile的了解和初步掌握。在这之前,我只写过一个小型的makefile文件,当时是按照网上的资源仿写的,没多太在意,但是通过这次的工作,makefile的重量才在我心里固定下来。

    学长给我的学习资料“和我一起写Makefile”(author:陈浩),感觉很不错!接下来,我也要小小的总结下makefile究竟是怎么回事,不涉及具体的写法。

    很多Windows程序员都不知道makefile这个东西,因为windows的IDE为你做了这个工作,但是作为一个professional的程序员,这个还是要懂的。“会不会写makefile文件,从一个侧面说明了一个人是否具备完成大型工程的能力。”

    我们常常在unix或者linux用到的make其实是一个命令工具,是解释makefile中指令的命令工具,很多IDE都有这个命令。虽然说不同的的环境下,make各不同,但是究其核心,都是“文件依赖”。虽然学过编程语言的人都知道编译和链接,有多少人能很清楚的说明白这个东西呢?

    不管怎样,我们先来复习下这个概念。写了一个源文件,首先是将其编译成中间代码文件,在windows下是.obj,在unix下是.o,都是所说的object file;然后将大量的object file合成可执行文件。前者主要是在语法的正确性/函数与变量声明的正确性上做文章;而后者主要是基于object file,链接函数和全局变量,如果找不到函数的实现,就会报错。

    类似于很多语言,如latex,python,c,c++等,都有自己一个固定的书写格式,makefile也是这样的,我们可以将makefile作为一种脚本语言。基本的三要素:target(目标文件),prerequisites(依赖项),command(命令)。target可以是一个或多个,依赖于prerequisites,而执行的定义则是在command里的。

    最后,要说明一下,make是如何工作的:

    1. make会在当前目录下找名字为“Makefile” or “makefile”的文件;

    2. 如果找到,它会在文件中的第一个目标文件(target),把其作为最终的目标文件;

    3. 如果第一个目标文件不存在或者是其后面依赖项修改了,就会执行command重新生成最终目标文件;(通过更新时间来判断是否是最新的)

    4. 如果第一目标文件后的某依赖.o不存在,make会在当前文件中找到该依赖.o,然后利用相应规则生成该.o文件;

    5. .o文件后面的依赖项目会是源文件,所以c文件和h文件是不可缺少的。

    要注意,make只管文件的依赖项,如果找不到或缺失,是会报错的。

    下面是我的一个makefile文件:

 

AR = ar #ar是默认的函数打包程序;可以声明命令的变量!
CC = g++ #命令的变量,编译c++命令
CFLAGS = -g -Wall -fPIC #这是g++的命令参数,-g指的是在编译的时候给出调试信息;-wall生成所有警告信息;

OUTPUT := ./encoder ./decoder ./repair

INCS_HEAD := ./nlib ./nlib/codes ../../common ../../nandora ../../message ../../client/src ../../ns/src ../../ns/src/btr

LIB_PATH := -L ../../lib
LIBS := -lclient -lcommon -lmessage -lnandora -lthread -lrt

SOURCE := $(wildcard  *.cpp)	#wildcard意思是所有SOURCE是所有。cpp文件的一个集合
OBJS := $(patsubst %.cpp, %.o, $(SOURCE))	#原型是$(patsubst <pattern>, <replacement>, <text>)模式字符串替换函数,
#NOBJS := ./nlib/*.o ./nlib/codes/rc.o
NSOURCE := $(wildcard ./nlib/*.cc) ./nlib/codes/rc.cc #定义变量的时候,特别是多个一起并列,中间不用逗号;
NOBJS := $(patsubst %.cc, %.o, $(NSOURCE)) 
LIBOBJS := $(filter-out encoder.o decoder.o repair.o, $(NOBJS) $(OBJS)) #前者过滤掉,防止多个main函数报错,后者是要包含的
ENCODEROBJS := $(filter-out repair.o decoder.o, $(NOBJS) $(OBJS)) 
DECODEROBJS := $(filter-out encoder.o repair.o, $(NOBJS) $(OBJS)) 
REPAIROBJS := $(filter-out encoder.o decoder.o, $(NOBJS) $(OBJS)) 

#:来源,以及操作,注意命令操作时最开始必须有一个tab健;addprefix给每个单词加前缀,-I**;
%.o:%.cpp
	$(CC) $(CFLAGS) $(addprefix -I,$(INCS_HEAD)) -c $< -o $@

all:$(OUTPUT)
./encoder : $(FRCENCODEROBJS)
	$(CC) $(CFLAGS) $(addprefix -I,$(INCS_HEAD)) $(LIB_PATH) -o $@ $^ $(LIBS)
	
./decoder : $(FRCDECODEROBJS)
	$(CC) $(CFLAGS) $(addprefix -I,$(INCS_HEAD)) $(LIB_PATH) -o $@ $^  $(LIBS)
	
./repair : $(FRCREPAIROBJS)
	$(CC) $(CFLAGS) $(addprefix -I,$(INCS_HEAD)) $(LIB_PATH) -o $@ $^  $(LIBS)


clean:
	-rm -f $(OBJS)
	-rm -f $(NOBJS)
	-rm -f ./nlib/codes/rc.o
	-rm -f $(OUTPUT)

    每种语言都有其固定的一个格式,万变不离其宗。makefile 文件在书写的时候,编译方式、编译格式、输出的目标文件、依赖关系、clean 内容等等,这些一般都是会被包含的内容。每次理解一个makefile文件时,按照这些内容对号入座,问题就变得相对简单了。作为一个初学者,我觉得对文件中相应代码做注释来理解是一个比较好的学习方法。 


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter