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

Using swift_c_module in a Swift framework #190

Closed
chronal-xnor opened this issue Apr 19, 2019 · 3 comments
Closed

Using swift_c_module in a Swift framework #190

chronal-xnor opened this issue Apr 19, 2019 · 3 comments

Comments

@chronal-xnor
Copy link
Contributor

First off, I'm aware there's a roadmap item for Framework support in rules_apple and that right now it's not a supported use case for these tools. However, I'm striving to use the tools that are available to hack one together regardless.

Creating a pure Swift framework seems to be "some-assembly-required" right now, but is possible. I can take a .swiftmodule and .swiftdoc from the outputs of a swift_library target, put them in the Modules/ folder of a framework, and import it successfully into an independent XCode project.

However, if that swift_library depends on a swift_c_module, things get tricky. If you stay inside Bazel, things are nice and lovely because the '-fmodule-map-file=blah' linker argument gets passed along through the dependency chain, presumably via the SwiftClangModuleInfo provider, and your import MyCLibrary statements just work. However, if you try to use the above strategy to pack up a framework by hand, it'll fail to import in user projects with the error:

UserApplication.swift:10:8: error: missing required module 'MyCLibrary'
import MySwiftLibrary

Some inspection of the .swiftmodule using llvm-bcanalyzer --dump reveals (part of) the problem:

<INPUT_BLOCK NumWords=52 BlockCodeSize=4>
  <SEARCH_PATH abbrevid=9 op0=1 op1=0/> blob data = '/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Frameworks'
  <IMPORTED_MODULE abbrevid=4 op0=0 op1=0/> blob data = 'Foundation'
  <IMPORTED_MODULE abbrevid=4 op0=0 op1=0/> blob data = 'Swift'
  <IMPORTED_MODULE abbrevid=4 op0=0 op1=0/> blob data = 'SwiftOnoneSupport'
  <IMPORTED_MODULE abbrevid=4 op0=0 op1=0/> blob data = 'MyCLibrary'
</INPUT_BLOCK>

Namely, the Swift module declares an import dependency on MyCLibrary, but there's no SEARCH_PATH that will find the module map.

I've tried everything I can think of to declare the existence of MyCLibrary through a module.map or module.modulemap file in the resulting framework but nothing clues the "end-user" XCode project in to look for the module in the same framework.

It feels like there should be a way to incorporate the C-header/module declaration stuff in the module.map into the .swiftmodule the same way the symbols from the depended cc_library target end up incorporated into the .a, but I haven't seen anything that indicates this is possible inside or outside of Bazel.

Is there anything I can do to avoid infecting "end-user" projects with additional build settings that make my framework harder to use and expose implementation details? Or am I just doomed to make my users add an extra import path in their build settings to get this Swift/C interop detail?

@chronal-xnor
Copy link
Contributor Author

chronal-xnor commented Apr 22, 2019

A different solution for this problem:

  1. Create a swift_library target that depends on your swift_c_module target.
  2. Have the swift_c_module target's module.map declare the same module name as the swift_library's module_name
  3. Pass the copts = ["-import-underlying-module"] attribute to the swift_library to tell the compiler to look for a C/obj-c module with the same name

Then the C and Swift stuff both end up in the same module, and there's nothing extra to import.

This feels a little janky and rules_swift should probably do this implicitly. Either way, I feel that having a story about how C dependencies are handled is an important step towards allowing frameworks to be built with swift built under Bazel.

@chronal-xnor
Copy link
Contributor Author

@allevato
Copy link
Member

Frameworks are a bit of a special case in Apple land, especially w.r.t. how they interact with modules. Frameworks really only support a single module due to the search/load logic used by the compiler (they assume that the .framework name and the .swiftmodule name match, and won't look for modules of different names inside that bundle. So the solution in your earlier comment of using -import-underlying-module is really the only way to make that work.

Even if we exclude Objective-C and Apple platforms from the equation, there are situations where it's handy to have C code in the same module as Swift code (the Swift core libraries do this in a handful of places), but it's also rare enough that it hasn't been a high priority to figure out how to handle it generally when the hack above gets most of the way there, and when rules_apple is planning on handling the case there (where it is more common, especially due to the framework issue).

Since rules_apple is going to handle the common case, I'm going to close out this issue—I'll suggest following the issue over there to get a solution that handles your use case.

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

No branches or pull requests

2 participants