11

From the FindBoost.cmake module of CMake 3.8:

foreach(COMPONENT ${Boost_FIND_COMPONENTS})
  if(_Boost_IMPORTED_TARGETS AND NOT TARGET Boost::${COMPONENT})
    string(TOUPPER ${COMPONENT} UPPERCOMPONENT)
    if(Boost_${UPPERCOMPONENT}_FOUND)
      if(Boost_USE_STATIC_LIBS)
        add_library(Boost::${COMPONENT} STATIC IMPORTED)
      else()
        # Even if Boost_USE_STATIC_LIBS is OFF, we might have static
        # libraries as a result.
        add_library(Boost::${COMPONENT} UNKNOWN IMPORTED)
      endif()

and the corresponding comment from the docu of that module:

It is important to note that the imported targets behave differently than variables created by this module: multiple calls to find_package(Boost) in the same directory or sub-directories with different options (e.g. static or shared) will not override the values of the targets created by the first call.

I see the rational for having the targets not being GLOBAL.

However, what is the preferred way of making them global?

I'm used to defining the dependencies of my project in a sub-directory including any find_package(...) calls. Consequently, the Boost imported targets are not available in another directory, e.g. /tests/CMakeLists.txt:

<project_root>
  /3rdparty
    /git-submodule-of-a-small-lib
    /CMakeLists.txt
  /include
    /...
  /tests
    /CMakeLists.txt
  /CMakeLists.txt
Torbjörn
  • 5,512
  • 7
  • 46
  • 73

2 Answers2

19

There is a IMPORTED_GLOBAL target property for this in CMake >= 3.11:

set_target_properties(Boost::unit_test_framework PROPERTIES IMPORTED_GLOBAL TRUE)

For older versions: find_package() uses standard add_library() calls, so you can always change/extend its functionality to have IMPORTED targets always GLOBAL with something like:

3rdparty\CMakeLists.txt

function(add_library)
    set(_args ${ARGN})
    if ("${_args}" MATCHES ";IMPORTED")
        list(APPEND _args GLOBAL)
    endif()
    _add_library(${_args})
endfunction()

find_package(Boost REQUIRED COMPONENTS unit_test_framework)

Disclaimer

As @CraigScott has commented overwriting CMake's build-in functions is dangerous:

References

Florian
  • 39,996
  • 9
  • 133
  • 149
  • 1
    It is not advisable to override functions and then try to call the one being overridden by using the undocumented CMake behavior of calling the same function name but with an underscore prepended. This "call the old implementation" doesn't extend past the first override, meaning if you override it twice, you can never reach the first original function any more. Instead, if the function is overridden in this way twice, you create infinite recursion. – Craig Scott Jun 08 '18 at 13:20
  • @CraigScott I see your point and admit that I struggle between the usefulness and maliciousness of overwriting build-in functions. Sometimes it's really required and has saved my day with something like [this](https://stackoverflow.com/questions/29818725/cmake-how-to-unit-test-your-own-cmake-script-macros-functions), but yes - since it's not officially supported - it's dangerous to use. I would like to add this officially to CMake with all the proper tests and functionality. – Florian Jun 12 '18 at 19:53
1

I managed to workaround the problem of having the imported Boost targets not available in the global project scope by including 3rdparty/CMakeLists.txt not by add_subdirectory(3rdparty) but via include(3rdparty/CMakeLists.txt) as this evaluates 3rdparty/CMakeLists.txt in the caller's scope.

Torbjörn
  • 5,512
  • 7
  • 46
  • 73
  • 3
    I understand this solves your problem, and this is a good answer, but given Florian's perfect analysis that should be the accepted answer. – Antonio Jun 08 '18 at 05:31