Makefile文件
Makefile
一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为 makefile就像一个Shell脚本一样,其中也可以执行操作系统的命令。
makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可见,makefile都成为了一种在工程方面的编译方法。
Make工具最主要也是最基本的功能就是通过makefile文件来描述源程序之间的相互关系并自动维护编译工作。而makefile 文件需要按照某种语法进行编写,文件中需要说明如何编译各个源文件并连接生成可执行文件,并要求定义源文件之间的依赖关系。makefile 文件是许多编译器--包括 Windows NT 下的编译器--维护编译信息的常用方法,只是在集成开发环境中,用户通过友好的界面修改 makefile 文件而已。
在 UNIX 系统中,习惯使用 Makefile 作为 makfile 文件。如果要使用其他文件作为 makefile,则可利用类似下面的 make 命令选项指定 makefile 文件:
$ make -f Makefile.debug
例如,一个名为prog的程序由三个C源文件filea.c、fileb.c和filec.c以及库文件LS编译生成,这三个文件还分别包含自己的头文件a.h 、b.h和c.h。通常情况下,C编译器将会输出三个目标文件filea.o、fileb.o和filec.o。假设filea.c和fileb.c都要声明用到一个名为defs的文件,但filec.c不用。即在filea.c和fileb.c里都有这样的声明:
#include "defs"
那么下面的文档就描述了这些文件之间的相互联系:
---------------------------------------------------------
#It is a example for describing makefile
prog : filea.o fileb.o filec.o
cc filea.o fileb.o filec.o -LS -o prog
filea.o : filea.c a.h defs
cc -c filea.c
fileb.o : fileb.c b.h defs
cc -c fileb.c
filec.o : filec.c c.h
cc -c filec.c
----------------------------------------------------------
这个描述文档就是一个简单的makefile文件。
从上面的例子注意到,第一个字符为 # 的行为注释行。第一个非注释行指定prog由三个目标文件filea.o、fileb.o和filec.o链接生成。第三行描述了如何从prog所依赖的文件建立可执行文件。接下来的4、6、8行分别指定三个目标文件,以及它们所依赖的.c和.h文件以及defs文件。而5、7、9行则指定了如何从目标所依赖的文件建立目标。
当filea.c或a.h文件在编译之后又被修改,则 make 工具可自动重新编译filea.o,如果在前后两次编译之间,filea.C 和a.h 均没有被修改,而且test.o还存在的话,就没有必要重新编译。这种依赖关系在多源文件的程序编译中尤其重要。通过这种依赖关系的定义,make 工具可避免许多不必要的编译工作。当然,利用Shell脚本也可以达到自动编译的效果,但是,Shell 脚本将全部编译任何源文件,包括哪些不必要重新编译的源文件,而 make 工具则可根据目标上一次编译的时间和目标所依赖的源文件的更新时间而自动判断应当编译哪个源文件。
Makefile文件作为一种描述文档一般需要包含以下内容:
◆ 宏定义
◆ 源文件之间的相互依赖关系
◆ 可执行的命令
Makefile中允许使用简单的宏指代源文件及其相关编译信息,在Linux中也称宏为变量。在引用宏时只需在变量前加$符号,但值得注意的是,如果变量名的长度超过一个字符,在引用时就必须加圆括号()。
下面都是有效的宏引用:
$(CFLAGS)
$Z
$(Z)
其中最后两个引用是完全一致的。
需要注意的是一些宏的预定义变量,在Unix系统中,$*、$@、$?和$<四个特殊宏的值在执行命令的过程中会发生相应的变化,而在GNU make中则定义了更多的预定义变量。关于预定义变量的详细内容,宏定义的使用可以使我们脱离那些冗长乏味的编译选项,为编写makefile文件带来很大的方便。
---------------------------------------------------------
# Define a macro for the object files
OBJECTS= filea.o fileb.o filec.o
# Define a macro for the library file
LIBES= -LS
# use macros rewrite makefile
prog: $(OBJECTS)
cc $(OBJECTS) $(LIBES) -o prog
……
---------------------------------------------------------
此时如果执行不带参数的make命令,将连接三个目标文件和库文件LS;但是如果在make命令后带有新的宏定义:
make "LIBES= -LL -LS"
则命令行后面的宏定义将覆盖makefile文件中的宏定义。若LL也是库文件,此时make命令将连接三个目标文件以及两个库文件LS和LL。
在Unix系统中没有对常量NULL作出明确的定义,因此我们要定义NULL字符串时要使用下述宏定义:
STRINGNAME=
makefile 中的变量(宏)
GNU 的 make 工具除提供有建立目标的基本功能之外,还有许多便于表达依赖性关系
以及建立目标的命令的特色。其中之一就是变量或宏的定义能力。如果你要以相同的编译
选项同时编译十几个 C 源文件,而为每个目标的编译指定冗长的编译选项的话,将是非
常乏味的。但利用简单的变量定义,可避免这种乏味的工作:
# Define macros for name of compiler
CC = gcc
# Define a macr o for the CC flags
CCFLAGS = -D_DEBUG -g -m486
# A rule for building a object file
test.o: test.c test.h
$(CC) -c $(CCFLAGS) test.c
在上面的例子中,CC 和 CCFLAGS 就是 make 的变量。GNU make 通常称之为变量,
而其他 UNIX 的 make 工具称之为宏,实际是同一个东西。在 makefile 中引用变量的值
时,只需变量名之前添加 $ 符号,如上面的 $(CC) 和 $(CCFLAGS)。
GNU make 有许多预定义的变量,这些变量具有特殊的含义,可在规则中使用。表 13-2
给出了一些主要的预定义变量,除这些变量外,GNU make 还将所有的环境变量作为自己
的预定义变量。
表 13-2 GNU make 的主要预定义变量
预定义变量
含义
$*
不包含扩展名的目标文件名称。
$+
所有的依赖文件,以空格分开,并以出现的先后为序,可能包含重复的依赖文件。
$<
第一个依赖文件的名称。
$?
所有的依赖文件,以空格分开,这些依赖文件的修改日期比目标的创建日期晚。
$@
目标的完整名称。
$^
所有的依赖文件,以空格分开,不包含重复的依赖文件。
$%
如果目标是归档成员,则该变量表示目标的归档成员名称。例如,如果目标名称为
mytarget.so(image.o),则 $@ 为 mytarget.so,而 $% 为 image.o。
AR
归档维护程序的名称,默认值为 ar。
ARFLAGS
归档维护程序的选项。
AS
汇编程序的名称,默认值为 as。
ASFLAGS
汇编程序的选项。
CC
C 编译器的名称,默认值为 cc。
CFLAGS
C 编译器的选项。
CPP
C 预编译器的名称,默认值为 $(CC) -E。
CPPFLAGS
C 预编译的选项。
CXX
C++ 编译器的名称,默认值为 g++。
CXXFLAGS
C++ 编译器的选项。
FC
FORTRAN 编译器的名称,默认值为 f77。
FFLAGS
FORTRAN 编译器的选项。
不知道大家在去一下开源站点上下载程序的时候当打开下载下来的程序的程序的时候,你们很奇怪的
发现里面没有我们想要的工程文件(一般指VC的工程文件)或者是C语言的,这时如果我们想编译的时候就
会感觉无从下手,但是大家没有没注意过里面一般会有一个相应的makefile文件,所以这就是问题的关键了。
下面我就来告诉大家如果来不用VC的IDE编译我们的工程文件。在此首先要来感谢一下看雪的一位老大的文章
(http://bbs.pediy.com/showthread.php?t=56912)
这里面讲得很详细了,我就不说其它的一些话了,直接来说一下如果来用makefile+nmake来编译VC的工程。
大家应该都知道在VC下的编译程序是CL.exe,链接程序是LINK.EXE如果不相信的话,可以到你的VC安装目录下
(../vc98/bin/)去找到这两个文件然后把这两个文件改一下名字,再打开个文件编译一下,看一下结果你就知道
了。但是今天我所把说的makefile如果于它们没有直接的关于,和makefile直接打交道的应该是同一目录下的另一个
程序nmake这个程序,它用来分析makefile文件,关于nmake
语法:NMAKE [options] [macros] [targets] [@commandfile]
说明:其中,options是NMAKE的选项,macros是在命令行中的宏定义,targets是NMAKE的目标文件列表,commandfile是包含命令行输入的文本文件(或响应文件)。
NMAKE 使用指定 /F 选项的Makefile(生成文件,通常名字是makefile);如果未指定 /F 选项,则使用当前目录下的Makefile。如果未指定Makefile,
则 NMAKE 使用推理规则生成命令行 targets。
NMake本身很简单,与NMAKE配合的是Makefile。Makefile的语法比较复杂,通常需要开发者自己手动编写Makefile.
但是这个nmake是于VC的IDE没有关系的,也就是说VC IDE的编译是不依赖于NMAKE的,如不信可以把nmake文件名字
改一下试试。
下面开始了:
首先来配置一下你的../vc98/bin到环境变量。
在你的(C:/NMAKE/)磁盘上创建相应目录,我在根目录下创建这个可以随便,我是为了方便。
VC下的程序编译主要是两类一个是console,win32的,这两种相对于第二种前一种还要简单些。
控制台程序:
demo.cpp
#include"windows.h"
#include"stdio.h"
int main(void)
{
getchar();
return 0;
}
相应的makefile:
# makefile的注释是以#号开始
NAME = demo #你的文件的名子
OBJS = $(NAME).obj#要形成的OBJ文件
#链接参数
LINK_FLAG =/DEBUGTYPE:COFF /DEF $(NAME).def /OUT:$(NAME).exe /MAP:$(NAME).MAP /PDB:$(NAME).pdb /subsystem:console /DEBUG
# kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept 这是MS的参数
#编译参数
ML_FLAG =/Od /G5 /Gz /GA
# /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c 这是MS的参数
#输出参数
OutPut_FLAG= /Fm:$(NAME).map
$(NAME).exe: $(OBJS)
link $(LINK_FLAG) $(OBJS)
$(OBJS): $(NAME).cpp
cl $(OutPut_FLAG) $(NAME).cpp
把这两个文件放到同一个目录下,在命令行下到这个目录,执行nmake命令,如果成功的话会给你指示。
NAME = 2
OBJS = $(NAME).obj
LINK_FLAG = /subsystem:windows /DEBUG
#/nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept MS的参数
ML_FLAG = /MT /GD /c
#/nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_AFXDLL" /FD /c MS的参娄
$(NAME).exe: $(OBJS) $(NAME).res
link $(LINK_FLAG) $(OBJS) 2dlg.obj stdafx.obj $(NAME).res
$(OBJS): $(NAME).cpp 2dlg.cpp stdafx.cpp
cl $(ML_FLAG) $(NAME).cpp 2dlg.cpp stdafx.cpp
$(NAME).res:$(NAME).rc
rc $(NAME).rc
其实他和上面的类似,只是在参数上有些复杂了。如果参数设置不当的话会出现下面这样的问题以及类似的错误提示
libcmt.lib(crt0.obj) : error LNK2001: unresolved external symbol _main
2.exe : fatal error LNK1120: 1 unresolved externals
NMAKE : fatal error U1077: 'link' : return code '0x460'
Stop.
由于时间的原因我只是给大家说清楚有这会事就行了,如果大家想再深入一步学习的话,那就还要自己更深入的学习和到网上去
找更多的资料。
我的资料都来自网络和MSDN。上面我提到的关于MS的参数大家是从VC的工程文件的*.DSP中看到的,大家可以用文本编辑器打开看一下。
MS设置的参数。
关于makefile的制做方法,大家可以到上面我提到的链接上去下载一下。里面有附件可供下载。
Error spawning link.exe 是我把LINK.exe改名了出的错,Error spawning cl.exe是我把CL.exe改名了出的错。