include_guard(DIRECTORY) #[[ Add qt translation target. qm_add_translation( [LOCALES locales] [PREFIX prefix] [SOURCES files... | DIRECTORIES dirs... | TARGETS targets... | TS_FILES files...] [TS_DIR dir] [QM_DIR dir] [TS_OPTIONS options...] [QM_OPTIONS options...] [TS_DEPENDS targets...] [QM_DEPENDS targets...] ) Arguments: LOCALES: language names, e.g. zh_CN en_US, must specify if SOURCES or TARGETS is specified PREFIX: translation file prefix, default to target name SOURCES: source files DIRECTORIES: source directories TARGETS: target names, the source files of which will be collected TS_FILES: ts file names, add the specified ts file TS_DIR: ts files destination, default to `CMAKE_CURRENT_SOURCE_DIR` QM_DIR: qm files destination, default to `CMAKE_CURRENT_BINARY_DIR` TS_DEPENDS: add lupdate task as a dependency to the given targets QM_DEPENDS: add lrelease task as a dependency to the given targets ]] # function(qm_add_translation _target) set(options) set(oneValueArgs PREFIX TS_DIR QM_DIR) set(multiValueArgs LOCALES SOURCES DIRECTORIES TARGETS TS_FILES TS_OPTIONS QM_OPTIONS TS_DEPENDS QM_DEPENDS) cmake_parse_arguments(FUNC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) # Get linguist tools if(NOT TARGET Qt${QT_VERSION_MAJOR}::lupdate OR NOT TARGET Qt${QT_VERSION_MAJOR}::lrelease) message(FATAL_ERROR "qm_add_translation: linguist tools not defined. Add find_package(Qt5 COMPONENTS LinguistTools) to CMake to enable.") endif() set(_src_files) set(_include_dirs) # Collect source files if(FUNC_SOURCES) list(APPEND _src_files ${FUNC_SOURCES}) endif() # Collect source directories if(FUNC_DIRECTORIES) foreach(_item IN LISTS FUNC_DIRECTORIES) file(GLOB _tmp ${_item}/*.h ${_item}/*.hpp ${_item}/*.hh ${_item}/*.hxx ${_item}/*.cpp ${_item}/*.cxx ${_item}/*.c ${_item}/*.cc ${_item}/*.m ${_item}/*.mm ) list(APPEND _src_files ${_tmp}) endforeach() endif() # Collect source files if(FUNC_TARGETS) foreach(_item IN LISTS FUNC_TARGETS) get_target_property(_type ${_item} TYPE) if((_type STREQUAL "UTILITY") OR (_type STREQUAL "INTERFACE_LIBRARY")) continue() endif() set(_tmp_files) get_target_property(_tmp_files ${_item} SOURCES) list(FILTER _tmp_files INCLUDE REGEX ".+\\.(h|hh|hpp|hxx|c|cc|cpp|cxx|m|mm)$") list(FILTER _tmp_files EXCLUDE REGEX "^(qasc|moc)_.+") # Need to convert to absolute path get_target_property(_target_dir ${_item} SOURCE_DIR) foreach(_file IN LISTS _tmp_files) get_filename_component(_abs_file ${_file} ABSOLUTE BASE_DIR ${_target_dir}) list(APPEND _src_files ${_abs_file}) endforeach() unset(_tmp_files) get_target_property(_tmp_dirs ${_item} INCLUDE_DIRECTORIES) list(APPEND _include_dirs ${_tmp_dirs}) endforeach() endif() if(_src_files) if(NOT FUNC_LOCALES) message(FATAL_ERROR "qm_add_translation: source files collected but LOCALES not specified!") endif() elseif(NOT FUNC_TS_FILES) message(FATAL_ERROR "qm_add_translation: no source files or ts files collected!") endif() add_custom_target(${_target}) set(_qm_depends) if(FUNC_TS_OPTIONS) set(_ts_options ${FUNC_TS_OPTIONS}) endif() if(FUNC_QM_OPTIONS) set(_qm_options ${FUNC_QM_OPTIONS}) endif() if(_src_files) if(FUNC_PREFIX) set(_prefix ${FUNC_PREFIX}) else() set(_prefix ${_target}) endif() if(FUNC_TS_DIR) set(_ts_dir ${FUNC_TS_DIR}) else() set(_ts_dir ${CMAKE_CURRENT_SOURCE_DIR}) endif() set(_ts_files) foreach(_loc IN LISTS FUNC_LOCALES) list(APPEND _ts_files ${_ts_dir}/${_prefix}_${_loc}.ts) endforeach() # Include options set(_include_options) foreach(_inc IN LISTS _include_dirs) list(APPEND _include_options "-I${_inc}") endforeach() # May be an lupdate bug, so we skip passing include directories # list(APPEND _ts_options ${_include_options}) if(_ts_options) list(PREPEND _ts_options OPTIONS) endif() _qm_add_lupdate_target(${_target}_lupdate INPUT ${_src_files} OUTPUT ${_ts_files} ${_ts_options} CREATE_ONCE ) # Add update dependencies # add_dependencies(${_target} ${_target}_lupdate) foreach(_item IN LISTS FUNC_TS_DEPENDS) add_dependencies(${_item} ${_target}_lupdate) endforeach() # list(APPEND _qm_depends DEPENDS ${_target}_lupdate) else() if(FUNC_PREFIX) message(WARNING "qm_add_translation: no source files collected, PREFIX ignored") endif() if(FUNC_TS_DIR) message(WARNING "qm_add_translation: no source files collected, TS_DIR ignored") endif() if(FUNC_TS_DEPENDS) message(WARNING "qm_add_translation: no source files collected, TS_DEPENDS ignored") endif() endif() if(FUNC_QM_DIR) set(_qm_dir ${FUNC_QM_DIR}) else() set(_qm_dir ${CMAKE_CURRENT_BINARY_DIR}) endif() set(_qm_target) if(_qm_options) list(PREPEND _qm_options OPTIONS) endif() _qm_add_lrelease_target(${_target}_lrelease INPUT ${_ts_files} ${FUNC_TS_FILES} DESTINATION ${_qm_dir} ${_qm_options} ${_qm_depends} ) add_dependencies(${_target} ${_target}_lrelease) # Add release dependencies if(FUNC_TARGETS) foreach(_item IN LISTS FUNC_TARGETS) add_dependencies(${_item} ${_target}_lrelease) endforeach() endif() foreach(_item IN LISTS FUNC_QM_DEPENDS) add_dependencies(${_item} ${_target}_lrelease) endforeach() endfunction() # ---------------------------------- # Private functions # ---------------------------------- # Input: cxx source files # Output: target ts files function(_qm_add_lupdate_target _target) set(options CREATE_ONCE) set(oneValueArgs) set(multiValueArgs INPUT OUTPUT OPTIONS DEPENDS) cmake_parse_arguments(_LUPDATE "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) set(_lupdate_deps ${_LUPDATE_DEPENDS}) set(_my_sources ${_LUPDATE_INPUT}) set(_my_tsfiles ${_LUPDATE_OUTPUT}) add_custom_target(${_target} DEPENDS ${_lupdate_deps}) get_target_property(_lupdate_exe Qt${QT_VERSION_MAJOR}::lupdate IMPORTED_LOCATION) foreach(_ts_file IN LISTS _my_tsfiles) # make a list file to call lupdate on, so we don't make our commands too # long for some systems get_filename_component(_ts_name ${_ts_file} NAME) set(_ts_lst_file "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${_ts_name}_lst_file") set(_lst_file_srcs) foreach(_lst_file_src IN LISTS _my_sources) set(_lst_file_srcs "${_lst_file_src}\n${_lst_file_srcs}") endforeach() get_directory_property(_inc_DIRS INCLUDE_DIRECTORIES) foreach(_pro_include IN LISTS _inc_DIRS) get_filename_component(_abs_include "${_pro_include}" ABSOLUTE) set(_lst_file_srcs "-I${_pro_include}\n${_lst_file_srcs}") endforeach() file(WRITE ${_ts_lst_file} "${_lst_file_srcs}") get_filename_component(_ts_abs ${_ts_file} ABSOLUTE) if(_LUPDATE_CREATE_ONCE AND NOT EXISTS ${_ts_abs}) set(_options_filtered) foreach(_opt IN LISTS _LUPDATE_OPTIONS) qm_has_genex(${_opt} _has_genex) if(_has_genex) continue() endif() list(APPEND _options_filtered ${_opt}) endforeach() message(STATUS "Linguist update: Generate ${_ts_name}") get_filename_component(_abs_file ${_ts_file} ABSOLUTE) get_filename_component(_dir ${_abs_file} DIRECTORY) make_directory(${_dir}) execute_process( COMMAND ${_lupdate_exe} ${_options_filtered} "@${_ts_lst_file}" -ts ${_ts_file} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} OUTPUT_QUIET COMMAND_ERROR_IS_FATAL ANY ) endif() add_custom_command( TARGET ${_target} COMMAND ${_lupdate_exe} ARGS ${_LUPDATE_OPTIONS} "@${_ts_lst_file}" -ts ${_ts_file} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DEPENDS ${_my_sources} BYPRODUCTS ${_ts_lst_file} VERBATIM ) endforeach() endfunction() # Input: ts files # Output: list to append qm files function(_qm_add_lrelease_target _target) set(options) set(oneValueArgs DESTINATION OUTPUT) set(multiValueArgs INPUT OPTIONS DEPENDS) cmake_parse_arguments(_LRELEASE "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) set(_lrelease_files ${_LRELEASE_INPUT}) set(_lrelease_deps ${_LRELEASE_DEPENDS}) get_target_property(_lrelease_exe Qt${QT_VERSION_MAJOR}::lrelease IMPORTED_LOCATION) set(_qm_files) foreach(_file IN LISTS _lrelease_files) get_filename_component(_abs_FILE ${_file} ABSOLUTE) get_filename_component(_qm_file ${_file} NAME) # everything before the last dot has to be considered the file name (including other dots) string(REGEX REPLACE "\\.[^.]*$" "" FILE_NAME ${_qm_file}) get_source_file_property(output_location ${_abs_FILE} OUTPUT_LOCATION) if(output_location) set(_out_dir ${output_location}) elseif(_LRELEASE_DESTINATION) set(_out_dir ${_LRELEASE_DESTINATION}) else() set(_out_dir ${CMAKE_CURRENT_BINARY_DIR}) endif() set(_qm_file "${_out_dir}/${FILE_NAME}.qm") add_custom_command( OUTPUT ${_qm_file} COMMAND ${CMAKE_COMMAND} -E make_directory ${_out_dir} COMMAND ${_lrelease_exe} ARGS ${_LRELEASE_OPTIONS} ${_abs_FILE} -qm ${_qm_file} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DEPENDS ${_lrelease_files} VERBATIM ) list(APPEND _qm_files ${_qm_file}) endforeach() add_custom_target(${_target} ALL DEPENDS ${_lrelease_deps} ${_qm_files}) if(_LRELEASE_OUTPUT) set(${_LRELEASE_OUTPUT} ${_qm_files} PARENT_SCOPE) endif() endfunction()