【问题标题】:How to tell Clang to stop pretending to be other compilers?如何告诉 Clang 停止伪装成其他编译器?
【发布时间】:2016-11-24 18:21:04
【问题描述】:

我过去遇到过这个问题:LLVM 定义了__GNUC__,但它不能使用 GCC 可以使用的程序。我在 Windows 上再次遇到它:LLVM 定义了_MSC_VER,但它不能使用 VC++ 可以使用的相同程序。 (对我来说)更糟糕的是我们为 LLVM Clang 和 Apple Clang 提供了专门的代码路径(由于版本方案不同,定义不同),我们必须与工具斗争才能让它使用它们。

我们如何告诉 Clang 停止伪装成其他编译器?有开关或选项吗?


Clang 文档 discuss the unwanted MS behavior,但他们没有说明如何阻止它:

为了与使用 MSVC 编译的现有代码兼容,clang 定义了 _MSC_VER 和 _MSC_FULL_VER 宏。这些默认值分别为 1800 和 180000000,使 clang 看起来像 Visual C++ 2013 的早期版本。-fms-compatibility-version= 标志覆盖这些值。它接受一个带点的版本元组,例如 19.00.23506。更改 MSVC 兼容版本会使 clang 的行为更像该版本的 MSVC。例如,-fms-compatibility-version=19 将启用 C++14 功能并将 char16_t 和 char32_t 定义为内置类型。

【问题讨论】:

  • 你为什么不简单地调用 clang -U__GNUC__ -U_MSC _VER?我知道这并不理想,但我上次尝试它有效。
  • 查看代码,没有标志可以控制:github.com/llvm-mirror/clang/blob/…
  • 如果clang不能消费gcc能消费的程序,那肯定是bug。你举报了吗?
  • 遇到了类似的问题。您找到解决方案/解决方法了吗?
  • #ifdef __REALLY_REALLY_GNUC__

标签: gcc visual-c++ clang llvm


【解决方案1】:

__GNUC__ 并不特指 GCC。所有支持 GNU C 扩展的编译器都定义了它,包括 clang 和 ICC。

专门检测 GCC 的常规方法是排除其他“兼容”编译器

#if defined(__GNUC__) && !defined(__llvm__) && !defined(__INTEL_COMPILER)
#define REAL_GCC   __GNUC__ // probably
#endif

Clang 前端定义了 __clang__,但其他使用 LLVM 后端的前端也定义了 __llvm__(例如 IBM XL C/C++ 版本 13.1.1 到 16.1)。排除__clang__ 而不是__llvm__ 可能会更好,这取决于您要排除它的原因。 (例如,出于解析原因,而不是出于优化原因,例如 LLVM 在进行内联之前评估 __builtin_constant_p(),因此它对内联函数的 args 毫无用处。)

另请参阅https://sourceforge.net/p/predef/wiki/Compilers/ 以获取大型列表。

https://blog.kowalczyk.info/article/j/guide-to-predefined-macros-in-c-compilers-gcc-clang-msvc-etc..html 也出现在 google 搜索结果中,但不够完整。


您应该抱怨的是,GCC 本身并没有定义您可以检测到的特定于 GCC 的宏,只有它支持的 C 的 GNU 方言版本。 (GNU C 是一种语言,GCC 是该语言的编译器。不幸的是,GCC 的 __GNUC_MINOR__ / __GNUC_PATCHLEVEL__ 宏将两者混为一谈。)

Clang 根据它声称完全兼容的 gcc 版本定义了 __GNUC__ / __GNUC_MINOR__ / __GNUC_PATCHLEVEL__。 (可能仅适用于 GCC 文档保证会起作用的东西,而不适用于碰巧与该版本或更高版本的 gcc 一起使用的东西。对于 GCC 本身,已记录 = 支持。 被编译器接受并不意味着它在未来的 GCC 版本中得到支持和保证。这可能是 clang 证明支持某些 GNU C 版本的理由)。

例如,clang 7.0.1 将 GNUC/MINOR/PATCHLEVEL 定义为 4/2/1,即与 GCC 4.2.1 兼容

例如来自the GCC manual

#define GCC_VERSION (__GNUC__ * 10000 \
                     + __GNUC_MINOR__ * 100 \
                     + __GNUC_PATCHLEVEL__)
…
/* Test for GCC > 3.2.0 */
#if GCC_VERSION > 30200

如果您正在测试最近的 GCC 支持但最近的 clang 还不支持的特定 GNU C 功能,那么您可能应该这样做。

【讨论】:

  • "GNUC 并不是专门指 GCC。" - __GNUC__ 就是指 GCC 编译器。它是well documented,并且已经使用了多年。甚至 GCC 的人都告诉 Clang 停止定义它,因为它意味着 GCC 编译器。
  • “专门检测 GCC 的正常方法是排除其他“兼容”编译器” - 不,这不是正常方法。检测 GCC 的正常方法是存在__GNUC__。 ICC 有一个开关来禁用该行为。
  • @jww:如果 GCC 自己有一个宏就好了,但事实上的现实是我在这个答案中描述的,非 GCC 编译器决定定义 @987654340 @ 表示与某些版本的 GCC 的兼容性。 (无论这是否完全准确。)MacOS 上现有的 Clang 安装太多,现在无法忽略,即使明天 Clang 会改变。 (半相关,MacOS 甚至安装了gcc,它实际上是clang 的符号链接,也会导致 SO 问题的混淆。)
【解决方案2】:

似乎从第 10 版开始,clang 现在有了一个选项。见the docs for clang

-fgnuc-version=

此标志控制 GNUC 和相关宏的值。此标志不会启用或禁用 Clang 中实现的任何 GCC 扩展。将版本设置为零会导致 Clang 未定义 GNUC 和其他 GNU 命名空间宏,例如 GXX_WEAK

因此,要禁用此行为,请在 clang 命令行中指定 -fgnuc-version=0

【讨论】:

  • 将此标志与 clang 11、libstdc++、linux 和 -D_GLIBCXX_DEBUG 一起使用后,我在标准库中遇到了一百万个错误。所以,Peter Cordes 的回答仍然是唯一的解决方案。
  • 嗯,很有趣。我可以想象这会导致错误,尽管我很惊讶甚至(clang?)标准库也需要这个定义。但我想有一个原因(遗留代码......)__GNUC__ 是默认定义的......不过,感谢您提供额外的信息和编辑! :-)
【解决方案3】:

我们如何告诉 Clang 不要再伪装成其他编译器?

我不知道该怎么做,也不知道它是否可行。你为什么要这样做?我感觉你想这样做是为了解决一个问题,但也许有更好的解决方案。

[在 Windows 上,] LLVM 定义 _MSC_VER,

因为您可能正在使用一个标准库,该库将对其进行检查以生成兼容的代码。

Clang 在 Windows 上的目标不仅仅是生成在 Windows 上运行的独立代码,而是可以与使用 VC++ 编译的库链接的代码。从源码的角度来看,用clang编译还是VC++编译应该是没有区别的。

如果您希望能够调用现有的 DLL,并且它的头文件检查 _MSC_VER,您需要设置它是使用 VC++ 还是 clang。

但是 [clang on Windows] 不能使用 VC++ 可以使用的相同程序。

自从编写此问题以来,Clang 在 Windows 上的功能已经有了很大的改进。现在有几个主要项目在其 Windows 版本中使用 clang。

如果您发现 VC++ 接受而 clang 不接受的正确程序,请提交错误报告。在大多数情况下,这可能是一个疏忽。有少数功能和 VC++ 扩展未在 clang-on-Windows 中实现,因为它们的优先级不够高。如果它们是您的优先事项,请告诉我们。

【讨论】:

  • “你为什么要这样做?” - 我们浪费了将近三个人月来修复 Clang 错误,因为它不能为编译器消耗程序假装是。毫不奇怪,它在 PowerPC 上变得更糟。 Clang 伪装成 GCC 和 IBM XL/C,并且不能为任何一个编译器使用程序。我厌倦了浪费时间支持 Clang。我们不再希望 Clang 伪装成其他编译器。相反,Clang 可以使用直接的 C/C++ 代码。换句话说,做这种废话的混蛋不会受到惩罚。我们为他们做的废话支付罚款。
  • C++ 既是语言又是库。除特殊情况外,“直接 C/C++ 代码” 依赖于需要了解编译器工作细节的库,而编译器本身依赖于其标准库实现。您尝试在 Windows 上使用哪些库(例如,VC++ 的标准库、libstd、libc++ 等)?您发现 VC++ 可以编译但 clang 不能编译的代码类型是什么?我(以及我团队中的其他“混蛋”)感谢您提交 LLVM 错误报告以及您对 LLVM 和 clang 所做的任何贡献。
  • @AdrianMcCarthy:我认为 jww 是在谈论 libcrypto++ 的内联 asm,并说他们通过 ifdefing 放弃了 clang,所以 clang 使用纯 C/C++ 后备代码而不是 GNU 的代码路径包括内联汇编在内的扩展。 (如果某些问题出自仅仅碰巧在 GCC 上工作的代码,并且依赖于一厢情愿而不是文档中的任何正式保证,我不会感到惊讶。我已经有一段时间没有查看了他们的代码,但 IIRC 内联汇编与内在函数相比被过度使用,显然是因为一些蹩脚的编译器不支持内在函数。)
  • 因此,即使对于本应有效的东西,libcrypto++ 以纯 C/++ 代码所没有的新方式运行编译器,并且遇到真正的编译器错误,我并不感到惊讶。但请记住,在其他问答中,我看到 jww 基本上责怪 GCC 没有按照他希望的方式工作,因为与 MSVC 不同的设计理念是必须启用 -mwhatever 来使用内在函数是不方便的方式他们想使用 libcrypto++。或类似的东西。所以我对他的咆哮持保留态度,尽管其中经常有对某事的有效抱怨。
【解决方案4】:

我也很惊讶地发现 Clang 定义了 _MSC_VER,但我所做的是做这样的事情:

#if defined(_MSC_VER) && !defined(__clang__)
// Real MSVC here
#elif defined(__clang__)
// Real CLANG here

【讨论】:

  • 这里也一样。对 Clang 定义 _MSC_VER 感到惊讶。
【解决方案5】:

你可以使用Hedley library:

请注意,GCCGNUC 宏是不同的。许多编译器 伪装成 GCC(通过定义 __GNUC____GNUC_MINOR____GNUC_PATCHLEVEL__,但往往没有实现所有功能 他们假装支持的 GCC 版本。解决方法 这个,HEDLEY_GCC_VERSION 宏只为 GCC 定义,而 HEDLEY_GNUC_VERSION 将在编译器定义时定义 __GNUC__.

所以,HEDLEY_GCC_VERSION 仅为 GCC 定义。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多