GCC的编译配置⽂件(SpecFile)
3.15 配置外部⼯具程序和参数
[: 这篇⽂章原是 GCC ⼿册中的⼀节, 3.15 是它在⼿册中的编号.]
读取配置文件失败gcc是⼀个驱动式的程序. 它调⽤其它程序来依次进⾏编译, 汇编和链接. GCC 分析命令⾏参数, 然后决定该调⽤哪⼀个⼦程序, 哪些参数应该传递给⼦程序. 所有这些⾏为都是由 SPEC 字符串(spec strings)来控制的. 通常情况下, 每⼀个 GCC 可以调⽤的⼦程序都对应着⼀个SPEC 字符串, 不过有少数的⼦程序需要多个 SPEC 字符串来控制他们的⾏为. 编译到 GCC 中的 SPEC 字符串可以被覆盖, ⽅法是使⽤-specs=命令⾏参数来指定⼀个 SPEC ⽂件(spec file).
Spec ⽂件(Spec files) 就是⽤来配置 SPEC 字符串的. 它包含了⼀系列⽤空⾏分隔的指令. 指令的类型由⼀⾏的第⼀个⾮空格字符决定, 它们可能是:
%command
定义 SPEC ⽂件的预处理命令, 预处理命令包括以下的命令:
%include < file>
specialized山地车搜索⽂件, 并将其内容插⼊到 SPEC ⽂件的当前位置.
%include_noerr < file>
和'%include' ⼀样, 但是在没有到⽂件的情况下不会产⽣错误消息.
%rename old_name new_name
将 SPEC 字符串old_name改名为new_name.
*[ spec_name]:
这条命令让编译器创建,覆盖或删除指定名字的 SEPC 字符串. 所有在这条指令之后到下⼀条指令或空⾏之前的⽂本都被认为是这个 SPEC 字符串的内容. 如果它的内容为空, 则这个名字将被删除(如果该名字不存在, 则什么都不会发⽣). 内容不为空时, 如果该名字不存在, 则会创建⼀个新的
SPEC 字符串, 如果已经存在了, 他们的内容将会被替换. 如果内容的第⼀个字符为 ' +', 则这些内容会被追加到原来定义的内容后⾯, ⽽不是覆盖了.
[ suffix]:
创建⼀个新的 ' [suffix] spec'(后缀处理) 组合. 这个指令之后到下⼀个指令或空⾏之前的⾏组成了这个后缀的 SPEC 字符串. 当编译器遇到指这种后缀的输⼊⽂件时, 它会依次执⾏ SPEC 字符串中的指令, 这些指令指明了如何编译那个⽂件. 例如:
.ZZ:
z-compile -input %i
它的意思是: 任何以 '.ZZ' 结尾的输⼊⽂件都将使⽤ 'z-compile' 程序进⾏处理, 调⽤的时候将会使⽤-input开关以及 '%i' 替换后的结果(详见下⽂) 作为命令⾏参数.
作为 SPEC 字符串的内容, suffix 指令后的⽂本还可以是下⾯的某⼀个:
柯震东的女友@ language
它表明这个后缀是某种已知语⾔的别名. 它和 GCC 命令⾏中⽤于指定语⾔的-x选项类似. ⽐如:
.ZZ:
@c++
这说明 .ZZ ⽂件实际上是 C++ 的源代码⽂件.
# name
这会产⽣⼀条错误消息:
当前系统没有安装name编译器.
GCC 已经内建了⼀份巨⼤的后缀列表. 这条指令将后缀添加到列表的结尾处, 由于这个列表使⽤的时候是从结尾向后搜索的,实际上可以使⽤这种技术来覆盖之前的条⽬.
GCC 已经内置了如下的 SPEC 字符串. SPEC ⽂件可以覆盖他们或者创建新的. 注意, 某些 GCC 的实现也可能添加它们⾃⼰的 SEPC 字符串到这个列表⾥⾯.
asm          传递给汇编器的选项
asm_final    传递给汇编后处理器的选项
cpp          传递给 C 预处理器的选项
cc1          传递给 C 编译器的选项
cc1plus      传递给 C++ 编译器的选项
endfile      链接的最后需要包含的⽬标⽂件
link        传递给链接器的选项
lib          命令⾏传递给链接器的要包含的库
libgcc      决定给链接器传递哪个 GCC ⽀持库
linker      设置链接器的名字
predefines  传递给 C 预处理器的宏定义
signed_char  传递给 CPP 的⽤于说明 char 默认是否是有符号类型的宏
startfile    ⼀开始就需要传递给链接器的⽬标⽂件
冯绍峰爱杨幂下⾯是⼀个简单的 SPEC ⽂件的例⼦:
%rename lib                old_lib
*lib:
--start-group -lgcc -lc -leval1 --end-group %(old_lib)
这个例⼦把 'lib' 改名为 'old_lib' 然后⽤⼀个新的定义覆盖了之前的 'lib' 定义. 新定义在包含旧的定义⽂本之前添加了⼀些额外的命令⾏选项.
SEPC 字符串是传递给相应程序的命令⾏选项的列表. 另外, SEPC 字符串可以包含 '%' 作为前缀的字符串来表⽰变量. 变量可以⽤来代替⼀串⽂本, 或者作为向命令⾏插⼊⽂本的条件. 使⽤这些概念可以产⽣⾮常复杂的命令⾏.
下⾯是所有的⽤于 SPEC 字符串的预定义变量. 注意,这些变量在表⽰⽂本时不会⾃动在结果两边产⽣空格. 你可以在链接这些变量时使⽤常量字符串来⼀起链接.
%%
在程序名字或参数中⽤于表⽰⼀个 ' %'.
%i
⽤来表⽰当前正在处理的输⼊⽂件名
%b
表⽰输⼊⽂件的基本名字. 它不包含最后⼀个点号以及之后的内容, 也不包含路径名.
%B
和 ' %b' 相同, 但是它包含了后缀名(最后⼀个点号后的内容).
%d
⽤包含或附加 ' %d' 来标记⼀个临时⽂件名, GCC 正常退出后会⾃动删除带有这种标记的⽂件. 和 ' %g' 不同, 这个变量不会在参数中产⽣⽂本内
容.
%g suffix
产⽣指定后缀suffix的⽂件名, 每次编译产⽣⼀个. 和 ' %d' ⼀样, 它也会被标记为临时⽂件. 为了减少拒绝服务攻击的可能性, 即使上⼀次产⽣的
⽂件名已经知道了, 下⼀次产⽣的⽂件名也是⽆法预料的. 例如, ' %g.s ... %g.o ... %g.s' 可能被转换成 '
ccUVUUAU.s ccXYAXZ12.o
ccUVUUAU.s'. 后缀名需要可以和 ' [.A-Za-z]*' 这样的正则表达式匹配或者使⽤特定的 ' %O'. ' %O' 使⽤的时候假设它已经被处理过了. 以前, ' %g'只是简单的每次出现都被替换成⼀个⽂件名, 没有后缀的情况下使得攻击很容易成功.
%u suffix
和 ' %g' ⼀样, 但是每次出现就会产⽣⼀个新的临时⽂件名, ⽽不是每次编译的时候⼀个.
%U suffix
⽣成上⼀次使⽤ ' %u suffix' 时产⽣的⽂件名, 如果之前没有过, 就⽣成⼀个新的. 在没有使⽤过 ' %u suffix' 的情况下, 它和 ' %g suffix' ⼀样, 只是他们不会共享后缀名字空间. 因此, ' %g.s ... %U.s ... %g.s ... %U.s' 会⽣成两个不同的⽂件名, ⼀个是 ' %g.s' 产⽣的, 另⼀个是 ' %U.s'. 以前, ' %U'只是替换成上⼀个的 ' %u' 产⽣的⽂件名, 不会带任何的后缀.
%j suffix
如果存在 HOST_BIT_BUCKET, 并且它可写, 也没有使⽤-save-temps, 这会被替换成 HOST_BIT_BUC
KET 的名字. 否则, 产⽣⼀个临时⽂件名,和 ' %u' 效果相同. 这个临时⽂件并不⽤来在两个进程间通信, 只是作为垃圾进⾏处理.
[: HOST_BIT_BUCKET 是 GCC 中配置宿主系统环境时⽤到⼀个参数. 它是由宿主系统定义的⼀个路径名, 可以被当作⼀个⽂件来进⾏写⼊, 但是所有写⼊的内容都会被丢弃. 这通常被称为bit bucket(⽐特流垃圾桶) 或null device(空设备), 在 UNIX 中它通常就是 /dev/null.
%| suffix %m suffix
和 ' %g' 类似, 只是可以使⽤-pipe (管道). 使⽤管道时, ' %|' 被替换成⼀个破折号, ' %m' 被替换成空. 这是让程序从标准输⼊读取数据和往标准输出写数据的常⽤⽅式. 如果你需要处理更复杂的情况, 你还可以使⽤ ' %{pipe:X}': 参考f/lang-specs.h中的例⼦.
%. SUFFIX
对于匹配后缀的参数进⾏替换. 后缀到空格或下⼀个 % 结束.
%w
对包含或跟随有 ' %w' 的参数标记为此次编译的输出⽂件. 这会将这个参数放到 ' %o' 的参数序列⾥⾯.
%o
替换所有输出⽂件的名字, 并⾃动在他们周围放置空格. 你仍然应该在 ' %o' 的周围书写空格, 否则结果将是未定义的. ' %o' ⽤于运⾏链接器. 没有可识别后缀的输⼊⽂件将不会被编译, 但是他们被包含在输出⽂件⾥, 因此, 他们可以被链接.
%O
为⽬标⽂件替换后缀. 注意, 当它紧接在 ' %g, %u, 和 %U' 后⾯时会被特别处理, 因为它们需要完整的⽂件名. 处理的⽅法是假设 '%O' 已经被处理过了, 除⾮ ' %g, %u, 和 %U' 当前不⽀持使⽤特殊的 '%O' 后缀, 例如, '.o'.
%p
替换标准的预定义宏为当前的⽬标类型. 运⾏ cpp 程序时使⽤.
%P
和 ' %p' 类似, 只是会在每个预定义宏的名字前后加上 ' __', 除⾮宏的名字是以 ' __' 或 ' _L' 开头的. 其中L是⼀个⼤写字母. 这是 ISO C 所要求的.
%I
贴换所有的-iprefix(来⾃ GCC_EXEC_PREFIX),  -isysroot(来⾃ TARGET_SYSTEM_ROOT),  -isystem(来⾃ COMPILER_PATH 和 -B 选项) 以及所需的-imultilib.
%s
当前参数是某个库或可执⾏⽂件的名字. 搜索标准的⽬录列表来查这个⽂件名, 如果到了, 就⽤全名来进⾏替换. 当前⽬录被包含在扫描的⽬录列表的第⼀位.
%T
当前参数是⼀个连接脚本. 在库⽂件的⽬录列表⾥⾯搜索那个⽂件. 如果⽂件到了, 插⼊⼀个--script选项和⽂件的全路径名到命令⾏中. 如果⽂件没到将会产⽣⼀条错误信息. 注意, 当前⽬录不会被搜索.
%e str
把str作为错误消息打印出来.  str使⽤换⾏作为结束. 检测到不⼀致的选项时可以使⽤它.
%( name)崔贤珠
贴换名字为name的 SPEC 字符串内容到当前位置.
%x{ option}
为 ' %X' 添加⼀个选项.
%X
输出⽤ -W1 或 ' %x' 添加的所有链接器选项.
%Y
输出⽤ -Wa 添加的所有汇编器选项.
%Z
输出⽤ -Wp 添加的所有汇编器选项.
%a
处理 asm spec. ⽤于计算传递给汇编器的开关.
%A
处理 asm_final spec. 如果需要汇编后处理器的话, ⽤于计算传递给汇编后处理器的选项.
%l
处理 link spec. ⽤于计算传递给链接器的命令⾏参数. 它⼀般是使⽤的 ` %L %G %S %D 和 %E' 序列.
%D
为所有 GCC 认为可能包含可执⾏⽂件的⽬录导出⼀个 -L 选项. 如果⽬标⽀持多库, 那么每个⽬录都会考虑多库⽬录.
%L
保罗 韦斯利妻子处理 lib spec. 这个 SPEC 字符串决定了连接器的命令⾏需要包含的库⽂件.
%G
处理 libgcc spec. 这个 SEPC 字符串决定了传递给连接器命令⾏参数中使⽤的 GCC ⽀持库.
%S
处理 startfile spec. 这个 SEPC 字符串表⽰传递给链接器的第⼀个⽬标⽂件. 通常这个⽂件名是
%E
处理 endfile spec. 表⽰传递给链接器的最后⼀个⽬标⽂件.
%C
处理 cpp spec. ⽤于构造传递给 C 预处理器的参数.
%1
处理 cc1 spec. ⽤于构造传递给当前的 C 编译器(' cc1') 的选项.
%2
处理 cc1plus spec. ⽤于构造传递给当前的 C++ 编译器(' cc1plus') 的选项.
%*
替换匹配选项的可变部分. 详见下⽂. 注意, 替换字符串中的逗号会被空格取代.
%<S
删除命令⾏中所有的 -S 选项. 注意这个命令是位置相关的. 在 SPEC 字符串中这个命令之前的 -S 会被删除, 之后的不会.
%: function( args)
调⽤指定名字的函数, 并传递参数. 参数⾸先被当作嵌套的 SPEC 字符串处理, 然后分割成⼀个参数数组. 函数返回⼀个字符串, 插⼊到当前命令所在的位置.
下⾯是内建⽀持的 SPEC 函数:
getenv
getenv spec 函数带两个参数: ⼀个环境变量名称和⼀个字符串. 如果环境变量没有定义, 则会产⽣⼀个严重错误. 返回值是环境变量
的值和字符串连接在⼀起的. 例如, 如果 TOPDIR 被定义为 /path/to/top, 那么:
%:getenv(TOPDIR /include)
将会展开成 /path/to/top/include.
if-exists
if-exists 函数带⼀个参数, ⼀个⽂件的绝对路径名. 如果⽂件存在, if-exists 返回路径名. 下⾯是⼀个⽤法的简单例⼦:
*startfile:
crt0%O%s %:if-exists(crti%O%s) crtbegin%O%s
if-exists-else
if-exists-else 函数和 if-exists 函数类似, 不同的是它带有两个参数. 第⼀个参数是⽂件的路径名. 如果⽂件存在, if-exists-else 函数返
回路径名. 如果不存在, 返回第⼆个参数. 这样, if-exists-else 函数可以根据⽂件是否存在来选择这个⽂件或其它 . 下⾯是⼀个使⽤的
例⼦:
*startfile:
crt0%O%s %:if-exists(crti%O%s) \
%:if-exists-else(crtbeginT%O%s crtbegin%O%s)
replace-outfile
replace-outfile 函数带有两个参数. 它在输出⽂件数组中查第⼀个参数, 并⽤第⼆个参数来替换.下⾯是⽰例:
%{fgnu-runtime:%:replace-outfile(-lobjc -lobjc-gnu)}
remove-outfile
remove-outfile 函数带⼀个参数. 它在输出⽂件数组中查这个参数并将其删除. ⽰例:
%:remove-outfile(-lm)
pass-through-libs
pass-through-libs 可以带任意数量的参数. 它查 -l 选项和以 .a 结尾的⾮选项内容(确保是输⼊链接器的库⽂件), 将到的所有参
数加上 -plugin-opt=-pass-through= 前缀, 并添加空格分隔后返回. 这个列表⽤于传递给 LTO 链接器插件.
%:pass-through-libs(%G %L %G)
print-asm-header
print-asm-header 函数不带参数, 它只是简单的打印如下的标语:
Assembler options
=================
Use "-Wa,OPTION" to pass "OPTION" to the assembler.
它⽤于在使⽤ --target-help 的输出中分隔编译器选项和汇编器选项.
%{ S}
如果给 GCC 传递了 - S选项, 替换这个选项. 如果么有, 它什么也不做. 注意这个选项的前导破折号, 替换的时候会被⾃动加上. 因此, SPEC 字符串' %{foo}' 匹配命令⾏中的 -foo 选项, 并且输出⼀个 -foo.
%W{S}
和 %{S} 类似, 只是会标记最后提供的那个参数为⽂件, 并且在失败的时候删除这个⽂件.
%{ S*}
贴换传递给 GCC 的所有以 - S开头的选项, 通常它们都还带有⼀个参数. 这主要⽤于对 -o, -D, -I 等选项使⽤. GCC 认为-o foo是⼀个选项, 它的名字以 'o' 开头.  '%{o*}'替换这个, 包括空格. 因此, 将会产⽣两个参数.
%{S*&T*}
和 %{S*} 类似, 只是保持S和T选项的顺序(在 SEPC 中S和T的顺序并不重要). 可以使⽤任意数量的 & 分隔的变量, 其中的每个字段都是可选的. 对 CPP ⽐较有⽤的例⼦如: ' %{D*&U*&A*}'.
%{S:X}
如果 GCC 中使⽤了 - S选项, ⽤X来代替.
%{!S:X}
如果没有在 GCC 中使⽤ - S选项, 替换成X.
%{S*:X}
如果有⼀个或多个以 - S开头的选项, 则替换成X. 通常, 不管选项出现了多少次, 都只替换成⼀个X. 但是, 如果X中包含了 '%*', 那么每⼀个选项都会替换成⼀个X, 其中的 '%*' 被选项中匹配 '*' 的部分替代.
%{.S:X}
如果处理的⽂件以S开头, 则替换成X