原文地址:http://www.cs.umd.edu/class/fall2002/cmsc214/Tutorial/makefile.html
分开编译:
一个.cpp文件包含一个对应的.h文件,以及一下:
class中所有类方法的实现
独立的函数
全局变量(尽量避免使用)
一个对应的.h文件应包含:
class的声明
函数的原型
extern变量(全局变量)
.h文件的目的在于exportservice给其他的.cpp
编译一个.cpp文件
当编译器看到带有#include.h的.cpp文件时会怎么做呢?预处理器会在.cpp的一个副本上插入这个.h文件,完成预处理阶段后就将整个修改后的副本发给编译器。所以.cpp文件只知道该.h中的类的成员变量和方法的接口,而不知道这些方法是怎么实现的。例如MovieTheater.cpp#include了Vector.h。只要了解Vector类的大小以及其中类方法的接口即可保证正确的初始化Vector类(足够的内存),和正确的函数使用。有了这些信息就可编译.cpp,生存目标文件(ObjectFile, .o)。
最后要运行时,将所有的目标文件link在一起即可。
g++-Wall -c MovieTheater.cpp //-Wall选项是打印所有warning信息
-c选项告诉g++是编译而不是编译完后继续link
常见的误区
“当你使用-c选项编译一个.cpp文件时,会顺带把.cpp所#include的所有.h文件相关的.cpp文件一起编译”。
这是错误的。只会编译单个.cpp。
“当写下如下命令时:
g++-Wall foo.cpp bar.cpp baz.cpp
会在编译foo.cpp时查看bar.cpp和baz.cpp”
这也是错误的。会分开编译。
Makefiles
makefile的一个好处就是没修改的类可以不重新编译。只编译修改过的。当一个工程需要100多个文件时,重新编译每个文件是很花时间的。
一个makefile中一般包含如下东西:
atarget (usually a file)
thedependencies (files which the target depends on)
andcommands to run, based on the target and dependencies.
例子如下:
Movie.o:Movie.cpp Movie.h Vector.h
g++-Wall -c Movie.cpp
基本语法:
<target>:[ <dependency > ]*
[ <TAB> <command> <endl> ]+
一个makefile文件中一般包含很多entries。
在target和冒号之后是依赖关系。这些依赖关系一般是用来告诉makefile这个target要不要重新编译的。make工具会根据一个非常简单的规则:依赖文件的最后修改时间(lastmodified, mtime)。这些更新是递归性质的,例如依赖的A.h没改变,但是A.h依赖的B.h作出了改变这也是需要重新编译的。
因为A可能包含B类的实例,B的大小变了,A的大小也跟着变了,那么.cpp也需要根据变化作出改变。
Making .o files
最常见的target就是.o文件
例子:
Movie.o: Movie.cpp Movie.h Vector.h
g++-Wall -c Movie.cpp
目标target是Movie.o。依赖的文件是后面的。
Deciding on 依赖
把所有的用 “”#include的.h文件都写进来。
能够使用以下方法来定制一种依赖
Vector:Foo.h
在这个例子中,不会生成任何target。只是告诉编译器一种依赖关系的存在。
命令(commands)
需要在命令前加TAB键。
Excuteable as targets
makefile的目的是生成可执行文件。
例子如下:
p1:MovieList.o Movie.o NameList.o Name.o Iterator.o
g++-Wall MovieList.o Movie.o NameList.o Name.o Iterator.o -o p1
即把所有的.o链接在一起,生成可名字为p1的可执行文件,如果没-o选项则默认生成a.out
Howto name the makefile:
顺序如下:
键下make后,
先在当前工作目录找makefile,找不到找Makefile。
如果键下的是make-f <filename>则找filename那个文件
宏—为了灵活性
尽量使用宏,如:
OBJS= MovieList.o Movie.o NameList.o Name.o Iterator.o
CC = g++ DEBUG = -g CFLAGS = -Wall -c $(DEBUG) LFLAGS = -Wall $(DEBUG) p1 : $(OBJS) $(CC) $(LFLAGS) $(OBJS) -o p1
宏的格式:
<macro_name>= <macro_string>
常见的宏
CC:编译器名字
DEBUG :调试标志
LFLAGS :linking时的标志
CFLAGS :编译时的标志
Dummy targets
make clean
\rm*.o *~ p1
rm前面的\防止你删除文件时的一些抱怨。
上面的意思是删除所有包含.o和 ~的文件,同时删除p1。
make tar
打包文件用的
tar:
tar cfv p1.tar Movie.h Movie.cpp Name.h Name.cpp NameList.h \ NameList.cpp Iterator.cpp Iterator.h
将生成p1.tar
make all
当需要生成几个可执行文件或者完成多于一个任务时。
例子
all:p1 p2 p3
p1: Foo.o main1.o g++ -Wall Foo.o main1.o -o p1 p2: Bar.o main2.o g++ -Wall Bar.o main2.o -o p2 p3: Baz.o main3.o g++ -Wall Baz.o main3.o -o p3
all没有命令行。
在使用makefile时常见的错误
忘记在命令前加TAB
依赖关系弄错了。
一个完整的例子:
OBJS = MovieList.o Movie.o NameList.o Name.o Iterator.o CC = g++ DEBUG = -g CFLAGS = -Wall -c $(DEBUG) LFLAGS = -Wall $(DEBUG) p1 : $(OBJS) $(CC) $(LFLAGS) $(OBJS) -o p1 MovieList.o : MovieList.h MovieList.cpp Movie.h NameList.h Name.h Iterator.h $(CC) $(CFLAGS) MovieList.cpp Movie.o : Movie.h Movie.cpp NameList.h Name.h $(CC) $(CFLAGS) Movie.cpp NameList.o : NameList.h NameList.cpp Name.h $(CC) $(CFLAGS) NameList.cpp Name.o : Name.h Name.cpp $(CC) $(CFLAGS) Name.cpp Iterator.o : Iterator.h Iterator.cpp MovieList.h $(CC) $(CFLAGS) Iterator.cpp clean: \rm *.o *~ p1 tar: tar cfv p1.tar Movie.h Movie.cpp Name.h Name.cpp NameList.h \ NameList.cpp Iterator.cpp Iterator.h