Add dynamic framework to BuckSample
This PR unlocks the ability of adding dynamic frameworks which can be shared between bundles (App, Extension or Framework). It works in Buck CLI and generated workspace.
A shared module named SwiftShared is added and imported into ExampleApp and ExampleMessageExtension.
The bare bone of the BUCK rules look like this:
- The BUCK file for
SwiftShared:
apple_library(
name = "SwiftShared",
# Setting preferred_linkage to shared makes this a dylib.
preferred_linkage = "shared",
# Set the install_name so consumers of this dylib know where to find it.
linker_flags = ["-Wl,-install_name,@rpath/%s.framework/%s" % (name, name)],
configs = { "Debug": {
# Make this a dylib in Xcode
"MACH_O_TYPE": "mh_dylib",
}},
)
apple_bundle(
name = "SwiftSharedFramework",
# Specifying the the flavor "shared" makes this a dynamic framework
binary = ":SwiftShared#shared",
extension = "framework",
)
- The BUCK file for the app:
apple_binary(
name = "ExampleAppBinary",
# The app binary depends on the library
deps = [
":SwiftShared",
],
)
apple_bundle(
name = "ExampleApp",
binary = ":ExampleAppBinary",
extension = "app",
# the app bundle depends on the framework
deps = [":SwiftSharedFramework"],
)
A library is a file where binary code lives, while a framework is a directory (bundle) containing a library and some resources. ExampleAppBinary depends on SwiftShared library because the binary needs to know where the actual code is, and the dependency from ExmapleApp bundle to SwiftSharedFramework simply copies the framework to app bundle (ExmapleApp.app). That being said, an extension only needs to depend on the library to compile, not the framework, since the main app bundle is the one who copies over the framework.
Verification
-
binary from Buck CLI build has undefined symbols from
SwiftShared
-
binary from Xcode build has undefined symbols from
SwiftShared
I looked more closely to the output and found that the binary of generated project still links to a static library, although the framework is copied in the main app bundle.
I used otool to examine what dylibs are linked to the binary. The binary of Buck CLI build does link to @rpath/SwiftShared.framework/SwiftShared, while the binary of generated project does not.
(The screenshot of Buck CLI build. The binary of generated project doesn't have the highlighted line. )
Then I looked at the build phases. SwiftShared is built into both static library and framework. 😱

Great work @qyang-nj and @dfed
We made the app binary (
//App:ExampleApp) depends on both theSwitfSharedandSwfitSharedFramework. As previously stated, depending onSwiftSharedFrameworkisn't necessary for Buck CLI, but it makes generated project links to the framework. Now, generated project links to both a static library (libSwiftShared.a) and a dynamic framework (SwfitShared.framework). Based on observation, the dynamic linking overrides the static linking, which makes generated project behaves the same way as Buck CLI build.
I am not sure that the overriding is happening properly. I actually think the code is being duplicated.
The only type in SwiftShared is PublicSharedClass. I wanted to verify that this type only appears in the SwiftShared.framework binary and not the main binary.
I first ran nm on the SwiftShared binary to see what the mangled names of these types are.
They look like this in my build:
00000000000018a0 T _$S11SwiftShared06PublicB5ClassC16readFromResourceSSyF
00000000000017e0 T _$S11SwiftShared06PublicB5ClassCACycfC
0000000000001880 T _$S11SwiftShared06PublicB5ClassCACycfc
0000000000001f8c s _$S11SwiftShared06PublicB5ClassCMF
0000000000002210 b _$S11SwiftShared06PublicB5ClassCML
0000000000001830 T _$S11SwiftShared06PublicB5ClassCMa
00000000000021a0 d _$S11SwiftShared06PublicB5ClassCMf
0000000000002178 D _$S11SwiftShared06PublicB5ClassCMm
0000000000001f44 S _$S11SwiftShared06PublicB5ClassCMn
00000000000021b0 D _$S11SwiftShared06PublicB5ClassCN
0000000000001d60 T _$S11SwiftShared06PublicB5ClassCfD
0000000000001d40 T _$S11SwiftShared06PublicB5ClassCfd
0000000000001f1c s _$S11SwiftSharedMXM
I then grep'ed for those names in the ExampleApp binary, and they appear.

This indicates that the code is being duplicated, which would make sense since we still see the main app linking to libSwiftShared.a in Xcode.

Update: I was mistaken about this. See @qyang-nj's comment below.
~~It seems like these symbols are being duplicated in a Buck CLI build as well.~~
~~I built the project with make build.~~
~~ExampleApp binary~~ |
~~SwiftShared binary~~ |
|---|---|
![]() |
![]() |
Buck CLI build is expected. The U means undefined, which will be linked at runtime.
@qyang-nj and I learned that https://github.com/airbnb/BuckSample/pull/53 caused the code duplication described in https://github.com/airbnb/BuckSample/pull/52#issuecomment-474643614
@bachand This PR should be working for both Buck CLI and generated Xcode project. I'd like you to take a closer look and validate it.
Looking through open PRs where I'm tagged: should we close this?

