Transitioning to Namespace in CMakeLists.txt

This is a bit of an odd question, but I’m trying to write “elegant” CMake code instead of hacky. The issue is that I work on a project that brings in libraries with find_package() and some of the dependency libraries have been getting “better” at CMake. For example, we used to do:

find_package(FOO REQUIRED)
...
target_link_libraries(mylib foo)

or the like. But now the sub-library now has namespaces so we get to do:

target_link_libraries(mylib FOO:foo)

So I’m now trying to figure out if there is an “elegant” way I can transition to using the namespace notation. This is an issue because we have a multi-repository project that can have target_link_libraries like that in multiple repos and all need to move to namespace.

Eventually, of course, we’ll move all the repos to use the namespace notation but it’s always easier to “trickle in” changes to CMake in this multi-repo setup rather than saying “All users must upgrade these 5 subrepositories at once.” (It’s not impossible to do the later, but it’s always easier to get changes accepted if the changes are smaller.)

I could obviously have:

option(USE_FOO_NAMESPACE "libfoo has namespace" OFF)
...
if (USE_FOO_NAMESPACE)
  target_link_libraries(mylib FOO::foo)
else ()
  target_link_libraries(mylib foo)
endif ()

and then when we build we could use -DUSE_FOO_NAMESPACE=ON for our testing and once all is in place, change the option default and then eventually clean up the ugly code.

But I was just wondering if there is some fancy, cool, better CMake way to do this. My CMake is…practical, let’s say, rather than elegant, and I’m always willing to learn.

The project doing namespaces now can do:

install(EXPORT SameNamespacedExport
  # NAMESPACE Foo:: # without this argument
  FILE TargetsWithoutNamespace-targets.cmake # different filename
  ${args}) # but the rest the same

I don’t know if this works or not. If you get errors about exporting targets multiple times, add targets for each:

foreach (target IN LISTS namespaced_targets)
  add_library("${target}-nonamespace" INTERFACE)
  target_link_libraries("${target}-nonamespace" INTERFACE "${target}")
  set_property(TARGET "${target}-nonamespace" PROPERTY EXPORT_NAME "${target}")
  install(TARGET "${target}-nonamespace" EXPORT NoNamespaceExportSet)
endforeach ()
install(EXPORT NoNamespaceExportSet) # see above

The config file can then do:

set(_include_unnamespaced FALSE)
if ("${CMAKE_FIND_PACKAGE_NAME}_FIND_VERSION" VERSION_LESS "1.0") # use the right version number here.
  set(_include_unnamespaced TRUE)
elseif (NOT DEFINED "${CMAKE_FIND_PACKAGE_NAME}_FIND_VERSION")
  message(DEPRECATION) # Warn about this being deprecated.
  set(_include_unnamespaced TRUE)
endif ()

if (_include_unnamespaced)
  include("$CMAKE_CURRENT_LIST_DIR}/TargetsWithoutNamespace-targets.cmake")
endif ()
unset(_include_unnamespaced)

This allows for a transition while warning users that things are changing.

1 Like