Checking for target-less components existence in config files

Assuming a separated export file for every install component.

In the book “Professional CMake” it’s suggested, as a recommended practice, that consumable projects should let users install runtime only components (binaries) separately from development ones (headers, ecc).
Some chapters before, it’s said that config files, other than solving inter-component dependencies, must also check if the required components have actually been installed in the system.
For example, some explicit code is needed to make sure the runtime component is installed when requested by a find_package() call, and the same thing for the development one.
The the book provides the following snippet:

cmake snippet
# Work out the set of components to load
# Ensure Runtime is included if Development was given <...>
# No components given handling <...>
set(comps ${${CMAKE_FIND_PACKAGE_NAME}_FIND_COMPONENTS})
# Storing comps in a safer variable name.
set(${CMAKE_FIND_PACKAGE_NAME}_comps ${comps})
# Find external dependencies <...>
# Check all required components are available before trying to load any
foreach(comp IN LISTS ${CMAKE_FIND_PACKAGE_NAME}_comps)
    if(${CMAKE_FIND_PACKAGE_NAME}_FIND_REQUIRED_${comp}
       AND NOT EXISTS ${CMAKE_CURRENT_LIST_DIR}/MyProj_${comp}.cmake)
        set(${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE
            "MyProj missing required component: ${comp}")
        set(${CMAKE_FIND_PACKAGE_NAME}_FOUND FALSE)
        return()
    endif()
endforeach()

(I cannot explain why using two names for the same value, comps and ${CMAKE_FIND_PACKAGE_NAME}_comps, if comps is “usafe”, why bothering with it?)

The script checks if the requested components are installed by checking if the corresponding export file exist.
This works only for components that include an exported target. If I were to apply the aforementioned recommended practice, the “development” component would fail the check, because it exports no targets.

“Dummy” export sets aren’t allowed by cmake, it complains about the export set being undefined. I get that export files’ only job is to define imported targets, but perhaps their role is more than that: they are a convenient and automatic way to tell which component/export set has been installed.
My current solution would be to force the creation of an empty export set with an install script, but that looks very wrong. What could I do?


Another approach would be to generate the config file at install time, and write down which components are being installed. But even if an (install time)variable with that data existed, for some reason the cmake --install --component <comp> has been designed in such a way that installing multiple components at a time is impossible!
It makes absolutely no sense. It is possible to install multiple components without problems, if they are part of ALL, isn’t it? Why can’t they be installed explicitly?

Cc: @craig.scott

I don’t think the question is that specific to the book.
I only used it for reference. I hope I provided all the necessary information, the question should hold up with or without book-specific knowledge.

I’m aware. Craig is probably still one of the best to answer it. I’ll try and take a look later as well.

1 Like

It’s in part to do with parts of the example that you’ve left out. :wink: If you have a call to find_dependency() nestled in among those lines (which the original example in the book does) and the dependency package you try to find has been implemented with the same pattern, its comps variable would overwrite the one for this package. When you then try to iterate over comps in your package’s foreach() loop instead of ${CMAKE_FIND_PACKAGE_NAME}_comps, you would be iterating over a list of components for the dependency, not for this package.

You don’t have to do the check that way if you don’t want to. It just happens to be a convenient way to do it when you know there will be at least one exported target. In the case of a header-only library, there’s no library that needs to be installed for runtimes to use, so you can just skip over checking that particular component.

Craig is also drowning in his backlog of stuff to get done by the end of the year. :tired_face:

I figured that. I guess you went for comps in the first part because it made the code more readable. Fair enough.

In fact, I like that approach so much that I’m going to implement for non-target components too, with a dummy empty export file.

For example, I have an “examples” component that requires the “development” component, not the “runtime”. “development” in turn requires “runtime”.
I see “inconsistent imported target” and “missing required files” as the same fatal error, so I want to provide the same checks for both.