Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid umbrella header crash error in remote compilation when no default umbrella header generated #897

Merged
merged 1 commit into from
Aug 21, 2024

Conversation

gyfelton
Copy link
Contributor

@gyfelton gyfelton commented Aug 20, 2024

What changed:

  1. When generate_default_umbrella_header is set to False, still add statements around extern double {module_name}VersionNumber; to umbrella header so that clang can resolve the situation when module_name is the same for two different modules (where their path is diff).
  2. When generate_default_umbrella_header is set to False, use extern instead of FOUNDATION_EXPORT since FOUNDATION_EXPORT is extern and trying to define it in else clause will cause warning: ambiguous expansion of macro 'FOUNDATION_EXPORT' error.

Why this change:

The original PR that introduced the flag generate_default_umbrella_header essentially wants to not import UIKit/Foundation to restrict on what to import by default, which is a good thing. However, it kinda overkill by also not including the statements around version number and version string. Which apparently affects how clang resolves module mapping (from a module name to a path) and in our set up, leading to error similar to:

path/to/alice.modulemap:2:21: error: umbrella for module 'bob' already covers this directory

   umbrella header "foo-umbrella.h"
                    ^

For reference, this is originated from diag::err_mmap_umbrella_clash error in https://github.com/llvm/llvm-project

For our case, the umbrella header name for both alice and bob modules are foo, which is totally fine since their path is different. But for some reason when trying to compile the code in a remote execution service such as buildfarm, above error shows up. I cannot reproduce it with local or sandboxed mode.

Now looking back, the theory is that when there is clash on umbrealla header name, it will use the module's version string/number to figure out which is which? I don't really have any other theory for now. I cannot find such reference to VersionNumber/VersionString that is meaningful. Nor can I setup a test case here since it only break remote execution.

This does not revert the original PR in anyway, I still get to implicitly import Foundation/UIKit with this change.

Tests

No change on testing since this flag was not tested anywhere for now. I can add this flag to some random testing targets in this PR though but tests so far are still all passing with this turned off.

…lt umbrella header generated

Fix err_mmap_umbrella_clash error produced by clang that so far can only be reproduced by running a target that turn off generate_default_umbrella_header and compile in a remote execution service
@gyfelton gyfelton marked this pull request as ready for review August 20, 2024 20:59
FOUNDATION_EXPORT double {module_name}VersionNumber;
FOUNDATION_EXPORT const unsigned char {module_name}VersionString[];
content += """
{extern_keyword} double {module_name}VersionNumber;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice findings!

if generate_default_umbrella_header:
extern_keyword = "FOUNDATION_EXPORT"
content += """\
#ifdef __OBJC__
Copy link
Contributor

@congt congt Aug 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we always keep the else clause so that we always use FOUNDATION_EXPORT. It will be something like

if generate_default_umbrella_header:
    content += """\
#ifdef __OBJC__
#    import <Foundation/Foundation.h>
#    if __has_include(<UIKit/UIKit.h>)
#        import <UIKit/UIKit.h>
#    endif
#endif
"""

content += """\
#ifndef __OBJC__
#    ifndef FOUNDATION_EXPORT
#        if defined(__cplusplus)
#            define FOUNDATION_EXPORT extern "C"
#        else
#            define FOUNDATION_EXPORT extern
#        endif
#    endif
#endif
"""

Or something like

if generate_default_umbrella_header:
    content += """\
#ifdef __OBJC__
#    import <Foundation/Foundation.h>
#    if __has_include(<UIKit/UIKit.h>)
#        import <UIKit/UIKit.h>
#    endif
#else
#    ifndef FOUNDATION_EXPORT
#        if defined(__cplusplus)
#            define FOUNDATION_EXPORT extern "C"
#        else
#            define FOUNDATION_EXPORT extern
#        endif
#    endif
#endif
"""
else:
    content += """\
#ifndef __OBJC__
#    ifndef FOUNDATION_EXPORT
#        if defined(__cplusplus)
#            define FOUNDATION_EXPORT extern "C"
#        else
#            define FOUNDATION_EXPORT extern
#        endif
#    endif
#endif
"""

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will end having error: unknown type name 'FOUNDATION_EXPORT' error if we also replace extern with FOUNDATION_EXPORT.
this is because when generate_default_umbrella_header is off and the code has ifdef __OBJC__ is true, you won't get the FOUNDATION_EXPORT macro defined.

If we add just

#    ifndef FOUNDATION_EXPORT
#        if defined(__cplusplus)
#            define FOUNDATION_EXPORT extern "C"
#        else
#            define FOUNDATION_EXPORT extern
#        endif
#    endif

we end up having the warning: ambiguous expansion of macro 'FOUNDATION_EXPORT' error. which is sth i don't want

Copy link
Contributor

@congt congt Aug 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's weird. Could it be that we need to always define FOUNDATION_EXPORT extern when ifdef __OBJC__ is true (even if defined(__cplusplus) is true)? Could you try

if generate_default_umbrella_header:
    content += """\
#ifdef __OBJC__
#    import <Foundation/Foundation.h>
#    if __has_include(<UIKit/UIKit.h>)
#        import <UIKit/UIKit.h>
#    endif
#else
#    ifndef FOUNDATION_EXPORT
#        if defined(__cplusplus)
#            define FOUNDATION_EXPORT extern "C"
#        else
#            define FOUNDATION_EXPORT extern
#        endif
#    endif
#endif
"""
else:
    content += """\
#ifdef __OBJC__
#    define FOUNDATION_EXPORT extern
#else
#    ifndef FOUNDATION_EXPORT
#        if defined(__cplusplus)
#            define FOUNDATION_EXPORT extern "C"
#        else
#            define FOUNDATION_EXPORT extern
#        endif
#    endif
#endif
"""

That's essentially your current logic so not a big deal.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exactly, might as well be very explicit about the two code path this is going with

@gyfelton gyfelton merged commit a15336e into master Aug 21, 2024
13 checks passed
@gyfelton gyfelton deleted the export-module-map-version-number branch August 21, 2024 13:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants