Cockatrice/oracle/CMakeLists.txt
BruebachL 0934380a5c
[Translations] Load Qt's own translations and build ours with the binary (#7384)
* [Translations] Load Qt's own translations and build ours with the binary

Three separate bugs kept translations from working.

installNewTranslator() loaded the application translation into
qtTranslator. QTranslator discards its previous contents on every load, so
that wiped the qt_<lang>.qm loaded one line earlier and installed an empty
translator instead. Qt's own strings were never translated on any platform.
Give each file its own translator.

QLibraryInfo::path(TranslationsPath) was the only directory searched for
Qt's translations. A relocated Qt can report a path that does not exist: the
KDE Flatpak runtime reports /usr/translations while its files live in
/usr/share/qt6/translations. Probe the usual install layouts and only hand
back the ones that exist.

The .qm files were attached to the default build target only, so building
the executable on its own left it without any translations and the language
selector empty. Attach them to the target as well.

Move the shared lookup into TranslationLoader so client and Oracle cannot
drift again, and have it report a missing translation at debug level. An
unset language is the default for English, which has no file to begin with,
and warning about it only added noise to every fresh debug log.

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>

* [Tests] Define main() in translation_loader_test

Without a GTest installation the fallback in tests/CMakeLists.txt links
only gtest, not gtest_main, so the test has to bring its own main(). Every
other test already does; only this one relied on the distribution package
and failed to link on CI.

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>

* [Tests] Put the translation loader fixture next to the test executable

A multi-config generator (MSBuild, Xcode) writes the executable to a
per-config subdirectory while the .qm stays in the current binary
directory, so applicationDirPath() never saw the fixture and
ABuildTreeFindsTheTranslationsNextToTheExecutable failed on Windows.

* [Translations] Depend on Qt's lrelease target and skip the loader test without lrelease

Attaching the .qm custom commands to a second custom target races with
Qt's own <Project>_lrelease target on MSBuild/Makefiles and is rejected
by Xcode, so depend on Qt's target instead. Only build the translation
loader test when Qt6::lrelease is available.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
Co-authored-by: ZeldaZach <zahalpern+github@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 15:04:30 +02:00

316 lines
9.4 KiB
CMake

cmake_minimum_required(VERSION 3.16)
project(Oracle VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
# ------------------------
# Paths and directories
# ------------------------
set(DESKTOPDIR
share/applications
CACHE STRING "path to .desktop files"
)
set(ORACLE_MAC_QM_INSTALL_DIR "oracle.app/Contents/Resources/translations")
set(ORACLE_UNIX_QM_INSTALL_DIR "share/oracle/translations")
set(ORACLE_WIN32_QM_INSTALL_DIR "translations")
# ------------------------
# Sources
# ------------------------
set(oracle_SOURCES
src/main.cpp
src/oraclewizard.cpp
src/oracleimporter.cpp
src/pages.cpp
src/pagetemplates.cpp
src/parsehelpers.cpp
src/raw_json_scanner.cpp
../cockatrice/src/client/settings/cache_settings.cpp
../cockatrice/src/client/settings/card_counter_settings.cpp
../cockatrice/src/client/settings/shortcuts_settings.cpp
../cockatrice/src/client/network/update/client/release_channel.cpp
../cockatrice/src/interface/pixel_map_generator.cpp
../cockatrice/src/interface/theme_config.cpp
../cockatrice/src/interface/theme_manager.cpp
${VERSION_STRING_CPP}
)
# ------------------------
# Translations
# ------------------------
if(UPDATE_TRANSLATIONS)
file(GLOB_RECURSE translate_oracle_SRCS src/*.cpp src/*.h ../cockatrice/src/settingscache.cpp)
set(translate_SRCS ${translate_oracle_SRCS})
set(oracle_TS "${CMAKE_CURRENT_SOURCE_DIR}/oracle_en@source.ts")
else()
file(GLOB oracle_TS "${CMAKE_CURRENT_SOURCE_DIR}/translations/*.ts")
endif(UPDATE_TRANSLATIONS)
if(WIN32)
set(oracle_SOURCES ${oracle_SOURCES} oracle.rc)
endif(WIN32)
if(APPLE)
set(MACOSX_BUNDLE_ICON_FILE appicon.icns)
set_source_files_properties(
${CMAKE_CURRENT_SOURCE_DIR}/resources/appicon.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources
)
set(oracle_SOURCES ${oracle_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/resources/appicon.icns)
endif(APPLE)
set(oracle_RESOURCES oracle.qrc)
# ------------------------
# Qt resources
# ------------------------
qt6_add_resources(oracle_RESOURCES_RCC ${oracle_RESOURCES})
# ------------------------
# Include directories
# ------------------------
include_directories(../cockatrice/src)
# ------------------------
# Optional libraries
# ------------------------
# ZLIB
find_package(ZLIB)
if(ZLIB_FOUND)
include_directories(${ZLIB_INCLUDE_DIRS})
add_definitions("-DHAS_ZLIB")
list(APPEND oracle_SOURCES src/zip/unzip.cpp src/zip/zipglobal.cpp)
else()
message(STATUS "Oracle: zlib not found; ZIP support disabled")
endif()
# LZMA
find_package(LibLZMA)
if(LIBLZMA_FOUND)
include_directories(${LIBLZMA_INCLUDE_DIRS})
add_definitions("-DHAS_LZMA")
list(APPEND oracle_SOURCES src/lzma/decompress.cpp)
else()
message(STATUS "Oracle: LibLZMA not found; xz support disabled")
endif()
# ------------------------
# Build executable
# ------------------------
set(ORACLE_MAC_QM_INSTALL_DIR "oracle.app/Contents/Resources/translations")
set(ORACLE_UNIX_QM_INSTALL_DIR "share/oracle/translations")
set(ORACLE_WIN32_QM_INSTALL_DIR "translations")
# Qt6 Translations are linked after the executable is created in manual mode
qt6_add_executable(
oracle
WIN32
MACOSX_BUNDLE
${oracle_SOURCES}
${oracle_RESOURCES_RCC}
${oracle_MOC_SRCS}
MANUAL_FINALIZATION
)
target_precompile_headers(oracle PRIVATE "${CMAKE_SOURCE_DIR}/cmake/pch/qtwidgets_pch.h")
# ------------------------
# Link libraries
# ------------------------
target_link_libraries(
oracle
PUBLIC libcockatrice_card
PUBLIC libcockatrice_settings
PUBLIC libcockatrice_protocol
PUBLIC ${ORACLE_QT_MODULES}
)
# Only user_level.h is needed from the network headers (pulled in transitively
# by pixel_map_generator.h); nothing from the network libraries is linked.
target_include_directories(oracle PRIVATE ${CMAKE_SOURCE_DIR}/libcockatrice_network)
if(ZLIB_FOUND)
target_link_libraries(oracle PUBLIC ${ZLIB_LIBRARIES})
endif()
if(LIBLZMA_FOUND)
target_link_libraries(oracle PUBLIC ${LIBLZMA_LIBRARIES})
endif()
# ------------------------
# Install rules
# ------------------------
if(UNIX)
if(APPLE)
set(MACOSX_BUNDLE_INFO_STRING "${PROJECT_NAME}")
set(MACOSX_BUNDLE_GUI_IDENTIFIER "com.cockatrice.${PROJECT_NAME}")
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${PROJECT_NAME}-${PROJECT_VERSION}")
set(MACOSX_BUNDLE_BUNDLE_NAME ${PROJECT_NAME})
set(MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION})
set(MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION})
set_target_properties(oracle PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/cmake/Info.plist)
install(TARGETS oracle BUNDLE DESTINATION ./)
else()
# Assume linux
install(TARGETS oracle RUNTIME DESTINATION bin/)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/oracle.png DESTINATION ${ICONDIR}/hicolor/48x48/apps)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/oracle.svg DESTINATION ${ICONDIR}/hicolor/scalable/apps)
endif()
elseif(WIN32)
install(TARGETS oracle RUNTIME DESTINATION ./)
endif()
if(NOT WIN32 AND NOT APPLE)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/oracle.desktop DESTINATION ${DESKTOPDIR})
endif(NOT WIN32 AND NOT APPLE)
# ------------------------
# Qt plugin handling
# ------------------------
if(APPLE)
# these needs to be relative to CMAKE_INSTALL_PREFIX
set(plugin_dest_dir oracle.app/Contents/Plugins)
set(qtconf_dest_dir oracle.app/Contents/Resources)
# Qt plugins: iconengines, platforms, styles, tls (Qt6)
install(
DIRECTORY "${QT_PLUGINS_DIR}/"
DESTINATION ${plugin_dest_dir}
COMPONENT Runtime
FILES_MATCHING
PATTERN "*.dSYM" EXCLUDE
PATTERN "*_debug.dylib" EXCLUDE
PATTERN "iconengines/*.dylib"
PATTERN "platforms/*.dylib"
PATTERN "styles/*.dylib"
PATTERN "tls/*.dylib"
)
install(
CODE "
file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${qtconf_dest_dir}/qt.conf\" \"[Paths]
Plugins = Plugins
Translations = Resources/translations\")
"
COMPONENT Runtime
)
install(
CODE "
file(GLOB_RECURSE QTPLUGINS
\"\${CMAKE_INSTALL_PREFIX}/${plugin_dest_dir}/*.dylib\")
set(BU_CHMOD_BUNDLE_ITEMS ON)
include(BundleUtilities)
fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/oracle.app\" \"\${QTPLUGINS}\" \"${QT_LIBRARY_DIR}\")
"
COMPONENT Runtime
)
endif()
if(WIN32)
# these needs to be relative to CMAKE_INSTALL_PREFIX
set(plugin_dest_dir Plugins)
set(qtconf_dest_dir .)
list(APPEND libSearchDirs ${QT_LIBRARY_DIR})
install(
DIRECTORY "$<TARGET_FILE_DIR:oracle>/"
DESTINATION ./
FILES_MATCHING
PATTERN "*.dll"
PATTERN "*.pdb" EXCLUDE
PATTERN "*.dir*" EXCLUDE
PATTERN "*_autogen*" EXCLUDE
PATTERN "*.tlog*" EXCLUDE
PATTERN "CMakeFiles*" EXCLUDE
PATTERN "x64*" EXCLUDE
PATTERN ".qt*" EXCLUDE
PATTERN ".qsb*" EXCLUDE
PATTERN ".lupdate*" EXCLUDE
)
# Qt plugins: iconengines, platforms, styles, tls (Qt6)
install(
DIRECTORY "${QT_PLUGINS_DIR}/"
DESTINATION ${plugin_dest_dir}
COMPONENT Runtime
FILES_MATCHING
PATTERN "iconengines/qsvgicon.dll"
PATTERN "platforms/qdirect2d.dll"
PATTERN "platforms/qminimal.dll"
PATTERN "platforms/qoffscreen.dll"
PATTERN "platforms/qwindows.dll"
PATTERN "styles/qcertonlybackend.dll"
PATTERN "styles/qopensslbackend.dll"
PATTERN "styles/qschannelbackend.dll"
PATTERN "styles/qwindowsvistastyle.dll"
PATTERN "tls/qcertonlybackend.dll"
PATTERN "tls/qopensslbackend.dll"
PATTERN "tls/qschannelbackend.dll"
)
install(
CODE "
file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${qtconf_dest_dir}/qt.conf\" \"[Paths]
Plugins = Plugins
Translations = Resources/translations\")
"
COMPONENT Runtime
)
install(
CODE "
file(GLOB_RECURSE QTPLUGINS
\"\${CMAKE_INSTALL_PREFIX}/${plugin_dest_dir}/*.dll\")
set(BU_CHMOD_BUNDLE_ITEMS ON)
include(BundleUtilities)
fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/Oracle.exe\" \"\${QTPLUGINS}\" \"${libSearchDirs}\")
"
COMPONENT Runtime
)
endif()
# ------------------------
# Qt translations
# ------------------------
if(Qt6LinguistTools_FOUND)
#Qt6 Translations happen after the executable is built up
if(UPDATE_TRANSLATIONS)
qt6_add_translations(
oracle
TS_FILES
${oracle_TS}
SOURCES
${translate_SRCS}
QM_FILES_OUTPUT_VARIABLE
oracle_QM
)
else()
qt6_add_translations(oracle TS_FILES ${oracle_TS} QM_FILES_OUTPUT_VARIABLE oracle_QM)
endif()
if(UNIX)
if(APPLE)
install(FILES ${oracle_QM} DESTINATION ${ORACLE_MAC_QM_INSTALL_DIR})
else()
install(FILES ${oracle_QM} DESTINATION ${ORACLE_UNIX_QM_INSTALL_DIR})
endif()
elseif(WIN32)
install(FILES ${oracle_QM} DESTINATION ${ORACLE_WIN32_QM_INSTALL_DIR})
endif()
# Qt attaches the .qm files to the default build target only, so building the executable on its own
# (CLion, --target oracle) leaves it without any translations: nothing is translated and the
# language selector in the wizard stays empty.
# Depend on Qt's own lrelease target instead of attaching the .qm custom commands to a second
# target: two independent targets owning the same outputs race on MSBuild/Makefiles and are an
# error on Xcode. Older Qt versions already add this dependency themselves.
foreach(_lrelease_target Oracle_lrelease oracle_lrelease)
if(TARGET ${_lrelease_target})
add_dependencies(oracle ${_lrelease_target})
endif()
endforeach()
endif()
qt6_finalize_target(oracle)