看了篇挺实用的makefile制作教程,由于原文是英文的,所以做了下简单翻译,供大家参考,不准确的地方请多包涵指正。
原文地址:http://www.cs.colby.edu/maxwell/courses/tutorials/maketutor/
译文如下:
Makefile是组织代码编译的一个简单方法。这个教程并不打算深入分析make的用法,而旨在让初学者能在中小项目中简单快速的制作自己的makefiles。
一个简单的例子
我们以hellomake.c、hellofunc.c、hellomake.h这三个文件为例来开始这个教程,这三个文件分别表示一个典型的main函数,存放在单独文件中的功能函数,及头文件。
hellomake.c 文件内容如下:
#include
int main() {
// call a function in another file
myPrintHelloMake();
return(0);
}
hellofunc.c 文件内容如下:
#include
#include
void myPrintHelloMake(void) {
printf("Hello makefiles!\n");
return;
}
hellomake.h 文件内容如下:
/*
example include file
*/
void myPrintHelloMake(void);
通常,你会使用下面的命令来编译这些代码:
gcc -o hellomake hellomake.c hellofunc.c -I.
这样我们就编译了这两个.c文件并且把可执行文件命名为hellomake。"-I."是告诉gcc编译器在当前目录查找hellomake.h这个头文件。如果不用makefile,要实现不断的测试、修改、排错我们常在终端里用方向键上键来查找最后使用的编译命令,目的仅仅是为了不用每次都去敲这些命令,特别是你添加了.c文件到你的工程里。
可惜的是,这种编译方法有两个不足。其一、当你丢失了这些编译命令(eg:关闭了终端)或者重启了电脑,你就不得不重新敲这些编译命令,至少这样是很低效的。其二、当你仅对其中的一个.c文件做了修改,你每次都要重新编译所有的文件,这是很耗时和低效的。所以我们就该看看使用makefile我们能做成什么样。
你能创建的最简单的makefile大致如下:
Makefile 1
hellomake: hellomake.c hellofunc.c
gcc -o hellomake hellomake.c hellofunc.c -I.
如果你将这个编译规则写入名为Makefile或者makefile的文件里然后在命令行里敲入make并回车,这样就会执行你写在makfile中的编译命令。注意,不带参数的make将执行makefile里的第一条编译规则。此外,在第一行的冒号(:)之后列出这个命令所依赖的文件列表后,如果这些文件做了改动,make就知道hellomake这条编译规则需要重新执行。然而,当你解决了第一个问题而不用重复的使用方向键上键来查找最后一个编译命令时,你马上就能发现系统在编译最近修改的工程时依然很低效。
一个需要重申的重要事情是,makefile里gcc命令前有一个制表符。在任何命令前都必须有一个制表符,否则make就会出错。
为了使编译高效一点,我们尝试做如下修改:
Makefile 2
CC=gcc
CFLAGS=-I.
hellomake: hellomake.o hellofunc.o
$(CC) -o hellomake hellomake.o hellofunc.o -I.
现在我们定义了CC和CFLAGS两个常量。这些常量用来告诉make我们想怎样编译hellomake.c和hellofunc.c。事实上,CC宏是给C编译器使用的,CFLAGS是传递给编译命令的参数列表。通过把hellomake.o和hellofunc.o放在编译规则的依赖列表中,make就知道它必选先单独编译.c文件,然后再编译hellomake可执行文件。
对于编译小型工程来说这足以应付了。但是我们却忽略了对头文件依赖这件事。举个例子,如果你对hellomake.h做了些修改,make应该重新编译.c文件,但他却没有这么做。为了解决这个问题,我们需要告诉make .c文件依赖那些.h文件。我们可以在makefile中添加一条简单的规则来实现这个目的。
Makefile 3
CC=gcc
CFLAGS=-I.
DEPS = hellomake.h
%.o: %.c $(DEPS)
$(CC) -c -o $@ $< $(CFLAGS)
hellomake: hellomake.o hellofunc.o
gcc -o hellomake hellomake.o hellofunc.o -I.
首先创建了DEPS这个宏,它指出了.c文件依赖的.h文件。然后我们定义所有后缀为.o的文件都应用这条规则。这条规则表明.o文件不仅依赖对应的.c文件而且还依赖DEPS宏定义的.h文件。这条规则还表明要生成.o文件需要使用定义在CC宏里的编译器。-c表示要生成object文件, -o $@表示使用冒号(:)左边的名称命名编译出的文件。$<表示依赖列表的第一项,CFLAGS定义同上。
为了使编译规则从头至尾都更通用,我们再做最终的简化,在此使用$@和$^这两个特殊的宏,他们分别表示冒号(:)左边和右边的内容。在下面的例子中,所有的头文件都列为DEPS宏的一部分,所有的object文件都列为OBJ宏的一部分。
Makefile 4
CC=gcc
CFLAGS=-I.
DEPS = hellomake.h
OBJ = hellomake.o hellofunc.o
%.o: %.c $(DEPS)
$(CC) -c -o $@ $< $(CFLAGS)
hellomake: $(OBJ)
gcc -o $@ $^ $(CFLAGS)
我们能否把.h文件放入一个include目录,源码文件放入一个src目录,把库文件放入一个lib目录,甚至隐藏掉那些烦人的到处都是的.o文件?很显然,都是可以的。下面的makefile文件定义了include目录和lib目录,并且把object文件放在了src目录的obj子目录中。还定义了一个宏来包含你想添加的任何库,如math库-lm。这个makefile应该放在src目录中。注意这个makefile还包含了一条规则,如果你敲入make clean并回车将会清理你的源文件目录和object目录。.PHONY规则确保make会执行定义在名为clean文件中的任务。
Makefile 5
IDIR =../include
CC=gcc
CFLAGS=-I$(IDIR)
ODIR=obj
LDIR =../lib
LIBS=-lm
_DEPS = hellomake.h
DEPS = $(patsubst %,$(IDIR)/%,$(_DEPS))
_OBJ = hellomake.o hellofunc.o
OBJ = $(patsubst %,$(ODIR)/%,$(_OBJ))
$(ODIR)/%.o: %.c $(DEPS)
$(CC) -c -o $@ $< $(CFLAGS)
hellomake: $(OBJ)
gcc -o $@ $^ $(CFLAGS) $(LIBS)
.PHONY: clean
clean:
rm -f $(ODIR)/*.o *~ core $(INCDIR)/*~
到此为止,你已经拥有一个不错的makefile文件,你对它进行修改来管理一些中小型的软件工程。你可以想makefile中添加多条规则,甚至能创建调用其他规则的规则。
要获取更多关于makefile和make的信息,请访问 GNU Make Manual(http://www.gnu.org/software/make/manual/make.html) ,它能告诉你超过你期望更多的东西(真的)。