In this protip I analyze the drawbacks of using external projects to implement multi-library projects.
Note: A previous article (I) and a follow-up to this one (III) exist.
In the previous article on this subject I showed a way of implementing multi-library projects by means of
ExternalProject_Add and CMake export registry. The solution allowed us to include third-party library sources as if they were installed in the system (i.e. with
find_package). The solutions comes no free of drawbacks. After using this implementation for a while, I found some disappointing effects.
The B and C sub-libraries (see the previous article) are built as external projects. External projects are configured at execution time. Indeed, CMake workflow consists roughly of 2 steps: the configuration time and the execution time. Configuration time is when
cmake processes our
CMakeLists.txt. Execution time is the time we run
make or whatever the compilation tool we chose. As a result of that, what is explained in my previous article won't work as expected:
Cannot reference targets in the sub-libraries: this is something we already knew and it's a benefit/drawback of using external projects. We are obliged to use configuration files, which leads me to my next point.
Configuration files (
*-config.cmake) of B and C are not written as A is being configured: indeed, the fact that external projects are configured at execution time, makes that configuration files of B and C won't be written at A's configuration time, resulting in
find_package(B) not working.
Assuming the 1st problem cannot be solved, in order to solve the second, we can do two things (both commented in this post): one solution is to implement A yet as another external project, sometimes referred as the "superbuild" solution since our top-most
CMakeLists.txt doesn't do anything but orchestrating several external projects, including our A root project:
CMakeLists.txt // "superbuild" make list
CMakeLists.txt // depends on B
CMakeLists.txt // depends on B and C
We have only to ensure, in the root
CMakeLists.txt, that A is compiled after C, and C in turn after B:
ExternalProject_Add ( B ... )
ExternalProject_Add ( C ... DEPENDS B )
ExternalProject_Add ( A ... DEPENDS B C )
Another solution is to keep A as the root project and make use of imported libraries, provided we know where B and C are created.
ExternalProject_Get_Property give us some clues but eventually we'll have to hard-code the path to them. This solution is also depicted in the previous post and here. I don't like the solution that much since it's a bit hacky, but I must admit it's simple.
Yeah... a bit disappointing. How do you deal with multi-library projects? Let me know in the comments! I hope this protip was useful!