Let's say I have a Sockets framework, that defines an Obj-C protocol SocketResponder. Then let's say in my Services framework which depends upon and links against the Sockets framework. My Services framework has a mixture of Obj-C and little bit of Swift code, and one of the Obj-C types, say NetworkTimeResponder conforms to the SocketResponder protocol, so:
// Services/NetworkTime.h
#import <Sockets/Sockets.h>
@interface NetworkTimeResponder: <SocketResponder>
…
This seems extremely straight forward, but the compiler gives the error:
Include of non-modular header inside framework module 'Services.NetworkTime': "...Sockets.framework/Headers/Sockets.h"
I've read a buuuuunch of "solutions" to this (every page of StackOverflow results), but none apply to this scenario.
I believe I understand the issue as, if the Application then imports the headers for the Services and Sockets frameworks, then there are actually two definitions of the types in Sockets framework headers and so it could be problematic.
But I'm really confused, because how is this different than say, AppKit importing Foundation and declaring types that conform to NSCopying
, and my application importing both AppKit and Foundation? The issue seems to only arise in the case of modules, which comes up because one of my framework has a little Swift in it? I'm not familiar with how modules fundamentally differ and affect this.
Can anyone explain this?
CodePudding user response:
TLDR: the target framework being imported and linked against, also needed to be a module.
These projects I am using were started a decade ago, so their compiler settings have evolved over time. Both "Defines Module" and "Enables C and Obj-C Modules" (DEFINES_MODULE
and CLANG_ENABLE_MODULES
) were off. However, having added a little bit of Swift code to one of my frameworks, Xcode then sets both DEFINES_MODULE
and CLANG_ENABLE_MODULES
to YES.
So now, if the "modular" framework, inside of any of its public headers, imports any public header from the "non-modular" framework, that's when this error pops up.
What it really comes down to is "hey this target is a module, and that one isn't."
Now, why is that a problem? This digs into part of why modules exist in the first place. If we look at CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES
(which some not-so-informed people will suggest turn on), then the answer becomes clearer:
"Enabling this setting allows non-modular includes to be used from within framework modules. This is inherently unsafe, as such headers might cause duplicate definitions when used by any client that imports both the framework and the non-modular includes."
If you know how header includes work, and you understand why C/C uses tons of #ifdef guarding in the headers to avoid duplication of declarations, then the crux is: modules avoid all of that headache, and guarantee only a single declaration exists. By "allowing non-modular includes in framework modules" you're now reintroducing the possibility of having duplicates.
https://clang.llvm.org/docs/Modules.html
So the moral of the story is, if a given target is a module, then everything it #imports in a public header should also be a module.