I almost lost hope, but at last I figured out how to do it.
Here is how you can deploy a truly stand alone OSX application bundle out of a stand alone ROOT GUI application which you can distribute to other colleagues without a ROOT installation.
Libraries
First get ROOT from git and compile it as described in http://root.cern.ch/drupal/content/installing-root-source you don’t need to install. After that make clean and make a duplicate copy of the whole root directory and name it e.g. root_runtime. In root_runtime, delete everything except the directories lib, icons, fonts, etc and bin. In the bin directory delete everything except the script thisroot.sh and root-config.
Then go inside the lib directory and check for the dependencies of all libs to see if anyone depends on some library other than those in the /ur/lib, which come automatically with OSX. Specifically you can grep for any dependency that is in /opt/ for systems that have macports installed.
If you see any output, that means that some library has a dependency on a non-OSX-standard library and needs to be fixed. In my case these were:
/opt/local/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.8 )
/opt/local/lib/liblzma.5.dylib (compatibility version 6.0.0, current version 6.5.0)
/opt/local/lib/libpcre.1.dylib (compatibility version 4.0.0, current version 4.1.0)
/opt/local/lib/libssl.1.0.0.dylib (compatibility version 1.0.0, current version 1.0.0)
/opt/local/lib/libssl.1.0.0.dylib (compatibility version 1.0.0, current version 1.0.0)
/opt/local/lib/libcrypto.1.0.0.dylib (compatibility version 1.0.0, current version 1.0.0)
/opt/local/lib/libsqlite3.0.dylib (compatibility version 9.0.0, current version 9.6.0)
/opt/local/lib/libssl.1.0.0.dylib (compatibility version 1.0.0, current version 1.0.0)
/opt/local/lib/libcrypto.1.0.0.dylib (compatibility version 1.0.0, current version 1.0.0)
Copy these libraries to your root_runtime/lib/ directory using sudo and correct the ownership to yourself using chown and the permissions using chmod 644. Then apply the following ruby script for fix:
libs=Dir["*.dylib"] +Dir["*.so"]
libs.permutation(2).each do |first, second|
puts "Correcting #{first} inside #{second}"
system("install_name_tool -change '/opt/local/lib/#{first}' './#{first}' ./#{second}")
The Application
Open a terminal, set the environment to your original root directory (thisroot.sh) and build your own project. I took the example from the ROOT User Guide, GUI section. Then compile it and make sure it works on your system. I put the code here for convenience:
Application.cxx
[code]#include <TApplication.h>
#include “Frame.h”
void showFrame() {
new Frame(gClient->GetRoot(),200,200);
}
int main(int argc, char **argv) {
TApplication theApp(“App”,&argc,argv);
showFrame();
theApp.Run();
return 0;
}
[/code]
Frame.h
#include <TGFrame.h>
class TGWindow;
class TGMainFrame;
class TRootEmbeddedCanvas;
class Frame : public TGMainFrame {
private:
TRootEmbeddedCanvas *fEcanvas;
public:
Frame(const TGWindow *p,UInt_t w,UInt_t h);
virtual ~Frame();
void DoDraw();
virtual void CloseWindow();
ClassDef(Frame,0)
};
Frame_LinkDef.h
#pragma link C++ class Frame;
Frame.cxx
[code]#include <TGClient.h>
#include <TCanvas.h>
#include <TF1.h>
#include <TRandom.h>
#include <TGButton.h>
#include <TRootEmbeddedCanvas.h>
#include <TApplication.h>
#include “Frame.h”
Frame::Frame(const TGWindow *p,UInt_t w,UInt_t h) : TGMainFrame(p,w,h) {
fEcanvas = new TRootEmbeddedCanvas (“Ecanvas”,this,200,200);
AddFrame(fEcanvas, new TGLayoutHints(kLHintsExpandX | kLHintsExpandY,
10,10,10,1));
TGHorizontalFrame *hframe=new TGHorizontalFrame(this, 200,40);
TGTextButton *draw = new TGTextButton(hframe,"&Draw");
draw->Connect(“Clicked()”,“Frame”,this,“DoDraw()”);
hframe->AddFrame(draw, new TGLayoutHints(kLHintsCenterX,5,5,3,4));
TGTextButton *exit = new TGTextButton(hframe,"&Exit ",
“gApplication->Terminate()”);
hframe->AddFrame(exit, new TGLayoutHints(kLHintsCenterX,5,5,3,4));
AddFrame(hframe,new TGLayoutHints(kLHintsCenterX,2,2,2,2));
SetWindowName(“Simple Example”);
MapSubwindows();
Resize(GetDefaultSize());
MapWindow();
}
void Frame::DoDraw() {
TF1 *f1 = new TF1(“f1”,“sin(x)/x”,0,gRandom->Rndm()*10);
f1->SetFillColor(19);
f1->SetFillStyle(1);
f1->SetLineWidth(3);
f1->Draw();
TCanvas *fCanvas = fEcanvas->GetCanvas();
fCanvas->cd();
fCanvas->Update();
}
void Frame::CloseWindow()
{
TGMainFrame::CloseWindow();
gApplication->Terminate(0);
}
Frame::~Frame() {}
[/code]
Compile the code as described in the ROOT User Guide.
The Bundle
This has been adapted from this post: http://apple.stackexchange.com/questions/23725/is-automator-intended-to-create-distributable-stand-alone-apps. Open the automator, choose to start a new Application, then drag the following 2 actions:
-
Run apple script:
Inside you write
on run {input, parameters}
set p to POSIX path of (path to me)
return {p}
end run
in order to pass the current path to the next step. The you insert the second action:
-
Run shell script:
inside you write
source $1Contents/Resources/root_runtime/bin/thisroot.sh
$1Contents/Resources/application
make sure to select /bin/bash as shell and pass input: as argument. Replace application with the name of your binary which you produced in the previous step. Save it as an app bundle.
Final step
copy the executable binary of your code and the whole root_runtime directory inside the bundle’s Resources directory, leave everything else there. You can use Finder to “show package content” or just use terminal “cp -r”.
Voila! that’s it. It is big (about 130 MB), but it works everywhere. You can make a DMG file to compress (about half size) it and send it to colleagues:
You can further trim your root_runtime by eliminating all the libs that you are sure are not needed. The program might not run on older versions of OSX, only the version that you actually compiled your code on. For me that is fine, because most colleagues already have the latest version. You might want to experiment with compiler flags such as:
or similar to make compatibility for older versions of OSX, but I didn’t try it.
Hope someone out there likes this too.
