imported library compile flags not loaded

BLUF (Bottom Line Up Front) Pascal quote applies
If a package provides a CMake FooConfig.cmake file and also provides pkg-config support (foo.pc), should I reasonably expect that the information in both locations are consistent?

If not, is there a (good) way to force the pkg-config information to be applied when I “import” the package? Specifically, the compiler flags?

Painful Detail

I have the feeling this must be a common issue, but I couldn’t quite find a match anywhere. I am (externally to my project) building and installing libzmq and cppzmq in a toolchain OCI image. Both products are built like this (fragment of my Dockerfile):

     ; ${CMAKE}                                                 \
         ${CMAKE_FLAGS}                                         \
         ${cmake_INSTALL_FLAGS}                                 \
         -DCMAKE_BUILD_TYPE=Release                             \
         -DENABLE_DRAFTS=on                                     \
         -S ..                                                  \
     ; make ${MAKEFLAGS} install                                \
     ; /sbin/ldconfig                                           \

The ENABLE_DRAFTS option defines a compiler macro ZMQ_BUILD_DRAFT_API, which must be defined for my code to compile.

In my project then, I simply add

find_package(ZeroMQ REQUIRED)
find_package(cppzmq REQUIRED)

The installed ZeroMQConfig.cmake is this:

# ZeroMQ cmake module
#
# The following import targets are created
#
# ::
#
#   libzmq-static
#   libzmq
#
# This module sets the following variables in your project::
#
#   ZeroMQ_FOUND - true if ZeroMQ found on the system
#   ZeroMQ_INCLUDE_DIR - the directory containing ZeroMQ headers
#   ZeroMQ_LIBRARY -
#   ZeroMQ_STATIC_LIBRARY


####### Expanded from @PACKAGE_INIT@ by configure_package_config_file() #######
####### Any changes to this file will be overwritten by the next CMake run ####
####### The input file was ZeroMQConfig.cmake.in                            ########

get_filename_component(PACKAGE_PREFIX_DIR "${CMAKE_CURRENT_LIST_DIR}/../../../" ABSOLUTE)

macro(set_and_check _var _file)
  set(${_var} "${_file}")
  if(NOT EXISTS "${_file}")
    message(FATAL_ERROR "File or directory ${_file} referenced by variable ${_var} does not exist !")
  endif()
endmacro()

macro(check_required_components _NAME)
  foreach(comp ${${_NAME}_FIND_COMPONENTS})
    if(NOT ${_NAME}_${comp}_FOUND)
      if(${_NAME}_FIND_REQUIRED_${comp})
        set(${_NAME}_FOUND FALSE)
      endif()
    endif()
  endforeach()
endmacro()

####################################################################################

if(NOT TARGET libzmq AND NOT TARGET libzmq-static)
  include("${CMAKE_CURRENT_LIST_DIR}/ZeroMQTargets.cmake")

  if (TARGET libzmq)
    get_target_property(ZeroMQ_INCLUDE_DIR libzmq INTERFACE_INCLUDE_DIRECTORIES)
  else ()
    get_target_property(ZeroMQ_INCLUDE_DIR libzmq-static INTERFACE_INCLUDE_DIRECTORIES)
  endif()

  if (TARGET libzmq)
    get_target_property(ZeroMQ_LIBRARY libzmq LOCATION)
  endif()
  if (TARGET libzmq-static)
    get_target_property(ZeroMQ_STATIC_LIBRARY libzmq-static LOCATION)
  endif()
endif()

(as I type this, I realize that nothing here exports compile flags; so it may be an intentional limitation of the ZeroMQ package)

The CppZmq package is header-only, and does export pkg-config options. In particular,

$ pkg-config --cflags cppzmq
-I/opt/foss/include -DZMQ_BUILD_DRAFT_API=1

The installed cppzmqConfig.cmake is this:

# cppzmq cmake module
#
# The following import targets are created
#
# ::
#
#   cppzmq-static
#   cppzmq
#
# This module sets the following variables in your project::
#
# cppzmq_FOUND - true if cppzmq found on the system
# cppzmq_INCLUDE_DIR - the directory containing cppzmq headers
# cppzmq_LIBRARY - the ZeroMQ library for dynamic linking
# cppzmq_STATIC_LIBRARY - the ZeroMQ library for static linking


####### Expanded from @PACKAGE_INIT@ by configure_package_config_file() #######
####### Any changes to this file will be overwritten by the next CMake run ####
####### The input file was cppzmqConfig.cmake.in                            ########

get_filename_component(PACKAGE_PREFIX_DIR "${CMAKE_CURRENT_LIST_DIR}/../../../" ABSOLUTE)

macro(set_and_check _var _file)
  set(${_var} "${_file}")
  if(NOT EXISTS "${_file}")
    message(FATAL_ERROR "File or directory ${_file} referenced by variable ${_var} does not exist !")
  endif()
endmacro()

macro(check_required_components _NAME)
  foreach(comp ${${_NAME}_FIND_COMPONENTS})
    if(NOT ${_NAME}_${comp}_FOUND)
      if(${_NAME}_FIND_REQUIRED_${comp})
        set(${_NAME}_FOUND FALSE)
      endif()
    endif()
  endforeach()
endmacro()

####################################################################################

include(CMakeFindDependencyMacro)
find_package(ZeroMQ QUIET)

# libzmq autotools install: fallback to pkg-config
if(NOT ZeroMQ_FOUND)
    list (APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/libzmq-pkg-config)
    find_package(ZeroMQ REQUIRED)
endif()

if(NOT ZeroMQ_FOUND)
    message(FATAL_ERROR "ZeroMQ was NOT found!")
endif()

if(NOT TARGET cppzmq)
    include("${CMAKE_CURRENT_LIST_DIR}/cppzmqTargets.cmake")
    get_target_property(cppzmq_INCLUDE_DIR cppzmq INTERFACE_INCLUDE_DIRECTORIES)
endif()

Now, when I build a library that uses it, I expected that by adding to the target’s link libraries, I would inherit compilation flags. Other packages I’ve used work that way. But I was wrong.

add_library( ${myTarget} SHARED )
target_sources( ${myTarget} PRIVATE
   mySource.cpp
)
target_link_libraries( ${myTarget} PUBLIC
  cppzmq
)

# This should have been defined when ZMQ was brought in. Empirically, it is not. I require it to
# enable the active_poller_t
target_compile_definitions( ${myTarget} PUBLIC ZMQ_BUILD_DRAFT_API )

The last line is what I didn’t want to do. Since pkg-config knows about the additional #define I need, shouldn’t I be able to “inherit” it automagically? How, short of hard-coding like I did, can I get the installed configuration of a tool?

That looks to me like the real problem is the cppzmqConfig.cmake file is missing the compiler definition. When a <packageName>Config.cmake file is provided, it should be complete. The presence of a <packageName>.pc file should be irrelevant. Your project should not have to try to read both. It appears they have provided the ZMQ_BUILD_DRAFT_API compiler define only in <packageName>.pc, but neglected to add it to their <packageName>Config.cmake. I think you already know this, but I’m just being clear that rather than trying to read both files in your project, your time may be better spent convincing upstream to fix their <packageName>Config.cmake file. Since you are producing a docker image for the build, you could also consider patching the <packageName>Config.cmake file that you put in that docker image, but that’s not a great solution long term (any build elsewhere outside of that image would fail).

I suspected, but wasn’t sure. My comprehension of CMake config files is nominal.

I trolled around a bit, and remembered that ZMQ isn’t friendly to CMake. The primary developers are committed to autotools.

Is there a canonical way to include compile definitions? Is that a lesser known/used macro that should be set by the xxxConfig.cmake file?

A <packageName>Config.cmake file can use the same commands as a project would use. The target_compile_definitions() command is what could be used in this particular case, or the properties could be set directly using set_property() or set_target_properties(). If using the commands, use INTERFACE rather than PRIVATE or PUBLIC. If using one of the set_... property commands, the property to set would be INTERFACE_COMPILE_DEFINITIONS.