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
-
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 :)
-
@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.