Search code examples
ioscxcodemacosxcodebuild

XCode Build System: Messing up preprocessors definitions and included header files?


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?


Solution

  • 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.