3. 隐含规则

隐含规则make 程序内置的规则,他会在 make 命令的第一阶段加载,并可以根据隐含规则来生成对应的目标文件。

默认的隐含规则会在没有 .o 目标文件时,会尝试使用 cc 编译器将同名的 .c 文件生成同名的 .o 文件。如果不存在同名的 .c 文件也会尝试使用 yacc 词法分析工具将同名的 .y 文件生成 同名的 .c 文件。如果也没有 .y 文件,则会尝试使用 g++ 编译器将同名的 .cc.cpp 编译成对应的 .o 文件。这样的规则称为隐含规则。类似的隐含规则还有很多。

在上节课的示例中我们定义了显式规则如下:

main.o : main.c $(HEADERS)
    gcc -o $@ -c $<

file1.o : file1.c $(HEADERS)
    gcc -o $@ -c $<

file2.o : file2.c $(HEADERS)
    gcc -o $@ -c $<

在执行 make 时,是使用 gcc 编译器 对 mail.cfile1.cfile2.c 编译生成对应的 .o 文件。编译命令如下:

gcc -o file1.o -c file1.c
gcc -o file2.o -c file2.c
gcc -o main.o -c main.c

我们删除 Makefile 中的上述显式规则,你会发现在执行 make 命令时依旧可以编译成功。

Makefile 文件内容如下:

.PHONY: all clean

SOURCES := file1.c file2.c main.c
HEADERS := file1.h file2.h
OBJECTS := file1.o file2.o main.o
TARGET := myapp

all: $(TARGET)  # 编译最终目标

$(TARGET) : $(OBJECTS)
    gcc -o $@ $^

clean:  # 清除目标文件
    rm $(OBJECTS)

运行 make 结果如下:

weimingze@mzstudio:~/myapp$ make
cc    -c -o file1.o file1.c
cc    -c -o file2.o file2.c
cc    -c -o main.o main.c
gcc -o myapp file1.o file2.o main.o
weimingze@mzstudio:~/myapp$ ./myapp
库函数 myfunc1 被调用
库函数 myfunc2 被调用

上述程序中,实际是使用的是隐含规则 来生成 mail.ofile1.ofile2.o 这三个文件,他们使用的命令是 $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $^

其中 CCCPPFLAGSCFLAGS 都是隐含规则的变量,Makefile 允许我们修改这些变量来更换编译器和编译选项。

隐含规则的变量

变量
说明
AR
档案管理程序,缺省为:ar
AS
汇编编译程序,缺省为:as
CC
C语言编译程序,缺省为:cc
CXX
C++编译程序,缺省为:g++
CO
从RCS文件中解压缩抽取文件程序,缺省为:co
CPP
带有标准输出的C语言预处理程序,缺省为:$(CC) -E
FC
Fortran 以及 Ratfor 语言的编译和预处理程序,缺省为:f77
GET
从SCCS文件中解压缩抽取文件程序,缺省为:get
LEX
将 Lex 语言转变为 C 或 Ratfor程序的程序,缺省为:lex
PC
Pascal 程序编译程序,缺省为:pc
YACC
将 Yacc语言转变为 C程序的程序,缺省为:yacc
MAKEINFO
将Texinfo 源文件转换为信息文件的程序,缺省为:makeinfo
TEX
从TeX源产生TeX DVI文件的程序,缺省为:tex
TEXI2DVI
从Texinfo源产生TeX DVI 文件的程序,缺省为:texi2dvi
WEAVE
将Web翻译成TeX的程序,缺省为:weave
CWEAVE
将CWeb翻译成TeX的程序,缺省为:cweave
TANGLE
将Web翻译成 Pascal的程序,缺省为:tangle
RM
删除文件的命令,缺省为:rm -f
以下为 编译时的额外标志,如果不重新定义,缺省为空
ARFLAGS
用于档案管理程序的标志,缺省为:rv
ASFLAGS
用于汇编编译器的额外标志 (当具体调用‘.s'或‘.S'文件时)。
CFLAGS
用于C编译器的额外标志。
CXXFLAGS
用于C++编译器的额外标志。
COFLAGS
用于RCS co程序的额外标志。
CPPFLAGS
用于C预处理以及使用它的程序的额外标志 (C和 Fortran 编译器)。
FFLAGS
用于Fortran编译器的额外标志。
GFLAGS
用于SCCS get程序的额外标志。
LDFLAGS
用于调用linker(‘ld’)的编译器的额外标志。
LFLAGS
用于Lex的额外标志。
PFLAGS
用于Pascal编译器的额外标志。
RFLAGS
用于处理Ratfor程序的Fortran编译器的额外标志。
YFLAGS
用于Yacc的额外标志。

在上述示例中,我们将编译器改为 gcc ,将编译选项 添加一个 -g -Wall 参数,改写后代码如下:

改写后的 Makefile 文件如下:

.PHONY: all clean

SOURCES := file1.c file2.c main.c
HEADERS := file1.h file2.h
OBJECTS := file1.o file2.o main.o
TARGET := myapp

# 修改隐含规则的变量,更改编译命令
CC = gcc
CFLAGS = -g -Wall

all: $(TARGET)  # 编译最终目标

$(TARGET) : $(OBJECTS)
    gcc -o $@ $^


clean:  # 清除目标文件
    rm $(OBJECTS)

使用 make 编译结果如下:

weimingze@mzstudio:~/myapp$ make
gcc -g -Wall   -c -o file1.o file1.c
gcc -g -Wall   -c -o file2.o file2.c
gcc -g -Wall   -c -o main.o main.c
gcc -o myapp file1.o file2.o main.o

从上述 make 命令的执行结果可以看出。我们通过修改 CC 等变量的值将 C 语言的编译器改成了 gcc,而不再使用默认的编译器 cc

可用的隐含规则

  1. .c 文件生成同名 .o 文件。
  2. .cc.C.cpp 文件生成同名 .o 文件。
  3. .s 文件生成同名 .o 文件。
  4. .f 文件生成同名 .o 文件。
  5. .o 文件生成同名无后缀文件。
  6. .i 文件生成同名 .o 文件。
  7. .y 文件生成同名 .c 文件。
  8. .l 文件生成同名 .c 文件。
  9. .o 文件生成同名 lib开头,.a结尾的静态库文件。
  10. .dvi 文件生成同名 .ps 文件。

实验:

尝试使用 隐含规则编译多个 C++ 文件。将多个 C++ 文件编译成一个可执行文件。如果你没有 C++ 文件,你可以尝试将 file1.cfile2.cmail.c 的文件后缀由 .c 改为 .cpp 然后尝试运行 make 编译。