@@ -35,6 +35,64 @@ function(filter_debug_dlls input_dll_list output_dll_list)
3535 set (${output_dll_list} "${filtered_dlls} " PARENT_SCOPE )
3636endfunction ()
3737
38+ # Resolve the directory containing the shared library file backing an
39+ # imported target. Needed to bundle non-Qt third-party dependencies
40+ # (yaml-cpp, KDReports) whose location is not exposed via dedicated
41+ # CMake variables the way CONTAINER_LIB_DIR / RABBITMQ_SHRD_LIB_DIR are.
42+ function (resolve_imported_lib_dir target out_var )
43+ set (${out_var} "" PARENT_SCOPE )
44+ if (NOT TARGET ${target} )
45+ return ()
46+ endif ()
47+ foreach (prop
48+ IMPORTED_LOCATION_RELEASE
49+ IMPORTED_LOCATION_RELWITHDEBINFO
50+ IMPORTED_LOCATION_MINSIZEREL
51+ IMPORTED_LOCATION_DEBUG
52+ IMPORTED_LOCATION
53+ )
54+ get_target_property (loc ${target} ${prop} )
55+ if (loc AND EXISTS "${loc} " )
56+ get_filename_component (dir "${loc} " DIRECTORY )
57+ set (${out_var} "${dir} " PARENT_SCOPE )
58+ return ()
59+ endif ()
60+ endforeach ()
61+ endfunction ()
62+
63+ # Glob and install the shared library plus any versioned symlinks for
64+ # `target` from its resolved location. `dest` is the destination
65+ # relative to the install prefix; `glob_patterns` is a list of file
66+ # globs (extension-specific, e.g. "libfoo*.so*"). Silently no-ops when
67+ # the target does not exist or its location cannot be resolved.
68+ function (install_imported_runtime_lib target dest )
69+ set (globs ${ARGN} )
70+ if (NOT TARGET ${target} )
71+ message (STATUS "install_imported_runtime_lib: skipping ${target} (not a target)" )
72+ return ()
73+ endif ()
74+ resolve_imported_lib_dir (${target} _lib_dir )
75+ if (NOT _lib_dir)
76+ message (WARNING "install_imported_runtime_lib: could not resolve lib dir for ${target} " )
77+ return ()
78+ endif ()
79+ set (_collected "" )
80+ foreach (_pattern ${globs} )
81+ file (GLOB _matches "${_lib_dir} /${_pattern} " )
82+ list (APPEND _collected ${_matches} )
83+ endforeach ()
84+ if (_collected)
85+ list (REMOVE_DUPLICATES _collected)
86+ message (STATUS "Bundling ${target} from ${_lib_dir} : ${_collected} " )
87+ install (FILES ${_collected}
88+ DESTINATION ${dest}
89+ COMPONENT CargoNetSim_COMP
90+ )
91+ else ()
92+ message (WARNING "install_imported_runtime_lib: no files matched for ${target} in ${_lib_dir} " )
93+ endif ()
94+ endfunction ()
95+
3896# Check if building of installer is enabled
3997if (CARGONET_BUILD_INSTALLER)
4098
@@ -43,8 +101,18 @@ if(CARGONET_BUILD_INSTALLER)
43101 COMMENT "Creating an installer for CargoNetSim."
44102 )
45103
46- # Ensure that CargoNetSim is built first by specifying it as a dependency
47- add_dependencies (CargoNetSimInstaller CargoNetSim )
104+ # Make sure every binary that ships in the package is built before
105+ # CPack runs. The CLI and the standalone RabbitMQ config tool are
106+ # opt-in but ship under the same component as the GUI, so wire them
107+ # in conditionally.
108+ set (CARGONETSIM_INSTALLER_DEPS CargoNetSim)
109+ if (TARGET cargonetsim-cli)
110+ list (APPEND CARGONETSIM_INSTALLER_DEPS cargonetsim-cli)
111+ endif ()
112+ if (CARGONET_BUILD_RABBITMQ_CONFIG AND TARGET RabbitMQConfig)
113+ list (APPEND CARGONETSIM_INSTALLER_DEPS RabbitMQConfig)
114+ endif ()
115+ add_dependencies (CargoNetSimInstaller ${CARGONETSIM_INSTALLER_DEPS} )
48116
49117 # Install CargoNetSim executable with component
50118 # This is needed because the install in src/CMakeLists.txt may not be processed correctly by CPack
@@ -187,6 +255,11 @@ if(CARGONET_BUILD_INSTALLER)
187255 message (FATAL_ERROR "CRITICAL: RabbitMQ DLLs not found! RABBITMQ_SHRD_LIB_DIR=${RABBITMQ_SHRD_LIB_DIR} " )
188256 endif ()
189257
258+ # yaml-cpp and KDReports DLLs (REQUIRED at runtime — Backend
259+ # links yaml-cpp::yaml-cpp, GUI links KDReports::kdreports).
260+ install_imported_runtime_lib (yaml-cpp::yaml-cpp bin "yaml-cpp*.dll" )
261+ install_imported_runtime_lib (KDReports::kdreports bin "kdreports*.dll" "KDReports*.dll" )
262+
190263 # Download RabbitMQ installer
191264 if (NOT EXISTS "${RABBITMQ_INSTALLER_FILE} " )
192265 message (STATUS "Downloading RabbitMQ Server installer..." )
@@ -535,6 +608,15 @@ if(CARGONET_BUILD_INSTALLER)
535608 endif ()
536609 endif ()
537610
611+ # yaml-cpp and KDReports dylibs (REQUIRED at runtime — Backend
612+ # links yaml-cpp::yaml-cpp, GUI links KDReports::kdreports).
613+ install_imported_runtime_lib (yaml-cpp::yaml-cpp
614+ CargoNetSim.app/Contents/Frameworks
615+ "libyaml-cpp*.dylib" )
616+ install_imported_runtime_lib (KDReports::kdreports
617+ CargoNetSim.app/Contents/Frameworks
618+ "libkdreports*.dylib" "libKDReports*.dylib" )
619+
538620 # Create qt.conf for macOS bundle
539621 file (WRITE "${CMAKE_CURRENT_BINARY_DIR } /qt.conf.macos"
540622 "[Paths]\n Plugins = PlugIns\n " )
@@ -567,37 +649,40 @@ if(CARGONET_BUILD_INSTALLER)
567649 )
568650 else ()
569651 message (WARNING "Qt version < 6.3 detected. Manual Linux deployment may be required." )
570- # Fallback: Install Qt libraries manually
571- set (REQUIRED_QT_LIBS
572- "${QT_LIB_DIR} /libQt6Core.so.6"
573- "${QT_LIB_DIR} /libQt6Network.so.6"
574- "${QT_LIB_DIR} /libQt6Xml.so.6"
575- "${QT_LIB_DIR} /libQt6Gui.so.6"
576- "${QT_LIB_DIR} /libQt6Widgets.so.6"
577- "${QT_LIB_DIR} /libQt6PrintSupport.so.6"
578- "${QT_LIB_DIR} /libQt6Sql.so.6"
579- "${QT_LIB_DIR} /libQt6DBus.so.6"
580- "${QT_LIB_DIR} /libQt6XcbQpa.so.6"
652+ # Fallback: install each required Qt module by globbing the
653+ # full symlink chain (`.so`, `.so.6`, `.so.6.10.1`). Without
654+ # the SONAMEd file the symlinks point to nothing and the
655+ # binary fails to start with "cannot open shared object".
656+ set (REQUIRED_QT_MODULES
657+ Core Network Xml Gui Widgets PrintSupport Sql DBus XcbQpa
581658 )
582- foreach (QT_LIB ${REQUIRED_QT_LIBS} )
583- if (EXISTS "${QT_LIB} " )
584- install (FILES "${QT_LIB} "
659+ foreach (QT_MOD ${REQUIRED_QT_MODULES} )
660+ file (GLOB QT_LIB_FILES "${QT_LIB_DIR} /libQt6${QT_MOD} .so*" )
661+ if (QT_LIB_FILES)
662+ install (FILES ${QT_LIB_FILES}
585663 DESTINATION lib
586664 COMPONENT CargoNetSim_COMP
587665 )
588- # Also install the .so symlinks
589- get_filename_component (LIB_NAME "${QT_LIB} " NAME )
590- string (REGEX REPLACE "\\ .so\\ .6$" ".so" LIB_SYMLINK "${LIB_NAME} " )
591- if (EXISTS "${QT_LIB_DIR} /${LIB_SYMLINK} " )
592- install (FILES "${QT_LIB_DIR} /${LIB_SYMLINK} "
593- DESTINATION lib
594- COMPONENT CargoNetSim_COMP
595- )
596- endif ()
666+ else ()
667+ message (WARNING "Qt module not found in ${QT_LIB_DIR} : libQt6${QT_MOD} .so*" )
597668 endif ()
598669 endforeach ()
599670 endif ()
600671
672+ # ICU shared libraries: Qt's text codecs (Qt6Core) link against
673+ # libicui18n / libicuuc / libicudata at the version Qt was built
674+ # against. The target machine may not have a matching ICU, so
675+ # bundle whatever Qt was linked against.
676+ file (GLOB QT_ICU_LIBS "${QT_LIB_DIR} /libicu*.so*" )
677+ if (QT_ICU_LIBS)
678+ install (FILES ${QT_ICU_LIBS}
679+ DESTINATION lib
680+ COMPONENT CargoNetSim_COMP
681+ )
682+ else ()
683+ message (STATUS "Qt ICU libraries not found in ${QT_LIB_DIR} (system ICU will be used at runtime)" )
684+ endif ()
685+
601686 # Install Qt plugins
602687 if (EXISTS "${QT_PARENT_DIR} /plugins/platforms/" )
603688 install (DIRECTORY "${QT_PARENT_DIR} /plugins/platforms/"
@@ -654,6 +739,12 @@ if(CARGONET_BUILD_INSTALLER)
654739 endif ()
655740 endif ()
656741
742+ # yaml-cpp and KDReports shared libraries (REQUIRED at runtime —
743+ # Backend links yaml-cpp::yaml-cpp, GUI links KDReports::kdreports).
744+ install_imported_runtime_lib (yaml-cpp::yaml-cpp lib "libyaml-cpp.so*" )
745+ install_imported_runtime_lib (KDReports::kdreports lib
746+ "libkdreports*.so*" "libKDReports*.so*" )
747+
657748 # Create qt.conf for Linux
658749 file (WRITE "${CMAKE_CURRENT_BINARY_DIR } /qt.conf.linux"
659750 "[Paths]\n Prefix = ..\n Plugins = lib/plugins\n Libraries = lib\n " )
@@ -663,13 +754,10 @@ if(CARGONET_BUILD_INSTALLER)
663754 COMPONENT CargoNetSim_COMP
664755 )
665756
666- # Install .desktop file
667- if (EXISTS "${CMAKE_SOURCE_DIR } /resources/linux/CargoNetSim.desktop" )
668- install (FILES "${CMAKE_SOURCE_DIR } /resources/linux/CargoNetSim.desktop"
669- DESTINATION share/applications
670- COMPONENT CargoNetSim_COMP
671- )
672- endif ()
757+ # The desktop entry is created at install time by the IFW
758+ # CreateDesktopEntry operation (see Linux component script
759+ # below). It points at the CargoNetSim binary and at the
760+ # bundled icon, so no static .desktop file ships in the payload.
673761
674762 # Install icons for XDG compliance
675763 if (EXISTS "${CMAKE_SOURCE_DIR } /resources/linux/CargoNetSim.png" )
@@ -684,19 +772,42 @@ if(CARGONET_BUILD_INSTALLER)
684772 )
685773 endif ()
686774
687- # Create a wrapper script that sets LD_LIBRARY_PATH
688- file (WRITE "${CMAKE_CURRENT_BINARY_DIR } /CargoNetSim.sh"
689- "#!/bin/bash
690- # CargoNetSim launcher script
691- SCRIPT_DIR=\" $(cd \" $(dirname \"\$ {BASH_SOURCE[0]}\" )\" && pwd)\"
692- export LD_LIBRARY_PATH=\"\$ {SCRIPT_DIR}/../lib:\$ {LD_LIBRARY_PATH}\"
693- export QT_PLUGIN_PATH=\"\$ {SCRIPT_DIR}/../lib/plugins\"
694- exec \"\$ {SCRIPT_DIR}/CargoNetSim\" \" $@\"
695- " )
696- install (PROGRAMS "${CMAKE_CURRENT_BINARY_DIR } /CargoNetSim.sh"
697- DESTINATION bin
698- COMPONENT CargoNetSim_COMP
699- )
775+ # Bundled third-party .so files (Container, RabbitMQ-C,
776+ # KDReports, yaml-cpp, Qt itself) carry hardcoded RPATHs from
777+ # their original install prefix. Rewrite them at install time
778+ # to $ORIGIN-based paths so the bundled libraries find their
779+ # peers and so Qt plugins find the bundled Qt libs. Combined
780+ # with the project-wide CMAKE_INSTALL_RPATH set at the root
781+ # (executables get $ORIGIN/../lib;$ORIGIN), this lets users
782+ # invoke the binaries directly — no wrapper script, no
783+ # LD_LIBRARY_PATH dance.
784+ find_program (PATCHELF_EXECUTABLE patchelf )
785+ if (PATCHELF_EXECUTABLE)
786+ message (STATUS "Found patchelf: ${PATCHELF_EXECUTABLE} " )
787+ install (CODE "
788+ file(GLOB _all_libs \"\$ {CMAKE_INSTALL_PREFIX}/lib/*.so*\" )
789+ foreach(_lib \$ {_all_libs})
790+ if(IS_SYMLINK \"\$ {_lib}\" )
791+ continue()
792+ endif()
793+ execute_process(
794+ COMMAND \" ${PATCHELF_EXECUTABLE} \" --set-rpath \\ $ORIGIN \"\$ {_lib}\"
795+ ERROR_QUIET)
796+ endforeach()
797+
798+ # Qt plugins live at lib/plugins/<category>/*.so so
799+ # $ORIGIN/../.. resolves to lib/, where the bundled Qt
800+ # libs live.
801+ file(GLOB_RECURSE _qt_plugins \"\$ {CMAKE_INSTALL_PREFIX}/lib/plugins/*.so\" )
802+ foreach(_plugin \$ {_qt_plugins})
803+ execute_process(
804+ COMMAND \" ${PATCHELF_EXECUTABLE} \" --set-rpath \\ $ORIGIN/../.. \"\$ {_plugin}\"
805+ ERROR_QUIET)
806+ endforeach()
807+ " COMPONENT CargoNetSim_COMP)
808+ else ()
809+ message (WARNING "patchelf not found. Bundled .so RPATHs will not be relocatable. Install with: sudo apt install patchelf" )
810+ endif ()
700811
701812 endif () # Platform-specific deployment
702813
@@ -1162,7 +1273,7 @@ Component.prototype.createOperations = function() {
11621273 set (CPACK_IFW_PACKAGE_ALLOW_SPACE_IN_PATH ON )
11631274
11641275 # Linux-specific settings
1165- set (CPACK_IFW_PACKAGE_RUN_PROGRAM "@TargetDir@/bin/CargoNetSim.sh " )
1276+ set (CPACK_IFW_PACKAGE_RUN_PROGRAM "@TargetDir@/bin/CargoNetSim" )
11661277 set (CPACK_IFW_PACKAGE_RUN_PROGRAM_DESCRIPTION "Launch CargoNetSim now" )
11671278
11681279 # Use PNG icon for Linux
@@ -1225,7 +1336,7 @@ Component.prototype.createOperations = function() {
12251336 'Name=CargoNetSim\\ n' +
12261337 'GenericName=Cargo Network Simulator\\ n' +
12271338 'Comment=Simulation software for cargo networks\\ n' +
1228- 'Exec=' + targetDir + '/bin/CargoNetSim.sh \\ n' +
1339+ 'Exec=' + targetDir + '/bin/CargoNetSim\\ n' +
12291340 'Icon=' + targetDir + '/share/icons/hicolor/256x256/apps/CargoNetSim.png\\ n' +
12301341 'Terminal=false\\ n' +
12311342 'Categories=Science;Simulation;Education;\\ n'
0 commit comments