I’ve stumbled upon the following question in a forum:
My application uses multiple frameworks. Each framework in turn is dependent on one or more other frameworks. This is achieved using symbolic links. I used this structure to reduce the application’s size.
See MyApp.app bundle structure as below:
myApp.app / Contents / Frameworks/ ABC.framework / ABC (symlink to current version) Resources (symlink to current version) Versions/ A / ABC Resources/ Current / (symlink to A) Frameworks / (symlink to myApp.app 's Framework folder) XYZ.framework/ ABC (symlink to current version) Resources (symlink to current version) Versions/ A / ABC Resources/ Current / (symlink to A) Frameworks / (symlink to myApp.app 's Framework folder) Info.plist MacOS/ pkgInfo Resoures/When I code sign MyApp.app and validate with command:
"codesign -vv / Path_To_Application_Bundle"
It gives below error all the time:
“unsealed contents present in the root directory of an embedded framework”
The Problem
What’s wrong with this setup is an incorrectly-configured framework bundle, which leads to incorrectly-configured application bundle. As a file structure, a bundle must not link to outside files. In turn, code-signing can’t handle this non-standard structure.
From the setup shown above, it looks like ABC.framework
is trying to use code from XYZ.framework
. The original poster solved this by creating symbolic links (probably a practice carried-over from Linux). However this isn’t the right way to do it on macOS.
Similarly shared resources of a framework would need to be linked via code and served that way. Not by symbolic links since this would violate the bundle structure.
Solution to the Linking Problem
When a framework needs to use code from other frameworks, then the standard method is to configure its runpath search path (that is LD_RUNPATH_SEARCH_PATHS
). This is a list of paths that the dynamic linker (dyld
) would look for frameworks and libraries.
If both frameworks belong to the same application, then setting up the runpath search path is pretty straightforward. Configure LD_RUNPATH_SEARCH_PATHS
of the framework to have @executable_path/../Frameworks
as one of the designated search paths. Afterwards package those frameworks in the main application bundle’s Contents/Frameworks
directory. Remember that the main application’s executable is in the Contents/MacOS
directory – therefore @executable_path/../Frameworks
points to the app bundle’s standard directory that contains frameworks and plain dynamic libraries.
For more information on these run paths, type man dyld
on the Terminal to show the dynamic linker’s manual page and read up the Dynamic Library Loading section.
Shared Resources in Frameworks
However if the framework need to use non-code resources from other frameworks, then you’ll need to link it using code. Wrap it using a function which returns the resource that the framework has. Then the consuming library or framework would just need to call this function to obtain the resource.
Let’s say there are two frameworks contained within an application bundle. Then one framework would provide images or other resources to other frameworks or even the main executable.
SharedAssets.framework
– contains shared images and other assets.Consumer.framework
– uses resources from the above framework.
Then you create a function in SharedAssets.framework
to wrap the resources that it has. For example, a function that exposes image resources in the framework could look like the following snippet:
// Marker class to get the current framework bundle fileprivate class Dummy: NSObject {} public func GetFooImage() -> NSImage { let bundle = Bundle(for: Dummy.self) let imageURL = bundle.url(forResource: ... ) let image = NSImage(byReferencingURL: imageURL) return image }
Thus getting an the image from SharedAssets.framework
into Consumer.framework
would be as simple as importing the library and calling the function:
// in `Consumer.framework` import SharedAssets func ... { ... let fooImage = GetFooImage() ... // use the `fooImage` object }
Next Steps
Have a look at your project’s custom build scripts and make sure that all bundles are self-contained. Also have a double check on all of your frameworks’ run path settings and ensure that they make sense.
0 thoughts on “Code Signing Failure due to Symlink Folders”
You must log in to post a comment.