# check\_required\_components logics

**URL:** <https://discourse.cmake.org/t/check-required-components-logics/10567>\
**Category:** Code\
**Created:** [April 4, 2024, 1:18pm UTC](https://discourse.cmake.org/t/check-required-components-logics/10567 "2024-04-04T13:18:51Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![emaschino](https://discourse.cmake.org/user_avatar/discourse.cmake.org/emaschino/32/6126_2.png) [@emaschino](https://discourse.cmake.org/u/emaschino)\
**Post date:** [April 4, 2024, 1:18pm UTC](https://discourse.cmake.org/t/check-required-components-logics/10567/1 "2024-04-04T13:18:51Z")

</div>

Hi,

I’m not sure to understand the logics of `check_required_components` macro generated by `configure_package_config_file` of `CMakePackageConfigHelpers` package. It reads as follows:

```auto
macro(check_required_components _NAME)
  foreach(comp ${${_NAME}_FIND_COMPONENTS})
    if(NOT ${_NAME}_${comp}_FOUND)
      if(${_NAME}_FIND_REQUIRED_${comp})
        set(${_NAME}_FOUND FALSE)
      endif()
    endif()
  endforeach()
endmacro()

```

This has the effect of setting `<package>_FOUND` to FALSE when one of its components is found! Shouldn’t line `if(${_NAME}_FIND_REQUIRED_${comp})` rather reads as `if(NOT ${_NAME}_FIND_REQUIRED_${comp})` so that `<package>_FOUND` is set to FALSE when one of its components is missing?

I’ve read the documentation [Adding Components](https://cmake.org/cmake/help/latest/guide/importing-exporting/index.html#id14) where I can see that generation of `check_required_components` macro is explicitely disabled by flag `NO_CHECK_REQUIRED_COMPONENTS_MACRO` in `configure_package_config_file` call, but I’m not getting the point of the original `check_required_components` logics, then 🤔

---

<div class="post-metadata">

**Author:** ![craig.scott](https://discourse.cmake.org/user_avatar/discourse.cmake.org/craig.scott/32/20_2.png) [@craig.scott](https://discourse.cmake.org/u/craig.scott)\
**Post date:** [April 4, 2024, 9:57pm UTC](https://discourse.cmake.org/t/check-required-components-logics/10567/2 "2024-04-04T21:57:14Z")

</div>

> [@emaschino](#):
>
> This has the effect of setting `<package>_FOUND` to FALSE when one of its components is found!

The line before the one you quoted explicitly tests if the component is NOT found. What the combined logic is saying is that if a component is not found, and it is a required component, the package as a whole is considered not found.

---

<div class="post-metadata">

**Author:** ![emaschino](https://discourse.cmake.org/user_avatar/discourse.cmake.org/emaschino/32/6126_2.png) [@emaschino](https://discourse.cmake.org/u/emaschino)\
**Post date:** [April 5, 2024, 2:43pm UTC](https://discourse.cmake.org/t/check-required-components-logics/10567/3 "2024-04-05T14:43:28Z")

</div>

Indeed, thanks!

But playing with the `examples/more/components` example in [this test repo](https://github.com/pr0g/cmake-examples.git) that I’ve found reading some posts here, I’m thus not getting the point.

Author created a `phrases` package with `greetings` and `farewells` components. The `library/phrases-config.cmake.in` file doesn’t check the package’s components and simply builds out a list of the packages’s components:

```auto
foreach(component ${@PROJECT_NAME@_FIND_COMPONENTS})
    include(${CMAKE_CURRENT_LIST_DIR}/${component}-config.cmake)
endforeach()

```

In the consuming application, the `application/CMakeLists.txt` looks for the `phrases` package, requiring the `greetings` and `farewells` components:

```auto
find_package(phrases REQUIRED CONFIG COMPONENTS greetings farewells)

```

So far, so good.

Now, I want to go one step further and use CMake’s built-in features to generate and check the `phrases` package’s config file. To this end, in `library/CMakeLists.txt`, the call to `configure_file` is replaced by:

```auto
include(CMakePackageConfigHelpers)

configure_package_config_file(${PROJECT_NAME}-config.cmake.in ${PROJECT_NAME}-config.cmake
                              INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME})

```

And `library/phrases-config.cmake.in` file is updated to add check of the package’s components:

```auto
@PACKAGE_INIT@

foreach(component ${@PROJECT_NAME@_FIND_COMPONENTS})
    include(${CMAKE_CURRENT_LIST_DIR}/${component}-config.cmake)
endforeach()

check_required_components(@PROJECT_NAME@)

```

With these changes, the consuming application now complains that the `phrases` package is considered to be NOT FOUND:

```auto
CMake Error at CMakeLists.txt:4 (find_package):
  Found package configuration file:

    E:/dev/cmake-examples/examples/more/components/library/install/lib/cmake/phrases/phrases-config.cmake

  but it set phrases_FOUND to FALSE so package "phrases" is considered to be
  NOT FOUND.

```

Is it expected behaviour? Hence my initial post…

---

<div class="post-metadata">

**Author:** ![craig.scott](https://discourse.cmake.org/user_avatar/discourse.cmake.org/craig.scott/32/20_2.png) [@craig.scott](https://discourse.cmake.org/u/craig.scott)\
**Post date:** [April 6, 2024, 12:10am UTC](https://discourse.cmake.org/t/check-required-components-logics/10567/4 "2024-04-06T00:10:37Z")

</div>

@tomhh may be better placed to answer your questions about what was done in his repo and why.

---

<div class="post-metadata">

**Author:** ![tomhh](https://discourse.cmake.org/user_avatar/discourse.cmake.org/tomhh/32/493_2.png) [@tomhh](https://discourse.cmake.org/u/tomhh)\
**Post date:** [April 6, 2024, 11:20am UTC](https://discourse.cmake.org/t/check-required-components-logics/10567/5 "2024-04-06T11:20:56Z")

</div>

Thanks for the tag @craig.scott and interesting problem @emaschino.

I’m afraid I created this example some time ago so I’m just trying to refresh my memory 😅 I’m away from my machine at the moment and will try and check later this weekend, but in the meantime maybe try printing out all the CMake variables which might tell you more about what’s going on. You can do this with something like…

```auto
function(list_cmake_variables)
  get_cmake_property(variable_names VARIABLES)
  foreach(variable_name ${variable_names})
    message(STATUS "${variable_name}=${${variable_name}}")
  endforeach()
endfunction()

```

My bet is the name used in `check_required_components` is maybe wrong, though I’m afraid I’m not sure. I’ll try and get back to this and have another look before the end of the weekend 👍

---

<div class="post-metadata">

**Author:** ![tomhh](https://discourse.cmake.org/user_avatar/discourse.cmake.org/tomhh/32/493_2.png) [@tomhh](https://discourse.cmake.org/u/tomhh)\
**Post date:** [April 7, 2024, 11:10am UTC](https://discourse.cmake.org/t/check-required-components-logics/10567/6 "2024-04-07T11:10:42Z")

</div>

I had a little look into this and I’m afraid I can’t really see how `check_required_components` is meant to work in this context either…

From what I can tell, the way `check_required_components` is generated won’t work because of how it looks for the component variables (`${comp}_FOUND`). These don’t seem to exist when looking at all variables (only `phrases_FOUND=1` is set).

I found [this reference](https://cmake.org/cmake/help/latest/guide/importing-exporting/index.html#adding-components) which makes more sense and is essentially a manual version of writing the `check_required_components` macro.

For the `phrases` example, the `phrases-config.cmake.in` might look something like this:

```cmake
@PACKAGE_INIT@

set(_phrases_supported_components greetings farewells)

foreach(component ${@PROJECT_NAME@_FIND_COMPONENTS})
  if (NOT ${component} IN_LIST _phrases_supported_components)
    set(phrases_FOUND False)
    set(phrases_NOT_FOUND_MESSAGE "Unsupported component: ${component}")
  else()
    include(${CMAKE_CURRENT_LIST_DIR}/${component}-config.cmake)
  endif()
endforeach()

```

Then from the application `CMakeLists.txt`, if file you call `find_package` with `chitchat` (non-existing component):

```cmake
find_package(phrases REQUIRED CONFIG COMPONENTS greetings farewells chitchat)

```

You get the error:

```bash
CMake Error at CMakeLists.txt:4 (find_package):
  Found package configuration file:

    /path/to/cmake-example/examples/more/components/library/install/lib/cmake/phrases/phrases-config.cmake

  but it set phrases_FOUND to FALSE so package "phrases" is considered to be
  NOT FOUND. Reason given by package:

  Unsupported component: chitchat

```

The thing is, if you just try and pass a component that doesn’t exist with the existing code you get:

```auto
CMake Error at path/to/cmake-example/examples/more/components/library/install/lib/cmake/phrases/phrases-config.cmake:2 (include):
  include could not find requested file:

    path/to/cmake-example/examples/more/components/library/install/lib/cmake/phrases/chitchat-config.cmake
Call Stack (most recent call first):
  CMakeLists.txt:4 (find_package)

```

Which maybe isn’t quite as informative, but does give a clue the problem is with `chitchat`.

I also hope @craig.scott doesn’t mind me referencing a section from his book, Professional CMake, but on page 527 of the 16th edition there’s this section (emphasis mine):

> _In the past when all details about a package were provided through variables, it was customary to check whether all required variables were set at the end of the config file before returning. A macro called `check_required_components()` was defined for this purpose, but **projects that provide imported targets should perform these checks themselves**. The imported targets should only be created if all required components will be found. Otherwise, a failed `find_package()` call will still leave behind targets, which would interfere with any later call to `find_package()` for the same package name but with different arguments (e.g. to search in different locations).) **This makes the `check_required_components()` macro largely redundant.**_

I hope this helps, and please let me know if I missed something.

---

<div class="post-metadata">

**Author:** ![tomhh](https://discourse.cmake.org/user_avatar/discourse.cmake.org/tomhh/32/493_2.png) [@tomhh](https://discourse.cmake.org/u/tomhh)\
**Post date:** [April 7, 2024, 5:48pm UTC](https://discourse.cmake.org/t/check-required-components-logics/10567/7 "2024-04-07T17:48:53Z")

</div>

FYI I’ve also updated the repo with some improvements after this investigation, added here for visibility - [Component example improvement by pr0g · Pull Request #12 · pr0g/cmake-examples · GitHub](https://github.com/pr0g/cmake-examples/pull/12).

Any suggestions on fixes/improvements let me know, I’ll merge tomorrow most likely.

Also this whole experience reminds me of this [xkcd](https://xkcd.com/356/) 😅

---

<div class="post-metadata">

**Author:** ![emaschino](https://discourse.cmake.org/user_avatar/discourse.cmake.org/emaschino/32/6126_2.png) [@emaschino](https://discourse.cmake.org/u/emaschino)\
**Post date:** [April 8, 2024, 7:50am UTC](https://discourse.cmake.org/t/check-required-components-logics/10567/8 "2024-04-08T07:50:20Z")

</div>

Thanks @craig.scott and @tomhh for taking the time to look at this. I ended up with a solution similar to your PR that doesn’t make use of `check_required_components`.
