macOS Frameworks
todo: please write a better description of what the framework is
macOS provides a unique way for distributing dynamic libraries: Frameworks.
Frameworks are actually a set of dynamic libraries and corresponding resources to them. A special kind of Frameworks are system Plugins (or drivers).
All macOS system APIs come in the form of a framework.
Many third-party libraries come in a form of Frameworks for macOS (i.e. SDL), while providing an option to be loaded as a regular dynamic library
Linking a Framework
The framework can be linked either via a source code:
{$linkframework SDL2}
or via a linker instructions. (for FPC each parameter should be prefixed with "-k")
-framework SDL2
(fpc command-line would look like:)
-k-framework -kSDL2
Since the concept of Frameworks exist in macOS only, thus the instructions should be carefully isolated from other systems (as might producer compile time or link time errors)
If the linker results in the error, such as "framework cannot be found". The search path for the framework must be specified (using linker "-F" parameter):
-F /Library/Frameworks
Note:
- the linker search path will be set to the current system-wide SDK used, as configured by Xcode command-line tools.
- the third party Frameworks should either be installed, or the path should be explicitly specified via -F parameter.
- -F is only used for the developer machine at link-time. It doesn't affect how the framework is searched at the run-time
Distributing Application with a Framework
Once of the beauty of bundles system in the macOS, is that Application hides its all resources necessary within a bundle.
All resources, including Library dependencies, such as third-party Frameworks. This removes the need from the end user to install a third-party Frameworks at their macOS separately.
(NEVER distribute system frameworks with your application!)
The typical placement of a Framework in macOS bundle is under "Frameworks" directory of "Contents"
MyApp.app/ Contents/ Frameworks/ SDL2.Framework Else.Framework Foo.Framework Info.plist MacOS/ myapp PkgInfo Resources/
(for iOS the placement is similar, but different)
However, such framework will need to be loaded in Run-time. Thus the linker MUST BE given an instruction to specify "Frameworks" folder as runtime framework load path.
This is done by specifying additional linker command
-rpath @loader_path/../Frameworks
- "@loader_path" - is a special value that's recognized by dynamic loader (see "man dyld") during the applicaiton load. For the macOS application bundle it would be the "MacOS" directory. As a result "@loader_path../Frameworks" will actually point to "Frameworks" folder of the bundler.
Troubleshooting
If "rpath" was not specified, the application might fail to start. The error log (or running from the command line) might reveal an error like that
user@user-mac MacOS % ./myapp dyld: Library not loaded: @rpath/SDL2.framework/Versions/A/SDL2_mixer Referenced from: /Applications/MyApp.app/Contents/MacOS/./myapp Reason: image not found
zsh: abort ./myapp
In order to see what RPATH was specified by the linker, simply run "otool -l myapp" command. At the end of the load commands list you should see "LC_RPATH" entry (or entries)
Load command 28 cmd LC_RPATH cmdsize 40 path @loader_path/../Frameworks (offset 12)
If you see no "LC_RPATH" then none was specifeid
Frameworks or Dynamic Libraries
Frameworks themselves are simply a special directory structure. That contain a dynamic library, and possibly headers and other resources. The same approach as application Bundles.
In many cases it's possible to load a dynamic library that resides within the framework structure in run-time, without linking the framework.
There's no restriction so far NOT to use dynamic libraries, the choice should be driven by the code maintenance tasks. Linking frameworks might be an easier when it comes to an update (as there's no need to track the version of the library, if needs to change).