proper way to wrap an imported target around a third-party target

Hi,

I’m trying to enforce namespace usage for imported libraries.
For third-party libraries that can be imported through find_package but the don’t provide a namespace, I tried to wrap them in a namespace (see attached file)
I have the following situation:
application depends privately on a home-made (static)library which depends privately on a third-party library . When declaring dependency to in , I wrap it into third::third.
on CMake 3.19.2 it works fine
on CMake 3.5.1 (that I must support as it is the default version on Ubuntu 16.04), when configuring , I have the following error: Target “client” links to target “third::third” but the target was not found…

Why the difference in behaviour and would it be possible to have a “clean” workaround?

I don’t see an attached file (though the spam filter may have blocked it since you appear to be a new user). A GitHub Gist or other hosting service might suffice in the meantime. I don’t think there’s enough info without that to surmise what is going wrong.

Hrm. It seems that they don’t provide backports for Xenial’s CMake. I would recommend that you suggest downloading a newer CMake from the prebuilt binaries if possible.

I indeed cannot upload. It’s not long though. I post it directly:

# MyLib CMakeLists

...

find_package(third REQUIRED)
# Work around for third-party package that don't use namespace
IF(third_FOUND AND NOT TARGET third::third)
	add_library(third::third UNKNOWN IMPORTED)
	set_target_properties(third::third PROPERTIES
	IMPORTED_LINK_INTERFACE_LANGUAGES "CXX"
		IMPORTED_LOCATION "${third_LIBRARY}"
		INTERFACE_INCLUDE_DIRECTORIES "${third_INCLUDE_DIR}"
		)
ENDIF()
target_link_libraries(${TARGET_NAME} PRIVATE third::third)

# Then export and install mylib::mylib

...

# client CMakeLists

find_package(mylib REQUIRED)
target_link_libraries(${TARGET_NAME} PRIVATE mylib::mylib)

I cannot rely on recent versions of CMake as I’ve still got users with 3.5.1 that may not be able to switch to newer version.

regards

Hmm. I am guessing that CMake 3.5.1 did not prune target references properly or something. In your mylib-config.cmake file, you’ll need similar logic around a find_package(third).

include(CMakeFindDependencyMacro)
find_dependency(third)
if (NOT third_FOUND)
  set(mylib_FOUND 0)
  list(APPEND mylib_NOT_FOUND_REASON "third party dependency not found: ${third_NOT_FOUND_REASON}")
  # Possibly other logic.
else ()
  # make third::third if needed.
endif ()

Thanks,

I’m afraid I did not understand properly the role of mylib-config.cmake. Do I have to write again the add_library/set_target_properties parts in this file?

By the way, i’d appreciate a pointer on the exact function of this config file.

regards

No, your targets are there via install(EXPORT) (for the install tree) and/or export(EXPORT) (for the build tree) commands in your project. What those lines do not do is make an IMPORTED targets you had available (e.g., because they may have paths that are build-machine specific and not useful on another machine).

The mylib-config.cmake file is there to implement the logic of find_package(mylib). There is a module for doing the bare minimum, but if you have anything “extra” going on like dependent imported targets, a CMake API to provide, or logic for components, then you’re basically stuck with writing it yourself.

Hi,

I looked more precisely at the answers above and I’m still confused.

I have actually several use-cases.

Let’s call “mylib” my own library that I export as mylib::mylib (so far, everything is OK)

I’d like to declare a dependency to a third party library “third” that is detectable through find_package.

  1. third::third exists
    my cmakelist.txt contains:
find_package(third REQUIRED)
target_link_libraries(mylib <whatever> third::third)
list(APPEND DEP third)
configure_file(<Path to MyLibConfig.cmake> MyLibConfig.cmake @ONLY)

my mylibConfig.cmake contains:

include(CMakeFindDependencyMacro)
foreach(_lib_ IN ITEMS @DEP@)
	find_dependency(${_lib_} REQUIRED)
endforeach()

# include the generated target file
include("${CMAKE_CURRENT_LIST_DIR}/@TARGET_NAME@Targets.cmake")

everything is fine

  1. third namespace exists but the exported target has a differente name third::third_other (it happens for instance with Eigen that exports Eigen3::Eigen and not Eigen3::Eigen3)
    my cmakelist.txt contains:
find_package(third REQUIRED)
target_link_libraries(mylib <whatever> third::third_other)
list(APPEND DEP third)
configure_file(<Path to MyLibConfig.cmake> MyLibConfig.cmake @ONLY)

my mylibConfig.cmake contains:

include(CMakeFindDependencyMacro)
foreach(_lib_ IN ITEMS @DEP@)
	find_dependency(${_lib_} REQUIRED)
endforeach()

# include the generated target file
include("${CMAKE_CURRENT_LIST_DIR}/@TARGET_NAME@Targets.cmake")

It still works but I don’t understand why as the name third_other does not seem to be propagated through my MyLibConfig.cmake

  1. third namespace exists but there is no target third::xxx (for instance OpenCV)
    I tried to wrap it in my cmakelist as mentioned in a previous message but it does not work as the wrapped target is not propagate properly with my mylibConfig.cmake (If I understood you correctly)
    should I copy my wrapping from the cmakelist to the mylibConfig.cmake file (in pseudo-code):
include(CMakeFindDependencyMacro)
foreach(_lib_ IN ITEMS @DEP@)
	find_dependency(${_lib_} REQUIRED)
	IF(${_lib_}_FOUND AND NOT TARGET ${_lib_}::${_lib_})
		add_library(${_lib_}::${_lib_} UNKNOWN IMPORTED)
		set_target_properties(${_lib_}::${_lib_} PROPERTIES
			IMPORTED_LINK_INTERFACE_LANGUAGES "CXX"
			IMPORTED_LOCATION "${${_lib_}_LIBRARIES}"
			INTERFACE_INCLUDE_DIRECTORIES "${${_lib_}_INCLUDE_DIRS}"
			)

endforeach()

So far I found only two unsatisfying solutions:
first one:
my cmakelist.txt contains:

find_package(third REQUIRED)
target_include_directories(mylib <whatever> third_INCLUDE_DIRS)
target_link_libraries(mylib <whatever> third_LIBRARIES)
list(APPEND DEP third)
configure_file(<Path to MyLibConfig.cmake> MyLibConfig.cmake @ONLY)

my mylibConfig.cmake is unchanged

second one:
I create a Findthird.cmake file that looks for third locations and finishes with:

IF(third_FOUND AND NOT TARGET third::third)
add_library(third::third UNKNOWN IMPORTED)
set_target_properties(third::third PROPERTIES
	IMPORTED_LINK_INTERFACE_LANGUAGES "CXX"
	IMPORTED_LOCATION "${third_LIBRARIES}"
	INTERFACE_INCLUDE_DIRECTORIES "${third_INCLUDE_DIRS}"
	)
ENDIF()

but both solution create a dependency to all submodules in “third” (see point 4 below).

  1. how to declare dependency only to a submodule of “third” for each use-case above? (for instance, using only opencv_superres and not all OpenCV libraries

It’s a long post with many questions in one but I hope it will make the problem clearer.

Regards