【问题标题】:XCode Build System: Messing up preprocessors definitions and included header files?XCode 构建系统:弄乱预处理器定义和包含的头文件?
【发布时间】:2021-01-26 03:31:12
【问题描述】:

这里是第一个问题。

我在使用 XCode 构建系统时遇到了一些问题,特别是在预处理器定义方面。

我正在尝试为 Objective-C 运行时定义一个宏,以避免强制将调度函数强制转换为适当的函数指针类型。通常的方法是使用#define OBJC_OLD_DISPATCH_PROTOTYPES,然后在下一行包含标题。一旦包含了标头,就已经定义了宏并相应地配置了标头。

但这就是它开始变得奇怪的地方!

该宏根本无法识别,并且包含了标头,就好像 #define 语句不存在一样,因此它无法 #define OBJC_OLD_DISPATCH_PROTOTYPES 并且它被(重新?)定义为 0。

ma​​in.c

#include <stdio.h>
#define OBJC_OLD_DISPATCH_PROTOTYPES 1
#include <objc/objc-runtime.h>

int main(int argc, const char * argv[]) {
    // From there:
    //  - Build System: OBJC_OLD_DISPATCH_PROTOTYPES is always 0, except if defined in build settings
    //  - Clang (only): OBJC_OLD_DISPATCH_PROTOTYPES is 1
    printf("%d\n", OBJC_OLD_DISPATCH_PROTOTYPES);
}

在“Apple Clang - Preprocessing”部分下的项目构建设置中定义预处理器宏时,构建系统会按预期运行。它使用clang-D 参数定义全局宏,使其可用于项目使用的任何文件。

但是,当我使用 clang main.c 从终端使用 clang 时,源代码可以正确编译。

有人可以告诉我需要配置什么才能使构建系统正常运行吗?

【问题讨论】:

  • 在任何地方将 CONFIG 更改为 XYZ_PQR_ABC。你还遇到问题吗?如果不是,那么 XCode 系统可能会使用 CONFIG 本身。如果是这样,问题是别的。您尚未创建 MCVE(Minimal, Complete, Verifiable Example — 或 MRE 或 SO 现在使用的任何名称)或 SSCCE(Short, Self-Contained, Correct Example),因此我们无法判断您如何检测到 CONFIG0 而不是1 包含您的标题后。您是否已验证(使用编译器的 -H 选项)包含的是您的标头?
  • 我使用与我的问题匹配的 Objective-c 运行时编辑了问题以包含一个 MCVE。唯一定义 OBJC_OLD_DISPATCH_PROTOTYPES 的地方是 objc-api.h 从第 100 行到第 110 行。
  • 关闭前缀标题选项怎么样?
  • 默认情况下似乎是不活动的,那里没有定义任何东西。

标签: ios c xcode macos xcodebuild


【解决方案1】:

使用 Xcode IDE 构建时会发出警告:

Ambiguous expansion of macro 'OBJC_OLD_DISPATCH_PROTOTYPES'

直接使用 Xcode 的输出确实为 0,但使用clang main.c 输出为 1。不同之处在于 Xcode 默认使用带有启用模块的 clang:如果在命令行中启用模块,则会在命令行上收到相同的警告:

clang -fmodules main.c  

解决方案

在 Xcode 中,选择目标,转到“构建设置”选项卡并在“Apple Clang - 语言 - 模块”部分中,将“启用模块(C 和 Objective-C)”条目切换为“否”:

那么无论你在命令行上使用 Xcode 还是 Clang,你都会在这两种情况下得到预期的结果。

说明:

如果你使用模块,会发生以下情况:

  • 使用模块的二进制表示,而不是包含文本和编译结果的预处理器
  • 模块是(独立)预编译的,即它们使用模块预编译时的定义
  • 因此,在 include/import 语句之前对代码中的定义进行预处理不会影响模块(也不会影响其他导入的模块)。
  • 如果启用了模块,不仅@imports 会受到影响,而且#includes 也会在后台转换为模块导入

所以你对 OBJC_OLD_DISPATCH_PROTOTYPES 有一个矛盾的定义。 预编译模块为 OBJC_OLD_DISPATCH_PROTOTYPES 使用 0,您将其重新定义为 1。

顺便说一句:如果你使用

#define OBJC_OLD_DISPATCH_PROTOTYPES 0

然后您使用与预编译模块相同的定义,因此即使启用了模块,也不会出现宏的模棱两可扩展的警告。

如果没有启用模块,预处理器会包含文本,编译结果并返回预期的结果,即在 objc.h 中使用所需的 typedef。

【讨论】:

  • 我已经知道!OBJC_OLD_DISPATCH_PROTOTYPES 的意思了。糟糕,#definemissing a value 是正确的,这是一个错字。禁用模块有效,但您能简要解释一下原因吗?
  • 我已经开始在这里阅读这些内容了:clang.llvm.org/docs/Modules.html
  • “宏的每个定义和取消定义都被认为是一个不同的实体。”所以我想这不是一个错误。只是启用模块后,包含的头文件会忽略源代码中的任何 #define 语句。 clang.llvm.org/docs/Modules.html#macros
  • 我用解释更新了答案,如何在命令行上获得相同的行为('-fmodules' 选项)以及使用模块时的矛盾定义。我同意您的评估,即这不是错误,尤其是在阅读 If a macro name is used and the set of active directives is not consistent, the program is ill-formed. Otherwise, the (unique) meaning of the macro name is used. 之后
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-13
  • 2012-07-27
  • 1970-01-01
  • 1970-01-01
  • 2014-06-06
  • 1970-01-01
相关资源
最近更新 更多