Suppress warning "umbrella header for module 'Foo' does not include header 'Bar.h'"

I tried to make clang module, that has umbrella header with some platform dependent includes, like

Foo.h:
#if TARGET_OS_IPHONE
#import "iOS.h"
#endif
...

module.modulemap:
module Foo {
  umbrella header "Foo.h"
}

If I'm importing it to swift and compiling for macOS, swift compiler, of course, yells at me:

error: umbrella header for module 'Foo' does not include header 'iOS.h'

We can relax this to warning by turning of setting "treat warning as error" but that is a really bad option.

There are many of system frameworks (for example AVFoundation), that has same problem, but compiler doesn't warn on them. I tried setting attribute [system] and/or adding #pragma clang system_header to every header, but no luck.

Also, in clang there is ability to explicitly turn off this warning by passing -Wno-incomplete-umbrella. It is not very good solution, but it is something at least.

Is there any workarounds? May be I can mimic my module as it is a system one, so compiler will be happy?

1 Like

You could try using a requires declaration inside a submodule declaration for each platform.

module Foo {
  umbrella header "Foo.h"
  module iOS {
    requires ios
    header "Foo~iOS.h"
  }
  module macOS {
    requires macos
    header "Foo~macOS.h"
  }
}

I've no idea if the above will work, and there's probably a better way.

It is actually in the opposite way :) You need specify ios header in macos target and vice versa. This is because, as I understand, module must "know" every header in directory if you specify umbrella header. Anyway, if you make submodule, as for my experience, you need to make subdirectories for each. So, ios and macos headers should be in their own directory. My example shows very simple condition, but it is actually much more complicated, so it will be a hell to create submodule for each configuration.

Moreover, I don't have direct control on that code, I can just make some trivial modification as I'm copying that code from one location to another (like regexps). And those trivial modifications doesn't include parsing preprocessor directives :slight_smile:

The opposite way would have to exclude headers, but that seems worse if there are more than two platforms.

module Foo {
  umbrella header "Foo.h"
  module iOS {
    requires ios
    exclude header "Foo~macOS.h"
    exclude header "Foo~tvOS.h"
    exclude header "Foo~watchOS.h"
  }
  // etc.
}

The documentation contains the following note:

Any headers not included by the umbrella header should have explicit header declarations.

This is what the submodules in my previous post are for. I don't think you'll need subdirectories, because the submodules are just a container for the requirements.

I don’t think it’s possible to change this - it’s part of Clang, not Swift (since modulemaps are only for C targets). We do have many Clang experts and developers here though who might be able to shed some light on why Clang enforces this.

I think I've found some workaround. I've moved my module so that it is not in header search path (-I flag) and passed -Xcc -isystem -Xcc $path_to_module_dir (it is done by System Header Search Paths in xcode).
So now my module pretends to be as system one and no warnings or errors from compiler :slight_smile: