summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoerg Bornemann <joerg.bornemann@qt.io>2024-04-18 12:28:35 +0200
committerJoerg Bornemann <joerg.bornemann@qt.io>2024-04-25 10:19:47 +0200
commitd39e9f21c98eb4788f3434f139b828862b2d8823 (patch)
treef53f3aec5cacb6b0c3c448b92261a774ce4d6705
parent5992c457de417dd32f7503db5f2dfa30cc54f494 (diff)
CMake: Exclude all AUTOGENerated files from lupdate
To fix QTBUG-118808 we added code to filter ui_*.h files from lupdate's input. This was done by creating a big regular expression. Unfortunately, CMake cannot handle regular expressions that contain a certain amount of sub-expressions. See QTBUG-123995. We now fix the issue more generally by filtering out everything that's generated by CMake's AUTOGEN facility. The data flow is like follows: 1. On project configuration, .lupdate/foo_project.cmake is generated. This file contains the location of the AUTOGEN directory per target. The AUTOGEN_BUILD_DIR target property is taken into account. 2. At build time, GenerateLUpdateProject.cmake reads .lupdate/foo_project.cmake, and filters out source files that are below the AUTOGEN directory for the currently handled target. The filtered result is written to .lupdate/foo_project.json. 3. On 'update_translations', lupdate reads .lupdate/foo_project.json and runs on the source files mentioned there. Fixes: QTBUG-118808 Fixes: QTBUG-123995 Pick-to: 6.6 6.7 Change-Id: Id23b94c22b2eadeb7f96321bf631731b9f257bce Reviewed-by: Alexey Edelev <alexey.edelev@qt.io>
-rw-r--r--src/linguist/GenerateLUpdateProject.cmake53
-rw-r--r--src/linguist/Qt6LinguistToolsMacros.cmake4
-rw-r--r--tests/auto/cmake/linguist/CMakeLists.txt1
-rw-r--r--tests/auto/cmake/linguist/test_i18n_filter_autogen_files/CMakeLists.txt30
-rw-r--r--tests/auto/cmake/linguist/test_i18n_filter_autogen_files/app1/CMakeLists.txt9
-rw-r--r--tests/auto/cmake/linguist/test_i18n_filter_autogen_files/app1/main.cpp27
-rw-r--r--tests/auto/cmake/linguist/test_i18n_filter_autogen_files/app1/mainwindow.ui31
-rw-r--r--tests/auto/cmake/linguist/test_i18n_filter_autogen_files/app2/CMakeLists.txt12
-rw-r--r--tests/auto/cmake/linguist/test_i18n_filter_autogen_files/app2/main.cpp27
-rw-r--r--tests/auto/cmake/linguist/test_i18n_filter_autogen_files/app2/mainwindow.ui31
-rw-r--r--tests/auto/cmake/linguist/test_i18n_filter_autogen_files/check_ts_file.cmake31
11 files changed, 235 insertions, 21 deletions
diff --git a/src/linguist/GenerateLUpdateProject.cmake b/src/linguist/GenerateLUpdateProject.cmake
index 40854aa8d..e359708c5 100644
--- a/src/linguist/GenerateLUpdateProject.cmake
+++ b/src/linguist/GenerateLUpdateProject.cmake
@@ -44,34 +44,42 @@ function(filter_unsuitable_lupdate_input out_var)
set("${out_var}" "${result}" PARENT_SCOPE)
endfunction()
-# Remove ui_foo.h for each foo.ui file found in the sources.
-# filter_generated_ui_headers(existing_files .../src/foo.ui .../target_autogen/include/ui_foo.h)
+# Remove files from SOURCES that are in EXCLUDED_DIR.
+# filter_files_in_directory(existing_files
+# EXCLUDED_DIR .../target_autogen
+# SOURCES .../src/foo.ui .../target_autogen/include/ui_foo.h)
# -> .../src/foo.ui
-function(filter_generated_ui_headers out_var)
- set(ui_file_paths ${ARGN})
- list(FILTER ui_file_paths INCLUDE REGEX "/[^/]+\\.ui$")
+function(filter_files_in_directory out_var)
+ set(no_value_options "")
+ set(single_value_options EXCLUDED_DIR)
+ set(multi_value_options SOURCES)
+ cmake_parse_arguments(PARSE_ARGV 1 arg
+ "${no_value_options}" "${single_value_options}" "${multi_value_options}"
+ )
- set(filter_regex "")
- foreach(file_path IN LISTS ui_file_paths)
- get_filename_component(file_name "${file_path}" NAME_WLE)
- if(NOT "${filter_regex}" STREQUAL "")
- string(APPEND filter_regex "|")
+ set(result "")
+ foreach(source_file IN LISTS arg_SOURCES)
+ file(RELATIVE_PATH relative_path "${arg_EXCLUDED_DIR}" "${source_file}")
+ if(IS_ABSOLUTE "${relative_path}" OR (relative_path MATCHES "^\\.\\."))
+ list(APPEND result "${source_file}")
endif()
- string(APPEND filter_regex "(/ui_${file_name}\\.h$)")
endforeach()
-
- set(result ${ARGN})
- if(NOT "${filter_regex}" STREQUAL "")
- list(FILTER result EXCLUDE REGEX ${filter_regex})
- endif()
-
set("${out_var}" "${result}" PARENT_SCOPE)
endfunction()
function(prepare_json_sources out_var)
- filter_nonexistent_files(sources ${ARGN})
+ set(no_value_options "")
+ set(single_value_options AUTOGEN_DIR)
+ set(multi_value_options SOURCES)
+ cmake_parse_arguments(PARSE_ARGV 1 arg
+ "${no_value_options}" "${single_value_options}" "${multi_value_options}"
+ )
+
+ filter_nonexistent_files(sources ${arg_SOURCES})
filter_unsuitable_lupdate_input(sources ${sources})
- filter_generated_ui_headers(sources ${sources})
+ if(DEFINED arg_AUTOGEN_DIR)
+ filter_files_in_directory(sources EXCLUDED_DIR ${arg_AUTOGEN_DIR} SOURCES ${sources})
+ endif()
list_to_json_array("${sources}" result)
set("${out_var}" "${result}" PARENT_SCOPE)
endfunction()
@@ -107,7 +115,7 @@ foreach(path_var IN LISTS path_variables)
endforeach()
endforeach()
-prepare_json_sources(json_sources ${absolute_sources})
+prepare_json_sources(json_sources SOURCES ${absolute_sources})
list_to_json_array("${absolute_include_paths}" json_include_paths)
list_to_json_array("${absolute_translations}" json_translations)
@@ -120,7 +128,10 @@ set(content "{
")
if(lupdate_subproject_count GREATER 0)
foreach(i RANGE 1 ${lupdate_subproject_count})
- prepare_json_sources(json_sources ${absolute_subproject${i}_sources})
+ prepare_json_sources(json_sources
+ AUTOGEN_DIR ${lupdate_subproject${i}_autogen_dir}
+ SOURCES ${absolute_subproject${i}_sources}
+ )
list_to_json_array("${absolute_subproject${i}_include_paths}" json_include_paths)
list_to_json_array("${absolute_subproject${i}_excluded}" json_sources_exclusions)
string(APPEND content " {
diff --git a/src/linguist/Qt6LinguistToolsMacros.cmake b/src/linguist/Qt6LinguistToolsMacros.cmake
index 61700546b..b9c924c9c 100644
--- a/src/linguist/Qt6LinguistToolsMacros.cmake
+++ b/src/linguist/Qt6LinguistToolsMacros.cmake
@@ -286,11 +286,15 @@ set(lupdate_subproject_count ${targets_length})
set(includePaths "$<TARGET_PROPERTY:${target},INCLUDE_DIRECTORIES>")
set(sources "$<FILTER:$<TARGET_PROPERTY:${target},SOURCES>,EXCLUDE,${exclude_ts}>")
set(excluded "$<TARGET_PROPERTY:${target},QT_EXCLUDE_SOURCES_FROM_TRANSLATION>")
+ set(autogen_build_dir_genex "$<TARGET_PROPERTY:${target},AUTOGEN_BUILD_DIR>")
+ set(default_autogen_build_dir "$<TARGET_PROPERTY:${target},BINARY_DIR>/${target}_autogen")
+ set(autogen_dir "$<IF:$<BOOL:${autogen_build_dir_genex}>,${autogen_build_dir_genex},${default_autogen_build_dir}>")
string(APPEND content "
set(lupdate_subproject${n}_source_dir \"$<TARGET_PROPERTY:${target},SOURCE_DIR>\")
set(lupdate_subproject${n}_include_paths \"${includePaths}\")
set(lupdate_subproject${n}_sources \"${sources}\")
set(lupdate_subproject${n}_excluded \"${excluded}\")
+set(lupdate_subproject${n}_autogen_dir \"${autogen_dir}\")
")
math(EXPR n "${n} + 1")
endforeach()
diff --git a/tests/auto/cmake/linguist/CMakeLists.txt b/tests/auto/cmake/linguist/CMakeLists.txt
index 90be96016..e08d12f5e 100644
--- a/tests/auto/cmake/linguist/CMakeLists.txt
+++ b/tests/auto/cmake/linguist/CMakeLists.txt
@@ -63,4 +63,5 @@ _qt_internal_test_expect_pass(test_create_translation_same_base_names)
_qt_internal_test_expect_pass(test_translation_api)
_qt_internal_test_expect_pass(test_i18n_auto_ts_file_names)
_qt_internal_test_expect_pass(test_i18n_exclusion)
+_qt_internal_test_expect_pass(test_i18n_filter_autogen_files)
_qt_internal_test_expect_pass(test_i18n_source_language)
diff --git a/tests/auto/cmake/linguist/test_i18n_filter_autogen_files/CMakeLists.txt b/tests/auto/cmake/linguist/test_i18n_filter_autogen_files/CMakeLists.txt
new file mode 100644
index 000000000..0e3871ef6
--- /dev/null
+++ b/tests/auto/cmake/linguist/test_i18n_filter_autogen_files/CMakeLists.txt
@@ -0,0 +1,30 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(test_i18n_filter_autogen_files)
+set(CMAKE_CXX_STANDARD 20)
+find_package(Qt6 COMPONENTS Widgets LinguistTools)
+qt_standard_project_setup()
+add_subdirectory(app1)
+add_subdirectory(app2)
+
+set(ts_file "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}_de.ts")
+qt_add_lupdate(
+ TS_FILES "${ts_file}"
+ SOURCE_TARGETS app1 app2
+ LUPDATE_TARGET update_ts_files
+ NO_GLOBAL_TARGET
+)
+
+# Make sure that we build the app before creating the .ts file.
+# We want to have AUTOMOC and AUTOUIC files generated.
+add_dependencies(update_ts_files app1 app2)
+
+# Update the .ts files by default and check the content of the updated .ts file.
+add_custom_target(force_ts_update ALL
+ COMMENT "Checking .ts file contents..."
+ COMMAND "${CMAKE_COMMAND}" -DTS_FILE="${ts_file}"
+ -P "${CMAKE_CURRENT_SOURCE_DIR}/check_ts_file.cmake"
+)
+add_dependencies(force_ts_update update_ts_files)
diff --git a/tests/auto/cmake/linguist/test_i18n_filter_autogen_files/app1/CMakeLists.txt b/tests/auto/cmake/linguist/test_i18n_filter_autogen_files/app1/CMakeLists.txt
new file mode 100644
index 000000000..6ff3989e7
--- /dev/null
+++ b/tests/auto/cmake/linguist/test_i18n_filter_autogen_files/app1/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+qt_add_executable(app1
+ main.cpp
+ mainwindow.ui
+)
+target_link_libraries(app1 PRIVATE Qt6::Widgets)
diff --git a/tests/auto/cmake/linguist/test_i18n_filter_autogen_files/app1/main.cpp b/tests/auto/cmake/linguist/test_i18n_filter_autogen_files/app1/main.cpp
new file mode 100644
index 000000000..21db50503
--- /dev/null
+++ b/tests/auto/cmake/linguist/test_i18n_filter_autogen_files/app1/main.cpp
@@ -0,0 +1,27 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtCore/QCoreApplication>
+
+#include "ui_mainwindow.h"
+
+class MyObject : public QObject
+{
+ Q_OBJECT
+public:
+ MyObject(QObject *parent = nullptr)
+ : QObject(parent)
+ {
+ qDebug() << tr("Hello from app1!");
+ }
+};
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication app(argc, argv);
+ MyObject obj;
+ Ui::MainWindow wnd;
+ app.exec();
+}
+
+#include "main.moc"
diff --git a/tests/auto/cmake/linguist/test_i18n_filter_autogen_files/app1/mainwindow.ui b/tests/auto/cmake/linguist/test_i18n_filter_autogen_files/app1/mainwindow.ui
new file mode 100644
index 000000000..2b9bac77c
--- /dev/null
+++ b/tests/auto/cmake/linguist/test_i18n_filter_autogen_files/app1/mainwindow.ui
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>800</width>
+ <height>600</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>App1's Main Window</string>
+ </property>
+ <widget class="QWidget" name="centralwidget"/>
+ <widget class="QMenuBar" name="menubar">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>800</width>
+ <height>22</height>
+ </rect>
+ </property>
+ </widget>
+ <widget class="QStatusBar" name="statusbar"/>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/tests/auto/cmake/linguist/test_i18n_filter_autogen_files/app2/CMakeLists.txt b/tests/auto/cmake/linguist/test_i18n_filter_autogen_files/app2/CMakeLists.txt
new file mode 100644
index 000000000..d5c26b126
--- /dev/null
+++ b/tests/auto/cmake/linguist/test_i18n_filter_autogen_files/app2/CMakeLists.txt
@@ -0,0 +1,12 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+qt_add_executable(app2
+ main.cpp
+ mainwindow.ui
+)
+target_link_libraries(app2 PRIVATE Qt6::Widgets)
+set_target_properties(app2 PROPERTIES
+ AUTOGEN_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/wossname_autogen"
+)
diff --git a/tests/auto/cmake/linguist/test_i18n_filter_autogen_files/app2/main.cpp b/tests/auto/cmake/linguist/test_i18n_filter_autogen_files/app2/main.cpp
new file mode 100644
index 000000000..3b432078f
--- /dev/null
+++ b/tests/auto/cmake/linguist/test_i18n_filter_autogen_files/app2/main.cpp
@@ -0,0 +1,27 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtCore/QCoreApplication>
+
+#include "ui_mainwindow.h"
+
+class MyObject : public QObject
+{
+ Q_OBJECT
+public:
+ MyObject(QObject *parent = nullptr)
+ : QObject(parent)
+ {
+ qDebug() << tr("Hello from app2!");
+ }
+};
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication app(argc, argv);
+ MyObject obj;
+ Ui::MainWindow wnd;
+ app.exec();
+}
+
+#include "main.moc"
diff --git a/tests/auto/cmake/linguist/test_i18n_filter_autogen_files/app2/mainwindow.ui b/tests/auto/cmake/linguist/test_i18n_filter_autogen_files/app2/mainwindow.ui
new file mode 100644
index 000000000..6b0b6f2de
--- /dev/null
+++ b/tests/auto/cmake/linguist/test_i18n_filter_autogen_files/app2/mainwindow.ui
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>800</width>
+ <height>600</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>App2's Main Window</string>
+ </property>
+ <widget class="QWidget" name="centralwidget"/>
+ <widget class="QMenuBar" name="menubar">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>800</width>
+ <height>22</height>
+ </rect>
+ </property>
+ </widget>
+ <widget class="QStatusBar" name="statusbar"/>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/tests/auto/cmake/linguist/test_i18n_filter_autogen_files/check_ts_file.cmake b/tests/auto/cmake/linguist/test_i18n_filter_autogen_files/check_ts_file.cmake
new file mode 100644
index 000000000..0e0118180
--- /dev/null
+++ b/tests/auto/cmake/linguist/test_i18n_filter_autogen_files/check_ts_file.cmake
@@ -0,0 +1,31 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+file(READ "${TS_FILE}" ts_file_content)
+
+set(expected_strings
+ "<source>Hello from app1!</source>"
+ "<source>Hello from app2!</source>"
+)
+foreach(needle IN LISTS expected_strings)
+ string(FIND "${ts_file_content}" "${needle}" pos)
+ if(pos EQUAL "-1")
+ message(FATAL_ERROR
+ "Expected string '${needle}' was not found in '${TS_FILE}'. "
+ "The file content is:\n${ts_file_content}"
+ )
+ endif()
+endforeach()
+
+set(forbidden_strings
+ "/ui_mainwindow.h\""
+)
+foreach(needle IN LISTS forbidden_strings)
+ string(FIND "${ts_file_content}" "${needle}" pos)
+ if(NOT pos EQUAL "-1")
+ message(FATAL_ERROR
+ "Excluded string '${needle}' was found in '${TS_FILE}'. "
+ "The file content is:\n${ts_file_content}"
+ )
+ endif()
+endforeach()