前面我们已经介绍过了如何通过gcc编译代码生成文件,但是,当项目较多的时候,往往则需要一个自动化的编译工具辅助我们完成这项操作。像Windows那样通过Ctrl+F5即可一键完成项目所有编译工作。
Makefile语法基础
在Linux下,自动化编译工具是通过make命令来完成的(一些工具厂商也提供了它们自己的make命令,如gmake等),make命令的基本格式如下:
make [-f makefile] [label]
它可以通过-f参数指定输入文件,当省略-f参数时,默认输入文件名为Makefile,由于我们通常不用这个-f参数,往往就用默认的Makefile文件名。
Makefile是一个文本文件,它是基于一定的语法规则的,它的基本执行规则定义如下:
target : [prerequisites]
command
注意:Makefile的target是顶格写的,而Command需要加一个Tab键。我这里为了排版看起来舒服点,每一行都多加了一个Tab键,如果要使用本文的Makefile示例,请去掉各行的第一个Tab键,否则make的时候报错。
例如,我们编写一个简单的Makefile:
clean:
@echo "clean"
all:
@echo "all"
当我们直接执行make命令的时候,输出如下:
tianfang > make
clean
tianfang > make all
all
tianfang > make clean
clean
从中我们可以看到:默认情况下构建第一个标签。可以通过在命令行参数中通过参数构建指定标签。
然后我们再来看看依赖性是如何工作的,这次我们修改一下Makefile,让all标签依赖于clean标签:
clean :
@echo "clean"
all : clean
@echo "all"
再次执行make all的时候,发现会先执行clean标签:
tianfang > make all
clean
all
用Makefile来构建项目
通过对Makefile的语法有一个简单的了解后,下面就可以用Makefile简化我们的构建操作了。还是针对前面的那个stack的例子吧,首先我们来实现一个最简单的例子:
all :
gcc -o run main.c stack.c
这样直接通过make命令就可以实现对gcc -o run main.c stack.c整条命令的执行了。
更加一步,我们想实现增量编译,则要实现如下规则:
这个时候就需要前面的依赖性出马了:
run : stack.o main.o
gcc -o run main.o stack.o
stack.o : stack.c
gcc -c stack.c
main.o : main.c
gcc -c main.c
这里的target都是文件,run默认依赖于stack.o和main.o,因此,当构建run的时候,就会先构建stack.o和main.o,输出方式如下:
tianfang > make
gcc -c stack.c
gcc -c main.c
gcc -o run main.o stack.o
当我们只改了其中某个文件的时候,例如stack.c,这是由于main.c没有变化,因此不会重新编译main.o,只会重新构建stack.o和run,从而实现我们的增量编译的目的。(这个其实才是make比shell或脚本语言编写的批处理方式要强大的地方)
tianfang > make
gcc -c stack.c
gcc -o run main.o stack.o
通过自动推导改进Makefile
通过上面的例子可以看到,虽然我们可以实现增量编译,但是整个Makefile过程是非常复杂的,需要对每个.o文件编写编译脚本。如果项目文件较多,并且有增删的话,则编写Makefile文件非常麻烦。
为了改进这个问题,makefile提供了一个自动推导的功能,通过它可以简化我们的编写过程。例如,前面的例子可以简化如下:
CC = gcc
objs = stack.o main.o
run : $(objs)
$(CC) -o run $(objs)
这里我们引入了两个变量,第一个行的CC制定了编译器为gcc(如果不指定则是默认的cc),第二行制定了我们的obj文件。
这样,只需要执行make命令即可生产我们的程序:
tianfang > make
gcc -c -o stack.o stack.c
gcc -c -o main.o main.c
gcc -o run stack.o main.o
可可以看到,make命令会自动推导出如何根编出.o文件来。如果我们的项目文件变化了,只需要改objs变量即可,非常方便。
不过,有的时候我们可能觉得这中自动推导的方式不够用,需要手动控制编译选项,这个时候我们可以自己指定推导规则:
CC = gcc
objs = stack.o main.o
run : $(objs)
$(CC) -o run $(objs)
$(objs): %.o: %.c
$(CC) -c -g $< -o $@
Makefile的功能非常强大,对应的语法也是非常复杂的,由于网上已经有人写得很详细了,因此我这里并不打算系统的介绍Makefile的各种规则,如果想进一步的了解,可以参考跟我一起写 Makefile这篇文章。