12 调用 Invocation(Part-1)
大多数情况下,当您使用C预处理器时,不需要显式调用它:C编译器会自动为您完成。然而,有时预处理器本身也非常有用。您可以通过 cpp
命令或 gcc -E
来调用预处理器。在GCC中,预处理器实际上是与编译器集成在一起的,并不是一个单独的程序,这两个命令都会调用GCC并告诉它在预处理阶段后停止。
这里列出的 cpp
选项同样被 gcc
接受,并且具有相同的意义。同样地,cpp
命令也接受所有常见的 gcc
驱动选项,尽管那些与预处理之后的编译阶段相关的选项会被忽略。
本文档仅记录了与预处理行为相关的特定选项。有关其他驱动选项的完整文档,请参阅GCC手册。
输入和输出文件
cpp
命令需要两个文件名作为参数:infile
和 outfile
。预处理器会读取 infile
以及任何通过 #include
指定的其他文件。所有由组合输入文件生成的输出都会写入 outfile
。
- 如果
infile
或outfile
是-
,则对于infile
表示从标准输入读取,对于outfile
表示写入标准输出。 - 如果省略了某个文件,则等同于为该文件指定了
-
。 - 您还可以使用
-o outfile
选项来指定输出文件。
除非另有说明,或者选项以 =
结尾,所有带参数的选项都可以将参数直接放在选项后面,或者在选项和参数之间加一个空格:-Ifoo
和 -I foo
的效果相同。
许多选项具有多字母名称;因此,不能将多个单字母选项组合在一起:-dM
与 -d -M
完全不同。
常用选项
-D name
将 name
预定义为宏,定义值为 1
。
-D name=definition
definition
的内容会被标记化并处理,就像在翻译阶段三的 #define
指令中出现的一样。特别是,嵌入的换行符会截断定义。
如果您是从 shell 或类似 shell 的程序调用预处理器,则可能需要使用 shell 的引号语法来保护具有特殊含义的字符(如空格)。
如果要在命令行上定义函数式宏,请在等号(如果有)之前用括号写出其参数列表。括号对大多数 shell 具有特殊意义,因此应该引用该选项。对于 sh
和 csh
,可以使用 -D'name(args…)=definition'
。
-D
和 -U
选项按照它们在命令行上给出的顺序进行处理。所有 -imacros file
和 -include file
选项会在所有 -D
和 -U
选项之后处理。
-U name
取消对 name
的任何先前定义,无论是内置的还是通过 -D
选项提供的。
-include file
像 #include "file"
出现在主源文件的第一行一样处理 file
。然而,搜索 file
的第一个目录是预处理器的工作目录,而不是包含主源文件的目录。如果在那里找不到,则按照正常的 #include "…"
搜索链继续查找。
如果给出了多个 -include
选项,文件将按照它们在命令行中出现的顺序被包含。
-imacros file
与 -include
完全相同,只是扫描 file
时产生的任何输出都会被丢弃。它定义的宏仍然有效。这允许您从头文件获取所有宏,而无需处理其声明。
所有通过 -imacros
指定的文件会在所有通过 -include
指定的文件之前处理。
-undef
不预定义任何系统特定或 GCC 特定的宏。标准预定义宏仍然有效。详见“标准预定义宏”。
-pthread
定义使用 POSIX 线程库所需的附加宏。您应该在编译和链接时一致地使用此选项。该选项支持 GNU/Linux 目标、大多数其他 Unix 衍生版本,以及 x86 Cygwin 和 MinGW 目标。
-M
不输出预处理结果,而是输出适合 make
的规则,描述主源文件的依赖关系。预处理器会输出一条 make
规则,其中包含该源文件的对象文件名、冒号以及所有包含的文件名(包括来自 -include
或 -imacros
命令行选项的文件)。
除非明确指定(使用 -MT
或 -MQ
),对象文件名由源文件名替换后缀为对象文件后缀并移除任何前导目录部分组成。如果有许多包含的文件,则规则会使用 \
和换行符分成多行。该规则没有命令。
此选项不会抑制预处理器的调试输出(例如 -dM
)。为了避免将此类调试输出与依赖规则混合,您应该使用 -MF
明确指定依赖输出文件,或者使用环境变量(如 DEPENDENCIES_OUTPUT
,详见“环境变量”)。调试输出仍会正常发送到常规输出流。
向驱动程序传递 -M
隐含 -E
,并通过隐式的 -w
抑制警告。
-MM
类似于 -M
,但不会提及在系统头文件目录中找到的头文件,也不会提及直接或间接从这些头文件中包含的头文件。
这意味着 #include
指令中使用尖括号还是双引号本身并不会决定该头文件是否出现在 -MM
的依赖输出中。
-MF file
当与 -M
或 -MM
一起使用时,指定一个文件来写入依赖关系。如果没有提供 -MF
选项,预处理器会将规则发送到与预处理输出相同的地方。
当与驱动程序选项 -MD
或 -MMD
一起使用时,-MF
会覆盖默认的依赖输出文件。
如果 file
是 -
,则依赖关系会被写入标准输出。
-MG
与请求生成依赖关系的选项(如 -M
)结合使用时,-MG
假设缺失的头文件是生成的文件,并将其添加到依赖列表中而不引发错误。依赖文件名直接从 #include
指令中获取,而无需添加任何路径。-MG
还会抑制预处理输出,因为缺少头文件会使预处理输出无用。
此功能用于自动更新 Makefile。
-Mno-modules
禁用为编译模块接口生成依赖关系。
-MP
此选项指示 CPP 为每个非主文件的依赖项添加一个伪目标,使其不依赖于任何内容。这些虚拟规则可以解决在删除头文件但未更新 Makefile 时 make
报错的问题。
典型的输出如下:
test.o: test.c test.h
test.h:
-MT target
更改依赖生成发出的规则的目标。默认情况下,CPP 获取主输入文件的名称,删除任何目录组件和文件后缀(如 .c
),并附加平台通常的对象文件后缀。结果即为目标。
-MT
选项将目标设置为您指定的确切字符串。如果您需要多个目标,可以将它们指定为 -MT
的单个参数,或者使用多个 -MT
选项。
例如,-MT '$(objpfx)foo.o'
可能会产生以下输出:
$(objpfx)foo.o: foo.c
-MQ target
与 -MT
相同,但它会对任何对 Make
特殊的字符进行转义。-MQ '$(objpfx)foo.o'
会产生以下输出:
$$(objpfx)foo.o: foo.c
默认目标会自动被转义,就像通过 -MQ
提供的一样。
-MD
-MD
等价于 -M -MF file
,但不隐含 -E
。驱动程序根据是否提供了 -o
选项来确定 file
。如果提供了 -o
,驱动程序会使用其参数并加上 .d
后缀;否则,它会取输入文件的名称,移除任何目录组件和后缀,并应用 .d
后缀。
如果 -MD
与 -E
一起使用,则任何 -o
开关都被理解为指定依赖输出文件(参见 -MF
);但如果在没有 -E
的情况下使用,每个 -o
被理解为指定目标对象文件。
由于 -E
不被隐含,-MD
可以作为编译过程的一个副作用生成依赖输出文件。
-MMD
类似于 -MD
,但仅提及用户头文件,而不提及系统头文件。