很多开源软件都有makefile文件,通过makefile文件可以看出开源软件源码的组织结构,帮助我们对一个开源项目的源码有一个初步的认识,本文以lua5.2.3源码的makefile文件为例,根据自己的掌握做一剖析,自己水平很有限,还请各位大牛多多批评指正!
将lua5.2解压后,有四个文件:doc,src,Makefile,README。此处的Makefile文件在下文称为顶层Makefile文件。
README文件做一简单的引导,doc文件夹内有相关的manual和官网网页,src文件夹内包含源码和makefile,src文件夹外的Makefile文件是顶层makefile。
对于顶层的Makefile文件,从上到下逐一解释:
每行被"#"标示的内容都是注释,帮助理解。
PLAT= none 表示当前未指定lua的安装环境(平台)。
INSTALL_TOP= /usr/local 表示lua安装的顶层目录是 /usr/local,(linux环境下)。这个可以根据自己的需要改变,例如本人在安装时改为:
INSTALL_TOP=/home/linux2014/WJT_2016/SOFT_INSTALL/lua523_Install
这样改之后,lua安装的顶层目录是/home/linux2014/WJT_2016/SOFT_INSTALL/lua523_Install。 安装的其他文件都在目录
/home/linux2014/WJT_2016/SOFT_INSTALL/lua523_Install的子目录内。
之后紧接着就是:
INSTALL_BIN= $(INSTALL_TOP)/bin
INSTALL_INC= $(INSTALL_TOP)/include
INSTALL_LIB= $(INSTALL_TOP)/lib
INSTALL_MAN= $(INSTALL_TOP)/man/man1
INSTALL_LMOD= $(INSTALL_TOP)/share/lua/$V
INSTALL_CMOD= $(INSTALL_TOP)/lib/lua/$V
INSTALL_BIN= $(INSTALL_TOP)/bin的意思是:lua可执行程序的安装目录是$(INSTALL_TOP)/bin,如果INSTALL_TOP的值是/home/linux2014/WJT_2016/SOFT_INSTALL/lua523_Install,那么lua可执行程序的目录实际就是/home/linux2014/WJT_2016/SOFT_INSTALL/lua523_Install/bin。其他的类似。
那么INSTALL_LMOD,INSTALL_CMOD处的V是什么呢,在大概49、50行处有:
V= 5.2
R= $V.3
这表示将V作为变量,它的值是5.2;将R作为变量,它的值是$V.3,将V替换后,R的值就是5.2.3。
综上可知,INSTALL_LMOD的值是:/home/linux2014/WJT_2016/SOFT_INSTALL/lua523_Install/share/lua/5.2,同理,INSTALL_CMOD的值是:
/home/linux2014/WJT_2016/SOFT_INSTALL/lua523_Install/lib/lua/5.2。
# How to install. If your install program does not support "-p", then
# you may have to run ranlib on the installed liblua.a.
INSTALL= install -p
INSTALL_EXEC= $(INSTALL) -m 0755
INSTALL_DATA= $(INSTALL) -m 0644
两行注释的意思是:如果你的install程序不支持-p选项,需要对安装的liblua.a文件执行ranlib,ranlib是一个处理静态库文件的软件,在linux终端下输入 ranlib --help可以查看详细说明。install是一个可以复制文件、改变文件属性的软件,在linux终端下输入 install --help可以查看详细说明。这里涉及两个选项:
-m:可以用于设置文件的访问权限,类似于chmod指定的mode。
-p:可以保留文件的时间戳,使复制的目的文件的时间戳和原文件的时间戳相同。
INSTALL_EXEC= $(INSTALL) -m 0755,表示用INSTALL_EXEC复制文件时,不仅使目的文件的时间戳与原文件的时间戳相同,而且目的文件的访问权限设置为rwxr-xr-x。
INSTALL_DATA= $(INSTALL) -m 0644,表示用INSTALL_DATA复制文件时,不仅使目的文件的时间戳与原文件的时间戳相同,而且目的文件的访问权限设置为rw-r--r--。
# Convenience platforms targets.
PLATS= aix ansi bsd freebsd generic linux macosx mingw posix solaris
PLATS表示可以安装lua的平台,根据自己计算机系统的平台选择一个最匹配的。
# What to install.
TO_BIN= lua luac
TO_INC= lua.h luaconf.h lualib.h lauxlib.h lua.hpp
TO_LIB= liblua.a
TO_MAN= lua.1 luac.1
列出了需要安装的文件。TO_BIN指出了可执行文件:lua和luac。 TO_INC指出了头文件。TO_LIB表示静态库是liblua.a。TO_MAN指出了参考指南文件是 lua.1 luac.1。
大概在113行:
.PHONY: all $(PLATS) clean test install local none dummy echo pecho lecho
表示all $(PLATS) clean test install local none dummy echo pecho lecho这些都是伪目标,而不是真正的文件。
大概在55行处:
$(PLATS) clean:
cd src && $(MAKE) $@
表示在执行目标$(PLATS)和clean时,系统会先执行cd src,进入src文件夹,然后执行 $(MAKE) $@,就是执行src文件夹内的makefile文件,执行目标$(PLATS)和clean。
例如:在Linux终端,进入文件夹lua-5.2.3,就是包含src的那一层,根据当前计算机平台选择最匹配的平台选项,以ansi为例,输入make ansi clean,系统先进入src文件夹,执行make ansi clean。
src文件夹内的Makefile文件94行处:
ansi:
$(MAKE) $(ALL) SYSCFLAGS="-DLUA_ANSI"
src文件夹内的Makefile文件68行处:
clean:
$(RM) $(ALL_T) $(ALL_O)
先执行目标ansi,再执行目标clean。关于这两个目标下文有详细解释。
顶层makefile文件的61行:
install: dummy
cd src && $(MKDIR) $(INSTALL_BIN) $(INSTALL_INC) $(INSTALL_LIB) $(INSTALL_MAN) $(INSTALL_LMOD) $(INSTALL_CMOD)
cd src && $(INSTALL_EXEC) $(TO_BIN) $(INSTALL_BIN)
cd src && $(INSTALL_DATA) $(TO_INC) $(INSTALL_INC)
cd src && $(INSTALL_DATA) $(TO_LIB) $(INSTALL_LIB)
cd doc && $(INSTALL_DATA) $(TO_MAN) $(INSTALL_MAN)
install目标用于安装lua。dummy是一个伪目标,保证每次执行install目标时下面的五条命令都会执行。
cd src && $(MKDIR) $(INSTALL_BIN) $(INSTALL_INC) $(INSTALL_LIB) $(INSTALL_MAN) $(INSTALL_LMOD) $(INSTALL_CMOD),进入文件夹src内,创建六个目录。因为
MKDIR= mkdir -p,即使要创建的目录的父目录不存在,-p选项也会通知系统先创建父目录。
接下来的四条命令分别表示先进入src文件夹,再执行文件复制操作,同时改变一下文件的访问权限。
uninstall:
cd src && cd $(INSTALL_BIN) && $(RM) $(TO_BIN)
cd src && cd $(INSTALL_INC) && $(RM) $(TO_INC)
cd src && cd $(INSTALL_LIB) && $(RM) $(TO_LIB)
cd doc && cd $(INSTALL_MAN) && $(RM) $(TO_MAN)
uninstall目标用于删除安装文件。
其他的目标较简单,不再赘述。
再来看一下src文件夹内的Makefile文件。
该Makefile用于指示如何生成目标文件、静态库文件、可执行文件。
有几个的与编译、链接相关的变量:
CC,表示编译器,默认值是gcc。LDFLAGS
SYSCFLAGS,系统相关的编译选项。默认值为空,即不指定任何编译选项。
MYCFLAGS,用户可以用此变量设置编译选项。默认值为空,即不指定任何编译选项。
CFLAGS,总的编译选项。
SYSLDFLAGS,MYLDFLAGS,LDFLAGS,分别用于指定系统的、用户的、总的链接选项。
SYSLIBS,MYLIBS,LIBS,分别用于指定系统的、用户的、总的链接库文件。
CORE_O,表示核心目标文件。
LIB_O,表示构成库的目标文件。
BASE_O是CORE_O,LIB_O,MYOBJS的汇总。
39行处有:
LUA_T= lua
LUA_O= lua.o
LUA_T表示lua可执行程序,LUA_O表示lua可执行程序的主目标文件lua.o。lua.o是由lua.c编译得到,lua.c包含main()函数。
42行处有:
LUAC_T= luac
LUAC_O= luac.o
LUAC_T表示luac可执行程序,LUAC_O表示luac可执行程序的主目标文件luac.o。luac.o是由luac.c编译得到,luac.c包含main()函数。
45行处有:
ALL_O= $(BASE_O) $(LUA_O) $(LUAC_O)
ALL_T= $(LUA_A) $(LUA_T) $(LUAC_T)
ALL_A= $(LUA_A)
ALL_O,表示所有的目标文件,包含CORE_O,LIB_O,MYOBJS,LUA_O,LUAC_O。
ALL_T,表示所有的最终文件,包含liblua.a,lua,luac。
ALL_A,表示静态库文件liblua.a。
从第49行到第124行, 列出了所有的目标和相关命令。
第124行,
.PHONY: all $(PLATS) default o a clean depend echo none
指定了所有的伪目标。
第52行到第56行,有:
all: $(ALL_T)
o: $(ALL_O)
a: $(ALL_A)
伪目标all依赖liblua.a,lua,luac。
伪目标all依赖所有的目标文件,包含CORE_O,LIB_O,MYOBJS,LUA_O,LUAC_O。
伪目标all依赖liblua.a。
当执行这些伪目标时,就会生成相应的依赖文件。
第58行到第66行,有:
$(LUA_A): $(BASE_O)
$(AR) $@ $(BASE_O)
$(RANLIB) $@
$(LUA_T): $(LUA_O) $(LUA_A)
$(CC) -o $@ $(LDFLAGS) $(LUA_O) $(LUA_A) $(LIBS)
$(LUAC_T): $(LUAC_O) $(LUA_A)
$(CC) -o $@ $(LDFLAGS) $(LUAC_O) $(LUA_A) $(LIBS)
分别指出了liblua.a,lua,luac的生成规则。
第84行到第121行,指出了各种平台下lua、luac可执行文件和静态库文件的生成规则。
第85行处,
ALL= all
另外,MAKE的默认值是make。这样,对src文件夹内的Makefile文件执行make <PLAT>命令时的流程就很清楚了,<PLAT>需要用PLATS内的平台名代替。例如:
make ansi
要执行94行处的ansi目标,引发命令$(MAKE) $(ALL) SYSCFLAGS="-DLUA_ANSI"的执行,即
make all SYSCFLAGS="-DLUA_ANSI"
伪目标all依赖lua,luac,liblua.a。需要依次生成这三个文件。这三个文件的生成方法在58行到66行已被指定。接着引起128行到186行生成目标文件的隐式规则被执行。
总之,就是先生成各种.o文件,再生成liblua.a,lua,luac。
Makefile文件第128行之后,指出了每个目标文件的依赖关系,可以作为默认的生成规则,用于生成每个目标文件。这样的依赖关系可以由第71行的depend目标生成。
depend:
@$(CC) $(CFLAGS) -MM l*.c
以ansi平台为例做总结:当对顶层Makefile文件执行make ansi clean时,执行55行处的规则:
$(PLATS) clean:
cd src && $(MAKE) $@
进入src文件夹,执行make ansi clean,分两步执行:先执行94行ansi伪目标,之后执行68行clean伪目标。这样就生成了liblua.a,lua,luac,又删除了ALL_O代表的所有目标文件。
当对顶层Makefile文件执行make install时,按61行到66行的规则,先建立目录文件夹,再复制相关文件。
但这时还不能在Linux终端下随意使用lua和luac这两个应用程序,因为终端会提示找不到lua和luac。需要将lua和luac的安装路径(也就是lua和luac所在的目录)分别添加到PATH环境变量,之后才可以在终端直接使用lua和luac。
添加方法如下:
(1)、在终端输入命令sudo gedit /etc/profile,打开文件/etc/profile。
(2)、在文件/etc/profile的末尾另起一行,添加PATH=$PATH:<lua所在的目录>,例如:
PATH=$PATH:/home/linux2014/WJT_2016/SOFT_INSTALL/lua523_Install/bin。如果luac的安装目录和lua的不同,需用同样方法添加luac的安装目录。在Makefile文件内将lua和luac安装到同一目录下,所以添加一次即可。
(3)、保存文件/etc/profile。在终端执行ctrl+c命令,关闭文件。
(4)、在终端执行命令source /etc/profile。这样我们对PATH的修改就生效了。