pkg_check_modules in FindPkgConfig creates interface library

When called with the IMPORTED_TARGET argument, the command pkg_check_modules in the module FindPkgConfig creates an interface library. From the code of FindPkgConfig.cmake:

add_library(PkgConfig::${_prefix} INTERFACE IMPORTED ${_global_opt})

What is the point of making this an interface library? Why not just create an imported library? Making an interface library is inconvenient because we cannot link it to another target with the PRIVATE keyword.

Huh? Linking to INTERFACE libraries as PRIVATE works just fine. Do you have an actual reproducer case available?

Hello. I mean the other way around. We cannot do:

target_link_libraries(PkgConfig::${_prefix} PRIVATE some_other_target)

What are you trying to do? This seems…odd.

Well, I have an executable, E, that links to a library, netcdf-fortran. I try to find netcdf-fortran with pkg-config:

find_package(PkgConfig)
pkg_check_modules(netcdff IMPORTED_TARGET netcdf-fortran)
target_link_libraries(E PRIVATE PkgConfig::netcdff)

I know that netcdf-fortran has a private dependency on another library, netcdf-c. So I am tempted to find netcdf-c (I have a find module for it) and declare this dependency:

find_package(netCDF)
target_link_libraries(PkgConfig::netcdff PRIVATE netCDF::netcdf)

Just append to its INTERFACE_LINK_LIBRARIES property. IMPORTED targets don’t support being on the left-hand side of target_* commands anyways (INTERFACE or not).

Eh, you sure an imported library can’t be on the left-hand side of target_*() commands with INTERFACE? It seems to be permitted with the following small test project:

cmake_minimum_required(VERSION 3.20)
project(ImportedLibTest)

add_library(MyThing STATIC IMPORTED)
target_link_libraries(MyThing INTERFACE /path/to/libsomething.a)
target_include_directories(MyThing INTERFACE /another/path/to/include)

I confirm what Craig Scott said. Putting the imported pkg-config target on the left-hand side of target_link_libraries with the INTERFACE keyword seems to work. Also when I try to replace INTERFACE by PRIVATE, the error message is:

INTERFACE library can only be used with the INTERFACE keyword of
target_link_libraries

So it does not say that the problem lies with the IMPORTED property.

Huh. I remember wanting to do that long ago and ended up just using the property modifications directly. Guess something changed at some point? Anyways, yes, INTERFACE is limited to INTERFACE usage requirements; it makes no sense to have PRIVATE or PUBLIC links because there’s the former is completely useless (for non-STATIC libraries, but then if you use $<LINK_ONLY> and you’re the same there too) and the latter is equivalent to INTERFACE since there’s no “self” to apply it to anyways.

Looking back at release notes, this functionality was added in CMake 3.11.

The code also enforces that IMPORTED libraries can only use INTERFACE as well:

    this->Makefile.IssueMessage(                                                                                                                                                                                                      
      MessageType::FATAL_ERROR,                                                                                                                                                                                                       
      "IMPORTED library can only be used with the INTERFACE keyword of "                                                                                                                                                              
      "target_link_libraries");  

OK, thank you. So, going back to my use case, are you suggesting that I do:

target_link_libraries(PkgConfig::netcdff INTERFACE $<LINK_ONLY:netCDF::netcdf>)

or are you saying this is useless anyway because it is redundant with the information that comes from pkg-config?

I think FindPkgConfig is currently needing some work for static libraries, so the $<LINK_ONLY> is what I would do for this case.

OK, thank you.