Bundling a camelbones app

Matts on 2005-06-09T15:01:24

Bundling a CamelBones app should be relatively straightforward - Mac OS X already comes with perl, so all you need is the CamelBones framework plus any relevant perl modules.

CamelBones is nice enough to add the Resources directory to @INC, along with Resources/$perlversion, so doing this should be easy.

It does get complicated though. So here's a step by step process:

####

First you have to remove the default CamelBones.framework from your XCode project, and add in the one in /Developer/CamelBones/Frameworks/

Next you have to right click on Targets and select "Add"->"New Build Phase"->"New Copy Files Build Phase". Drop down the selector in the dialog that opens and select "Frameworks" and close the dialog.

Next you drag the new CamelBones.framework from the Frameworks list into the new Copy Files build phase you just created.

####

That takes care of the framework. Now we need to do the perl modules. Unfortunately here I think I uncovered a shortcoming of XCode which makes this more complicated than it needs to be.

For the purposes of this mini tutorial I'm going to stick with a .app that only works on whatever OS you're running. If you need to make it work on other OS's you'll have to do the below in Resources/$version for multiple versions of perl.

####

First, determine what modules you need to add in. A good way might be Module::ScanDeps with the scandeps.pl script. But often only testing will show what you are missing (for this you need a separate Mac with no perl modules installed and no CamelBones installed - yes I know this is a pain).

Under "Other Sources", right click: "Add"->"New Group". Name this darwin-thread-multi-2level. Right click on that new group and add another group, call it "auto".

Right click on "Other Sources", "Add"->"Existing Files...". When the dialog pops up hit "/", and enter "/Library/Perl" into the dialog. Find the root of your perl module in the $version directory. Click OK with the directory of the perl module selected. When the dialog pops up, you want the second option: "Create Folder References for any addded folders".

If too many files get added in, just delete what you don't need.

Repeat for any other modules.

Now on your Target, add another "Copy Files" build phase. In the dialog select the "Resources" destination and leave the subpath blank. Drag your new perl modules from your Other Sources section into this new build phase.

The next 2 steps are only necessary if you have XS modules (or if the above modules were part of an XS module).

Now repeat the above step for the files in the darwin-thread-multi-2level directory, putting them in the darwin-thread-multi-2level group.

Create a new Copy Files build phase. Select the "Resources" destination, and set the subpath to "/darwin-thread-multi-2level". Drag the new files from the above group into this build phase.

Repeat again for the darwin-thread-multi-2level/auto/ group. Add another Copy Files build phase for these files, setting the subpath to "/darwin-thread-multi-2level/auto".

####

That's it. You're done. Verify by doing a build and shipping the .app to your clean mac (or someone else's clean mac). You can also check the perl modules are in the right place in your .app directory.

Here's a screen shot of what it should all look like.

BTW: I'm not subscribed to the CamelBones mailing list, so if someone wants to post a link to this journal entry there it might be of use to other CamelBones users.


Bundling using PAR

blech on 2005-06-09T16:26:52

Although not a CamelBones app, Blue Coconut includes a lot of Perl libraries. I use the following Shell Script step in Xcode to use PAR to bundle the required resources:

pp -I ./perl_lib -p -o BlueCoconut.par BlueCoconut.pl && unzip -o BlueCoconut.par -d par && rm -rf build/Blue\ Coconut.app/Contents/Resources/perl_lib && mv par/lib build/Blue\ Coconut.app/Contents/Resources/perl_lib && rm -rf par

Step by step: use pp (PAR packager) to make a PAR file for BlueCoconut.pl; unzip it into the par directory; remove the old perl_lib from the resources part of the app bundle, then move the new one in; then remove the remainder of the par folder.

This obviously needs work to make it more general (and more elegant, perhaps) but it works for me (with an additional script to handle one library that PAR gets confused on).

Re:Bundling using PAR

Matts on 2005-06-09T18:43:55

I did think about PAR, but there's a chicken and egg problem with it. Since you can't pp to create a .pm that contains all your modules, only pp to create an exe, you need to to distribute PAR with your application. Same problem, different strokes.

Re:Bundling using PAR

blech on 2005-06-09T18:53:33

Ah, but the way I do things doesn't require PAR to be shipped, because I unpack the PAR zipfile out to the application's bundle. All I need to do to make sure that the application can find the libraries is to make sure that's copied alongside the perl_lib folder in the resources, and then modify @INC:

BEGIN {
    my $lib = File::Spec->catfile($Bin, "perl_lib");
    unshift @INC, $lib;
}


Also, the use of (I think) -I means that I don't get a full executable, but a zipfile. (I really should look back over what that shell script does and provide a better explanation somewhere, but at least it's now out there for people to find.)

Can I steal this for the CB web site?

Sherm on 2005-06-09T17:17:19

Matt - great write up. Thanks! Is it OK with you if I copy it to the CB site? I'll give you credit, naturally. Or if you'd prefer, I'll simply add a link to this journal entry.



BTW, the first step (removing and replacing the framework, and adding the "copy files" build phase) shouldn't be necessary with the latest 1.0 beta, released last week. I updated the project templates to use the embedded framework by default - I'll double-check to verify that the latest templates are included in the package.

Re:Can I steal this for the CB web site?

Matts on 2005-06-09T18:46:06

Sure - feel free to steal it (and the .jpg).

I didn't know the first step was already done - I simply followed advice found on the mailing list somewhere. Though you do have to do the "Copy Files" stage for the framework.