Ok, the system of loading frameworks I talked about in my previous post doesn't always work. In fact, it can be kind of terrible. Here's and example that I came up against recently that proved very difficult to solve, at least in a clean hi-I'm-a-mac sort of way.
What I want to do was create a screen saver plug-in. So, what would the
@executable_path be in this case? After a little poking around, it appears to be the application that runs and dynamically loads the plug-ins is the ScreenSaverEngine.app, located here:
That's not very helpful for loading a plugin bundle's frameworks. Any sort of
@executable_path relative directory would not be a good place to put any of my bundle's resources since they wouldn't be encapsulated and wouldn't allow a drag-installer. Well, then, let's turn our attention to the
@loader_path. This is promising, the loader path would be the location that my plug-in executable is loaded from. Perfect.
Not so fast
But wait, I'm including a framework that itself requires a framework. In the stand-alone-application version of this (i.e. not a plugin) I can put both of these frameworks in the application's
Contents/Frameworks/ directory and we'll be all set. Then the executable in the framework can load the second framework relative to the
@executable_path/../Frameworks but this breaks down for a plug-in. In this case, the
@loader_path of the second framework is the directory containing the main executable of the first framework, not the plugin. However, the plugin needs to link to this framework too (it is a utility framework). The
@loader_framework now resolves to two separate places, the plug-in executable and the first framework executable. The
@executable_path is equally useless, as described earlier. What to do...?
Three (bad) solutions
Well, I came up with three solutions, all with nasty side effects.
- First, we could take the executable code out of the two framework bundles and then put it in the plug-in's
Contents/MacOSdirectory. Then the
@loader_pathwould resolve to the same location for all three pieces. This essentially throws away all of the benefits of frameworks. However, in our case, we don't ship headers or resources in these frameworks and we could live without the versioning capabilities. This might work.
- Second, we could put the frameworks in a known, system-wide location. For example,
/System/Library/Application Support/Sidelight/...or something. This would require shipping a package installer to install a screen saver plug-in. Bad. It would also prevent a drag-uninstaller from ever working. Also bad. This solution won't work.
- Third, we could include the framework twice. Once in the plug-in's Frameworks directory, and another in the Framework's Frameworks directory. The framework we are using is about 10MB. Even with zippy broadband access this seems like a profligate use of disk space and RAM (since both frameworks would have to be loaded). This also might cause problems with conflicting symbol names. I didn't go down this path far enough to try it out.
There you have it. Three pretty terrible solutions. The first one seems the best, but not satisfying.
The real solution
So a not-so-bad solution to this problem would be to statically link the libraries we are using. There would be an initial hit at load time, but since this is a non-interactive application, it would be tolerable. Or, perhaps the real solution is to re-architect the code to remove the second framework. Most of its functionality is already included in the Cocoa frameworks. I just need a way to abstract the utility interface so I can maintain my cross-platform code base and then use Cocoa for the utilities rather than a cross-platform utility framework.
They're still sorta great
Frameworks are still pretty good, especially if you are Apple. When you are Apple you get your own reserved Frameworks directory, to which everyone can link. If you are not Apple, frameworks are great as long as you don't have two levels of dependent frameworks in a loadable bundle. Is this a fringe case? Yes, I think so. But is seems pretty important to me.