cmake_minimum_required(VERSION 3.19) project(ApartConcept1Controller VERSION 1.0.0 DESCRIPTION "RS232 controller for the Apart Concept 1 amplifier" LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE) endif() # Required Qt6 component find_package(Qt6 REQUIRED COMPONENTS Widgets) # Optional Qt6 SerialPort: real hardware support is compiled in when available. # Without it the app still builds and runs in simulator mode. find_package(Qt6 QUIET OPTIONAL_COMPONENTS SerialPort) set(SOURCES src/main.cpp src/protocol/Concept1Protocol.cpp src/transport/SimulatorTransport.cpp src/core/Concept1Controller.cpp src/ui/ThemeManager.cpp src/ui/MainWindow.cpp ) set(HEADERS src/protocol/Concept1Protocol.h src/protocol/Concept1State.h src/transport/ITransport.h src/transport/SimulatorTransport.h src/core/Concept1Controller.h src/ui/ThemeManager.h src/ui/MainWindow.h ) if(Qt6SerialPort_FOUND) list(APPEND SOURCES src/transport/SerialTransport.cpp) list(APPEND HEADERS src/transport/SerialTransport.h) message(STATUS "Qt6 SerialPort found -> real RS232 hardware support ENABLED") else() message(WARNING "Qt6 SerialPort NOT found -> building with SIMULATOR only. " "Install it (e.g. 'sudo pacman -S qt6-serialport') for hardware support.") endif() qt_add_resources(SOURCES resources/app.qrc) # WIN32 makes this a GUI app on Windows (no console window). Ignored elsewhere. add_executable(apart-controller WIN32 ${SOURCES} ${HEADERS}) target_include_directories(apart-controller PRIVATE src) target_link_libraries(apart-controller PRIVATE Qt6::Widgets) if(Qt6SerialPort_FOUND) target_link_libraries(apart-controller PRIVATE Qt6::SerialPort) target_compile_definitions(apart-controller PRIVATE HAVE_QT_SERIALPORT) endif() # --------------------------------------------------------------------------- # Installation + packaging (Windows installer via CPack/NSIS) # --------------------------------------------------------------------------- install(TARGETS apart-controller RUNTIME DESTINATION .) if(WIN32) get_filename_component(_mingw_bin "${CMAKE_CXX_COMPILER}" DIRECTORY) find_program(WINDEPLOYQT_EXECUTABLE windeployqt HINTS "${_mingw_bin}" "${Qt6_DIR}/../../../bin") # Pass config-time paths into the install-time script below. install(CODE "set(_WINDEPLOYQT \"${WINDEPLOYQT_EXECUTABLE}\")") install(CODE "set(_MINGW_BIN \"${_mingw_bin}\")") install(CODE [[ if(POLICY CMP0207) cmake_policy(SET CMP0207 NEW) endif() # 1) Resolve the exe's full transitive DLL closure from the mingw bin # dir. This MUST run before windeployqt: at this point the package # contains only the .exe, so each dependency (Qt6Core.dll, libpng, # zlib, freetype, harfbuzz, pcre2, zstd, brotli, icu, libgcc/ # libstdc++/winpthread, ...) resolves to exactly one path. Running # after windeployqt would find duplicates (staging vs. mingw) and # abort with "Multiple conflicting paths". System DLLs are excluded. file(GET_RUNTIME_DEPENDENCIES EXECUTABLES "${CMAKE_INSTALL_PREFIX}/apart-controller.exe" RESOLVED_DEPENDENCIES_VAR _resolved UNRESOLVED_DEPENDENCIES_VAR _unresolved DIRECTORIES "${_MINGW_BIN}" PRE_EXCLUDE_REGEXES "api-ms-win-.*" "ext-ms-.*" POST_EXCLUDE_REGEXES ".*[Ss]ystem32.*" ".*[Ss]ysWOW64.*") foreach(_dll ${_resolved}) file(INSTALL DESTINATION "${CMAKE_INSTALL_PREFIX}" TYPE SHARED_LIBRARY FOLLOW_SYMLINK_CHAIN FILES "${_dll}") endforeach() # 2) windeployqt now adds the runtime-loaded Qt *plugins* # (platforms/qwindows.dll, styles, imageformats) plus any helper # DLLs those plugins need. It is fine for it to re-copy Qt DLLs. if(_WINDEPLOYQT) execute_process(COMMAND "${_WINDEPLOYQT}" --release --no-translations --no-system-d3d-compiler --dir "${CMAKE_INSTALL_PREFIX}" "${CMAKE_INSTALL_PREFIX}/apart-controller.exe") endif() ]]) endif() set(CPACK_PACKAGE_NAME "Apart Concept 1 Controller") set(CPACK_PACKAGE_VENDOR "ApartController") set(CPACK_PACKAGE_VERSION "${PROJECT_VERSION}") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "${PROJECT_DESCRIPTION}") set(CPACK_PACKAGE_INSTALL_DIRECTORY "Apart Concept 1 Controller") if(WIN32) set(CPACK_GENERATOR "NSIS") set(CPACK_NSIS_DISPLAY_NAME "Apart Concept 1 Controller") set(CPACK_NSIS_PACKAGE_NAME "Apart Concept 1 Controller") set(CPACK_NSIS_INSTALLED_ICON_NAME "apart-controller.exe") set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL ON) set(CPACK_NSIS_MODIFY_PATH OFF) # Start-menu + desktop shortcut pointing at the exe. set(CPACK_PACKAGE_EXECUTABLES "apart-controller" "Apart Concept 1 Controller") set(CPACK_NSIS_CREATE_DESKTOP_LINKS "apart-controller") endif() include(CPack)