Path conflicts when using angle-bracket includes in umbrella header

I have a C library that compiles to a framework for MacOS/iOS and to a static/shared library on other platforms. Since updating to Xcode 12 I've run into the warning about double-quoted headers in a framework umbrella header and I'd like to update the library to be more "correct", but doing so results in name/path conflicts for the library headers.

When another project references the library, a header can be located via <framework/header.h>, yet when building the library that path is incorrect. That is, the path to the header within the project structure is not the same as when packaged as a framework (or, for instance, when installed to /usr/local/include). This is a problem with dependencies between library headers. Futhermore, when building, the project must use the version of the headers located within the project directory--not those previously installed in some system include path.

According to the C spec, including via angle brackets is supposed to search for headers in the system include paths--not relative to the current directory--so how does using angle brackets in the umbrella header even work reliably in the first place?

It feels like I'm missing something obvious, here.

Can you provide more specifics on what the conflict is?

Apple tools aren't very friendly to any cross-platform work. The idea is to build everything in Xcode. And in Apple's defence, I've seen a number of cases where the easiest path to get something to compile on a Mac is to just compile the thing in whatever crazy cmake/ninja tools they use on Linux, with verbose mode turned on, and manually construct an Xcode project. It might take a full day to get it right, but then its done for good. There is a good chance that this approach will give you an easy(er) universal build and iOS version too.

Another option is to just turn off that warning. Many open source frameworks are never going to satisfy it.

Depending on what the conflicts are, another option might be to specify a custom framework header.

Say the structure for project 'mylib' is:

project/include/mylib.h
project/include/header1.h
project/include/header2.h
project/src/file.c

With the resulting framework structure something like:

mylib/Versions/A/mylib
mylib/Versions/A/Headers/mylib.h
mylib/Versions/A/Headers/header1.h
mylib/Versions/A/Headers/header2.h

The new requirements wants only angle brackets in all framework headers, but that doesn't work for internal dependencies. Say 'header2.h' were to try including 'header1.h" with something like:

#include <mylib/header.h>

That include path will be incorrect when building the project--there is no real 'mylib' directory. Maybe Xcode hacks the include paths to magically work, but other toolchains have no way of doing that.

Also, if there's an installed version of the headers in '/usr/local/include/mylib', a normal C toolchain will try to include 'header1.h' from there instead of from the project directory.

It seems to me that Apple (or the clang devs) have redefined what angle bracket includes mean within the context of a framework, and the new definition is not compatible with non-Xcode toolchains.

I currently have the warning disabled, but if at all possible I'd like to be creating properly structured frameworks.

That include path will be incorrect when building the project--there is no real 'mylib' directory. Maybe Xcode hacks the include paths to magically work, but other toolchains have no way of doing that.

You can always build a demo framework in Xcode and see how Xcode does it. For example, it might be the "-fmodule-name" parameter. I think that may be the "magic", but it won't help you if you didn't write the code anyway.

Also, if there's an installed version of the headers in '/usr/local/include/mylib', a normal C toolchain will try to include 'header1.h' from there instead of from the project directory.

It is a really bad idea to have anything installed in /usr/local when you are building something for distribution. That local version is often a custom build, specific to your machine. You could be building for another architecture or platform and it could use that include. Apple platforms now support 2 different processors across 6 different platforms.

It seems to me that Apple (or the clang devs) have redefined what angle bracket includes mean within the context of a framework, and the new definition is not compatible with non-Xcode toolchains.

I currently have the warning disabled, but if at all possible I'd like to be creating properly structured frameworks.

That sounds like a catch-22. If this is some open-source project, you aren't going to be able to change all the #includes to be framework-style. The Linux folks probably wouldn't appreciate that. What's the problem with turning it off?

To be honest, I think this is just a side effect of modern developers. There are few developers would could take an open-source project, turn it into a framework, and link it to an iOS or Mac app. They generally need Homebrew, CocoaPods, or something similar. So, for most developers, not using the proper framework include, naming, and usage convention probably means they did something wrong. Additionally, such a framework probably wouldn't work in Swift, which is all that most developer care about anymore.

Aside from Swift complications, I don't know if there is going to be any problem later on down the line. I have a similar framework that I've manually cobbled together. There is no way I'm going to change all the #includes in that framework. If it breaks in the future, then I'll fork the project and fix them in a script. I've already forked the project anyway. It is not like I can keep up with upstream updates.

You're not wrong. Apple is diverging away from other, non-Apple platforms. If you want to use code from other platforms, it is going to get more and more difficult. You might have to do as I suggested and start picking your battles.
Thanks for the response.

To be clear, this is my own library so reworking how it builds (even abandoning frameworks) is no huge issue. Frameworks for iOS and MacOS are simply convenient packaging for the resources (and Metal shaders) that go with it.

My main concerns here are to 1) make sure there isn't something wrong in my build settings, and 2) understand what's actually happening with the include paths.

I actually tried a custom framework header, but the warning is triggered not only from the umbrella header, but from any of the headers included by umbrella header. It seems that all public includes are expected to only reference the system search paths.

Regarding /usr/local, since that's kinda the typical place to install stuff on any Unix platform, so when installing as a static/shared library--even on Apple platforms--that's where it goes by default. The fact that an older version is installed somewhere on the system shouldn't break the build.

Anyway, I've disabled the warning for the time being. If frameworks are no longer viable for cross-platform code it's seriously annoying and unfortunate, but not catastrophic.

To be clear, this is my own library so reworking how it builds (even abandoning frameworks) is no huge issue. Frameworks for iOS and MacOS are simply convenient packaging for the resources (and Metal shaders) that go with it.

If this has Metal components, then it is a native project and should be a framework built in Xcode. There is no law against having other build systems for other code. If the biggest stumbling block is the module directory, you can easily create a symbolic link for that. The angle brackets vs quotes should be no big deal since other platforms just pick one version or the other at random.

I actually tried a custom framework header, but the warning is triggered not only from the umbrella header, but from any of the headers included by umbrella header.

An umbrella header is something entirely different. You don't want that.

A custom framework header is only useful if you are trying to build a framework called "MyLib" and there already exists a "MyLib.h" header. In your framework, you can set the framework header to be "MyLibHeader.h" or something similar to avoid a file name conflict.

It seems that all public includes are expected to only reference the system search paths.

How would it work otherwise? If there are any non-public headers, then any builds using your framework would fail.

Regarding /usr/local, since that's kinda the typical place to install stuff on any Unix platform, so when installing as a static/shared library--even on Apple platforms--that's where it goes by default. The fact that an older version is installed somewhere on the system shouldn't break the build.

That is strictly for software being built on your machine. If you are building software for other people, don't ever use /usr/local for anything. It is not a question of older versions. One day you are trying to build something for M1 and you #include some header that was generated via some configure script that was run on Intel. And that configure script has processor-specific definitions. The code builds fine but crashes at runtime due to page sizes or something.

If frameworks are no longer viable for cross-platform code it's seriously annoying and unfortunate, but not catastrophic.

It sounds like you have it backwards. There is nothing wrong with frameworks or using Xcode to make them. If this were someone else's code that you are trying to shove into a framework, then do it as best you can and don't worry about it. But if it is your code, then you have to decide who you are building this for. Build it for the convenience of your primary target platform. Hack up the other ones.
Path conflicts when using angle-bracket includes in umbrella header
 
 
Q