I've reached the limit of my sanity with this one, despite reading everything there is around Carthage and static Swift frameworks. I sense that the problem I'm facing is essentially an Xcode configuration problem. Nevertheless, since the topic is an actual one in this project, I hope someone could save me here.
Here is the demo project (I'm using Xcode 10). There's a workspace containing a FrameworkA, dependent on FrameworkB. FrameworkA has the Mach-O type set to dynamic, FrameworkB to static. In the FrameworkA folder, there's already the resulting built framework (there's a script phase that copies it over).
The App in the test folder links and embeds this framework. Try at first to build the app. You'll get a build failure on the line of import FrameworkA saying missing required module FrameworkB. Why?!?!
Appreciate your help.
P.S. If you open the Static.workspace, build the framework, then the app will start building. But I assume that's because of Xcode looking into the DerivedData somehow and finding FrameworkB. If you delete the Static's project derived data, this error will pop up again when trying to build the app.
P.S.2. Of course, running carthage archive results in the same problem.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
I am also having the same problem here. Oddly enough, this seems to succeed when built on the same machine, but when you move those artifacts to another machine, they don't work anymore.
I have seen archive behavior effect this as well. See: See: https://stackoverflow.com/a/44793192/731285
Also, see this open issue for a project that has the same issue: https://github.com/jedisct1/swift-sodium/issues/170
It's because Xcode picks it up from the DerivedData. Delete that, and the build should fail.
After a ton of digging, I believe this is a problem with how FrameworkA is built/linked.
Build the static workspace with --no-skip-current, then run
swift -F Carthage/Build/iOS/ -F Carthage/Build/iOS/Static/ -sdk `xcrun --sdk iphonesimulator12.2 --show-sdk-path` -target x86_64-apple-ios12.0-simulator -deprecated-integrated-repl
once in the repl type:
:print_module FrameworkA
as you can see there is no trace of FrameworkB apart from the import and this is what is throwing everything off
So yes, it works if you build Static.xcodeproj first because the App's project is finding stuff in derived data
From ViewController.swiftdeps
depends-external:
- "/Users/Tommaso/Code/lab/Carthage-2618/static-frameworks-problem/FrameworkA/FrameworkA.framework/Modules/FrameworkA.swiftmodule/x86_64.swiftmodule"
- "/Users/Tommaso/Library/Developer/Xcode/DerivedData/Static-fynxdwwbtsztawacmqqnsruwxyhr/Build/Products/Debug-iphonesimulator/FrameworkB.framework/Modules/FrameworkB.swiftmodule/x86_64.swiftmodule"
Honestly I don't know if this is possible at all. It's certainly not a problem with Carthage
Yeah I also think I realized it's an intrinsic Swift problem, and most probably it's the persistence of that _import_ in the binary, as @blender mentioned.
Here are 2 solutions for a dynamic framework A depending on a staticlibrary B.
library B is compiled with the framework A, a consuming client (an app or a framework) will still need to find the .swiftmodule or .modulemap file of the library B. The module information is used to expose the Swift API (or C,C++,objc) of module B when module A is imported!library B is only used for internal implementation of framework A, you can add the @_implementationOnly import B everywhere in framework A, and the requirement to know about the module information of B will disappeared for the consuming client side since the compiler does not need to expose back the API of module B (again this works if no public symbols in A refers to symbols in B)!A exports types in B, then you need to tell the compiler where to find the module / interfaces of B even when consuming A! This is why moving files around or trying to copy the framework A to an other machine without bringing the interface of B will cause a compiler issue telling you Missing required module 'B'. Simply tell the compiler where to find it the module information of B using HEADER_SEARCH_PATHS ! For example, in the jedisct1/swift-sodium#170 issue, you could add HEADER_SEARCH_PATHS = $(PROJECT_DIR)/Carthage/Checkouts/swift-sodium/Sodium (.xcconfig syntax ) or directly set it in the Xcode Project file in the Build Settings tab. framework A still knows about the absolute path of the interfaces of B, so if you update HEADER_SEARCH_PATHS for the interface of B after moving the framework A around and do not delete the original module map, you may get a build error with Redefinition of module 'B'.B in a Headers Phase (public tab) in Build Phases when Building A, this may create the needed header / module files?
Most helpful comment
Here are 2 solutions for a dynamic
framework Adepending on a staticlibrary B.library Bis compiled with theframework A, a consuming client (an app or a framework) will still need to find the.swiftmoduleor.modulemapfile of thelibrary B. The module information is used to expose the Swift API (or C,C++,objc) ofmodule Bwhenmodule Ais imported!library Bis only used for internal implementation offramework A, you can add the@_implementationOnly import Beverywhere inframework A, and the requirement to know about the module information ofBwill disappeared for the consuming client side since the compiler does not need to expose back the API ofmodule B(again this works if no public symbols inArefers to symbols inB)!Aexports types inB, then you need to tell the compiler where to find the module / interfaces ofBeven when consumingA! This is why moving files around or trying to copy theframework Ato an other machine without bringing the interface of B will cause a compiler issue telling youMissing required module 'B'. Simply tell the compiler where to find it the module information ofBusingHEADER_SEARCH_PATHS! For example, in the jedisct1/swift-sodium#170 issue, you could addHEADER_SEARCH_PATHS = $(PROJECT_DIR)/Carthage/Checkouts/swift-sodium/Sodium(.xcconfig syntax) or directly set it in the Xcode Project file in the Build Settings tab.framework Astill knows about the absolute path of the interfaces ofB, so if you updateHEADER_SEARCH_PATHSfor the interface ofBafter moving theframework Aaround and do not delete the original module map, you may get a build error withRedefinition of module 'B'.Bin aHeaders Phase(public tab) inBuild Phaseswhen BuildingA, this may create the neededheader/modulefiles?