Difference between revisions of "macOS Frameworks"

From Lazarus wiki
Jump to navigationJump to search
(→‎Linking a Framework: Fix typos; update to new Wiki syntax highlighting)
Line 30: Line 30:
 
* -F is only used for the developer machine at link-time. It doesn't affect how the framework is searched at the run-time
 
* -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==
+
== Distributing Application with a Framework ==
 +
 
 
[[Image:frameworkconfigureoption.png|thumb|Configuring search and rpath for frameworks in Lazarus]]
 
[[Image:frameworkconfigureoption.png|thumb|Configuring search and rpath for frameworks in Lazarus]]
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.  
+
One of the beauties of the '''bundles''' system in macOS is that the application hides all of its necessary resources within a [[Application Bundle|bundle]]. Resources include library dependencies, such as third-party Frameworks, which removes the need for the end user to have to install separately any needed third-party framework in their macOS system.  
  
 
(NEVER distribute system frameworks with your application!)
 
(NEVER distribute system frameworks with your application!)
Line 52: Line 52:
 
(for iOS the placement is similar, but different)
 
(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.  
+
However, such framework will need to be loaded in runtime. Thus the linker MUST BE given an instruction to specify the "Frameworks" folder as a runtime framework load path.
 +
 
 +
This is done by specifying additional linker command:
  
This is done by specifying additional linker command
 
 
  -rpath @loader_path/../Frameworks
 
  -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===
+
* "@loader_path" - is a special value that is recognized by the dynamic loader (see "man dyld") during the application 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 bundle.
 +
 
 +
=== 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
 
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  
 
  user@user-mac MacOS % ./myapp  

Revision as of 08:10, 15 February 2021

A framework is a bundle (a structured directory) that contains a dynamic shared library along with its associated resources, such as image files and header files. When you develop an application, your project links to one or more frameworks. Your code accesses the capabilities of a framework through the application programming interface (API), which is published by the framework through its header files. Because the library is dynamically shared, multiple applications can access the framework code and resources simultaneously. The system loads the code and resources of a framework into memory, as needed, and shares the one copy of a resource among all applications. A special kind of Framework is a system Plugin (or driver).

Distributing dynamic libraries as Frameworks is unique to macOS. All macOS system APIs come in the form of a framework. Many third-party libraries also come in the form of Frameworks for macOS (eg SDL) while providing an option to be loaded as a regular dynamic library.

Linking to a Framework

The framework can be linked either via a source code:

{$linkframework SDL2}
{$linkframework SDL2}

or via a linker instructions.

-framework SDL2

(for FPC each parameter should be prefixed with "-k")

-k-framework -kSDL2

Since the concept of Frameworks exists in macOS only, these instructions should be carefully isolated from other systems (otherwise they might produce compile time or link time errors).

If the linker produces an the error such as "framework cannot be found", the search path for the framework must be specified (using the "-F" linker 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.
  • any 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

Configuring search and rpath for frameworks in Lazarus

One of the beauties of the bundles system in macOS is that the application hides all of its necessary resources within a bundle. Resources include library dependencies, such as third-party Frameworks, which removes the need for the end user to have to install separately any needed third-party framework in their macOS system.

(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 runtime. Thus the linker MUST BE given an instruction to specify the "Frameworks" folder as a runtime framework load path.

This is done by specifying additional linker command:

-rpath @loader_path/../Frameworks
  • "@loader_path" - is a special value that is recognized by the dynamic loader (see "man dyld") during the application 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 bundle.

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).

See Also