| --- |
| short-description: Tools to create OS X packages |
| ... |
| |
| # Creating OSX packages |
| |
| Meson does not have native support for building OSX packages but it |
| does provide all the tools you need to create one yourself. The reason |
| for this is that it is a very hard task to write a system that |
| provides for all the different ways to do that but it is very easy to |
| write simple scripts for each application. |
| |
| Sample code for this can be found in [the Meson manual test |
| suite](https://github.com/jpakkane/meson/tree/master/manual%20tests/4%20standalone%20binaries). |
| |
| ## Creating an app bundle |
| |
| OSX app bundles are actually extremely simple. They are just a |
| directory of files in a certain format. All the details you need to |
| know are on [this |
| page](https://stackoverflow.com/questions/1596945/building-osx-app-bundle) |
| and it is highly recommended that you read it first. |
| |
| Let's assume that we are creating our app bundle into |
| `/tmp/myapp.app`. Suppose we have one executable, so we need to |
| install that into `Contents/MacOS`. If we define the executable like |
| this: |
| |
| ```meson |
| executable('myapp', 'foo1.c', ..., install : true) |
| ``` |
| |
| then we just need to initialize our build tree with this command: |
| |
| ```console |
| $ meson --prefix=/tmp/myapp.app \ |
| --bindir=Contents/MacOS \ |
| builddir \ |
| <other flags you might need> |
| ``` |
| |
| Now when we do `meson install` the bundle is properly staged. If you |
| have any resource files or data, you need to install them into |
| `Contents/Resources` either by custom install commands or specifying |
| more install paths to the Meson command. |
| |
| Next we need to install an `Info.plist` file and an icon. For those we |
| need the following two Meson definitions. |
| |
| ```meson |
| install_data('myapp.icns', install_dir : 'Contents/Resources') |
| install_data('Info.plist', install_dir : 'Contents') |
| ``` |
| |
| The format of `Info.plist` can be found in the link or the sample |
| project linked above. The simplest way to get an icon in the `icns` |
| format is to save your image as a tiff an then use the `tiff2icns` helper |
| application that comes with XCode. |
| |
| Some applications assume that the working directory of the app process |
| is the same where the binary executable is. If this is the case for |
| you, then you need to create a wrapper script that looks like this: |
| |
| ```bash |
| #!/bin/bash |
| |
| cd "${0%/*}" |
| ./myapp |
| ``` |
| |
| install it with this: |
| |
| ```meson |
| install_data('myapp.sh', install_dir : 'Contents/MacOS') |
| ``` |
| |
| and make sure that you specify `myapp.sh` as the executable to run in |
| your `Info.plist`. |
| |
| If you are not using any external libraries, this is all you need to |
| do. You now have a full app bundle in `/tmp/myapp.app` that you can |
| use. Most applications use third party frameworks and libraries, |
| though, so you need to add them to the bundle so it will work on other |
| peoples' machines. |
| |
| As an example we are going to use the [SDL2](https://libsdl.org/) |
| framework. In order to bundle it in our app, we first specify an |
| installer script to run. |
| |
| ```meson |
| meson.add_install_script('install_script.sh') |
| ``` |
| |
| The install script does two things. First it copies the whole |
| framework into our bundle. |
| |
| ```console |
| $ mkdir -p ${MESON_INSTALL_PREFIX}/Contents/Frameworks |
| $ cp -R /Library/Frameworks/SDL2.framework \ |
| ${MESON_INSTALL_PREFIX}/Contents/Frameworks |
| ``` |
| |
| Then it needs to alter the library search path of our |
| executable(s). This tells OSX that the libraries your app needs are |
| inside your bundle. In the case of SDL2, the invocation goes like |
| this: |
| |
| ```console |
| $ install_name_tool -change @rpath/SDL2.framework/Versions/A/SDL2 \ |
| @executable_path/../FrameWorks/SDL2.framework/Versions/A/SDL2 \ |
| ${MESON_INSTALL_PREFIX}/Contents/MacOS/myapp |
| ``` |
| |
| This is the part of OSX app bundling that you must always do |
| manually. OSX dependencies come in many shapes and forms and |
| unfortunately there is no reliable automatic way to determine how each |
| dependency should be handled. Frameworks go to the `Frameworks` |
| directory while plain `.dylib` files usually go to |
| `Contents/Resources/lib` (but you can put them wherever you like). To |
| get this done you have to check what your program links against with |
| `otool -L /path/to/binary` and manually add the copy and fix steps to |
| your install script. Do not copy system libraries inside your bundle, |
| though. |
| |
| After this you have a fully working, self-contained OSX app bundle |
| ready for distribution. |
| |
| ## Creating a .dmg installer |
| |
| A .dmg installer is similarly quite simple, at its core it is |
| basically a fancy compressed archive. A good description can be found |
| on [this page](https://el-tramo.be/guides/fancy-dmg/). Please read it |
| and create a template image file according to its instructions. |
| |
| The actual process of creating the installer is very simple: you mount |
| the template image, copy your app bundle in it, unmount it and convert |
| the image into a compressed archive. The actual commands to do this |
| are not particularly interesting, feel free to steal them from either |
| the linked page above or from the sample script in Meson's test suite. |
| |
| ## Putting it all together |
| |
| There are many ways to put the .dmg installer together and different |
| people will do it in different ways. The linked sample code does it by |
| having two different scripts. This separates the different pieces |
| generating the installer into logical pieces. |
| |
| `install_script.sh` only deals with embedding dependencies and fixing |
| the library paths. |
| |
| `build_osx_installer.sh` sets up the build with the proper paths, |
| compiles, installs and generates the .dmg package. |
| |
| The main reasoning here is that in order to build a complete OSX |
| installer package from source, all you need to do is to cd into the |
| source tree and run `./build_osx_installer.sh`. To build packages on |
| other platforms you would write scripts such as |
| `build_windows_installer.bat` and so on. |