Makefile 文件描述了整个工程的编译、连接等规则。在Makefile文件中描述了整个工程所有文件的编译顺序、编译规则。Makefile 有自己的书写格式、关键字、函数。在Makefile 中可以使用系统shell所提供的任何命令来完成想要的工作。Makefile(在其它的系统上可能是另外的文件名)在绝大多数的IDE 开发环境中都在使用,已经成为一种工程的编译方法。
网上的教程不是命令太多就是给人很复杂的感觉,自己不是很喜欢,因此,自己试着写一个,由简入繁。
1、Makefile基本格式:(版本一)
一个规则可分成三个部分:
-
目标:依赖1 依赖2 依赖3...
-
(制表符<TAB>)命令
例如:(源文件为main.c,add.c,sub.c,mul.c,生成的目标为app)
App: main.c add.c sub.c mul.c
gcc main.c add.c sub.c mul.c -o App
2、Makefile基本格式:(版本二)
版本一的缺点是只要修改一个文件,makefile就要把全部的源文件编译一次。浪费了太多时间和资源。
app:main.o add.o sub.o mul.o
gcc main.o add.o sub.o mul.o -o app
main.o:main.c
gcc main.c -c main.o
add.o:add.c
gcc add.c -c add.o
sub.o:sub.c
gcc sub.c -c sub.o
mul.o:mul.c
gcc mul.c -c mul.o
这样修改之后,虽然增加了一部分冗余的代码,但是当只改变其中某一文件的代码时不会重新编译其他的未修改的文件,减少了很多时间和资源,尤其是当这个工程比较大,文件比较多的时候。
3、Makefile基本格式:(版本三)
版本二的缺点就是有很多重复的东西,有其实第一行和第二行的“main.o add.o sub.o mul.o”部分,完全没必要重复写。
所以可以自定义一个变量,例如:obj=main.o add.o sub.o mul.o,而调用这个变量时使用美元符号和括号$(obj)
但是要注意malefile中有自带的变量就如同编程语言中的关键字,变量分为三类。第一类代表可执行程序的名字,例如 CC 代表编译器这个可执行程序;第二类代表程序使用的参数(多个参数使用空格分开),例如 CFLAGS 代表编译器执行时使用的参数(一种怪异的做法是直接在 CC 中包含参数);第三类代表安装目录,例如 prefix 等等,含义简单,下面只列出它们的默认值:
-
-----------------------------------------------------------------------------------
-
AR 函数库打包程序,可创建静态库.a文档。默认是"ar"。
-
AS 汇编程序。默认是"as"。
-
CC C编译程序。默认是"cc"。
-
CXX C++编译程序。默认是"g++"。
-
CPP C/C++预处理器。默认是"$(CC) -E"。
-
FC Fortran编译器。默认是"f77"。
-
PC Pascal语言编译器。默认是"pc"。
-
YACC Yacc文法分析器。默认是"yacc"。
-
-----------------------------------------------------------------------------------
-
ARFLAGS 函数库打包程序的命令行参数。默认值是"rv"。
-
ASFLAGS 汇编程序的命令行参数。
-
CFLAGS C编译程序的命令行参数。
-
CXXFLAGS C++编译程序的命令行参数。
-
CPPFLAGS C/C++预处理器的命令行参数。
-
FFLAGS Fortran编译器的命令行参数。
-
PFLAGS Pascal编译器的命令行参数。
-
YFLAGS Yacc文法分析器的命令行参数。
-
LDFLAGS 链接器的命令行参数。
-
-----------------------------------------------------------------------------------
-
prefix前缀 /usr/local
-
exec_prefix $(prefix)
-
bindir $(exec_prefix)/bin
-
sbindir $(exec_prefix)/sbin
-
libexecdir $(exec_prefix)/libexec
-
datadir $(prefix)/share
-
sysconfdir $(prefix)/etc
-
sharedstatedir $(prefix)/com
-
localstatedir $(prefix)/var
-
libdir $(exec_prefix)/lib
-
infodir $(prefix)/info
-
includedir $(prefix)/include
-
oldincludedir $(prefix)/include
-
mandir $(prefix)/man
-
srcdir 需要编译的源文件所在的目录,无默认值
在makefile中还有一种变量被成为自动变量。一般以美元符开头而且只能在命令部分使用:
- $@:规则中的目标
- $<:规则中的第一个依赖
- $^:规则中的所有依赖
根据这些规则malefile可以写成:
obj = main.o add.o sub.o mul.o
target = app
$(target):$(obj)
gcc $(obj) -o $(target)
main.o:main.c
gcc -c $< -o $@
add.o:add.c
gcc -c $< -o $@
sub.o:sub.c
gcc -c $< -o $@
mul.o:mul.c
gcc -c $< -o $@
4、Makefile基本格式:(版本四)
版本三下半部分显得工作比较重复,所以可以通过模式匹配的办法简化。使用的是%:
obj = main.o add.o sub.o mul.o
target = app
$(target):$(obj)
gcc $(obj) -o $(target)
%.o:%.c
gcc -c $< -o $@
5、Makefile基本格式:(版本五)
版本四还可以优化,就是把上半部分也使用自动变量替换。
obj = main.o add.o sub.o mul.o
target = app
$(target):$(obj)
gcc $^ -o $@
%.o:%.c
gcc -c $< -o $@
6、Makefile基本格式:(版本六)
版本四的makefile也有一个无法避免的问题,就是可移植性,所以需要用到makefile自带的一些函数,在使用函数之前要了解makefile的函数都是有返回值的:
wildcard:查找指定目录下的指定文件,用法:src = $(wildcard 目录/*.后缀)
patsubst:匹配替换,用法:obj = $(patsubst 替换目标, 替换源, $(src)),其中src是替换目录。
makefile可以继续修改为:
src = $(wildcard ./*.c)
obj = $(patsubst %c, %o, $(src))
target = app
$(target):$(obj)
gcc $^ -o $@
%.o:%.c
gcc -c $< -o $@
7、Makefile基本格式:(版本七)
版本六的makefile基本就是最简化的了,接下来为添加一些功能,使用make clean调用:
- 加-f是为了强制删除(避免警告)
- rm前面加-是为了使命令继续向下执行(忽略错误)
- .PHONY clean的目的是为了生成伪目标(防止出现更新错误)
src = $(wildcard ./*.c)
obj = $(patsubst %c, %o, $(src))
target = app
$(target):$(obj)
gcc $^ -o $@
%.o:%.c
gcc -c $< -o $@
.PHONY clean
clean:
-rm $(obj) $(target) -f