Frameworks in external vs Framework loading in Standalone.app
For the syphon externals we include our Syphon framework in the external .mxo, in the normal Contents/Frameworks location. The framework is built so it links using @loader_path so the executable running the bundle can look in its app bundle, or the mxo bundle for the Syphon.framework. The @loader_path is :
A user is attempting to build a standalone is getting the following error:
jit.gl.syphonclient: unable to load object bundle executable 2010-12-07 21:37:49.239 VPT_5.0a05[18916:207] Error loading /private/var/folders/Gi/GiTf5ITdHWm++psK9TEs5++++TQ/TemporaryItems/appRuntime-mxt/jit.gl.syphonclient.mxo/Contents/MacOS/jit.gl.syphonclient: dlopen(/private/var/folders/Gi/GiTf5ITdHWm++psK9TEs5 ++++TQ/TemporaryItems/appRuntime-mxt/jit.gl.syphonclient.mxo/Contents/MacOS/jit.gl.syphonclient, 262): Library not loaded: @loader_path/../Frameworks/Syphon.framework/Versions/A/Syphon Referenced from: /private/var/folders/Gi/GiTf5ITdHWm++psK9TEs5++++TQ/TemporaryItems/appRuntime-mxt/jit.gl.syphonclient.mxo/Contents/MacOS/jit.gl.syphonclient Reason: image not found
Which makes it look like the app / standalone runtime is putting the externals in a temp folder in /private/var/folders/xxxxx , and somehow that is either confusing the @loader_path (?) or something else is going wonky.
I asked the user to include the Syphon.framework in to their standalone app bundles .app/Contents/Frameworks/ folder, but they still error out.
What is the right way to include a 3rd party framework into a Standalone app, and the MXO bundle so that it works both in Max 5 editor, and in a standalone app?
Thanks so much for any information.
I don’t know that there is a ‘right’ way to handle the framework/dylib linking on the Mac. I can only assure you that this kind of linking stuff on the Mac is somewhere between a major headache and a minor disaster.
What I do for Tap.Tools is install my dylibs (basically the same as framework, ignoring details) inside of both the Max and Max Runtime application bundles. Then when the standalone is built, and the Runtime is copied into it, the dylibs are there and ready to go with no extra work on the part of the user.
Because I do it this way, I can use the much more predictable @executable_path rather than the mercurial (and no supported on all OS versions) @loader_path.
With the collective format, what happens is that the ‘data’ from the external object bundles are copied into a monolithic collective file and then somehow Max needs to be able to load the executable code at runtime. No guarantees are made as to how/where/what mechanism will be used to load the external executable code out of collectives/standalones/plug-ins and the mechanism is subject to change without notice.
There is a problem with what I described for using the @executable_path, however, which is that Max-for-Live has an @executable_path of the Live application bundle. There are a few ways to work around this depending on your appetites and/or ability to stomach certain compromises.
What I do in the current version of Tap.Tools is install a copy of my dylibs to a global location. Then I have a Max extension (an external which is loaded when Max launches) that checks to see if the dylib is able to be found. If it isn’t, then it copies it from the global location into the correct location so that when future objects are loaded they can link to it successfully.
Is this fun? No.
If you do what you’re told, write all your code in Cocoa and link only to Apple frameworks in global locations (on only the latest OS, etc), all will go well with you. If you want to start supporting multiple/scoped versions of things in non-standard and/or non-global locations then it quickly becomes an uphill climb.
Good luck on your adventure.
Is this fun? No.
That about sums it up. Thanks for the explanation of your workflow and ‘trials’. Sounds absolutely awesome.
God damn it. @loader_path encapsulates @executable_path so that it works for both plugin bundle paths and app bundle paths (im targeting 10.6 only due to some OS specifics im using so @loader_path is in theory the ‘better’ choice, and to my understanding should just work), so im not sure why the hell this does not. I can tell you right now I am not bothering with a custom external to copy a framework to a location. Thats crazy. Im sorry you had to deal with that. I’ve got better things to do :P
Can install_name_tool update the @executable or @loader path to the proper location? I dont even know what the hell it should be, which is part of my confusion. Why would a user making the proper Frameworks folder in the Runtime app bundle *not work*.
What are those temp file locations?
Externals which are added to a standalone by default are added to the collective file (a single packaged file containing patches and externals). The externals are extracted to a temporary location while loading the collective. In all likelihood, the collective file doesn’t support these embedded frameworks, either in packaging them within the collective, or in their extraction, or in their linking when extracted to a temporary location.
Unfortunately, I don’t have time to fully investigate this at the moment to verify exactly what the behavior is with embedded frameworks, or whether there’s some other workaround for this issue.
One approach can be to exclude the externals from the collective file and add manually (or in bulk) to the package’s support folder. This is a hassle for the user, but is possible now without any additional programming on either your or our part. Excluding externals can be accomplished by an undocumented feature, discussed in this thread (quickly, "excludeexternals true" at top of the collective script):
For a future version of Max, we can investigate supporting these kinds of embedded frameworks better, and/or making it easier to exclude one single external file from the collective.
Hope this helps.
Thanks. This was solved by having the user put the framework in the Runtime’s Contents/Frameworks path prior to running the build script. Not sure why having it there before made a diff.
Thanks Tap & JKC.
I can’t get it right,
Please, where is my mistake ?
- I copy the Syphon.framework folder I found in jit.gl.syphonclient.mxo package into the Contents/Frameworks folder of the MaxMSP Runtime package ;
- I build my app including jit.openexr.mxo, I get this message in the max window :
Starting Build for syphonclient5.app…
Copying External panel
Copying External fpic
Copying External jit.gl.syphonclient
Copying External jit.window
Copying External metro
Copying External jit.gl.render
Copying External toggle
Copying External jit.gl.videoplane
Copying External button
Copying External comment
Copying External message
Copying External jit.fpsgui
Copying External prepend
Copying External umenu
Copying External jit.pwindow.mxo
Copying External jit.openexr.mxo
Copying folder interfaces
- I’m happy ;
- I put the Cg.framework folder into the Contents/support folder of my app ;
- I run my app and …
jit.gl.syphonclient: unable to load object bundle executable
2010-12-21 00:08:14.565 syphonclient5[437:207] Error loading /private/var/folders/7+/7+bzfNT6EtGBEZS960RX4++++TM/TemporaryItems/appRuntime-mxt/jit.gl.syphonclient.mxo/Contents/MacOS/jit.gl.syphonclient: dlopen(/private/var/folders/7+/7+bzfNT6EtGBEZS960RX
4++++TM/TemporaryItems/appRuntime-mxt/jit.gl.syphonclient.mxo/Contents/MacOS/jit.gl.syphonclient, 262): Library not loaded: @loader_path/../Frameworks/Syphon.framework/Versions/A/Syphon
Referenced from: /private/var/folders/7+/7+bzfNT6EtGBEZS960RX4++++TM/TemporaryItems/appRuntime-mxt/jit.gl.syphonclient.mxo/Contents/MacOS/jit.gl.syphonclient
Reason: image not found
- I check the Syphon.framework is in the package of my app – it is ;
- I’m sad ;
Max 5.1.7 (44134) – OS 10.6.4 – MacBook Pro 2.5 GHz Intel Core 2 Duo