Flag -import-underlying-module Does Not Work

The -import-underlying-module build flag causes implicit imports of Objective-C files into Swift, within the same module. This flag unfortunately doesn’t work with Buck.

When Xcode generates frameworks, it generates module.modulemap and .hmap header map files to indicate header locations. The swift tool later uses these files to import Objective-C headers. However, since Buck doesn’t generate independent frameworks, it doesn’t generate these files. Thus, the -import-underlying-module flag doesn’t work in the swift tool.

This means that we have to explicitly pass in bridging headers to the swift tool. Doing this, however, results in a few more problems.

Unable to Use Bridging Header

Consider this example: A.h contains the line #import “B.h” , but B.h is put under folderB/ . This works perfectly with Xcode with the help of .hmap . But in Buck, this doesn’t work since it is not able to locate B.h .

In this PR, we updated Buck to allow it to generate header maps for use by the swift tool, allowing the tool to locate header files and import them.

Unable to Locate Bridging Header Inside *-Swift.h

When the swift tool generates *-Swift.h files, it explicitly imports bridging headers for Objective-C definitions. This breaks Buck builds.

According to Apple’s code, when using the -import-underlying-module flag, the generated *-Swift.h files import project headers. For example:

When providing bridging headers explicitly however (as we need to do with Buck) the generated *-Swift.h files end up importing bridging header files directly:

As you can see, the imported path is a relative one. When another file imports this *-Swift.h file, it won't be able to locate the bridging header.

In this commit, we updated Buck to pass -iquote buckRootPath as a compiler argument. That specifically tells the swift tool to look for the bridging header files at buckRootPath .

@import Does Not Work

There are two ways to import header files into Objective-C, #import and @import . @import doesn’t work in Buck since Buck doesn’t generate module.modulemap .

This actually requires us to replace @import M with #import <M/M.h> and/or #import <M/M-Swift.h> . This is simple enough for our own source code. However, it's a bit trickier for generated code. *-Swift.h files, for instance, always use @import .

To address this, we used an admittedly hacky solution, introducing this script to perform the replacement on the fly. In this commit, we added to Buck’s apple_library build rule a new objc_header_transform_script parameter, which allowed us to invoke the replacement script on all the *-Swift.h files.

This change knocked down our last big blocker for using Buck.