Makefile入门
1. gcc常用编译命令
gcc最简单的编译命令就是gcc *.c,编译当前目录下的所有源文件。现在,假设目录say下面存在三个文件say.h(定义模块函数),say.c(实现模块函数),say_usr.c(调用模块函数),那么直接在目录say中执行下面的命令,便可以完成编译:
gcc *.c
编译生成的二进制文件为a.out(默认文件名)。当然,仅仅从a.out这个名字,看不出程序是做什么的,因此,需要给生成的程序修改一个见名知意的名字,可以通过下面的命令完成:
gcc -o say_usr *.c
执行上面的命令,便生成了新的二进制文件say_usr。-o表示程序编译输出,后面跟属楚明。此时,程序中又增加了一个新的模块read(read.h,read.c),但程序中没有使用到该模块,编译时如果不想编译该模块,这是可以先使用下面的命令完成:
gcc -c say.c say_usr.c
gcc -o say_usr say.o say_usr.o
gcc中-c选项表明要编译那些文件,但后后面存在多个源文件时,-c和-o是不可以一起使用的。除了-c,编译命令还可以使用-E(预编译命令,生成文件后缀名i),-S(汇编命令,生成文件后缀名s)。常用的编译选项还包括-Wall(显示警告信息),-O优化级别(1~3,级别越大,优化效果越好),-g(生成调试信息)。-D(开启宏开关,如-D_DEBUG,表示开启_DEBUG)。
1. gcc常用编译命令
gcc最简单的编译命令就是gcc *.c,编译当前目录下的所有源文件。现在,假设目录say下面存在三个文件say.h(定义模块函数),say.c(实现模块函数),say_usr.c(调用模块函数),那么直接在目录say中执行下面的命令,便可以完成编译:
gcc *.c
编译生成的二进制文件为a.out(默认文件名)。当然,仅仅从a.out这个名字,看不出程序是做什么的,因此,需要给生成的程序修改一个见名知意的名字,可以通过下面的命令完成:
gcc -o say_usr *.c
执行上面的命令,便生成了新的二进制文件say_usr。-o表示程序编译输出,后面跟属楚明。此时,程序中又增加了一个新的模块read(read.h,read.c),但程序中没有使用到该模块,编译时如果不想编译该模块,这是可以先使用下面的命令完成:
gcc -c say.c say_usr.c
gcc -o say_usr say.o say_usr.o
gcc中-c选项表明要编译那些文件,但后后面存在多个源文件时,-c和-o是不可以一起使用的。除了-c,编译命令还可以使用-E(预编译命令,生成文件后缀名i),-S(汇编命令,生成文件后缀名s)。常用的编译选项还包括-Wall(显示警告信息),-O优化级别(1~3,级别越大,优化效果越好),-g(生成调试信息)。-D(开启宏开关,如-D_DEBUG,表示开启_DEBUG)。
在实际开发中,使用的好多模块模块都是以动态库或静态库出现的。如果想把say模块变成动态库,可以使用下面的编译方式:
gcc -c say.c -fPIC -o say.o
gcc -o libsay.so -shared say.o
gcc -c say_usr.c -o say_usr.o
gcc -o say_usr say_usr.o -L. -lsay
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
./say_usr
-L.表示链接当前目录下的动态库,注意say_usr.o要在-L和-lsay之前,否则链接会出现重命名错误。运行程序之前,需要先导出动态库路径(通过LD_LIBRARY_PATH),否则运行时,系统找不到libsay.so。
gcc不想msvc能够直接编译成静态库,需要借助ar工具将目标文件打包成静态库,命令如下:
gcc -c say.c -o say.o
ar -cr libsay.a say.o
gcc -c say_usr.c -o say_usr.o
gcc -o say_usr say_usr.o -L. –lsay
./say_usr
gcc -c say.c -fPIC -o say.o
gcc -o libsay.so -shared say.o
gcc -c say_usr.c -o say_usr.o
gcc -o say_usr say_usr.o -L. -lsay
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
./say_usr
-L.表示链接当前目录下的动态库,注意say_usr.o要在-L和-lsay之前,否则链接会出现重命名错误。运行程序之前,需要先导出动态库路径(通过LD_LIBRARY_PATH),否则运行时,系统找不到libsay.so。
gcc不想msvc能够直接编译成静态库,需要借助ar工具将目标文件打包成静态库,命令如下:
gcc -c say.c -o say.o
ar -cr libsay.a say.o
gcc -c say_usr.c -o say_usr.o
gcc -o say_usr say_usr.o -L. –lsay
./say_usr
然而,一个实际的项目,不可能就这么简简单单的三个文件,可能有好多模块,好多目录。那时,在手动给一个一个文件编译,是件不太现实的事情。因此,需要借助更高效的工具——makefile。
2. 简单makefile
makefile由两部分构成,规则,命令,格式如下:
taget:dependence
command
用上面的例子来说,say_usr依赖usr_say.o,say.o,生成命令为gcc –o say_usr say_usr.o say.o,say_usr.o依赖say_usr.c,say.h,生成命令为gcc –c say_usr.c –o say_usr.o,say.o依赖say.o,say.h,生成命令为gcc –c say.c –o say.o,最终的makefile文件如下:
#makefile for usr_say
2. 简单makefile
makefile由两部分构成,规则,命令,格式如下:
taget:dependence
command
用上面的例子来说,say_usr依赖usr_say.o,say.o,生成命令为gcc –o say_usr say_usr.o say.o,say_usr.o依赖say_usr.c,say.h,生成命令为gcc –c say_usr.c –o say_usr.o,say.o依赖say.o,say.h,生成命令为gcc –c say.c –o say.o,最终的makefile文件如下:
#makefile for usr_say
say_usr:say_usr.o say.o
gcc -o say_usr say_usr.o say.o
gcc -o say_usr say_usr.o say.o
say_usr.o:say_usr.c say.h
gcc -c say_usr.c -o say_usr.o
say.o:say.c say.h
gcc -c say.c -o say.o
gcc -c say_usr.c -o say_usr.o
say.o:say.c say.h
gcc -c say.c -o say.o
clean:
rm -f say_usr *.o
将makefile保存至源码路径,执行make,就可以自动完成编译,生成say_usr程序。一定要注意,makefile文件的每行均以table开始,否则make执行时会报错。
上面的例子仅仅是一个简单的例子,仅用于介绍makefile的规则,很明显工程大了,这样的makefile并不能减少我们的工作。因此必须对makefile优化。优化的思路可以建立在编译过程之上,一般程序的编译过程分为编译和链接两步,生成物分为最终文件,和中间文件。这样可以预先将最终文件,中间文件定义为变量,然后再执行明令时直接使用这些变量,优化后的makefile文件如下:
#makefile for say_usr
rm -f say_usr *.o
将makefile保存至源码路径,执行make,就可以自动完成编译,生成say_usr程序。一定要注意,makefile文件的每行均以table开始,否则make执行时会报错。
上面的例子仅仅是一个简单的例子,仅用于介绍makefile的规则,很明显工程大了,这样的makefile并不能减少我们的工作。因此必须对makefile优化。优化的思路可以建立在编译过程之上,一般程序的编译过程分为编译和链接两步,生成物分为最终文件,和中间文件。这样可以预先将最终文件,中间文件定义为变量,然后再执行明令时直接使用这些变量,优化后的makefile文件如下:
#makefile for say_usr
target=say_usr
objects=say_usr.o say.o
sources=say_usr.c say.c
objects=say_usr.o say.o
sources=say_usr.c say.c
$(target):$(objects)
gcc -o $(target) $(objects)
$(objects):$(sources)
gcc -c $(sources)
gcc -o $(target) $(objects)
$(objects):$(sources)
gcc -c $(sources)
clean:
@rm -f $(target) $(objects)
注意前面提到过,如果存在多个源文件-c和-o是不可以同时使用的,因此,编译中间文件的时候,只能使用gcc -c $(sources)。经过优化,代码已经少了很多。clean用于删除生成文件。正常情况下,执行make clean,生成的所有文件都会被删除,但如果在目录下存在一个名为clean的文件,make clean将会执行出错。为了防止此类情况发生,makefile引入了伪目标的概念,用.PHONY定义其他诸如clean之类的命令修改后的makefile如下:
#makefile for say_usr
@rm -f $(target) $(objects)
注意前面提到过,如果存在多个源文件-c和-o是不可以同时使用的,因此,编译中间文件的时候,只能使用gcc -c $(sources)。经过优化,代码已经少了很多。clean用于删除生成文件。正常情况下,执行make clean,生成的所有文件都会被删除,但如果在目录下存在一个名为clean的文件,make clean将会执行出错。为了防止此类情况发生,makefile引入了伪目标的概念,用.PHONY定义其他诸如clean之类的命令修改后的makefile如下:
#makefile for say_usr
target=say_usr
objects=say_usr.o say.o
sources=say_usr.c say.c
objects=say_usr.o say.o
sources=say_usr.c say.c
$(target):$(objects)
gcc -o $(target) $(objects)
$(objects):$(sources)
gcc -c $(sources)
gcc -o $(target) $(objects)
$(objects):$(sources)
gcc -c $(sources)
.PHONY:clean
clean:
@rm -f $(target) $(objects)
语句.PHONY:clean的作用是将clean命令定义为伪目标。伪目标可以有多个,但伪目标之间不可以相互依赖。如本列子,想编译程序的同时生成静态库,可以如下修改makefile如下:
#makefile for say_usr
clean:
@rm -f $(target) $(objects)
语句.PHONY:clean的作用是将clean命令定义为伪目标。伪目标可以有多个,但伪目标之间不可以相互依赖。如本列子,想编译程序的同时生成静态库,可以如下修改makefile如下:
#makefile for say_usr
target=say_usr
objects=say_usr.o say.o
sources=say_usr.c say.c
objects=say_usr.o say.o
sources=say_usr.c say.c
libsay.a:say.o
ar -cr libsay.a say.o
$(target):$(objects)
gcc -o $(target) $(objects)
$(objects):$(sources)
gcc -c $(sources)
ar -cr libsay.a say.o
$(target):$(objects)
gcc -o $(target) $(objects)
$(objects):$(sources)
gcc -c $(sources)
.PHONY:clean all
clean:
@rm -f $(target) $(objects) libsay.o
clean:
@rm -f $(target) $(objects) libsay.o
all:say_usr libsay.a
执行make all,say_usr和libsay.a便可以同时生成。
3. makefile函数
makefile函数的调用格式如下:
$(function <param1>, <param2>, …)
最常用的函数如下:
$(wildcard <pattern>):获取目录下符合规则的文件,多用于源文件,例如$(wildcard *.c)获取当前目录下的c源文件。
$(patsubst <pattern> <replacement> <text>):字符串替换函数,一般用于将原文件名变为目标文件名,如$(patsubst %.c, %.o, $(sources))将sources中的.c后传全部转换为.o,从而源文件名变成了目标文件名。
有了上面的两个函数,可以将例中的makefile做如下修改:
#makefile for say_usr
执行make all,say_usr和libsay.a便可以同时生成。
3. makefile函数
makefile函数的调用格式如下:
$(function <param1>, <param2>, …)
最常用的函数如下:
$(wildcard <pattern>):获取目录下符合规则的文件,多用于源文件,例如$(wildcard *.c)获取当前目录下的c源文件。
$(patsubst <pattern> <replacement> <text>):字符串替换函数,一般用于将原文件名变为目标文件名,如$(patsubst %.c, %.o, $(sources))将sources中的.c后传全部转换为.o,从而源文件名变成了目标文件名。
有了上面的两个函数,可以将例中的makefile做如下修改:
#makefile for say_usr
target=say_usr
sources=$(wildcard *.c)
objects=$(patsubst %.c, %.o, $(sources))
sources=$(wildcard *.c)
objects=$(patsubst %.c, %.o, $(sources))
$(target):$(objects)
gcc -o $(target) $(objects)
$(objects):$(sources)
gcc -c $(sources)
gcc -o $(target) $(objects)
$(objects):$(sources)
gcc -c $(sources)
.PHONY:clean
clean:
@rm -f $(target) $(objects)
新的makefile更加简洁,跟家利于维护。
4. 总结
1) gcc命令是编写makefile的基础,如果想学好makefile,必须熟悉常用的gcc命令
2) makefile规则很简单,由常量,目标依赖,命令规则,函数,及伪目标构成
3) 掌握makefile函数,将会是自己的makefile更加简洁宜读
clean:
@rm -f $(target) $(objects)
新的makefile更加简洁,跟家利于维护。
4. 总结
1) gcc命令是编写makefile的基础,如果想学好makefile,必须熟悉常用的gcc命令
2) makefile规则很简单,由常量,目标依赖,命令规则,函数,及伪目标构成
3) 掌握makefile函数,将会是自己的makefile更加简洁宜读