Best practice for exporting libraries my project uses for dependent projects to use when building

This has come up in the context of Tomviz as a consumer of ParaView/VTK, and as a producer in the context of Avogadro in the past. When we export libraries they often depend on/build upon other libraries. In the case of both projects two of the major libraries are Python and Qt. Not thinking so much of project specific solutions, but more towards what we recommend CMake projects do.

What is the best “modern” approach to exporting/importing dependent libraries? I used to think export Qt5_DIR, Python3_EXECUTABLE etc. I have spent more time that I remember diagnosing issues where developers of a dependent project linked to a different library, in a different path. Ideally it fails to link/run, when we have been less lucky it is more subtle.

I am really thinking of the case where my C++ project exports a number of Qt-derived classes for example, or I wrap C++ code using PyBind11 and a Python version. I want to offer a path for dependent projects to then make use of my project, so they do a find_package(my_proj), and then I have seen versions where the use file/config populated the cache for Qt/Python to the extreme where they wish me luck :slight_smile:

What do other projects do, what is the current recommended best practice in this case?

To document the path of least resistance, at least as I see it, is to demand our developers use a coordinating superbuild that must contain all projects. That can then pass in the same Qt, Python, etc. I can choose not to support any other build configurations as a project, and have this in place for Avogadro and more recently Tomviz.

I think for small projects wanting to build on Avogadro, Qt, VTK, ParaView, etc that answer doesn’t feel as satisfying. I personally would recommend exporting the versions of the libraries compiled against, along with their full paths in a config/lib file. CMake doesn’t have explicit standards here as far as I can tell, so it is up to the projects to establish some reasonable standards, or punt on any third party library dependencies and leave it up to dependent projects to figure out.

Exported targets work pretty hard not to expose path variables etc, hence my interest in what recommendations the CMake community might have. Having worked with CMake first as a packager, and later as a developer I have seen a number of approaches in the fifteen or so years I have been around.

What I’m currently doing in a project that uses Eigen (or also VTK), is to put this in the ProjectConfig.cmake.in

include(CMakeFindDependencyMacro)
find_dependency(Eigen)
include("${CMAKE_CURRENT_LIST_DIR}/ProjectTargets.cmake")

for what I remember of the documentation, find_dependency should forward the REQUIRED and QUIET options correctly and I guess you can also constrain the package version like this (the other arguments are forwarder to find_package)

That means the user has to install the correct dependency using the package manager (or whatever else).

We used to restrict config files to exporting the project configuration, and only calling find_* in the USE file. They have gone out of favor, but I have always felt that calling find_* in a config is a little heavy handed, and a the very least should be something I could disable. As CMake evolves some of these practices must also evolve, but I would actually prefer not to find anything in a config file.

I was thinking of two major use cases. One where we want a build environment that developers can make, and ensure everything is consistent. In this case we can compare complete paths, and are more interested in a build with little to no room for error. Then there is the relocatable SDK where I want to let you know which major libraries and versions the SDK was compiled with, and ensure that compatible versions are found. This is a little more fragile, but better for distributing and then linking to Qt, Python, etc. In this case I want to avoid any absolute paths, and depend on versions assuming that compatible compilers etc are being used.