rustc如何从bash进程替换编译源代码但是gcc不能?

Tom*_*cký 2 c unix linux bash rust

$ rustc <(echo 'fn main(){ print!("Hello world!");}')
$ ls
63
$ gcc <(echo '#include<stdio.h> int main(){ printf("Hello world!\n"); return 0;}')
/dev/fd/63: file not recognized: Illegal seek
collect2: error: ld returned 1 exit status
Run Code Online (Sandbox Code Playgroud)

为什么不能ld链接该程序?

ric*_*ici 5

gcc命令主要是一个调度引擎.对于每个输入文件,它从文件名的扩展名确定它是什么类型的文件,然后将文件传递到适当的处理器.所以.c文件由C编译器编译,.h文件被组装成预编译的头文件,.go文件被发送到cgo编译器,依此类推.

如果文件名没有扩展名或扩展名无法识别,则gcc假定它是某种应该参与最终链接步骤的目标文件.这些文件传递给collect2实用程序,然后ld可能会调用两次.这将是流程替换的情况,它产生的文件名/dev/fd/63不包括扩展名.

ld不依赖于文件名来标识目标文件格式.它通常由几个不同的目标文件识别器构建,每个目标文件识别器依赖于某种"幻数"(即,在文件开头或附近的特殊模式).它一次调用这些识别器,直到找到一个乐于解释文件的识别器.如果文件未被识别为二​​进制格式,则ld假定它是链接描述文件(纯文本文件)并尝试解析它.

当然,在尝试之间ld需要回放文件,并且由于进程替换安排传递管道而不是文件,因此搜索将失败.(如果您尝试通过将stdin重定向到管道来传递文件,也会发生同样的事情,您可以这样做:stdin如果您指定-为文件名,gcc将作为文件处理.但它坚持要告诉它什么样的文件它是.见下文.)

由于ld无法倒回文件,因此在文件与其第一次猜测不匹配后将失败.因此来自错误消息ld,这有点误导,因为您可能认为该文件已经被编译并且后续失败是在链接步骤中.事实并非如此; 因为文件名没有扩展名,gcc直接跳到链接阶段,几乎立即失败.

在进程替换,管道,标准输入和命名错误的文件的情况下,您仍然可以手动告诉gcc文件是什么.您可以使用-x选项进行操作,该选项在GCC手册部分中介绍了控制输出类型的选项(尽管在这种情况下,该选项实际上控制了输入的类型).

互联网上有很多类似问题的答案,包括StackOverflow上的各种答案,声称GCC试图检测输入文件的语言.它没有这样做,它从来没有.(我怀疑它会永远存在,因为它编译的某些语言彼此足够相似以至于不可能进行准确的检测.)唯一可以进行自动检测的组件是ld,只有在GCC不可逆转地决定将输入文件视为目标文件或链接描述文件.