3

I have a C++ project cat that depends on libzzz library. The libzzz has its own git repository and now I am going to create a repository for the cat project.

How to organize a CMake build scripts for the cat?

  • option 1: CMake scripts of cat consider libzzz built and installed in the system and cat project provide FindLibZZZ.cmake script that search libzzz in /usr/include/libzzz + /usr/lib/libzzz. But how to deal with non-linux platforms? I presonally don't like this option.

  • option 2: add some kind of link or dependency in GIT repository cat that would automatically checkout libzzz sources from its origin into some cat subdirectory. So cat's CMakeLists.txt consider libzzz is placed in some cat's subdirectory. How to do that?

Alexander Oh
  • 24,223
  • 14
  • 73
  • 76
pavelkolodin
  • 2,859
  • 3
  • 31
  • 74
  • I believe `git submodules` is the answer for my second option. – pavelkolodin Jan 25 '16 at 20:58
  • or `git subtree` really you should be looking at something like this: http://bazel.io/ – Alexander Oh Jan 25 '16 at 21:08
  • `But how to deal with non-linux platforms?` - What is a problem with non-linux platform? `find_path` and other `find_*` commands work not only on Linux, so FindLibZZZ.cmake could be multiplatform (and many of existing `Find` scripts are really multiplatform). – Tsyvarev Jan 25 '16 at 22:02
  • Take a look at CMake's [package scripts](https://cmake.org/cmake/help/v3.4/manual/cmake-packages.7.html). If both `libzzz` and `cat` projects are under your control this is by far the most powerful mechanism. It is in my opinion also the only approach that works well on Windows (thanks to the package registry). – ComicSansMS Jan 26 '16 at 11:46

2 Answers2

6

If libzzz is also a CMake project, then I'd suggest an alternative approach (let's call it option 3). Rather than relying on libzzz being already available on your system (which can make integrating into CI systems, etc. harder), you may want to consider building it as part of your overall project as well. Instead of trying to add it to cat via git submodules (valid, but has its own downsides, e.g. see here), you can make a top level build (aka superbuild) control the building of both libzzz and cat.

Bringing in external projects into a superbuild is often done using CMake's ExternalProject module. This allows you to download and build a project in one operation. Unfortunately, it does so at build time and provides no CMake targets for you to link against, so you end up having to manually work out the name and location of the library you want to link against from it, etc. Yes, you can robustly predict these for your build, but you have to manually handle all the platform differences. This is what CMake is supposed to take care of for us!

Instead of using ExternalProject in the normal way, you can, however, coax it to perform the download at CMake time. This then makes the external project's source code available immediately and you can call add_subdirectory to bring it directly into the main build. This in turn means any CMake targets defined by that no-longer-external build will also be visible, so you can simply link against those targets. In your particular example, the libzzz build would presumably define a zzz CMake target or similar and in your cat build, you would just add a call like:

target_link_libraries(cat PUBLIC zzz)

No need to manually work out any library names or locations, since CMake now does it for you. The trick to all this is getting ExternalProject to execute at CMake time rather than build time. The method is fully explained here, using GoogleTest as the example. The technique essentially involves using external_process to invoke a small script via CMake's script mode to call ExternalProject_Add immediately. That article includes a link to a fully generalised implementation you should be able to use to perform the downloading of libzzz in your particular case.

Community
  • 1
  • 1
Craig Scott
  • 9,238
  • 5
  • 56
  • 85
2

I believe that Option 1 is best if you ever plan to distribute the project to others (which it sounds like you do, if you are considering non-linux platforms). If you don’t plan to distribute cat to the wider world, then by all means go with Option 2, which is likely to be slightly easier to implement. However, suppose I want to use cat, and you go with option 2. In that case, there could be several problems:

  • If I already have libzzz because it’s also used by the dog project, then you are downloading and building something I already have. This is, at the very least, a waste of time.
  • If libzzz releases a critical security update, then I need to re-download your project also. If I’m lucky, you updated the cat repository to use the new version of libzzz. If I’m unlucky, then you have not noticed the new security update yet, you are in the middle of maintaining a different part of the repository, or you are dead and cat is completely unmaintained. If I’m really unlucky, I don’t notice that cat now needs to update because libzzz released a new version, and I am subject to the security problems.
  • Your cat project becomes popular. Now package maintainers everywhere need to find out how to compile cat with the existing libzzz from their repository.

I know I’ve seen an article from somebody who actually was a package maintainer arguing for Option 1, but I can’t seem to find it again. If I do, I’ll update this answer with the link.

Daniel H
  • 7,223
  • 2
  • 26
  • 41