Do you have a more detailed guide to creating binary bundles?

Added by Plasma Cutter 12 months ago

I saw the wiki documentation on the bundles, but I'm not understanding where the dylibs are coming from. Do you have more detailed instructions on the creation of binary bundles?


Replies (1)

RE: Do you have a more detailed guide to creating binary bundles? - Added by Hermi G 12 months ago

This guide assumes you can already compile your own working mplayer executable (that you can run as ./mplayer from Terminal.app). If you aren't that far then you need a guide on compiling the mplayer executable first. I can also help with that.

You have to compile the dylibs that mplayer depends upon and then distribute them along with your compiled mplayer binary.

This is important because mplayer will depend on libraries that you have installed on your computer but which will be missing on other users' computers. The mpBinaries format provides a convenient way of packaging these dynamic library files (dylibs) with the mplayer executable, as well as other resources that MPlayer uses.

Just to recap, I'll list the structure of an mpBinary:

yourBinary.mpBinaries
   Contents
      Info.plist  (the bundle identifier must be unique to your distribution)
      Resources
        dsa_pub.pem  (for updating of the binary with Sparkle. Do not reuse Adrian's public key!)
      MacOS
        mplayer
        lib
          libass.4.dylib
          libbluray.0.dylib
          ...
          libxml2.2.dylib
          libz.1.dylib
Note: You do not need to nest mpBinaries as the rev14(1410) MPE does. In fact, it no longer makes sense to do so as there is no single-threaded mplayer (ffmpeg) any more. So don't try to put mpBinaries inside other mpBinaries.

Now on to your question. How do you know what dylibs you need to include, and where do you get them from?

Your mplayer executable contains a list of all the dynamic libraries it uses. You get this list by running a program called otool: otool -L mplayer, which produces output like this:

otool -L mplayer:
    /opt/local/lib/libiconv.2.dylib (compatibility version 8.0.0, current version 8.0.0)
    /opt/local/lib/libncurses.5.dylib (compatibility version 5.0.0, current version 5.0.0)
    /usr/X11/lib/libpng12.0.dylib (compatibility version 45.0.0, current version 45.0.0)
    /opt/local/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.5)
    /opt/local/lib/libdvdread.4.dylib (compatibility version 6.0.0, current version 6.2.0)
    /opt/local/lib/libfreetype.6.dylib (compatibility version 13.0.0, current version 13.2.0)
    /opt/local/lib/libfontconfig.1.dylib (compatibility version 6.0.0, current version 6.4.0)
    /usr/lib/libbz2.1.0.dylib (compatibility version 1.0.0, current version 1.0.5)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 125.2.10)
    /opt/local/lib/libdvdnavmini.4.dylib (compatibility version 6.0.0, current version 6.2.0)
    /System/Library/Frameworks/IOKit.framework/Versions/A/IOKit (compatibility version 1.0.0, current version 275.0.0)
    /System/Library/Frameworks/Cocoa.framework/Versions/A/Cocoa (compatibility version 1.0.0, current version 15.0.0)
    /usr/X11/lib/libXext.6.dylib (compatibility version 11.0.0, current version 11.0.0)
    /usr/X11/lib/libX11.6.dylib (compatibility version 9.0.0, current version 9.0.0)
    /usr/X11/lib/libXss.1.dylib (compatibility version 2.0.0, current version 2.0.0)
    /usr/X11/lib/libXv.1.dylib (compatibility version 2.0.0, current version 2.0.0)
    /usr/X11/lib/libXinerama.1.dylib (compatibility version 2.0.0, current version 2.0.0)
    /usr/X11/lib/libXxf86vm.1.dylib (compatibility version 2.0.0, current version 2.0.0)
    /System/Library/Frameworks/Carbon.framework/Versions/A/Carbon (compatibility version 2.0.0, current version 152.0.0)
    /System/Library/Frameworks/QuartzCore.framework/Versions/A/QuartzCore (compatibility version 1.2.0, current version 1.6.3)
    /System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL (compatibility version 1.0.0, current version 1.0.0)
    /usr/X11/lib/libGL.1.dylib (compatibility version 1.2.0, current version 1.2.0)
    /System/Library/Frameworks/CoreAudio.framework/Versions/A/CoreAudio (compatibility version 1.0.0, current version 1.0.0)
    /System/Library/Frameworks/AudioUnit.framework/Versions/A/AudioUnit (compatibility version 1.0.0, current version 1.0.0)
    /System/Library/Frameworks/AudioToolbox.framework/Versions/A/AudioToolbox (compatibility version 1.0.0, current version 1.0.0)
    /usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 227.0.0)
    /System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices (compatibility version 1.0.0, current version 44.0.0)
    /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 550.42.0)
    /System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices (compatibility version 1.0.0, current version 38.0.0)
    /System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (compatibility version 300.0.0, current version 751.53.0)
    /System/Library/Frameworks/CoreVideo.framework/Versions/A/CoreVideo (compatibility version 1.2.0, current version 1.6.2)
    /System/Library/Frameworks/AppKit.framework/Versions/C/AppKit (compatibility version 45.0.0, current version 1038.35.0)

As you can see, mplayer depends on dylibs as well as system libraries. We can be sure that every OS X user will have system libraries installed, so let's pare down the results with a | grep dylib:

otool -L mplayer | grep dylib
    /opt/local/lib/libiconv.2.dylib (compatibility version 8.0.0, current version 8.0.0)
    /opt/local/lib/libncurses.5.dylib (compatibility version 5.0.0, current version 5.0.0)
    /usr/X11/lib/libpng12.0.dylib (compatibility version 45.0.0, current version 45.0.0)
    /opt/local/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.5)
    /opt/local/lib/libdvdread.4.dylib (compatibility version 6.0.0, current version 6.2.0)
    /opt/local/lib/libfreetype.6.dylib (compatibility version 13.0.0, current version 13.2.0)
    /opt/local/lib/libfontconfig.1.dylib (compatibility version 6.0.0, current version 6.4.0)
    /usr/lib/libbz2.1.0.dylib (compatibility version 1.0.0, current version 1.0.5)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 125.2.10)
    /opt/local/lib/libdvdnavmini.4.dylib (compatibility version 6.0.0, current version 6.2.0)
    /usr/X11/lib/libXext.6.dylib (compatibility version 11.0.0, current version 11.0.0)
    /usr/X11/lib/libX11.6.dylib (compatibility version 9.0.0, current version 9.0.0)
    /usr/X11/lib/libXss.1.dylib (compatibility version 2.0.0, current version 2.0.0)
    /usr/X11/lib/libXv.1.dylib (compatibility version 2.0.0, current version 2.0.0)
    /usr/X11/lib/libXinerama.1.dylib (compatibility version 2.0.0, current version 2.0.0)
    /usr/X11/lib/libXxf86vm.1.dylib (compatibility version 2.0.0, current version 2.0.0)
    /usr/X11/lib/libGL.1.dylib (compatibility version 1.2.0, current version 1.2.0)
    /usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 227.0.0)
Note: The otool output I am listing is not the best example as it includes the X11 libraries, which I normally disable for my mpBinaries releases. I am experimenting with OpenGL output, which currently requires X11, and I am not certain how these dylibs would behave on other systems (I use this binary only internally, and so have not had to use install_name_tool at all).

You now need to create a lib folder (located within the mpBinaries structure as described above), and copy each of the dylib files from these locations into this lib folder. There are some exceptions I have found. You do not need to copy the following 4 dylibs (if they are even listed), as they are part of the basic OS X distribution. /usr/lib/libstdc++.6.dylib, /usr/lib/libgcc_s.1.dylib, /usr/lib/libSystem.B.dylib, and /usr/lib/libobjc.A.dylib needn't be copied.

At this point you will have a lib folder with all the dylibs required for your mplayer distribution which are all useless because mplayer doesn't actually know that they are in the lib folder! Adrian and I used different methods to remedy this. I think my way is easier (though perhaps not as 'clean'), so the remainder of this guide will only address my method of distributing dylibs.

The otool -L command listed the locations where mplayer will look for dylibs. We need to change that to our lib folder using the install_name_tool. The problem is that this tool can only be used in this way if the target binary (or dylib) was compiled with the linker flag -headerpad_max_install_names. This flag creates additional space within the executable (or dylib) to store the dylib paths which we need to change.

Why do I keep saying (or dylib), you might ask. This is the bad news. Many of the dynamic libraries you copied into your lib folder reference other dylibs, including each other. For example, libpng depends upon libz. This means you have to use install_name_tool on all such dylibs, which also requires recompiling them with the -headerpad_max_install_names linker flag.

You can edit the configure script or makefile for each dylib that references another which isn't normally distributed with OS X, or you can take my approach and just edit LDFLAGS globally in ~/.profile. Here is what my .profile looks like:

cat ~/.profile
# MacPorts Installer addition on 2011-05-07_at_12:55:00: adding an appropriate PATH variable for use with MacPorts.
export PATH=/opt/local/bin:/opt/local/sbin:$PATH
# Finished adapting your PATH environment variable for use with MacPorts.

export LDFLAGS="-headerpad_max_install_names" 
With this linker flag set, every executable or dylib you compile (after this point) can be edited with install_name_tool to adjust the dylib references to the correct path (the lib folder). So recompile mplayer and let's see if it worked (use a new Terminal window for the change to .profile to take effect!).

Fixing Binary References:
Your newly compiled mplayer now has space for install_name_tool to work. You might want to read its manpage, but the syntax is install_name_tool -change target_lib destination input_file. For example:

install_name_tool -change /opt/local/lib/libz.1.dylib @executable_path/lib/libz.1.dylib mplayer

We have replaced the reference to /opt/local/lib/ with a reference to @executable_path/lib/. @executable_path is a token that expands to the path of mplayer, which will be located in the same path as the lib folder, so the executable will be able to find the dylibs when it runs.

Now before you go crazy with install_name_tool, wait! I have a script that will save you a lot of time and potential typos. Finish reading this paragraph, but don't actually run the commands. You should run install_name_tool on mplayer for each dylib that mplayer depends upon, with the exceptions I mentioned above. Use otool -L mplayer to verify that you have updated the path correctly. The install_name_tool -change command is a simple text substitution, so if you make a mistake, just put whatever path you want to replace as the first parameter with the correct path as the second. Now that script:

#! /bin/sh
#
# otool_bin.sh - Changes absolute library references to relative ones
#                 This allows the binary to be distributed to other users.
#                 Subsequent use of the script verifies proper dylib presence.
#
#                 ! This script must be executed from mplayer's directory. !
#
# Author: Hermi
#

ignoreLibs="/usr/lib/libSystem\.B\.dylib|/usr/lib/libobjc\.A\.dylib|/usr/lib/libgcc_s\.1\.dylib|/usr/lib/libstdc\+\+\.6\.dylib" 

libs=$(otool -L mplayer | grep -Eo "^.+\.dylib" | grep -Ev "$ignoreLibs")
for ref in $libs
do
  lib=$(echo $ref | grep -Eo "[^/]+\.dylib")

  if [ $(echo $ref | grep -o "/opt/local/lib/gcc45/") ]; then
    echo "$ref\t=>\t/usr/lib/$lib" 
    install_name_tool -change $ref /usr/lib/$lib mplayer
  elif [ ! $(echo $ref | grep -o "@executable_path") ]; then
    echo "$ref\t=>\t@executable_path/lib/$lib" 
    install_name_tool -change $ref @executable_path/lib/$lib mplayer
  fi
done

libs=$(otool -LX mplayer | grep -Eo "^.+\.dylib" | sed "s/@executable_path\///")
for ref in $libs
do
  if [ ! -e $ref ]; then
    echo "## ERROR: Could not locate '$ref'." 
  fi
done
You must place the script in the same directory as your mplayer binary along with the lib folder (you may of course use symlinks if desired). Execute the script with ./otool_bin.sh – the paths to mplayer and lib/ are hardcoded in the script. You may need to add additional libraries to the ignore list (or change the gcc45 reference) depending on your system configuration. The script will convert all dylib references to relative ones and list any dylibs which are missing from lib/.

Fixing Dylib References:
Now we only need to sort out the references between the dylibs themselves. cd to the lib folder and run otool -L *.dylib.
Here is a portion of the output of that command:

otool -L *.dylib
...
libxml2.2.dylib:
    @executable_path/lib/libxml2.2.dylib (compatibility version 10.0.0, current version 10.8.0)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 125.2.1)
    @executable_path/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.5)
    @executable_path/lib/libiconv.2.dylib (compatibility version 8.0.0, current version 8.0.0)
libz.1.dylib:
    @executable_path/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.5)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 125.2.1)
As you can see, I have already used install_name_tool to fix these dylibs. Your output will not have @executable_path references. libxml2 depends on both libiconv and libz, so both of these references had to be changed with install_name_tool. The references to libSystem is safe to ignore, as it is part of a standard OS X distribution. You'll also note that each dylib references it's own location. I am not 100% certain that you have to change these self-references in order to distribute a functioning binary. I think these self-refrences are only used by the linker when linking against these dylibs, so you wouldn't have to bother with install_name_tool or -headerpad_max_install_names if you modified this self-references before linking against the dylib (I think this method requires more work though). I decided to change every dylib's self-reference to the @executable_path version for good measure.

Note: Look carefully at the otool -L output for your dylibs. Some of your dylibs may depend upon other dylibs which mplayer itself does not. You need to track down these 'second order' dylibs and move them into the lib folder as well, then repeat this step until every reference is either present in the lib folder or is a system dylib or framework which you can be sure will be present on every end-user's filesystem.

Of course, before you can run install_name_tool on your dylibs you have to recompile them with -headerpad_max_install_names in your LDFLAGS. For every dylib in your lib folder you will have to recompile and then recopy the updated dylib back into your lib folder. I suggest making use of Finder labels to keep track of which dylibs you have replaced. Color all the old versions of the dylibs so when you copy over the new versions (which are compiled with -headerpad_max_install_names) your files should end up unlabeled.

At this point I assume that your mplayer executable only refers to system frameworks, built-in OS X dylibs, and the dylibs found in lib which are now referred to as @executable_path/lib/*.dylib. Your lib folder should contain all the dylibs whose references had to be modified in the mplayer executable, as well as all 'second order' dylibs, and they should have all been compiled with -headerpad_max_install_names.

Now, stop. Don't start using install_name_tool on those dylibs! I have a script that will do all the work for you. I also had one for the mplayer executable, but unfortunately it is on a computer I won't have access to until June (I'll update this post then).

Save the following bash script as otool_libs.sh in your lib folder.

#! /bin/sh
#
# otool_libs.sh - Changes absolute library references to relative ones
#                 This allows the libraries to be distributed to other users.
#                 Subsequent use of the script verifies proper dylib presence.
# Author: Hermi
#

ignoreLibs="@executable|/usr/lib/libSystem\.B\.dylib|/usr/lib/libobjc\.A\.dylib|/usr/lib/libgcc_s\.1\.dylib|/usr/lib/libstdc\+\+\.6\.dylib" 

if [ $# -gt 0 ]; then
  for file in $@
  do
    echo " > $file:" 
    libs=$(otool -LX $file | grep -Eo "^.+\.dylib" | grep -Ev "$ignoreLibs")
    for ref in $libs
    do
      lib=$(echo $ref | grep -Eo "[^/]+\.dylib")
      if [ $(echo $lib | grep "$file") ]; then
        echo "\t$ref\t=>\t@executable_path/lib/$lib" 
        install_name_tool -id @executable_path/lib/$lib $file
        continue
      fi
      if [ $(echo $ref | grep -Ev "libgcc_s\.1\.dylib|libstdc\+\+\.6\.dylib") ]; then
        echo "\t$ref\t=>\t@executable_path/lib/$lib" 
        install_name_tool -change $ref @executable_path/lib/$lib $file
      else
        echo "\t$ref\t=>\t/usr/lib/$lib" 
        install_name_tool -change $ref /usr/lib/$lib $file
      fi
    done

    libs=$(otool -LX $file | grep -Eo "^.+\.dylib" | sed "s/@executable_path\/lib\///")
    for ref in $libs
    do
      if [ ! -e $ref ]; then
        echo "## ERROR: Could not locate '$ref'." 
      fi
    done
  done
else
  echo "usage:  $0 *.dylib" 
fi

Execute the script as ./otool_libs.sh *.dylib (you need to be in the lib folder for it to work).
The script should change all references to relative ones, and running it a second time will verify that it worked by pointing out unresolved references (with ERROR could not locate $ref messages).

Note: You may need to edit this script if your dylibs reference different system libraries (or different versions) than mine did. I am referring to the dylibs in the ignoreLibs variable, and those in the grep that excludes the libgcc and libstdc++ dylibs. I'm not sure why I didn't try to reuse ignoreLibs here. It is possible my other computer has an updated version of this script.

If all went according to plan, you should now have a working mpBinaries bundle, which should be distributable on any Mac with the proper OS X version (I believe 10.4 or 10.5 is the minimum that MPE supports). I strongly suggest testing your binary out with a different computer, preferably one without dev tools installed, to make sure that your dylib references work properly. I made this mistake myself and accidentally released a binary that would only run on my computer.

I hope this helped!

- Hermi

(1-1/1)