跟我一起写 Makefile
1 概述
makefile可以指定那些文件需要先编译,那些文件需要后编译,那些文件需要重新编译。
makefile像shell脚本,可以执行操作系统的命令。
作用:自动化编译。
make是一个命令工具,用来解释makefile中的指令。
2 介绍
make的编译规则,以c文件和头文件为例:
- 若这个工程没有编译过,那么所有的c文件都要编译并被链接。
- 若这个工程的某几个c文件被修改,那么只编译被修改的c文件,并链接目标程序。
- 若这个工程中的头文件被改变了,那么需要编译引用了这几个头文件的c文件,并链接目标程序。
基本语法
target ...: prerequisites
command
command前一定要以一个tab键开头。
target是目标文件,prerequisites是源文件,也就是所谓的依赖文件,command就是shell命令,就是在命令行中编译的那一套命令。
基本规则就是,当prerequisites相较于target更新时,执行command。
判断更新的原理
通过比较target文件和prerequisites文件的修改日期,来判断是否执行command。
依赖关系的实质
说明目标文件是由那些文件生成的。
定义命令
在target后面没有prerequisites,也就是冒号之后什么都没有。
要执行命令,就需要显示的使用make label
, 如
clean :
rm edit main.o kid.o
就需要用make clean
来执行。
如何工作
- 在当前目录下寻找
makefile
或Makefile
文件。 - 以文件中的第一个target为最终的目标文件。
- 如果edit文件不存在,或是后面的依赖修改时间更新,那么会执行命令
- 如果edit文件的依赖不存在,就会寻找依赖并生成
…
在makefile中使用变量
作用:便于维护,只需要修改一个地方,就可以把文件中所有使用到该变量的值修改。
如在makefile中一开始定义:
objects = main.o kid.o command.o
和其他语言中的变量有些许不一样。
使用
edit : $(objects)
cc -o edit $(objects)
clean :
rm edit $(objects)
利用make的自动推导功能
main.o : defy.h
会自动把main.c
写进依赖,并自动推导出cc -c main.c
.
发现头文件并不需要写到command中,应该是因为头文件会在c文件中展开。
用于清空目标文件的规则
每一个Makefile中都应该写一个清空目标文件的规则。
.PHONY : clean
clean :
-rm edit $(objects)
.PHONY
表示clean
是一个“伪目标”。
有一个减号-
表示,当某些文件出现问题时,继续完成后面的事。
clean一般放在文件的最后。
Makefile组成
- 显示规则,指出要生成的文件、文件的依赖和生成的命令。
- 隐晦规则,自动推导。
- 变量定义,多为字符串。
- 文件指示,引用另一个makefile,指定有效部分,定义多行命令。
- 注释,#
指定特定的Makefile
make -f Make.linux
或者
make —-file Make.ATX
引用其他Makefile
include filename
如
include foo.make a.my b.my c.mk
使用-I
或—-include-dir
参数,指定目录寻找。
让make不理会无法读取的文件,从而继续执行, 添加-
-include filename
环境变量MAKEFILES
make会把这个变量的值做一个类似include的动作。
3 书写规则
可以把command和前面的写成一行
targets : prerequisites ; command
命令或其他的句子太长可以用\
分隔换行
objects = main.o kid.o command.o display.o \
insert.o search.o file.o utility.o
一般,make以/bin/sh
执行命令。
使用通配符
支持三个通配符:*
,?
,~
文件搜寻
VPATH变量
Makefile中的VPATH
变量,指定目录查找,当make在当前目录中没有查找到时,到指定目录中查找
VPATH = src:../headers
使用:
分隔。
vpath关键字
三种使用方法
- vpath
- vpath
- vpath
%
字符,表示
匹配0或多个字符。
伪目标
使用.PHONY
显式表明其为伪目标
,作用是避免和文件重名。
.PHONY : clean
clean :
rm *.o temp
表明要运行clean就需要执行make clean
.
目标和伪目标都可以成为依赖。
多目标
当多个目标依赖于同一个文件时。
使用自动化变量$@
, 表示当前规则中所有的目标的集合。
bigoutput little output: text.g
generate text.g -$(subset output,,$@) > $@
$
表示执行一个函数,subset
是函数名,output,,$@
是参数。
静态模式
更容易定义多目标的规则。
语法:
<targets>: <target-pattern>: <prereq-patternn>
<command>
如
objects = foo.o bar.o
all: $(objects)
$(objects): %.o: %.c
$(CC) -c $(CFLAGS) $< -o $@
依赖的名称来源于目标文件前的名称。
中间的%.o
表示筛选尾缀为.o
的目标。
$<
和$@
是自动化变量。
$<
表示所有的依赖目标集(这里是foo.c bar.c).
$@
表示所有的目标集(这里是foo.o bar.o)
自动生成依赖
使用GNU C/C++编译器的-MM
选项。
.d
文件包含.c
文件的依赖关系。
source = foo.c bar.c
include $(source:.c=.d)
做替换,把变量source中所有 .c 字符串替换为 .d
4 书写命令
注释的符号是#
这个和shell脚本一样高。
make 一般使用环境变量SHELL中定义的Shell来执行。
make默认由/bin/sh执行。
显示命令
使用@
在命令之前,命令将不会被打印在屏幕上。
调试Makefile,使用make的-n
或—-just-print
参数,只会打印命令而不会执行。
make的-s
或—-silent
或—-quiet
参数,表示禁止命令的显示。
命令执行
让后续命令在前面起作用之后执行,要用分号隔离开。
exec:
cd /home/hchen; pwd
命令出错
忽略命令的出错, 使用-
clean:
—rm -f *.o
也可以使用make的参数-i
或—-ignore-errors
.
或者使用.IGNORE
终止出错的规则,继续执行其他规则的参数-k
或—-keep-going
嵌套执行
总控Makefile.
SHELL和MAKEFLAGS变量总是会传递给下层。
-w
和—-print-directory
参数,打印目前工作目录信息。p
定义命令包
以define
开头,endif
结束。
define run-yacc
yacc $(firstword $^)
mv y.tab.c $@
endif
使用命令包
foo.c: foo.y
$(run-yacc)
5 使用变量
Makefile中定义的变量类似于C/C++中的宏。
变量命名,可以包含字符、数字、下划线(可以用数字开头),不应该含有 : # =
或空字符。大小写敏感。
使用:=
操作符,前面的变量不能使用后面的变量过,避免无限重复定义。
注意注释符#
的特性
dir := /foo/bar #directory
dir这个变量的值是/foo/bar
后面跟了四个空格。
?=
操作符
FOO ?= bar
如果FOO没有被定义,那么其值为bar。
变量的高级用法
替换。
把变量的值当作变量。
追加变量值
使用+=
.
override指示符
make的运行
一般makefile中都包含了编译、安装、打包等功能。