0

First question here.

I have some troubles with the XCode Build System, specifically with preprocessor definitions.

I'm trying to define a macro for the objective-c runtime to avoid enforcing the dispatch functions to be cast to an appropriate function pointer type. The usual way to go would be to use #define OBJC_OLD_DISPATCH_PROTOTYPES and then include the header on the next line. Once the header gets included, the macro is already defined and the header is configured accordingly.

But that's where it starts to get weird!

The macro is not recognized at all and the header gets included as if the #define statement was not there so it fails to #define OBJC_OLD_DISPATCH_PROTOTYPES and it gets (re?)defined as 0.

main.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);
}

The build system acts as expected when the preprocessor macro is defined in the project build settings under the "Apple Clang - Preprocessing" section. It defines the global macro using the -D parameter of clang making it available to any files used by the project.

However, source code compiles correctly when I use clang from a terminal using clang main.c.

Could someone tell me what I need to configure for the build system to behave normally?

OpSocket
  • 997
  • 11
  • 19
  • Change `CONFIG` to `XYZ_PQR_ABC` everywhere. Do you still run into the problem? If not, then maybe the XCode system uses `CONFIG` itself. If so, the problem is something else. You've not created an MCVE ([Minimal, Complete, Verifiable Example](https://stackoverflow.com/help/mcve) — or MRE or whatever name SO now uses) or an SSCCE ([Short, Self-Contained, Correct Example](http://sscce.org/)) so we can't tell how you're detecting that `CONFIG` is `0` and not `1` after including your header. Have you verified (with the `-H` option to the compiler) that it is your header that's included? – Jonathan Leffler Oct 15 '20 at 02:48
  • I edited the question to include a MCVE using the objective-c runtime which match my problem. The only place where `OBJC_OLD_DISPATCH_PROTOTYPES`gets defined is in `objc-api.h` from line 100 to 110. – OpSocket Oct 15 '20 at 03:27
  • How about turning off the prefix header options? – 9dan Oct 15 '20 at 03:59
  • It seems inactive by default, nothing is defined there. – OpSocket Oct 15 '20 at 04:02

1 Answers1

2

It gives a warning when building with Xcode IDE:

Ambiguous expansion of macro 'OBJC_OLD_DISPATCH_PROTOTYPES'

and the output is indeed 0 using Xcode directly, but 1 with clang main.c. The difference is that Xcode uses clang with enabled modules by default: You get the same warning on the command line if you enable modules there:

clang -fmodules main.c  

Solution

In Xcode, select the target, go to the "Build Settings" tab and in the "Apple Clang - Language - Modules" section, switch the "Enable Modules (C and Objective-C)" entry to 'NO':

build settings

Then you get the expected result in both cases, regardless of whether you use Xcode or Clang on the command line.

Explanation:

If you use modules the following happens:

  • instead of the preprocessor including the text and compiling the result, a binary representation of the module is used
  • modules are (independently) precompiled, i.e. they use the definitions from the time the module was precompiled
  • consequently, preprocess definitions from the code before the include/import statement have no effect on the module (nor on other imported modules).
  • if modules are enabled, not only @imports are affected, but also #includes are translated into module imports under the hood

So you have a contradictory definitions for the OBJC_OLD_DISPATCH_PROTOTYPES. The precompiled module uses a 0 for OBJC_OLD_DISPATCH_PROTOTYPES and you redefine it as 1.

BTW: if you use

#define OBJC_OLD_DISPATCH_PROTOTYPES 0

then you use the same definition that the precompiled module is using and therefore there is no warning about an ambiguous expansion of the macro even if modules are enabled.

Without enabled modules, the preprocessor includes the text, compiles the result and returns the expected result, i.e. in objc.h the desired typedef are used.

Stephan Schlecht
  • 26,556
  • 1
  • 33
  • 47
  • I already knew what `!OBJC_OLD_DISPATCH_PROTOTYPES` meant. Oops, you're right for the `#define`missing a value, that's a typo. Disabling modules works, but can you briefly explain why? – OpSocket Oct 18 '20 at 15:01
  • I've already started reading about these here: https://clang.llvm.org/docs/Modules.html – OpSocket Oct 18 '20 at 15:29
  • "Each definition and undefinition of a macro is considered to be a distinct entity." So i guess it's not a bug. It's just that with modules enabled, included header files ignores any `#define` statement in source code. https://clang.llvm.org/docs/Modules.html#macros – OpSocket Oct 18 '20 at 15:49
  • I updated the answer with an explanation, how to get the same behavior on command line ('-fmodules' option) and about contradictory definitions when using modules. I agree with your assessment that this is not a bug, especially after reading `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.` – Stephan Schlecht Oct 18 '20 at 18:35