Setting up ROOT from Cern in Xcode, linking the li

2019-02-19 23:57发布

问题:

I want to set up ROOT from CERN in my Xcode IDE but I'm having problems linking the libraries. I'm using root 6.04.14 and xcode 7.3. I created a mock up project where I simply have a .cpp where I include a basic class from root (#include "TFile.h"). This I can compile from command line by:

clang++ -std=c++11 -I/opt/root/root-6.04.14/include/root -L/opt/root/root-6.04.14/lib/root -lCore main.cpp

Now it comes to setting up everything in the Xcode IDE. I included "/opt/root/root-6.04.14/include/root" in the header search path and Xcode is not complaining, so I guess it finds the header files. I tried adding "/opt/root/root-6.04.14/lib/root -lCore" to the library search path but I get errors: In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/cmath:301: /opt/root/root-6.04.14/include/root/Math/math.h:65:11: error: no member named 'log1p' in the global namespace; did you mean simply 'log1p'? return ::log1p(x); ^~ /opt/root/root-6.04.14/include/root/Math/math.h:63:15: note: 'log1p' declared here inline double log1p( double x) { ^ /opt/root/root-6.04.14/include/root/Math/math.h:76:11: error: no member named 'expm1' in the global namespace; did you mean simply 'expm1'? return ::expm1(x); ^~ /opt/root/root-6.04.14/include/root/Math/math.h:74:15: note: 'expm1' declared here inline double expm1( double x) {

and so on... Furthermore when I look at the terminal command Xcode is running(at least that is what I think it does) there is no "-L/opt/root/root-6.04.14/lib/root -lCore" included. I then tried to put "-L/opt/root/root-6.04.14/lib/root -lCore" into other linker flags. Now it is included in the terminal command but still giving me the same error.

Question1: I noticed Xcode is running "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang" while I have been using clang++, where is the difference and how can I change it? Question2: What is the difference between adding the directory to the library search path and putting it in via the linker flag? Question3: The big one, where do I mess up?

回答1:

You've probably already figured this out, but just in case someone else stumbles on this issue here's how I've setup my ROOT (v6.06.04) in xcode. To demonstrate, lets start from scratch with a fresh xcode project.

Say we want to run the following program in xcode (note that it uses ROOT classes)

#include <iostream>
#include "TApplication.h"
#include "TCanvas.h"
#include "TGraph.h"
#include <vector>

int main(int argc, const char * argv[]) {
    // Create a TApplication so that we can get plot the histogram
    TApplication* myApp = new TApplication("myApp", 0, 0) ;

    // Create some vector information
    std::vector<double> x(100), y(100) ;

    for (int i=0; i<x.size(); i++) {
        x[i] = i * (10.0/x.size()) ;
        y[i] = std::cos(x[i]) ;
    }

    // Create a TGraph
    TGraph* graph = new TGraph(x.size(), &x[0], &y[0]) ;

    // Create a canvas and draw the graph
    TCanvas* canvas = new TCanvas("canvas","canvas") ;
    graph->Draw("ap0") ;
    canvas->Update() ;

    // Run the TApplication to produce all of the plots
    myApp->Run() ;
    return 0;
}

If you just copy and paste this program into xcode you'll see that the ROOT headers arent recognized by xcode and you get the error 'XXX.h' file not found. This is obviously because we need to tell xcode where to find the headers.

  1. Click on your project in the left-side menu
  2. Under "Build Settings" click the "+" -> "Add User-Defined Settings".

  1. This will add a parameter under the "User-Defined" section. Call the new parameter "ROOTSYS" and point it to the top directory of your ROOT installation. For me this is /Users/user/root_cern/root_v6.06.04/. (Note: This step isnt absolutely necessary but makes the rest less painful and allows you to update your ROOT installation without having to change the header and library paths below)

  2. Now (still in "Build Settings") go to "Search Paths" -> "User Header Search Paths". In this field add the path "$(ROOTSYS)/include"

  3. Set the "Search Paths" -> "Always Search User Paths" field to Yes

At this point the nasty errors on our headers have gone away! Unfortunately, the program wont build. A look at the build errors shows that there are TONS of linking errors! Clearly we need to update our linker flags to include all the ROOT libraries we want to compile against.

  1. Open a terminal and run $ROOTSYS/bin/root-config --libs. For me the output is:

    -L/Users/user/root_cern/root_v6.06.04/lib -lCore -lRIO -lNet -lHist -lGraf -lGraf3d -lGpad -lTree -lRint -lPostscript -lMatrix -lPhysics -lMathCore -lThread -lMultiProc -lpthread -Wl,-rpath,/Users/user/root_cern/root_v6.06.04/lib -stdlib=libc++ -lm -ldl

  2. Copy the output from the above root-config and paste it under "Build Settings" -> "Linking" -> "Other Linker Flags". Note that you can replace all instances of the path to your ROOT install directory with $(ROOTSYS) and you wont have to update them in the future when you update your ROOT version.

Now the program should build just fine! If you're STILL getting the linking errors, make sure that the TARGET you're trying to build inherits it's linking information from the project. Do this by going to "Other Linker Flags" for the target you are building and set it to $(inherited). You'll get the handy plot of a cos(x) function.

A few notes:

  • If you dont use a TApplication, the program will end before you get a chance to look at your plot.
  • You can end the program either through the xcode GUI or by selecting "File" -> "Quit Root" in the plot's menu bar.
  • Things get a bit more complicated if you're trying to write a class that you want ROOT to be able to stream out to the file. It's possible to do it by adding in an additional "Run Script" build phase to your target. For now though, this should get you started.