Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

problems with cmake and how I solved it



  • Hi,

    When I read about the interface libraries at cmake, I was immediately convinced that this would be the right way to go.
    Unfortunately, it turned out that cmake does not work as the manual would have you believe. Who knows, maybe I misunderstood something.
    Anyway - I just couldn't figure out why include directories are not accepted for the interface libraries.
    When I look at the properties of the library, it just says "link only".

    I was stunned - my dependencies were enormous. Here's what my previous cmake file for a plugin looked like.
    Please note that my plugin consists of only one source file.
    I have over 10 of them, which means 10x stupid copying :(

    cmake_minimum_required(VERSION 3.16)
    #---------------------------   PLUGIN   --------------------------------------
    project(LCToolTable LANGUAGES CXX)
    #-----------------------------------------------------------------------------
    include(FalconView)
    add_library(ppLCToolTable MODULE
        lctooltable.cpp
        )
    set(TS_FILES
        lctooltable_de_DE.ts
        )
    target_include_directories(ppLCToolTable
                               PRIVATE ${LinuxCNC_DIR}/include
                               PRIVATE ${LinuxCNC_DIR}/src
                               PRIVATE ${LinuxCNC_DIR}/src/emc/rs274ngc
                               PRIVATE ${LinuxCNC_DIR}/src/emc/tooldata
                               PRIVATE ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
                               PRIVATE ${OpenCASCADE_INCLUDE_DIR}
                               )
    target_link_libraries(ppLCToolTable
                          PRIVATE FVBaseLib
                          PRIVATE FVlcLib
                          PRIVATE Qt${QT_VERSION_MAJOR}::Gui
                          PRIVATE Qt${QT_VERSION_MAJOR}::Widgets
                          PRIVATE Qt${QT_VERSION_MAJOR}::Core
                          PRIVATE Qt${QT_VERSION_MAJOR}::Sql
                          PRIVATE Qt${QT_VERSION_MAJOR}::UiTools
                          PRIVATE Qt${QT_VERSION_MAJOR}::Xml
                          PRIVATE ${LC_LibLC}
                          PRIVATE ${LC_LibPM}
                          PRIVATE ${LC_LibNML}
                          PRIVATE ${LC_LibRS274}
                          PRIVATE ${LC_LibLCI}
                          PRIVATE ${LC_LibPP}
                          PRIVATE ${LC_LibHal}
                          PRIVATE ${LC_LibTD}
                          PRIVATE TKernel
                          PRIVATE TKMath
                          PRIVATE TKService
                          PRIVATE TKV3d
                          PRIVATE TKOpenGl
                          PRIVATE TKBRep
                          PRIVATE TKIGES
                          PRIVATE TKSTL
                          PRIVATE TKVRML
                          PRIVATE TKSTEP
                          PRIVATE TKSTEPAttr
                          PRIVATE TKSTEP209
                          PRIVATE TKSTEPBase
                          PRIVATE TKGeomBase
                          PRIVATE TKGeomAlgo
                          PRIVATE TKG3d
                          PRIVATE TKG2d
                          PRIVATE TKXSBase
                          PRIVATE TKShHealing
                          PRIVATE TKHLR
                          PRIVATE TKTopAlgo
                          PRIVATE TKMesh
                          PRIVATE TKPrim
                          PRIVATE TKCDF
                          PRIVATE TKBool
                          PRIVATE TKBO
                          PRIVATE TKFillet
                          PRIVATE TKOffset
                          PRIVATE TKLCAF
                          PRIVATE TKCAF
                          PRIVATE TKVCAF
                          PRIVATE TKBin
                          PRIVATE TKXml
                          PRIVATE m
                          PRIVATE stdc++
                          PRIVATE boost_python39
                          PRIVATE python3.9
                          PRIVATE crypt
                          PRIVATE pthread
                          PRIVATE dl
                          PRIVATE util
                          )
    if(COMMAND qt_create_translation)
        qt_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES})
    else()
        qt5_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES})
    endif()
    

    I was infinitely frustrated!

    Then I got busy with cmake functions. However, the build did not work. It was quite a bit of work until I found a working variant. Obviously the order of libraries matters :(

    Now I am overjoyed. No more stupid copying and the dependencies are programmed in exactly one place. So that's how I wanted it from the very beginning ;)

    The new cmake file for a plugin now looks like this:

    cmake_minimum_required(VERSION 3.16)
    #---------------------------   PLUGIN   --------------------------------------
    project(LCToolTable LANGUAGES CXX)
    #-----------------------------------------------------------------------------
    include(FalconView)
    add_library(ppLCToolTable MODULE
        lctooltable.cpp
        )
    set(TS_FILES
        lctooltable_de_DE.ts
        )
    target_link_libraries(ppLCToolTable
                          PRIVATE FVBaseLib
                          PRIVATE FVlcLib
                          )
    use_interface_library(ppLCToolTable
                          IFlinuxCNC
                          IFQt
                          IFOpenCASCADE
                          IFStandard
                          )
    if(COMMAND qt_create_translation)
        qt_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES})
    else()
        qt5_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES})
    endif()
    

    For this to work, there must be the definition of the interface library once. Secondly, there needs to be a function. But since I already had a file anyway, which is included in every cmake file, e.g. to set the settings, which cannot be set globally ...

    But one after the other. In the cmake file in the project root directory there are e.g. these definitions:

    add_library(IFQt          INTERFACE)
    target_include_directories(IFQt
                               INTERFACE ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
                               )
    target_link_libraries(IFQt
                          INTERFACE Qt${QT_VERSION_MAJOR}::Gui
                          INTERFACE Qt${QT_VERSION_MAJOR}::Widgets
                          INTERFACE Qt${QT_VERSION_MAJOR}::Core
                          INTERFACE Qt${QT_VERSION_MAJOR}::Sql
                          INTERFACE Qt${QT_VERSION_MAJOR}::UiTools
                          INTERFACE Qt${QT_VERSION_MAJOR}::Xml
                          )
    

    With that definition the usage of qt is one word in subproject.
    ... and in the file that is included in each cmake file this function:

    #--------------------------------------------------------------
    # replacement for usage of interface library definition
    #--------------------------------------------------------------
    function(use_interface_libraries)
      list(POP_FRONT ARGV tgt)
      message("processing target: ${tgt}")
      if(NOT TARGET ${tgt})
         message(FATAL_ERROR "There is no target named '${tgt}'")
         return()
      endif()
      foreach(ifl ${ARGV})
              message(" - add interface lib: ${ifl}")
              if(NOT TARGET ${ifl})
                 message(FATAL_ERROR "interface library '${ifl}' not found!")
                 return()
              endif()
              get_target_property(_IFL_TYPE ${ifl} TYPE)
              if(NOT ${_IFL_TYPE} STREQUAL "INTERFACE_LIBRARY")
                 message(FATAL_ERROR "given library '${ifl}' is NOT an interface library!")
                 return()
              endif()
              get_target_property(_INC_DIRS ${tgt} INCLUDE_DIRECTORIES)
              get_target_property(_LNK_LIBS ${tgt} LINK_LIBRARIES)
              get_target_property(_IF_INCS  ${ifl} INTERFACE_INCLUDE_DIRECTORIES)
              get_target_property(_IF_LIBS  ${ifl} INTERFACE_LINK_LIBRARIES)
              if(_IF_INCS AND _INC_DIRS)
                 set_target_properties(${tgt} PROPERTIES
                                       INCLUDE_DIRECTORIES "${_INC_DIRS};${_IF_INCS}"
                                       )
              elseif(_IF_INCS)
                 set_target_properties(${tgt} PROPERTIES
                                       INCLUDE_DIRECTORIES "${_IF_INCS}"
                                       )
              endif()
              if(_IF_LIBS AND _LNK_LIBS)
                 set_target_properties(${tgt} PROPERTIES
                                       LINK_LIBRARIES "${_LNK_LIBS};${_IF_LIBS}"
                                       )
              else()
                 set_target_properties(${tgt} PROPERTIES
                                       LINK_LIBRARIES "${_IF_LIBS}"
                                       )
              endif()
              unset(_IF_INCS-NOTFOUND)
              unset(_INC_DIRS)
              unset(_LNK_LIBS)
              unset(_IF_INCS)
              unset(_IF_LIBS)
      endforeach()
      get_target_property(_LIBS ${tgt} LINK_LIBRARIES)
      list(REMOVE_DUPLICATES _LIBS)
      set_target_properties(${tgt} PROPERTIES
                            LINK_LIBRARIES "${_LIBS}"
                            )
    endfunction(use_interface_libraries)
    

    With Qt its trivial at this point. I had an imported library with a bunch of include directories and a bunch of static and shared libraries. Instead of having to duplicate include-directories and libraries in each plugin, the usage is now a single word :)

    Maybe someone finds it useful


  • Lifetime Qt Champion

    Your macro use_interface_libraries is useless - simply link against QtX::FOO and the include directories and libraries and all the other stuff you do in your macro are done automatically.

    /Edit: and when you want a private Qt5 header you should link against the e.g. Qt5::GuiPrivate instead using target_include_directories() and ${Qt5Gui_PRIVATE_INCLUDE_DIRS}



  • LOL - I expected this kind of reply.

    Thought when I use Qt as example, then everybody could understand the issue.

    So then look at LinuxCNC - 4 include directories and 8 libraries - and no Qt-helper macros ...

    ... or opencascade with that 30 libraries

    The point is/was, that cmake interface library definition does only care for the libraries. Not the include directories.
    That was the reason for me to start with that research/work.

    So for me/my project it is not useless :)


  • Lifetime Qt Champion

    @django-Reinhard said in problems with cmake and how I solved it:

    The point is/was, that cmake interface library definition does only care for the libraries. Not the include directories.

    This is wrong when you use properly imported targets like Qt provides. Don't know about the other libs though but when they provide imported targets and the proper include dirs are not added then the library creates it's cmake find modules wrong.


Log in to reply