Distributing ROOT binaries with my program

Cling generally finds the system compiler and you do not need to redistribute it. However, the system compiler should have some level of version matching. That is, you can’t expect to build your project with gcc5 and deploy on a system with gcc14, for example.

We have had some efforts into reducing the minimal binary distribution of ROOT with -Dminimal. We have a person looking actively into this. My expectation is to be able to strip it down to libCore + libCling. That should be the minimal sufficient pieces of ROOT in terms of packaging.

Cling generally finds the system compiler and you do not need to redistribute it.

Understood. In terms of software distribution, I would distinguish several cases:

  • On Linux platforms, this will be as easy as adding a package dependency on the compiler that was used to build ROOT. Simple enough.
  • macOS should also be fine. There is always AppleClang hidden somewhere in the /Library file tree, and as far as I understand Apple is very conservative with version matching.
  • Windows will probably represent an issue. I do not want to force my users to install Visual Studio with Windows SDK just to be able to launch my program without crashing. For that reason I will probably package two versions: one with ROOT that requires all its dependencies (including VS) and one without it that is more lightweight, but does not ship any ROOT compatibility.

For the record, I should note that out of curiosity I tested my current iteration (with those empty header files and runtime-less EXTRA_CLING_ARGS) on a completely fresh installation of Windows 11, which does not contain any user-installed software. After installing the VC++ redistributable, I was able to run my program even without VS or Windows SDK present in the system. I was so impressed with this that I tried to download and run the full ROOT pre-compiled binary. It seems that root.exe launches, spins up ROOT core, but (as expected) is unable to process any interactive queries. I just thought that you may find that interesting in the context of this conversation.

I am currently trying to package ROOT under macOS, as a Mach-O v2 Universal binary. For the sake of completeness, I will update this thread once I figure out how to do that. After that I intend to turn my attention to fixing up CLING runtime to ensure that the interpreter not only launches, but is actually able to process queries.

Perhaps one follow up question: do you know if anybody managed to successfully run ROOT under Windows for ARM?

Related discussion:

It does not work yet. There are several outstanding issues.

It does not work yet. There are several outstanding issues.

In that case I won’t even try that. Thanks, this saved me a ton of time. :slight_smile:

…on to macOS, then!

Hi all, a quick progress update on the packaging effort for macOS.

I managed to spin up a macOS Sonoma VM with MacPorts, hoping to elegantly install root6 in the +universal variant. I found that this was broken because root6 depends on librsvg, which in turn depends on rust, and rust for some reason cannot build as +universal. Further research reveals that this was reported and supposedly resolved about 18 months ago but even in the latest version, the issue seems to persist.

So instead, I set out to build my own version of ROOT from source, much like I do for Linux. Prior to that I used MacPorts to install all dependencies of ROOT except for librsvg. Inspired with the root6 portfile, I used the following cmake configuration parameters:

cmake -Dminimal=ON -DCMAKE_CXX_STANDARD=20 -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64"

The last variable enables universal compilation, i.e. every compiler call is duplicated under the hood, and LIPO transparently combines output files into Mach-O v2 Universal binaries. This also has an effect on build duration and size of the output files – they are roughly doubled.

To my surprise this seem to have worked nicely. ROOT did not complain about the missing dependency at all. This should probably be pointed out to whomever maintains the ROOT MacPorts packaga. That being said, my build is still in progress – for the past 20 hours the job has been stuck on a step labeled: “Generating lib/modules.idx”. Today morning I ran some sanity checks, and can confirm that the the toolchain is simply not stuck, it is still doing something with root.exe that requires full 1 CPU core, probably related to C++ modules. I suspect that nobody previously tested them with universal compilation, so I may be in unexplored territory there.

If the situation goes on for one more day, I will attempt to re-run the build with -Dcxxmodules=OFF and -Druntime_cxxmodules=OFF. However, any suggestions are welcome.

Another macOS packaging update:

I lost patience with the build, so I terminated it at 99% and tried running bin/root.exe directly, only to find that the process immediately crashes and loops indefinitely during demangling. This is presumably what was responsible for the 100% CPU usage. As I wrote earlier, I suspected that this was a consequence of C++ modules, so I disabled this feature and tried again, only to find that this had no desirable effect. I was desperate to see ROOT build on my VM at all, so as a sanity check I disabled universal builds by removing the CMAKE_OSX_ARCHITECTURES variable, configured and built my ROOT again. This finally produced a package that worked, and ran bin/root.exe flawlessly.

At this point, it is clear that something about universal builds does not agree with ROOT – I am uncertain what that is. I will try to look into universal variants of LLVM to see if this is possible to accomplish at all. As always, any thoughts on this would be welcome.

In the meantime, I considered producing x86_64 and armv8 builds separately, and stitching them together manually with LIPO after the fact. I tried that too: the first half succeeded, but unfortunately, when I tried to cross-compile into armv8 the build failed again somewhere deep inside LLVM. One remaining idea that I had was to simply download pre-compiled root6 ports for both architectures, bypassing building ROOT from source entirely. However, I did not get time to do that today; perhaps in the coming days there will be an opportunity to attempt this.

Related use case: Add posibility to build only the Math libraries · Issue #6617 · root-project/root · GitHub

After a lot of low-level errors from LLVM related to universal builds, I have decided to distribute ROOT6 as a single-architecture binary under macOS. I will now try to identify the smallest viable subset of files necessary to distribute within my .app bundle.

1 Like

Alright, I suppose that for now I have managed to find a reasonable solution how to effectively distribute ROOT under all three major platforms. I would like to summarize my learnings in case anybody ever needs to do a similar thing (or in case ROOT is looking for possible improvements in the future).

  • In order to link against ROOT file reading libraries, one needs to link against the libCore.
  • Even though it is not obvious, libCore depends on libCling using dlopen(), which means that the CLING interpreter has to be distributed inside the package.
  • Since a packager cannot assume user’s environment variables, it is typically convenient to develop a wrapper script (or bootstrapper program), which augments environment and then launches the main program. Unfortunately, I found that once the dynamic linker runs, any further environment changes are ignored, so the program needs to restart itself to force linker to load augmented values in.
  • CLING has quite a lot of baggage (approx. 100 MB excess data), most notably the etc/ directory that contains modules, pre-compiled header and other files.
  • CLING starts up heavily: first it probes a lot of system paths, then it looks for headers from CI on a non-CI system. This can be misinterpreted by uses (or their antivirus) as an attempt to snoop around file system.
  • CLING imposes hefty requirements on user’s system: compatible STL headers, C++ compiler (on Windows, correct version of MS Visual Studio needs to be installed!). My comments above describe a nasty hack to sidestep this, but the end result is that CLING only starts but does not work (but also does not crash).
  • CLING also does not like macOS Mach-O Universal v2 binaries. LIPO also does not work, so using it effectively means that one has to select a single CPU architecture to support.

As we can see above, a lot of packaging problems would disappear if in the future somebody figured out a way to remove (or at least reduce) the dependency on CLING for simple integrations like mine.

In any case, I have managed to accomplish my goal. I would like to thank everyone who spent their time answering my pedantic questions. It is much appreciated!

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.