kang-sw
kang-sw

Reputation: 23

In cmake, can we avoid using find_package to import other packages?

Okay, the title is obviously weird. I hope anyone who can define my curiosity cleanly, would edit the title into proper one.

When using CMakeLists.txt, we usually import third-party packages via command find_package() macro, which looks for proper find_XXX.cmake script, and then results outputting variables such as XXX_LIBRARIES, XXX_INCLUDE_DIRECTORIES, etc.

However, recently I found some instruction guide for add_library() macro, that can include actual library binaries as its source(?), like this way:

add_library(my_target STATIC IMPORTED foo.lib)

And as I guess, as long as the name my_target is actually names a target, then we can add another compositions into that target, like any other cmake targets, like below.

target_inlcude_directories(my_target PUBLIC ${PKG_PREFIX}/include)
target_link_directories(my_target PUBLIC ${PKG_PREFIX}/lib)
target_compile_features(my_target PUBLIC std_cxx_17)

If this is possible, then wouldn't this be much cleaner way to describe exported package's compositions, rather than linking and adding those verbose *_LIBS *_DIRS variables manually for each? Like below?

add_executable(foo ${MY_SOURCES})
find_package(xxx)

# BEFORE
target_link_directories(foo PRIVATE ${XXX_LINK_DIRECTORIES}) # note: I don't remember what was it exactly.
target_link_libraries(foo PRIVATE ${XXX_LIBRARIES})
target_include_directories(foo PRIVATE ${XXX_INCLUDE_DIRECTORIES})

# AFTER
target_link_libraries(foo PRIVATE xxx::my_target)

Am I thinking something invalid?

Upvotes: 0

Views: 1360

Answers (2)

KamilCuk
KamilCuk

Reputation: 141493

can we avoid using find_package to import other packages?

You can, but it's the tool to be used just for that. find_package() is basically an include() with some special options.

wouldn't this be much cleaner way to describe exported package's compositions, rather than linking and adding those verbose *_LIBS *_DIRS variables manually for each? Like below?

It would be and it is used.

cmake is still ongoing relative big changes in a very short time. I think interface libraries weren't so popular or weren't available some time ago. The only (except some checks, etc) thing find_package does is it includes a file named FindXXX.cmake (or other file named differently). Only. It's completely dependent on what's inside the FindXXX.cmake file what happens.

Newer cmake FindXXX.cmake does exactly what you propose: they create an INTERFACE IMPORTED library, for example on my pc:

@/usr/share/cmake-3.20/Modules
$ grep add_library -wr --include='Find*' .
.... a lot of results ....
./FindBoost.cmake:    add_library(Boost::diagnostic_definitions INTERFACE IMPORTED)
./FindBoost.cmake:    add_library(Boost::disable_autolinking INTERFACE IMPORTED)
./FindBoost.cmake:    add_library(Boost::dynamic_linking INTERFACE IMPORTED)
./FindThreads.cmake:  add_library(Threads::Threads INTERFACE IMPORTED)
./FindHDF5.cmake:    add_library(HDF5::HDF5 INTERFACE IMPORTED)
./FindHDF5.cmake:        add_library("hdf5::${hdf5_target_name}" INTERFACE IMPORTED)
./FindHDF5.cmake:        add_library("hdf5::${hdf5_target_name}" UNKNOWN IMPORTED)
./FindHDF5.cmake:        add_library("hdf5::${hdf5_target_name}" INTERFACE IMPORTED)
./FindHDF5.cmake:        add_library("hdf5::${hdf5_target_name}" UNKNOWN IMPORTED)
./FindGTest.cmake:        add_library(GTest::GTest INTERFACE IMPORTED)
./FindGTest.cmake:        add_library(GTest::Main INTERFACE IMPORTED)
./FindGTest.cmake:        add_library(GTest::gtest ${GTEST_LIBRARY_TYPE} IMPORTED)
./FindGTest.cmake:        add_library(GTest::gtest_main ${GTEST_MAIN_LIBRARY_TYPE} IMPORTED)
./FindBZip2.cmake:    add_library(BZip2::BZip2 UNKNOWN IMPORTED)

Older or "more standard" (ie. older :) indeed create multiple variables - XXX_LINK_DIRECTORIES XXX_LIBRARIES etc. Note that does variable names are not the same everywhere (XXX_LIB? XXX_INC_DIRS? etc.) and change depending on which FindXXX.cmake you call because some developers decided on different names. A lot of documentation was written in the time the find_package used variables, so it's still visible in documentation, but nowadays interface libraries clearly dominate and what you propose is already used.

Upvotes: 2

fabian
fabian

Reputation: 82461

Note that the imported library is not set up properly in this case. Configuration scripts (<packageName>Config.cmake) which are used as fallback for find_package or if you use the CONFIG version usually do exactly this: create one or more imported targets for you to use in your project. I strongly recommend using those instead of the *_LIBS/*_DIRS variables, if available.

Here's the correct version of importing the library:

add_library(my_target STATIC IMPORTED)

# INTERFACE "visibility" needed here;
# also quote the path concatenation to avoid issues with spaces in PKG_PREFIX
target_include_directories(my_target INTERFACE "${PKG_PREFIX}/include")
target_link_directories(my_target INTERFACE "${PKG_PREFIX}/lib")

# do you really need the linking library to use the C++ 17 standard?
target_compile_features(my_target INTERFACE std_cxx_17)

# you need specify the absolute path to the lib as IMPORTED_LOCATION target property
# you may need to adjust the location according to the exact location of the lib file
#
# variables CMAKE_CURRENT_SOURCE_DIR (if used from CMakeLists.txt) or
# CMAKE_CURRENT_LIST_DIR (if used from a script) may be helpful
#
# requires seperate treatment for different OS; this is not included here
set_target_properties(my_target PROPERTIES IMPORTED_LOCATION "${PKG_PREFIX}/lib/my_target.a")

Note that in this case the library you need to link is my_target, not xxx::my_target.

Upvotes: 1

Related Questions