diff options
author | Orkun Tokdemir <[email protected]> | 2023-11-21 10:46:30 +0100 |
---|---|---|
committer | Orkun Tokdemir <[email protected]> | 2024-05-07 21:12:29 +0200 |
commit | 545b84b93c0b84a08c037e7b70deec3400d1f827 (patch) | |
tree | 0d3deef864f3df8294822f82d9e66106329695ec | |
parent | 0678a8a00da586e78d464608e83e89b4df589485 (diff) |
CMake: Add `qt_add_ui` API
This commit introduces a new `qt_add_ui` CMake API to be used instead
of AUTOUIC, due to several hard-to-fix bugs it has.
* Resolves the issue where Ninja required two passes to correctly build
`.ui` files.
* Avoids build folder leakage, in contrast to `AUTOUIC`
* Prevents unnecessary recompilation of unchanged source files when a
`.ui` file is modified with Ninja.
* Since `qt_add_ui` does not scan source files, it provides a faster
build than `AUTOUIC`.
These changes aim to streamline the build process and improve
efficiency when working with `.ui` files.
This addresses the following issues:
https://gitlab.kitware.com/cmake/cmake/-/issues/16776
https://gitlab.kitware.com/cmake/cmake/-/issues/25436
https://gitlab.kitware.com/cmake/cmake/-/issues/25195
Task-number: QTBUG-110369
Fixes: QTBUG-114563
Change-Id: I34cd3df70542becf6e08502f0cbcd1c11eeeadd6
Reviewed-by: Alexandru Croitor <[email protected]>
48 files changed, 2718 insertions, 0 deletions
diff --git a/src/widgets/Qt6WidgetsMacros.cmake b/src/widgets/Qt6WidgetsMacros.cmake index b15d1fe81d1..a7ff7af2f38 100644 --- a/src/widgets/Qt6WidgetsMacros.cmake +++ b/src/widgets/Qt6WidgetsMacros.cmake @@ -49,3 +49,293 @@ if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS) set("${outfiles}" "${${outfiles}}" PARENT_SCOPE) endfunction() endif() + +function(qt6_add_ui target) + if(NOT TARGET ${target}) + message(FATAL_ERROR "Target \"${target}\" does not exist.") + endif() + + set(options) + set(oneValueArgs INCLUDE_PREFIX) + set(multiValueArgs SOURCES OPTIONS) + + cmake_parse_arguments(arg "${options}" "${oneValueArgs}" "${multiValueArgs}" + ${ARGN}) + + set(sources ${arg_SOURCES}) + set(ui_options ${arg_OPTIONS}) + set(raw_include_prefix "${arg_INCLUDE_PREFIX}") + + if("${sources}" STREQUAL "") + message(FATAL_ERROR "The \"SOURCES\" parameter is empty.") + endif() + + + # Disable AUTOUIC for the target explicitly to avoid the AUTOUIC + # error message when the AUTOUIC is enabled for the target. + set_target_properties(${target} PROPERTIES AUTOUIC OFF) + + if(NOT "${raw_include_prefix}" STREQUAL "") + # generate dummy `_` folders from the given path + # e.g. if the given path is `../a/b/c`, then the generated path will be + # `_`. if the given path is ``../a/../b/c`, then the generated path will + # be `_/_` + function(_qt_internal_generate_dash_path_from_input input_path + out_generated_dash_path) + string(REGEX MATCHALL "\\.\\." upper_folder_list "${input_path}") + + list(JOIN upper_folder_list "" upper_folder_list) + string(REGEX REPLACE "\\.\\." "_/" additional_path + "${upper_folder_list}") + + # remove last / character + if(NOT "${additional_path}" STREQUAL "") + string(LENGTH "${additional_path}" additional_path_length) + math(EXPR additional_path_length "${additional_path_length} - 1") + string(SUBSTRING "${additional_path}" 0 ${additional_path_length} + additional_path) + endif() + + set(${out_generated_dash_path} "${additional_path}" PARENT_SCOPE) + endfunction() + # NOTE: If previous folders are less than ../ folder count in + # ${raw_include_prefix}, relative path calculation will miscalculate, + # so we need to add dummy folders to calculate the relative path + # correctly + _qt_internal_generate_dash_path_from_input("${raw_include_prefix}" + dummy_path_for_relative_calculation) + if(NOT "${dummy_path_for_relative_calculation}" STREQUAL "") + set(dummy_path_for_relative_calculation + "/${dummy_path_for_relative_calculation}") + set(raw_include_prefix_to_compare + "${dummy_path_for_relative_calculation}/${raw_include_prefix}") + else() + set(dummy_path_for_relative_calculation "/") + set(raw_include_prefix_to_compare "/${raw_include_prefix}") + endif() + # NOTE: This relative path calculation could be done just by using the + # below code + # cmake_path(RELATIVE_PATH normalized_include_prefix "${CMAKE_CURRENT_SOURCE_DIR}" + # but due to the backward compatibility, we need to use + # file(RELATIVE_PATH) and ../ folder calculation the with + # _qt_internal_generate_dash_path_from_input() function + if(WIN32) + set(dummy_path_for_relative_calculation + "${CMAKE_CURRENT_SOURCE_DIR}/${dummy_path_for_relative_calculation}") + set(raw_include_prefix_to_compare + "${CMAKE_CURRENT_SOURCE_DIR}/${raw_include_prefix_to_compare}") + string(REGEX REPLACE "//" "/" dummy_path_for_relative_calculation + "${dummy_path_for_relative_calculation}") + string(REGEX REPLACE "//" "/" raw_include_prefix_to_compare + "${raw_include_prefix_to_compare}") + endif() + + file(RELATIVE_PATH normalized_include_prefix + "${dummy_path_for_relative_calculation}" + "${raw_include_prefix_to_compare}") + + # if normalized_include_prefix ends with `/` remove it + string(REGEX REPLACE "/$" "" normalized_include_prefix + "${normalized_include_prefix}") + + _qt_internal_generate_dash_path_from_input("${normalized_include_prefix}" + additional_path) + endif() + + if(ui_options MATCHES "\\$<CONFIG") + set(is_ui_options_contains_config true) + endif() + get_property(is_multi GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) + if(is_multi AND is_ui_options_contains_config) + set(include_folder "$<CONFIG>") + endif() + + set(include_folder_to_add "${include_folder}") + if(NOT "${additional_path}" STREQUAL "") + set(include_folder_to_add "${include_folder_to_add}/${additional_path}") + endif() + + set(prefix_info_file_cmake + "${CMAKE_CURRENT_BINARY_DIR}/${target}_autogen/prefix_info.cmake") + if(EXISTS ${prefix_info_file_cmake}) + include(${prefix_info_file_cmake}) + set(prefix_info_file_cmake_exists true) + else() + set(prefix_info_file_cmake_exists false) + endif() + + foreach(source_file ${sources}) + get_filename_component(outfile "${source_file}" NAME_WE) + get_filename_component(infile "${source_file}" ABSOLUTE) + string(SHA1 infile_hash "${target}${infile}") + string(SUBSTRING "${infile_hash}" 0 6 short_hash) + set(file_ui_folder "${CMAKE_CURRENT_BINARY_DIR}/.qt/${short_hash}") + target_include_directories(${target} SYSTEM PRIVATE + "${file_ui_folder}/${include_folder_to_add}") + + set(target_include_folder_with_add_path + "${file_ui_folder}/${include_folder}") + # Add additional path to include folder if it is not empty + if(NOT "${additional_path}" STREQUAL "") + set(target_include_folder_with_add_path + "${target_include_folder_with_add_path}/${additional_path}") + endif() + + set(inc_dir_to_create "${target_include_folder_with_add_path}") + if(NOT "${raw_include_prefix}" STREQUAL "") + set(inc_dir_to_create + "${target_include_folder_with_add_path}/${raw_include_prefix}") + endif() + + set(output_directory "${target_include_folder_with_add_path}") + if(NOT "${normalized_include_prefix}" STREQUAL "") + set(output_directory + "${output_directory}/${normalized_include_prefix}") + endif() + + if(NOT EXISTS "${infile}") + message(FATAL_ERROR "${infile} does not exist.") + endif() + set_source_files_properties(${infile} PROPERTIES SKIP_AUTOUIC ON) + + macro(_qt_internal_set_output_file_properties file) + set_source_files_properties(${file} PROPERTIES + SKIP_AUTOMOC TRUE + SKIP_AUTOUIC TRUE + SKIP_LINTING TRUE) + endmacro() + + set(outfile "${output_directory}/ui_${outfile}.h") + # Before CMake 3.27, there is a bug for using $<CONFIG> in a generated + # file with Ninja Multi-Config generator. To avoid this issue, we need + # to add the generated file for each configuration. + # Explicitly set the output file properties for each configuration + # to avoid Policy CMP0071 warnings. + # See https://gitlab.kitware.com/cmake/cmake/-/issues/24848 + # Xcode generator cannot handle $<CONFIG> in the output file name. + # it changes $<CONFIG> with NOCONFIG. That's why we need to add the + # generated file for each configuration for Xcode generator as well. + if(is_multi AND outfile MATCHES "\\$<CONFIG>" + AND CMAKE_VERSION VERSION_LESS "3.27" + OR CMAKE_GENERATOR MATCHES "Xcode") + foreach(config ${CMAKE_CONFIGURATION_TYPES}) + string(REPLACE "$<CONFIG>" "${config}" outfile_with_config + "${outfile}") + _qt_internal_set_output_file_properties( + ${outfile_with_config}) + target_sources(${target} PRIVATE ${outfile_with_config}) + endforeach() + else() + _qt_internal_set_output_file_properties(${outfile}) + target_sources(${target} PRIVATE ${outfile}) + endif() + # remove double slashes + string(REGEX REPLACE "//" "/" outfile "${outfile}") + + # Note: If INCLUDE_PREFIX is changed without changing the corresponding + # include path in the source file and Ninja generator is used, this + # casues the double build issue. To avoid this issue, we need to + # remove the output file in configure step if the include_prefix is + # changed. + if(CMAKE_GENERATOR MATCHES "Ninja") + if(prefix_info_file_cmake_exists) + if(NOT "${${short_hash}_prefix}" STREQUAL + "${normalized_include_prefix}") + file(REMOVE_RECURSE "${file_ui_folder}") + list(APPEND prefix_info_list + "set(${short_hash}_prefix \"${normalized_include_prefix}\")") + elseif(NOT DEFINED ${short_hash}_prefix) + list(APPEND prefix_info_list + "set(${short_hash}_prefix \"${normalized_include_prefix}\")") + endif() + else() + set(prefix_info_list "") + list(APPEND prefix_info_list + "set(${short_hash}_prefix \"${normalized_include_prefix}\")") + endif() + file(MAKE_DIRECTORY ${file_ui_folder}) + endif() + + if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.17") + set(remove_command rm -rf) + else() + set(remove_command "remove_directory") + endif() + + add_custom_command(OUTPUT ${outfile} + DEPENDS ${QT_CMAKE_EXPORT_NAMESPACE}::uic + COMMAND ${CMAKE_COMMAND} -E ${remove_command} "${file_ui_folder}/${include_folder}" + COMMAND ${CMAKE_COMMAND} -E make_directory ${inc_dir_to_create} + COMMAND ${QT_CMAKE_EXPORT_NAMESPACE}::uic ${ui_options} -o + ${outfile} ${infile} + COMMAND_EXPAND_LISTS + MAIN_DEPENDENCY ${infile} VERBATIM) + endforeach() + + + get_target_property(is_guard_on ${target} _qt_ui_property_check_guard) + if(NOT is_guard_on) + # Ninja fails when a newline is used. That's why message is + # divided into parts. + set(error_message_1 + "Error: The target \"${target}\" has \"AUTOUIC\" enabled.") + set(error_message_2 + "Error: Please disable \"AUTOUIC\" for the target \"${target}\".") + + set(ui_property_check_dummy_file + "${CMAKE_CURRENT_BINARY_DIR}/${target}_autogen/ui_property_check_timestamp") + + add_custom_command(OUTPUT ${ui_property_check_dummy_file} + COMMAND ${CMAKE_COMMAND} -E make_directory + "${CMAKE_CURRENT_BINARY_DIR}/${target}_autogen" + COMMAND ${CMAKE_COMMAND} -E touch ${ui_property_check_dummy_file} + COMMAND + "$<$<BOOL:$<TARGET_PROPERTY:${target},AUTOUIC>>:${CMAKE_COMMAND}\ +;-E;echo;${error_message_1}>" + COMMAND + "$<$<BOOL:$<TARGET_PROPERTY:${target},AUTOUIC>>:${CMAKE_COMMAND}\ +;-E;echo;${error_message_2}>" + # Remove the dummy file so that the error message is shown until + # the AUTOUIC is disabled. Otherwise, the error message is shown + # only once when the AUTOUIC is enabled with Visual Studio generator. + COMMAND + "$<$<BOOL:$<TARGET_PROPERTY:${target},AUTOUIC>>:${CMAKE_COMMAND}\ +;-E;remove;${ui_property_check_dummy_file}>" + COMMAND + "$<$<BOOL:$<TARGET_PROPERTY:${target},AUTOUIC>>:${CMAKE_COMMAND}\ +;-E;false>" + COMMAND_EXPAND_LISTS + VERBATIM) + + # When AUTOUIC is enabled, AUTOUIC runs before ${target}_ui_property_check + # target. So we need to add ${target}_ui_property_check as a dependency + # to ${target} to make sure that AUTOUIC runs after ${target}_ui_property_check + if (NOT QT_NO_MIX_UI_AUTOUIC_CHECK) + set_target_properties(${target} PROPERTIES + _qt_ui_property_check_guard ON) + add_custom_target(${target}_ui_property_check + ALL DEPENDS ${ui_property_check_dummy_file}) + _qt_internal_assign_to_internal_targets_folder( + ${target}_ui_property_check) + add_dependencies(${target} ${target}_ui_property_check) + endif() + endif() + + # write prefix info file + if(CMAKE_GENERATOR MATCHES "Ninja") + list(PREPEND prefix_info_list "include_guard()") + string(REPLACE ";" "\n" prefix_info_list "${prefix_info_list}") + file(WRITE "${prefix_info_file_cmake}" "${prefix_info_list}") + endif() +endfunction() + +if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS) + function(qt_add_ui) + if(QT_DEFAULT_MAJOR_VERSION EQUAL 6) + qt6_add_ui(${ARGN}) + else() + message(FATAL_ERROR "qt_add_ui() is only available in Qt 6.") + endif() + endfunction() +endif() + diff --git a/src/widgets/doc/snippets/cmake-macros/examples.cmake b/src/widgets/doc/snippets/cmake-macros/examples.cmake index 3c58509fdf9..7d0023aed4a 100644 --- a/src/widgets/doc/snippets/cmake-macros/examples.cmake +++ b/src/widgets/doc/snippets/cmake-macros/examples.cmake @@ -6,3 +6,30 @@ set(SOURCES mainwindow.cpp main.cpp) qt_wrap_ui(SOURCES mainwindow.ui) qt_add_executable(myapp ${SOURCES}) #! [qt_wrap_ui] + +#! [qt6_add_ui_1] +qt_add_executable(myapp mainwindow.cpp main.cpp) +qt6_add_ui(myapp SOURCES mainwindow.ui) +#! [qt6_add_ui_1] + +#! [qt6_add_ui_2] +qt_add_executable(myapp mainwindow.cpp main.cpp) +qt6_add_ui(myapp INCLUDE_PREFIX "src/files" SOURCES mainwindow.ui) +#! [qt6_add_ui_2] + +#! [qt6_add_ui_3] +qt_add_executable(myapp widget1.cpp widget2.cpp main.cpp) +qt6_add_ui(myapp INCLUDE_PREFIX "src/files" SOURCES widget1.ui widget2.ui) +#! [qt6_add_ui_3] + +#! [qt6_add_ui_4] +qt_add_executable(myapp widget1.cpp widget2.cpp main.cpp) +qt6_add_ui(myapp INCLUDE_PREFIX "src/files" + SOURCES "my_ui_files_1/widget1.ui" "my_ui_files_2/widget2.ui") +#! [qt6_add_ui_4] + +#! [qt6_add_ui_5] +qt_add_executable(myapp widget1.cpp widget2.cpp main.cpp) +qt6_add_ui(myapp INCLUDE_PREFIX "src/files_1" SOURCES "my_ui_files/widget1.ui") +qt6_add_ui(myapp INCLUDE_PREFIX "src/files_2" SOURCES "my_ui_files/widget2.ui") +#! [qt6_add_ui_5] diff --git a/src/widgets/doc/snippets/cmake-macros/examples.cpp b/src/widgets/doc/snippets/cmake-macros/examples.cpp new file mode 100644 index 00000000000..683f8453e4d --- /dev/null +++ b/src/widgets/doc/snippets/cmake-macros/examples.cpp @@ -0,0 +1,26 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + + +//! [qt6_add_ui_1] +#include "mainwindow.h" +#include "ui_mainwindow.h" +//! [qt6_add_ui_1] + +//! [qt6_add_ui_2] +#include "mainwindow.h" +#include "src/files/ui_mainwindow.h" +//! [qt6_add_ui_2] + +//! [qt6_add_ui_3_1] +#include "src/files/ui_widget1.h" +//! [qt6_add_ui_3_1] + +//! [qt6_add_ui_3_2] +#include "src/files/ui_widget2.h" +//! [qt6_add_ui_3_2] + +//! [qt6_add_ui_5] +#include "src/files_1/ui_widget1.h" +#include "src/files_2/ui_widget2.h" +//! [qt6_add_ui_5] diff --git a/src/widgets/doc/src/cmake-macros.qdoc b/src/widgets/doc/src/cmake-macros.qdoc index 13bdcca67bc..906a18b2b0d 100644 --- a/src/widgets/doc/src/cmake-macros.qdoc +++ b/src/widgets/doc/src/cmake-macros.qdoc @@ -30,6 +30,10 @@ directory. The paths of the generated header files are added to \c{<VAR>}. \note This is a low-level macro. See the \l{CMake AUTOUIC Documentation} for a more convenient way to process \c{.ui} files with \c{uic}. +Since 6.8: +\note \l{qt6_add_ui}{qt_add_ui} is the recommended way to process \c{.ui} +files. + \section1 Options You can set additional \c{OPTIONS} that should be added to the \c{uic} calls. @@ -39,3 +43,132 @@ You can find possible options in the \l{uic}{uic documentation}. \snippet cmake-macros/examples.cmake qt_wrap_ui */ + +/*! +\page qt-add-ui.html +\ingroup cmake-macros-qtwidgets + +\title qt_add_ui +\keyword qt6_add_ui + +\summary {Adds .ui files to a target.} + +\include cmake-find-package-widgets.qdocinc + +\section1 Synopsis + +\badcode +qt_add_ui(<TARGET> + SOURCES file1.ui [file2.ui ...] + [INCLUDE_PREFIX <PREFIX>] + [OPTIONS ...]) +\endcode + +\versionlessCMakeCommandsNote qt6_add_ui() + +\cmakecommandsince 6.8 + +\section1 Description + +Creates rules for calling the \l{uic}{User Interface Compiler (uic)} on the +\c{.ui} files. For each input file, a header file is generated in the build +directory. The generated header files are added to sources of the target. + +\section1 Arguments + +\section2 TARGET + +The \c{TARGET} argument specifies the CMake target to which the generated header +files will be added. + +\section2 SOURCES + +The \c{SOURCES} argument specifies the list of \c{.ui} files to process. + +\section2 INCLUDE_PREFIX + +\c INCLUDE_PREFIX specifies the include prefix for the generated header files. +Use the same include prefix as in the \c{#include} directive in the source +files. If \c{ui_<basename>.h} is included without a prefix, then this argument +can be omitted. + +\section2 OPTIONS + +You can set additional \c{OPTIONS} that should be added to the \c{uic} calls. +You can find possible options in the \l{uic}{uic documentation}. + +\section1 Examples + +\section2 Without INCLUDE_PREFIX + +In the following snippet, the \c{mainwindow.cpp} file includes +\c{ui_mainwindow.h} and \c{mainwindow.h}. + +\snippet cmake-macros/examples.cpp qt6_add_ui_1 + +\c{CMakeLists.txt} is implemented as follows and it calls +\l{qt6_add_ui}{qt_add_ui} to add \c{ui_mainwindow.h} to the \c{myapp} target. + +\snippet cmake-macros/examples.cmake qt6_add_ui_1 + +In the above example, \c{ui_mainwindow.h} is included without a prefix. So the +\c{INCLUDE_PREFIX} argument is not specified. + +\section2 With INCLUDE_PREFIX + +\snippet cmake-macros/examples.cpp qt6_add_ui_2 + +In the above snippet, \c{mainwindow.cpp} includes \c{ui_mainwindow.h} with a +prefix. + +\snippet cmake-macros/examples.cmake qt6_add_ui_2 + +Since \c{ui_mainwindow.h} is included with a prefix, the \c{INCLUDE_PREFIX} +argument is specified as \c{src/files} in the above example. + +\section2 Multiple .ui files + +In the following snippets, both \c{widget1.cpp} and \c{widget2.cpp} include +\c{ui_widget1.h} and \c{ui_widget2.h} respectively. + +\c{widget1.cpp}: + +\snippet cmake-macros/examples.cpp qt6_add_ui_3_1 + +\c{widget2.cpp}: + +\snippet cmake-macros/examples.cpp qt6_add_ui_3_2 + +Both \c{ui_widget1.h} and \c{ui_widget2.h} are included with the same prefix + +\snippet cmake-macros/examples.cmake qt6_add_ui_3 + +In this case, the \c{INCLUDE_PREFIX} argument can be specified as \c{src/files} +for both files in the above snippet. + +\section1 When to prefer \l{qt6_add_ui}{qt_add_ui} over \c{AUTOUIC}? + +\l{qt6_add_ui}{qt_add_ui} has some advantages over \c{AUTOUIC}: + +\list +\li \l{qt6_add_ui}{qt_add_ui} ensures that the \c{.ui} files are generated +correctly during the first build, for the \c{Ninja} and \c{Ninja Multi-Config} +generators. + +\li \l{qt6_add_ui}{qt_add_ui} guarantees that the generated \c{.h} files are +not leaked outside the build directory. + +\li Since \l{qt6_add_ui}{qt_add_ui} does not scan source files, it provides a +faster build than \c{AUTOUIC}. + +\endlist + +\section1 When to prefer \l{qt6_add_ui}{qt_add_ui} over \c{qt_wrap_ui}? + +\l{qt6_add_ui}{qt_add_ui} has the \c{INCLUDE_PREFIX} argument, which can be used to +specify the include prefix for the generated header files. + +\note It is not recommended to use both \l{qt6_add_ui}{qt_add_ui} and +\c{AUTOUIC} for the same target. + +*/ diff --git a/tests/auto/cmake/CMakeLists.txt b/tests/auto/cmake/CMakeLists.txt index 3331ad260d0..b159de7c9ca 100644 --- a/tests/auto/cmake/CMakeLists.txt +++ b/tests/auto/cmake/CMakeLists.txt @@ -399,8 +399,44 @@ endif() _qt_internal_test_expect_pass(test_config_expressions) _qt_internal_test_expect_pass(test_QTP0003) + if(NOT NO_GUI) _qt_internal_test_expect_pass(test_collecting_plugins) endif() _qt_internal_test_expect_pass(test_qt_manual_moc) + +# check if the os is opensuse. If it is, we need to skip tests due to CI problems +set(is_opensuse FALSE) +if(UNIX) + if(EXISTS "/etc/os-release") + file(STRINGS "/etc/os-release" os_release_content) + foreach(line IN LISTS os_release_content) + if(line MATCHES "openSUSE" OR line MATCHES "opensuse") + set(is_opensuse TRUE) + break() + endif() + endforeach() + endif() +endif() + +if(NOT QNX AND NOT WASM AND NOT (WIN32 AND QT_BUILD_MINIMAL_STATIC_TESTS) + AND NOT is_opensuse) + # Since our CI machines are slow, ctest --build-and-test buffers the output + # of the configure step of a test, and the fact that we run all the test + # logic in the configure step, we need to divide the tests into smaller + # chunks to avoid CI stdout timeout errors. + # See https://gitlab.kitware.com/cmake/cmake/-/issues/25790 + _qt_internal_test_expect_pass(test_qt_add_ui_common) + _qt_internal_test_expect_pass(test_qt_add_ui_1) + _qt_internal_test_expect_pass(test_qt_add_ui_2) + _qt_internal_test_expect_pass(test_qt_add_ui_3) + _qt_internal_test_expect_pass(test_qt_add_ui_4) + _qt_internal_test_expect_pass(test_qt_add_ui_5) + _qt_internal_test_expect_pass(test_qt_add_ui_6) + _qt_internal_test_expect_pass(test_qt_add_ui_7) + _qt_internal_test_expect_pass(test_qt_add_ui_8) + _qt_internal_test_expect_pass(test_qt_add_ui_9) + _qt_internal_test_expect_pass(test_qt_add_ui_10) +endif() + diff --git a/tests/auto/cmake/test_qt_add_ui_1/CMakeLists.txt b/tests/auto/cmake/test_qt_add_ui_1/CMakeLists.txt new file mode 100644 index 00000000000..882abecc0b3 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_1/CMakeLists.txt @@ -0,0 +1,62 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(test) + +include(../test_qt_add_ui_common/RunCMake.cmake) +include(../test_qt_add_ui_common/functions.cmake) + +get_generators(generators) + +foreach(generator IN ITEMS ${generators}) + message(STATUS "Running tests for generator: ${generator}") + is_multi_config(${generator} multi_config_out) + if(multi_config_out) + set(configs "Debug" "Release") + else() + set(configs "single_config") + endif() + + foreach(config IN ITEMS ${configs}) + if("${config}" STREQUAL "single_config") + set(config_path "") + set(config_arg "") + else() + set(config_path "_${config}") + set(config_arg "${config}") + endif() + + # Test case: ui_mainwindow.h is included as + # "sub1/sub2/sub3/sub4/../../../../src/ui_files/ui_mainwindow.h". + # Expect 1: Successful build without the double build issue. + # Expect 2: No build folder leakage and generation of the + # sub1/sub2/sub3/sub4 folder in ${hash_folder}/include + string(CONCAT test_ui_file "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/uic_test/mainwindow.ui") + generate_hash_folder( "example" "${test_ui_file}" hash_folder) + string(CONCAT test_build_dir + "${CMAKE_CURRENT_BINARY_DIR}/UicBuildLeak_incPathGen" + "${config_path}-build") + string(CONCAT test_source_dir "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/uic_test") + string(CONCAT test_additional_args "-DMAINWINDOW_UI_PATH=sub1/sub2/" + "sub3/sub4/../../../../src/ui_files/") + string(CONCAT test_file_to_check "${test_build_dir}/.qt/${hash_folder}/" + "${config_arg}/src/ui_files/ui_mainwindow.h") + string(CONCAT test_folder_to_check "${test_build_dir}/.qt/" + "${hash_folder}/${config_arg}/sub1/sub2/sub3/sub4") + incremental_build_test( + TEST_NAME UicBuildLeak_incPathGen + SOURCE_DIR "${test_source_dir}" + BUILD_DIR "${test_build_dir}" + CONFIG "${config_arg}" + GENERATOR "${generator}" + ADDITIONAL_ARGS "${test_additional_args}" + FILE_TO_TOUCH "${test_ui_file}" + FILE_TO_CHECK "${test_file_to_check}" + FOLDER_TO_CHECK "${test_folder_to_check}" + ) + endforeach() +endforeach() + diff --git a/tests/auto/cmake/test_qt_add_ui_10/CMakeLists.txt b/tests/auto/cmake/test_qt_add_ui_10/CMakeLists.txt new file mode 100644 index 00000000000..6b223fb2819 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_10/CMakeLists.txt @@ -0,0 +1,62 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(test) + +include(../test_qt_add_ui_common/RunCMake.cmake) +include(../test_qt_add_ui_common/functions.cmake) + +get_generators(generators) + +foreach(generator IN ITEMS ${generators}) + message(STATUS "Running tests for generator: ${generator}") + is_multi_config(${generator} multi_config_out) + # A CI test fails with the below condition. So, we are running the test + # only for the Debug configuration. + if ("${generator}" MATCHES "Xcode" AND + CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64") + set(configs "Debug") + elseif(multi_config_out) + set(configs "Debug" "Release") + else() + set(configs "single_config") + endif() + + foreach(config IN ITEMS ${configs}) + if("${config}" STREQUAL "single_config") + set(config_path "") + set(config_arg "") + else() + set(config_path "_${config}") + set(config_arg "${config}") + endif() + + # Test case: mainwindow.ui file is touched after the first build. The + # ui_mainwindow.h should be generated without the ${config_arg} folder. + # Expect 1: Successful build without the double build issue. + string(CONCAT test_build_dir "${CMAKE_CURRENT_BINARY_DIR}/" + "qt_add_ui_simple_no_config${config_path}-build") + string(CONCAT test_ui_file "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/uic_test/mainwindow.ui") + string(CONCAT test_source_dir "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/uic_test") + string(CONCAT test_file_to_touch "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/uic_test/mainwindow.ui") + generate_hash_folder( + "example" + "${test_ui_file}" + hash_folder) + incremental_build_test( + TEST_NAME qt6_add_ui_simple_no_config + SOURCE_DIR "${test_source_dir}" + BUILD_DIR "${test_build_dir}" + GENERATOR "${generator}" + CONFIG "${config_arg}" + ADDITIONAL_ARGS "-DUI_NO_CONFIG_OPTIONS=ON" + FILE_TO_TOUCH "${test_file_to_touch}" + FILE_TO_CHECK + "${test_build_dir}/.qt/${hash_folder}/ui_mainwindow.h" + ) + endforeach() +endforeach() diff --git a/tests/auto/cmake/test_qt_add_ui_2/CMakeLists.txt b/tests/auto/cmake/test_qt_add_ui_2/CMakeLists.txt new file mode 100644 index 00000000000..dceb0c7a2ef --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_2/CMakeLists.txt @@ -0,0 +1,60 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(test) + +include(../test_qt_add_ui_common/RunCMake.cmake) +include(../test_qt_add_ui_common/functions.cmake) + +get_generators(generators) + +foreach(generator IN ITEMS ${generators}) + message(STATUS "Running tests for generator: ${generator}") + is_multi_config(${generator} multi_config_out) + # A CI test fails with the below condition. So, we are running the test + # only for the Debug configuration. + if ("${generator}" MATCHES "Xcode" AND + CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64") + set(configs "Debug") + elseif(multi_config_out) + set(configs "Debug" "Release") + else() + set(configs "single_config") + endif() + + foreach(config IN ITEMS ${configs}) + if("${config}" STREQUAL "single_config") + set(config_path "") + set(config_arg "") + else() + set(config_path "_${config}") + set(config_arg "${config}") + endif() + + # Test case: mainwindow.ui file is touched after the first build. + # Expect 1: Successful build without the double build issue. + set(test_build_dir + "${CMAKE_CURRENT_BINARY_DIR}/qt_add_ui_simple${config_path}-build") + string(CONCAT test_ui_file "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/uic_test/mainwindow.ui") + string(CONCAT test_source_dir "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/uic_test") + string(CONCAT test_file_to_touch "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/uic_test/mainwindow.ui") + generate_hash_folder( + "example" + "${test_ui_file}" + hash_folder) + incremental_build_test( + TEST_NAME qt6_add_ui_simple + SOURCE_DIR "${test_source_dir}" + BUILD_DIR "${test_build_dir}" + GENERATOR "${generator}" + CONFIG "${config_arg}" + FILE_TO_TOUCH "${test_file_to_touch}" + FILE_TO_CHECK + "${test_build_dir}/.qt/${hash_folder}/${config_arg}/ui_mainwindow.h" + ) + endforeach() +endforeach() diff --git a/tests/auto/cmake/test_qt_add_ui_3/CMakeLists.txt b/tests/auto/cmake/test_qt_add_ui_3/CMakeLists.txt new file mode 100644 index 00000000000..81a9a384312 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_3/CMakeLists.txt @@ -0,0 +1,199 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(test) + +include(../test_qt_add_ui_common/RunCMake.cmake) +include(../test_qt_add_ui_common/functions.cmake) + +get_generators(generators) + +foreach(generator IN ITEMS ${generators}) + message(STATUS "Running tests for generator: ${generator}") + is_multi_config(${generator} multi_config_out) + if(multi_config_out) + set(configs "Debug" "Release") + else() + set(configs "single_config") + endif() + + foreach(config IN ITEMS ${configs}) + if("${config}" STREQUAL "single_config") + set(config_path "") + set(config_arg "") + else() + set(config_path "_${config}") + set(config_arg "${config}") + endif() + + # Test case: Check whether api sets warning against AUTOUIC activation. + # Expect 1: Printing of the error message and failure of the build. + string(CONCAT test_source_dir "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/uic_test") + string(CONCAT test_build_dir "${CMAKE_CURRENT_BINARY_DIR}/" + "uic_warning_test${config_path}-build") + run_cmake_configure(SOURCE_DIR "${test_source_dir}" + BUILD_DIR "${test_build_dir}" + GENERATOR "${generator}" + CLEAN_FIRST + ADDITIONAL_ARGS "-DCMAKE_AUTOUIC=ON" + OUTPUT_VARIABLE cmake_output + ERROR_VARIABLE cmake_error + RESULT_VARIABLE cmake_result) + if(NOT cmake_result EQUAL 0) + message(FATAL_ERROR "cmake_output: ${cmake_output}\ncmake_error: " + "${cmake_error}\nFAIL: \"uic_warning_test\" test in " + "${CMAKE_CURRENT_BINARY_DIR}/uic_warning_test${config_path}-" + "build failed to configure") + else() + message(STATUS "PASS: \"uic_warning_test\" test in " + "${CMAKE_CURRENT_BINARY_DIR}/uic_warning_test${config_path}-" + "build was configured successfully") + endif() + + string(CONCAT test_build_dir "${CMAKE_CURRENT_BINARY_DIR}/" + "uic_warning_test${config_path}-build") + run_cmake_build( + BUILD_DIR "${test_build_dir}" + VERBOSE ON + CONFIG "${config_arg}" + OUTPUT_VARIABLE cmake_build_output + RESULT_VARIABLE cmake_build_result) + if(NOT cmake_build_result EQUAL 0) + message(STATUS "PASS: \"uic_warning_test\" test in " + "${CMAKE_CURRENT_BINARY_DIR}/uic_warning_test${config_path}" + "-build failed to build") + else() + message(FATAL_ERROR "FAIL: \"uic_warning_test\" test in " + "${CMAKE_CURRENT_BINARY_DIR}/uic_warning_test${config_path}-" + "build was built successfully") + endif() + expect_string_contains("${cmake_build_output}" "has \"AUTOUIC\" enabled" + SUCCESS_MESSAGE "\"uic_warning_test\" test in \ +${CMAKE_CURRENT_BINARY_DIR}/uic_warning_test${config_path}-build \ +has \"has AUTOUIC enabled\"" + FAILURE_MESSAGE "\"uic_warning_test\" test in \ +${CMAKE_CURRENT_BINARY_DIR}/uic_warning_test${config_path}-build \ +does not have \"has AUTOUIC enabled\"") + + + if("${generator}" MATCHES "Ninja") + # Test case: If INCLUDE_PREFIX is changed without changing the + # corresponding include path in the source file and Ninja generator + # is used, this casues the double build issue. + # Note: Only happens in Ninja generator. + # Expect 1: Failure of the build in the first build. + string(CONCAT test_source_dir "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/uic_test") + string(CONCAT test_build_dir "${CMAKE_CURRENT_BINARY_DIR}/" + "uic_double_build_test${config_path}-build") + run_cmake_configure(SOURCE_DIR "${test_source_dir}" + BUILD_DIR "${test_build_dir}" + GENERATOR "${generator}" + CLEAN_FIRST + ADDITIONAL_ARGS "-DMAINWINDOW_UI_PATH=sub1/sub2/sub3/" + "-DDO_NOT_GENERATE_FILE=ON" + OUTPUT_VARIABLE cmake_output + ERROR_VARIABLE cmake_error + RESULT_VARIABLE cmake_result) + + if(NOT cmake_result EQUAL 0) + message(FATAL_ERROR "cmake_output: ${cmake_output}\ncmake_error" + ": ${cmake_error}\nFAIL: \"uic_double_build_test\" test in " + "${CMAKE_CURRENT_BINARY_DIR}/uic_double_build_test" + "${config_path}-build failed to configure") + else() + message(STATUS "PASS: \"uic_double_build_test\" test in " + "${CMAKE_CURRENT_BINARY_DIR}/uic_double_build_test" + "${config_path}-build was configured successfully") + endif() + + string(CONCAT test_build_dir "${CMAKE_CURRENT_BINARY_DIR}/" + "uic_double_build_test${config_path}-build") + run_cmake_build( + BUILD_DIR "${test_build_dir}" + VERBOSE ON + CONFIG "${config_arg}" + OUTPUT_VARIABLE cmake_build_output + RESULT_VARIABLE cmake_build_result) + + if(NOT cmake_build_result EQUAL 0) + message(FATAL_ERROR "cmake_build_output: ${cmake_build_output}" + "FAIL: \"uic_double_build_test\" test in " + "${CMAKE_CURRENT_BINARY_DIR}/uic_double_build_test" + "${config_path}-build failed to build in the first build") + else() + message(STATUS "PASS: \"uic_double_build_test\" test in " + "${CMAKE_CURRENT_BINARY_DIR}/uic_double_build_test" + "${config_path}-build was built successfully in the first " + "build") + endif() + + string(CONCAT test_source_dir "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/uic_test") + string(CONCAT test_build_dir "${CMAKE_CURRENT_BINARY_DIR}/" + "uic_double_build_test${config_path}-build") + run_cmake_configure(SOURCE_DIR "${test_source_dir}" + BUILD_DIR "${test_build_dir}" + GENERATOR "${generator}" + # We change the INCLUDE_PREFIX here. + ADDITIONAL_ARGS "-DMAINWINDOW_UI_PATH=sub1/sub2/sub/" + "-DDO_NOT_GENERATE_FILE=ON" + OUTPUT_VARIABLE cmake_output + ERROR_VARIABLE cmake_error + RESULT_VARIABLE cmake_result) + + if(NOT cmake_result EQUAL 0) + message(FATAL_ERROR "cmake_output: ${cmake_output}\ncmake_error" + ":${cmake_error}\nFAIL: \"uic_double_build_test\" test in " + "${CMAKE_CURRENT_BINARY_DIR}/uic_double_build_test" + "${config_path}-build failed to configure in the second " + "build") + else() + message(STATUS "PASS: \"uic_double_build_test\" test in " + "${CMAKE_CURRENT_BINARY_DIR}/uic_double_build_test" + "${config_path}-build was configured successfully in the " + "second build") + endif() + + string(CONCAT test_build_dir "${CMAKE_CURRENT_BINARY_DIR}/" + "uic_double_build_test${config_path}-build") + run_cmake_build( + BUILD_DIR "${test_build_dir}" + VERBOSE ON + CONFIG "${config_arg}" + OUTPUT_VARIABLE cmake_build_output + ERROR_VARIABLE cmake_build_error + RESULT_VARIABLE cmake_build_result) + + if(NOT cmake_build_result EQUAL 0) + message(STATUS "PASS: \"uic_double_build_test\" test in" + "${CMAKE_CURRENT_BINARY_DIR}/uic_double_build_test" + "${config_path}-build failed to build in the first build " + "after changing INCLUDE_PREFIX") + else() + message(FATAL_ERROR "cmake_build_output: ${cmake_build_output}" + "\ncmake_build_error: ${cmake_build_error}\n" + "FAIL: \"uic_double_build_test\" test in " + "${CMAKE_CURRENT_BINARY_DIR}/uic_double_build_test" + "${config_path}-build was built successfully in the first " + "build after changing INCLUDE_PREFIX") + endif() + + set(expected_fail_string "No such file or directory|file not found") + expect_string_contains(${cmake_build_output} + "${expected_fail_string}" + SUCCESS_MESSAGE "\"uic_double_build_test\" test in \ +${CMAKE_CURRENT_BINARY_DIR}/uic_double_build_test${config_path}\ +-build has \"ui_mainwindow.h: No such file or directory\" in \ +the first build after changing INCLUDE_PREFIX" + FAILURE_MESSAGE "\"uic_double_build_test\" test in \ +${CMAKE_CURRENT_BINARY_DIR}/uic_double_build_test${config_path}\ +-build does not have \"ui_mainwindow.h: No such file or \ +directory\" in the first build after changing INCLUDE_PREFIX") + + endif() + endforeach() +endforeach() + diff --git a/tests/auto/cmake/test_qt_add_ui_4/CMakeLists.txt b/tests/auto/cmake/test_qt_add_ui_4/CMakeLists.txt new file mode 100644 index 00000000000..51c74352d6b --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_4/CMakeLists.txt @@ -0,0 +1,65 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(test) + +include(../test_qt_add_ui_common/RunCMake.cmake) +include(../test_qt_add_ui_common/functions.cmake) + +get_generators(generators) + +foreach(generator IN ITEMS ${generators}) + message(STATUS "Running tests for generator: ${generator}") + is_multi_config(${generator} multi_config_out) + # A CI test fails with the below condition. So, we are running the test + # only for the Debug configuration. + if ("${generator}" MATCHES "Xcode" AND + CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64") + set(configs "Debug") + elseif(multi_config_out) + set(configs "Debug" "Release") + else() + set(configs "single_config") + endif() + + foreach(config IN ITEMS ${configs}) + if("${config}" STREQUAL "single_config") + set(config_path "") + set(config_arg "") + else() + set(config_path "_${config}") + set(config_arg "${config}") + endif() + + # Test case: widget1.ui file is touched after the first build. + # Expect 1: Successful build without the double build issue. + # Expect 2: Only touched files to be built + set(test_build_dir + "${CMAKE_CURRENT_BINARY_DIR}/UicIncrementalBuild${config_path}-build") + string(CONCAT ui_file "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/UicIncrementalBuild/src/widget1.ui") + string(CONCAT test_source_dir "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/UicIncrementalBuild") + + generate_hash_folder( + "example" + "${ui_file}" + hash_folder) + string(CONCAT test_file_to_check "${test_build_dir}/.qt/${hash_folder}/" + "${config_arg}/src/ui_widget1.h") + incremental_build_test( + TEST_NAME UicIncrementalBuild + SOURCE_DIR "${test_source_dir}" + BUILD_DIR "${test_build_dir}" + CONFIG "${config_arg}" + GENERATOR "${generator}" + FILE_TO_TOUCH "${ui_file}" + FILE_TO_CHECK + "${test_file_to_check}" + FOLDER_TO_CHECK + "${test_build_dir}/.qt/${hash_folder}/${config_arg}/src" + CHECK_UNWANTED_BUILDS + ) + endforeach() +endforeach() diff --git a/tests/auto/cmake/test_qt_add_ui_5/CMakeLists.txt b/tests/auto/cmake/test_qt_add_ui_5/CMakeLists.txt new file mode 100644 index 00000000000..7b29bcf52b2 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_5/CMakeLists.txt @@ -0,0 +1,164 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(test) + +include(../test_qt_add_ui_common/RunCMake.cmake) +include(../test_qt_add_ui_common/functions.cmake) + +get_generators(generators) + +foreach(generator IN ITEMS ${generators}) + message(STATUS "Running tests for generator: ${generator}") + is_multi_config(${generator} multi_config_out) + if(multi_config_out) + set(configs "Debug" "Release") + else() + set(configs "single_config") + endif() + + foreach(config IN ITEMS ${configs}) + if("${config}" STREQUAL "single_config") + set(config_path "") + set(config_arg "") + else() + set(config_path "_${config}") + set(config_arg "${config}") + endif() + + + if("${generator}" MATCHES "Ninja") + # Test case: Add a new ui file to CMakeLists.txt after the first + # build. + # Expect 1: Expect ${target}_autogen/prefix_info.cmake to be + # generated as expected. + string(CONCAT test_source_dir "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/uic_test") + string(CONCAT test_build_dir "${CMAKE_CURRENT_BINARY_DIR}/" + "uic_prefix_info_cmake_test${config_path}-build") + run_cmake_configure(SOURCE_DIR "${test_source_dir}" + BUILD_DIR "${test_build_dir}" + GENERATOR "${generator}" + CLEAN_FIRST + ADDITIONAL_ARGS "-DMAINWINDOW_UI_PATH=sub1/sub2/sub3/" + OUTPUT_VARIABLE cmake_output + ERROR_VARIABLE cmake_error + RESULT_VARIABLE cmake_result) + + if(NOT cmake_result EQUAL 0) + message(FATAL_ERROR "cmake_output: ${cmake_output}\ncmake_error" + ":${cmake_error}\nFAIL: \"uic_prefix_info_cmake_test\" test" + " in ${CMAKE_CURRENT_BINARY_DIR}/uic_prefix_info_cmake_test" + "${config_path}-build failed to configure") + else() + message(STATUS "PASS: \"uic_prefix_info_cmake_test\" test in" + "${CMAKE_CURRENT_BINARY_DIR}/uic_prefix_info_cmake_test" + "${config_path}-build was configured successfully") + endif() + + string(CONCAT test_ui_file "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/uic_test/mainwindow.ui") + generate_hash_folder("example" "${test_ui_file}" + hash_folder_mainwindow) + string(CONCAT expected_prefix_info_cmake_content + "include_guard()\nset(${hash_folder_mainwindow}_prefix \"" + "sub1/sub2/sub3\")") + # read the content of the prefix_info.cmake file + string(CONCAT "${CMAKE_CURRENT_BINARY_DIR}/" + "uic_prefix_info_cmake_test${config_path}-build") + file(READ + "${test_build_dir}/example_autogen/prefix_info.cmake" + prefix_info_cmake_content) + + if("${prefix_info_cmake_content}" STREQUAL + "${expected_prefix_info_cmake_content}") + message(STATUS "PASS: \"uic_prefix_info_cmake_test\" test in " + "${CMAKE_CURRENT_BINARY_DIR}/uic_prefix_info_cmake_test" + "${config_path}-build has the expected content in " + "prefix_info.cmake") + else() + message(FATAL_ERROR "FAIL: \"uic_prefix_info_cmake_test\" test " + "in ${CMAKE_CURRENT_BINARY_DIR}/uic_prefix_info_cmake_test" + "${config_path}-build does not have the expected content in" + " prefix_info.cmake") + endif() + + string(CONCAT test_build_dir "${CMAKE_CURRENT_BINARY_DIR}/" + "uic_prefix_info_cmake_test${config_path}-build") + run_cmake_build( + BUILD_DIR "${test_build_dir}" + VERBOSE ON + CONFIG "${config_arg}" + OUTPUT_VARIABLE cmake_build_output + ERROR_VARIABLE cmake_build_error + RESULT_VARIABLE cmake_build_result) + + if(NOT cmake_build_result EQUAL 0) + message(FATAL_ERROR "cmake_build_output: ${cmake_build_output}" + "\ncmake_build_error: ${cmake_build_error}\n" + "FAIL: \"uic_prefix_info_cmake_test\" test in " + "${CMAKE_CURRENT_BINARY_DIR}/uic_prefix_info_cmake_test" + "${config_path}-build failed to build in the first build") + else() + message(STATUS "PASS: \"uic_prefix_info_cmake_test\" test in " + "${CMAKE_CURRENT_BINARY_DIR}/uic_prefix_info_cmake_test" + "${config_path}-build was built successfully in the first " + "build") + endif() + + string(CONCAT test_source_dir "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/uic_test") + string(CONCAT test_build_dir "${CMAKE_CURRENT_BINARY_DIR}/" + "uic_prefix_info_cmake_test${config_path}-build") + run_cmake_configure(SOURCE_DIR "${test_source_dir}" + BUILD_DIR "${test_build_dir}" + GENERATOR "${generator}" + ADDITIONAL_ARGS "-DMAINWINDOW_UI_PATH=sub1/sub2/sub3/" + "-DNEW_UI_PATH=sub5/sub6/sub7/ " "-DADD_NEW_UI=ON" + OUTPUT_VARIABLE cmake_output + ERROR_VARIABLE cmake_error + RESULT_VARIABLE cmake_result) + + if(NOT cmake_result EQUAL 0) + message(FATAL_ERROR "cmake_output: ${cmake_output}\ncmake_error" + ": ${cmake_error}\nFAIL: \"uic_prefix_info_cmake_test\" " + "test in ${CMAKE_CURRENT_BINARY_DIR}/uic_prefix_info_cmake_" + "test ${config_path}-build failed to configure in the" + "second build") + else() + message(STATUS "PASS: \"uic_prefix_info_cmake_test\" test in" + " ${CMAKE_CURRENT_BINARY_DIR}/uic_prefix_info_cmake_test" + "${config_path}-build was configured successfully in the " + "second build") + endif() + + string(CONCAT test_ui_file "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/uic_test/subdir/mainwindow.ui") + generate_hash_folder("example" "${test_ui_file}" + hash_folder_subdir_mainwindow) + set(expected_prefix_info_cmake_content + "include_guard()\nset(${hash_folder_subdir_mainwindow}_prefix \ +\"sub5/sub6/sub7\")") + string(CONCAT test_build_dir "${CMAKE_CURRENT_BINARY_DIR}/" + "uic_prefix_info_cmake_test${config_path}-build") + file(READ + "${test_build_dir}/example_autogen/prefix_info.cmake" + prefix_info_cmake_content) + + if("${prefix_info_cmake_content}" STREQUAL + "${expected_prefix_info_cmake_content}") + message(STATUS "PASS: \"uic_prefix_info_cmake_test\" test in " + "${CMAKE_CURRENT_BINARY_DIR}/uic_prefix_info_cmake_test" + "${config_path}-build has the expected content in " + "prefix_info.cmake") + else() + message(FATAL_ERROR "FAIL: \"uic_prefix_info_cmake_test\" test " + "in ${CMAKE_CURRENT_BINARY_DIR}/uic_prefix_info_cmake_test" + "${config_path}-build does not have the expected content " + "in prefix_info.cmake") + endif() + endif() + endforeach() +endforeach() + diff --git a/tests/auto/cmake/test_qt_add_ui_6/CMakeLists.txt b/tests/auto/cmake/test_qt_add_ui_6/CMakeLists.txt new file mode 100644 index 00000000000..2d57c6067d2 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_6/CMakeLists.txt @@ -0,0 +1,64 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(test) + +include(../test_qt_add_ui_common/RunCMake.cmake) +include(../test_qt_add_ui_common/functions.cmake) + +get_generators(generators) + +foreach(generator IN ITEMS ${generators}) + message(STATUS "Running tests for generator: ${generator}") + is_multi_config(${generator} multi_config_out) + # A CI test fails with the below condition. So, we are running the test + # only for the Debug configuration. + if ("${generator}" MATCHES "Xcode" AND + CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64") + set(configs "Debug") + elseif(multi_config_out) + set(configs "Debug" "Release") + else() + set(configs "single_config") + endif() + + foreach(config IN ITEMS ${configs}) + if("${config}" STREQUAL "single_config") + set(config_path "") + set(config_arg "") + else() + set(config_path "_${config}") + set(config_arg "${config}") + endif() + + # Test case: ui_mainwindow.h is included as + # "../../../../src/ui_files/ui_mainwindow.h". + # Test case: mainwindow1.ui file is touched after the first build. + # Expect 1: Successful build without the double build issue. + # Expect 2: No build folder leakage and generation of the "_/_/_/_/" + # folder in ${hash_folder}/include. + string(CONCAT test_ui_file "${CMAKE_CURRENT_SOURCE_DIR}/../test_qt_add_ui_" + "common/uic_test/mainwindow.ui") + generate_hash_folder("example" "${test_ui_file}" hash_folder) + string(CONCAT test_build_dir "${CMAKE_CURRENT_BINARY_DIR}/UicBuildLeak_" + "subFolderGen${config_path}-build") + string(CONCAT test_file_to_touch "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/uic_test/mainwindow.ui") + string(CONCAT test_file_to_check "${test_build_dir}/.qt/${hash_folder}/" + "${config_arg}/src/ui_files/ui_mainwindow.h") + incremental_build_test( + TEST_NAME UicBuildLeak_subFolderGen + SOURCE_DIR + "${CMAKE_CURRENT_SOURCE_DIR}/../test_qt_add_ui_common/uic_test" + BUILD_DIR "${test_build_dir}" + CONFIG "${config_arg}" + GENERATOR "${generator}" + ADDITIONAL_ARGS "-DMAINWINDOW_UI_PATH=../../../../src/ui_files/" + FILE_TO_TOUCH "${test_file_to_touch}" + FILE_TO_CHECK "${test_file_to_check}" + FOLDER_TO_CHECK + "${test_build_dir}/.qt/${hash_folder}/${config_arg}/_/_/_/_" + ) + endforeach() +endforeach() diff --git a/tests/auto/cmake/test_qt_add_ui_7/CMakeLists.txt b/tests/auto/cmake/test_qt_add_ui_7/CMakeLists.txt new file mode 100644 index 00000000000..8278f13a727 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_7/CMakeLists.txt @@ -0,0 +1,62 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(test) + +include(../test_qt_add_ui_common/RunCMake.cmake) +include(../test_qt_add_ui_common/functions.cmake) + +get_generators(generators) + +foreach(generator IN ITEMS ${generators}) + message(STATUS "Running tests for generator: ${generator}") + is_multi_config(${generator} multi_config_out) + # A CI test fails with the below condition. So, we are running the test + # only for the Debug configuration. + if ("${generator}" MATCHES "Xcode" AND + CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64") + set(configs "Debug") + elseif(multi_config_out) + set(configs "Debug" "Release") + else() + set(configs "single_config") + endif() + + foreach(config IN ITEMS ${configs}) + if("${config}" STREQUAL "single_config") + set(config_path "") + set(config_arg "") + else() + set(config_path "_${config}") + set(config_arg "${config}") + endif() + + # Test case 1: There are two widget1.ui files in different folders. + # Expect 1: Successful build without the double build issue. + # Expect 2: Only touched files to be built. + string(CONCAT ui_file "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/widget1.ui") + string(CONCAT test_source_dir "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/sub1/sub2/sub3/" + "sub4") + string(CONCAT test_build_dir "${CMAKE_CURRENT_BINARY_DIR}/" + "UicIncBuild_sameFileDiffFolder${config_path}-build") + string(CONCAT test_file_to_touch "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/widget1.ui") + generate_hash_folder( + "example" + "${ui_file}" + hash_folder) + incremental_build_test( + TEST_NAME UicIncBuild_sameFileDiffFolder + SOURCE_DIR "${test_source_dir}" + BUILD_DIR "${test_build_dir}" + CONFIG "${config_arg}" + GENERATOR "${generator}" + FILE_TO_TOUCH "${test_file_to_touch}" + CHECK_UNWANTED_BUILDS + ) + + endforeach() +endforeach() diff --git a/tests/auto/cmake/test_qt_add_ui_8/CMakeLists.txt b/tests/auto/cmake/test_qt_add_ui_8/CMakeLists.txt new file mode 100644 index 00000000000..f8d0763d355 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_8/CMakeLists.txt @@ -0,0 +1,67 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(test) + +include(../test_qt_add_ui_common/RunCMake.cmake) +include(../test_qt_add_ui_common/functions.cmake) + +get_generators(generators) + +foreach(generator IN ITEMS ${generators}) + message(STATUS "Running tests for generator: ${generator}") + is_multi_config(${generator} multi_config_out) + # A CI test fails with the below condition. So, we are running the test + # only for the Debug configuration. + if ("${generator}" MATCHES "Xcode" AND + CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64") + set(configs "Debug") + elseif(multi_config_out) + set(configs "Debug" "Release") + else() + set(configs "single_config") + endif() + + foreach(config IN ITEMS ${configs}) + if("${config}" STREQUAL "single_config") + set(config_path "") + set(config_arg "") + else() + set(config_path "_${config}") + set(config_arg "${config}") + endif() + + # Test case: ui_mainwindow.h is included as + # "sub1/sub2/sub3/../../../../../../../../../../../../../ui_mainwindow.h". + # Test case: mainwindow1.ui file is touched after the first build. + # Expect 1: Successful build without the double build issue. + # Expect 2: No build folder leakage and generation of the + # "_/_/_/_/_/_/_/_/_/_/sub1/sub2/sub3" folder in ${hash_folder}/include. + string(CONCAT test_ui_file "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/uic_test/mainwindow.ui") + generate_hash_folder("example" "${test_ui_file}" hash_folder) + string(CONCAT test_build_dir "${CMAKE_CURRENT_BINARY_DIR}/" + "UicBuildLeak_subFolderGen_complex${config_path}-build") + string(CONCAT test_additional_args "-DMAINWINDOW_UI_PATH=sub1/sub2/sub3" + "/../../../../../../../../../../../../../") + string(CONCAT test_file_to_touch "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/uic_test/mainwindow.ui") + string(CONCAT test_file_to_check "${test_build_dir}/.qt/${hash_folder}/" + "${config_arg}/ui_mainwindow.h") + string(CONCAT test_folder_to_check "${test_build_dir}/.qt/${hash_folder}/" + "${config_arg}/_/_/_/_/_/_/_/_/_/_/sub1/sub2/sub3") + incremental_build_test( + TEST_NAME UicBuildLeak_subFolderGen_complex + SOURCE_DIR + "${CMAKE_CURRENT_SOURCE_DIR}/../test_qt_add_ui_common/uic_test" + BUILD_DIR "${test_build_dir}" + CONFIG "${config_arg}" + GENERATOR "${generator}" + ADDITIONAL_ARGS "${test_additional_args}" + FILE_TO_TOUCH "${test_file_to_touch}" + FILE_TO_CHECK "${test_file_to_check}" + FOLDER_TO_CHECK "${test_folder_to_check}" + ) + endforeach() +endforeach() diff --git a/tests/auto/cmake/test_qt_add_ui_9/CMakeLists.txt b/tests/auto/cmake/test_qt_add_ui_9/CMakeLists.txt new file mode 100644 index 00000000000..9e28ddc9272 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_9/CMakeLists.txt @@ -0,0 +1,66 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(test) + +include(../test_qt_add_ui_common/RunCMake.cmake) +include(../test_qt_add_ui_common/functions.cmake) + +get_generators(generators) + +foreach(generator IN ITEMS ${generators}) + message(STATUS "Running tests for generator: ${generator}") + is_multi_config(${generator} multi_config_out) + if(multi_config_out) + # Since our CI machines are slow, ctest --build-and-test buffers the + # output of the configure step of a test, and the fact that we run all + # the test logic in the configure step, we need to exclude Release + # to avoid CI stdout timeout errors. + # See https://gitlab.kitware.com/cmake/cmake/-/issues/25790 + set(configs "Debug" "Release") + else() + set(configs "single_config") + endif() + + foreach(config IN ITEMS ${configs}) + if("${config}" STREQUAL "single_config") + set(config_path "") + set(config_arg "") + else() + set(config_path "_${config}") + set(config_arg "${config}") + endif() + + # Test case: ui_mainwindow.h is included as + # "sub2/sub3/../../../src/ui_files/ui_mainwindow.h". + # Expect 1: Successful build without the double build issue. + # Expect 2: No build folder leakage and generation of + # _/sub2/sub3 in ${hash_folder}/include. + # Note: This test case is a mix of previous two test cases. + string(CONCAT test_ui_file "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/uic_test/mainwindow.ui") + generate_hash_folder("example" "${test_ui_file}" hash_folder) + set(test_build_dir + "${CMAKE_CURRENT_BINARY_DIR}/UicBuildLeak_mix${config_path}-build") + string(CONCAT test_additional_args "-DMAINWINDOW_UI_PATH=sub2/sub3/" + "../../../src/ui_files/") + string(CONCAT test_file_to_check "${test_build_dir}/.qt/${hash_folder}/" + "${config_arg}/src/ui_files/ui_mainwindow.h") + string(CONCAT test_source_dir "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/uic_test") + incremental_build_test( + TEST_NAME UicBuildLeak_mix + SOURCE_DIR "${test_source_dir}" + BUILD_DIR "${test_build_dir}" + CONFIG "${config_arg}" + GENERATOR "${generator}" + ADDITIONAL_ARGS "${test_additional_args}" + FILE_TO_TOUCH "${test_ui_file}" + FILE_TO_CHECK "${test_file_to_check}" + FOLDER_TO_CHECK + "${test_build_dir}/.qt/${hash_folder}/${config_arg}/_/sub2/sub3" + ) + endforeach() +endforeach() + diff --git a/tests/auto/cmake/test_qt_add_ui_common/CMakeLists.txt b/tests/auto/cmake/test_qt_add_ui_common/CMakeLists.txt new file mode 100644 index 00000000000..628dc9373a0 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/CMakeLists.txt @@ -0,0 +1,7 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(test) + +add_executable(test main.cpp) diff --git a/tests/auto/cmake/test_qt_add_ui_common/RunCMake.cmake b/tests/auto/cmake/test_qt_add_ui_common/RunCMake.cmake new file mode 100644 index 00000000000..6b39fe33986 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/RunCMake.cmake @@ -0,0 +1,157 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +function(run_cmake_configure) + set(options CLEAN_FIRST) + set(oneValueArgs SOURCE_DIR BUILD_DIR RESULT_VARIABLE OUTPUT_VARIABLE + ERROR_VARIABLE GENERATOR) + set(multiValueArgs ADDITIONAL_ARGS) + + cmake_parse_arguments(arg "${options}" "${oneValueArgs}" "${multiValueArgs}" + ${ARGN}) + + if(NOT arg_SOURCE_DIR) + message(FATAL_ERROR "SOURCE_DIR not specified") + endif() + + if(NOT arg_BUILD_DIR) + message(FATAL_ERROR "BUILD_DIR not specified") + endif() + + is_multi_config(arg_GENERATOR multi_config_out) + if (NOT ${multi_config_out}) + set(run_arg_config_arg -Darg_TYPE=Debug) + endif() + + set(test_project_source_dir ${arg_SOURCE_DIR}) + set(test_project_build_dir ${arg_BUILD_DIR}) + + # Make sure that file paths are 'real' paths + get_filename_component(test_project_source_dir "${test_project_source_dir}" + REALPATH) + get_filename_component(test_project_build_dir "${test_project_build_dir}" + REALPATH) + + if(arg_CLEAN_FIRST) + file(REMOVE_RECURSE "${test_project_build_dir}") + endif() + file(MAKE_DIRECTORY "${test_project_build_dir}") + + execute_process(COMMAND + "${CMAKE_COMMAND}" + -S "${test_project_source_dir}" + -B "${test_project_build_dir}" + -G "${arg_GENERATOR}" + "-DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH}" + ${run_arg_config_arg} + ${arg_ADDITIONAL_ARGS} + RESULT_VARIABLE cmake_result + OUTPUT_VARIABLE cmake_output + ERROR_VARIABLE cmake_error + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_STRIP_TRAILING_WHITESPACE + ECHO_OUTPUT_VARIABLE + ECHO_ERROR_VARIABLE + ) + + # set output variables + set(${arg_RESULT_VARIABLE} ${cmake_result} PARENT_SCOPE) + set(${arg_OUTPUT_VARIABLE} ${cmake_output} PARENT_SCOPE) + set(${arg_ERROR_VARIABLE} ${cmake_error} PARENT_SCOPE) +endfunction() + +function(run_cmake_build) + set(options VERBOSE) + set(oneValueArgs CONFIG BUILD_DIR RESULT_VARIABLE OUTPUT_VARIABLE + ERROR_VARIABLE) + cmake_parse_arguments(arg "${options}" "${oneValueArgs}" "${multiValueArgs}" + ${ARGN}) + + if(NOT arg_BUILD_DIR) + message(FATAL_ERROR "BUILD_DIR not specified") + endif() + + if (arg_VERBOSE OR arg_VERBOSE STREQUAL "") + set(arg_VERBOSE_ARG --verbose) + endif() + + if(arg_CONFIG) + set(arg_BUILD_CONFIG_ARG --config ${arg_CONFIG}) + endif() + + execute_process(COMMAND ${CMAKE_COMMAND} + --build ${arg_BUILD_DIR} + ${arg_VERBOSE_ARG} + ${arg_BUILD_CONFIG_ARG} + RESULT_VARIABLE cmake_result + OUTPUT_VARIABLE cmake_output + ERROR_VARIABLE cmake_error + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_STRIP_TRAILING_WHITESPACE + ECHO_OUTPUT_VARIABLE + ECHO_ERROR_VARIABLE) + + set(${arg_RESULT_VARIABLE} ${cmake_result} PARENT_SCOPE) + set(${arg_OUTPUT_VARIABLE} ${cmake_output} PARENT_SCOPE) + set(${arg_ERROR_VARIABLE} ${cmake_error} PARENT_SCOPE) +endfunction() + +function(is_multi_config generator output) + if ("${generator}" MATCHES "Visual Studio" OR "${generator}" MATCHES "Xcode" + OR "${generator}" MATCHES "Ninja Multi-Config") + set(${output} TRUE PARENT_SCOPE) + else() + set(${output} FALSE PARENT_SCOPE) + endif() +endfunction() + +# check if string includes substring +function(_internal_string_contains output string substring) + if("${string}" MATCHES "${substring}") + set(${output} TRUE PARENT_SCOPE) + else() + set(${output} FALSE PARENT_SCOPE) + endif() +endfunction() + +function(expect_string_contains string substring) + set(oneValueArgs SUCCESS_MESSAGE FAILURE_MESSAGE) + cmake_parse_arguments(expect_string_contains "${options}" "${oneValueArgs}" + "${multiValueArgs}" ${ARGN}) + _internal_string_contains(result "${string}" "${substring}") + if("${result}" STREQUAL TRUE) + if (expect_string_contains_SUCCESS_MESSAGE) + message(STATUS "PASS: ${expect_string_contains_SUCCESS_MESSAGE}") + else() + message(STATUS "PASS: \"${string}\" contains \"${substring}\"") + endif() + else() + if (expect_string_contains_FAILURE_MESSAGE) + message(FATAL_ERROR + "FAIL: ${expect_string_contains_FAILURE_MESSAGE}") + else() + message(FATAL_ERROR "FAIL: \"${string}\" contains \"${substring}\"") + endif() + endif() +endfunction() + +function(expect_string_not_contains string substring) + set(oneValueArgs SUCCESS_MESSAGE FAILURE_MESSAGE) + cmake_parse_arguments(expect_string_not_contains + "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + _internal_string_contains(result ${string} ${substring}) + if(${result} STREQUAL FALSE) + if (expect_string_not_contains_SUCCESS_MESSAGE) + message(STATUS "PASS: ${expect_string_not_contains_SUCCESS_MESSAGE}") + else() + message(STATUS "PASS: \"${string}\" not contains \"${substring}\"") + endif() + else() + if (expect_string_not_contains_FAILURE_MESSAGE) + message(FATAL_ERROR + "FAIL: ${expect_string_not_contains_FAILURE_MESSAGE}") + else() + message(FATAL_ERROR "FAIL: \"${string}\" contains \"${substring}\"") + endif() + endif() +endfunction() diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicBuildFolderLeakageCommon/main.cpp b/tests/auto/cmake/test_qt_add_ui_common/UicBuildFolderLeakageCommon/main.cpp new file mode 100644 index 00000000000..ed46eb370d1 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicBuildFolderLeakageCommon/main.cpp @@ -0,0 +1,14 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: BSD-3-Clause + +#include <QApplication> + +#include "mainwindow.h" + +int main(int argc, char* argv[]) +{ + QApplication a(argc, argv); + MainWindow w; + w.show(); + return a.exec(); +} diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicBuildFolderLeakageCommon/mainwindow.h b/tests/auto/cmake/test_qt_add_ui_common/UicBuildFolderLeakageCommon/mainwindow.h new file mode 100644 index 00000000000..21b7824f8a8 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicBuildFolderLeakageCommon/mainwindow.h @@ -0,0 +1,26 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: BSD-3-Clause + +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include <QMainWindow> + +QT_BEGIN_NAMESPACE +namespace Ui { +class MainWindow; +} +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget* parent = nullptr); + ~MainWindow(); + +private: + Ui::MainWindow* ui; +}; +#endif // MAINWINDOW_H diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/main.cpp b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/main.cpp new file mode 100644 index 00000000000..ed46eb370d1 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/main.cpp @@ -0,0 +1,14 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: BSD-3-Clause + +#include <QApplication> + +#include "mainwindow.h" + +int main(int argc, char* argv[]) +{ + QApplication a(argc, argv); + MainWindow w; + w.show(); + return a.exec(); +} diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/mainwindow.cpp b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/mainwindow.cpp new file mode 100644 index 00000000000..1503e504b4b --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/mainwindow.cpp @@ -0,0 +1,28 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: BSD-3-Clause + +#include "mainwindow.h" + +#include <QVBoxLayout> + +#include "../../../../src/ui_files/ui_mainwindow.h" +#include "widget1.h" + +MainWindow::MainWindow(QWidget* parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + auto layout = new QVBoxLayout; + layout->addWidget(new Widget1); + + QWidget* w = new QWidget(this); + w->setLayout(layout); + + setCentralWidget(w); +} + +MainWindow::~MainWindow() +{ + delete ui; +} diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/mainwindow.h b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/mainwindow.h new file mode 100644 index 00000000000..47f1cdce0f8 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/mainwindow.h @@ -0,0 +1,25 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: BSD-3-Clause + +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include <QMainWindow> + +QT_BEGIN_NAMESPACE +namespace Ui { +class MainWindow; +} +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT +public: + MainWindow(QWidget* parent = nullptr); + ~MainWindow(); + +private: + Ui::MainWindow* ui; +}; +#endif // MAINWINDOW_H diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/ui_files/mainwindow.ui b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/ui_files/mainwindow.ui new file mode 100644 index 00000000000..828d7c1782b --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/ui_files/mainwindow.ui @@ -0,0 +1,33 @@ +<?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>MainWindow</string> + </property> + <widget class="QWidget" name="centralwidget"> + <layout class="QGridLayout" name="gridLayout"/> + </widget> + <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/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/ui_files/widget1.ui b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/ui_files/widget1.ui new file mode 100644 index 00000000000..db0c58d5df8 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/ui_files/widget1.ui @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Widget1</class> + <widget class="QWidget" name="Widget1"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QFormLayout" name="formLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Input:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="lineEdit"/> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>OnTextChanged:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLabel" name="OnTextChanged"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>TextLabel</string> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/widget1.cpp b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/widget1.cpp new file mode 100644 index 00000000000..ba53958a75d --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/widget1.cpp @@ -0,0 +1,25 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: BSD-3-Clause + +#include "widget1.h" + +#include "../../../../src/ui_files/ui_widget1.h" + +Widget1::Widget1(QWidget* parent) + : QWidget(parent) + , ui(new Ui::Widget1) +{ + ui->setupUi(this); + connect(ui->lineEdit, SIGNAL(textChanged(const QString&)), this, + SLOT(onTextChanged(const QString&))); +} + +Widget1::~Widget1() +{ + delete ui; +} + +void Widget1::onTextChanged(const QString& text) +{ + ui->OnTextChanged->setText(text); +} diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/widget1.h b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/widget1.h new file mode 100644 index 00000000000..327949b4d8f --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/widget1.h @@ -0,0 +1,28 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: BSD-3-Clause + +#ifndef WIDGET1_H +#define WIDGET1_H + +#include <QWidget> + +QT_BEGIN_NAMESPACE +namespace Ui { +class Widget1; +} +QT_END_NAMESPACE + +class Widget1 : public QWidget +{ + Q_OBJECT +public: + explicit Widget1(QWidget* parent = nullptr); + ~Widget1(); +public slots: + void onTextChanged(const QString& text); + +private: + Ui::Widget1* ui; +}; + +#endif // WIDGET1_H diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/widget1.ui b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/widget1.ui new file mode 100644 index 00000000000..db0c58d5df8 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/widget1.ui @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Widget1</class> + <widget class="QWidget" name="Widget1"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QFormLayout" name="formLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Input:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="lineEdit"/> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>OnTextChanged:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLabel" name="OnTextChanged"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>TextLabel</string> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/widget2.cpp b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/widget2.cpp new file mode 100644 index 00000000000..ca84756a7f4 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/widget2.cpp @@ -0,0 +1,25 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: BSD-3-Clause + +#include "widget2.h" + +#include "../../../../ui_widget1.h" + +Widget2::Widget2(QWidget* parent) + : QWidget(parent) + , ui(new Ui::Widget2) +{ + ui->setupUi(this); + connect(ui->lineEdit, SIGNAL(textChanged(const QString&)), this, + SLOT(onTextChanged(const QString&))); +} + +Widget2::~Widget2() +{ + delete ui; +} + +void Widget2::onTextChanged(const QString& text) +{ + ui->OnTextChanged->setText(text); +} diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/widget2.h b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/widget2.h new file mode 100644 index 00000000000..db1c3b7157a --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/widget2.h @@ -0,0 +1,29 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: BSD-3-Clause + +#ifndef WIDGET2_H +#define WIDGET2_H + +#include <QWidget> + +QT_BEGIN_NAMESPACE +namespace Ui { +class Widget2; +} +QT_END_NAMESPACE + +class Widget2 : public QWidget +{ + Q_OBJECT + +public: + explicit Widget2(QWidget* parent = nullptr); + ~Widget2(); +public slots: + void onTextChanged(const QString& text); + +private: + Ui::Widget2* ui; +}; + +#endif // WIDGET2_H diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/sub1/sub2/sub3/sub4/CMakeLists.txt b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/sub1/sub2/sub3/sub4/CMakeLists.txt new file mode 100644 index 00000000000..b8443c370de --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/sub1/sub2/sub3/sub4/CMakeLists.txt @@ -0,0 +1,39 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) + +project(UicIncrementalBuild_sameFileDifferentFolder LANGUAGES CXX) + +find_package(Qt6 REQUIRED COMPONENTS Core Widgets Gui) + +set(CMAKE_AUTOMOC ON) + +qt_add_executable(example + ../../../../src/ui_files/mainwindow.ui + ../../../../src/ui_files/widget1.ui + ../../../../widget1.ui + ../../../../src/mainwindow.h + ../../../../src/widget1.h + ../../../../src/widget2.h + ../../../../src/main.cpp + ../../../../src/mainwindow.cpp + ../../../../src/widget1.cpp + ../../../../src/widget2.cpp +) + +target_link_libraries(example PRIVATE Qt6::Widgets + Qt6::Core + Qt6::Gui) + +qt6_add_ui(example + INCLUDE_PREFIX "../../../../src/ui_files" + SOURCES "../../../../src/ui_files/mainwindow.ui" + "../../../../src/ui_files/widget1.ui" + OPTIONS "$<$<CONFIG:Debug>:-a>") + +qt6_add_ui(example + INCLUDE_PREFIX "../../../../" + SOURCES "../../../../widget1.ui" + OPTIONS "$<$<CONFIG:Debug>:-a>") + diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/widget1.ui b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/widget1.ui new file mode 100644 index 00000000000..facf4678f2d --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/widget1.ui @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Widget2</class> + <widget class="QWidget" name="Widget2"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QFormLayout" name="formLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Input:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="lineEdit"/> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>OnTextChanged:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLabel" name="OnTextChanged"> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/CMakeLists.txt b/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/CMakeLists.txt new file mode 100644 index 00000000000..81023c33828 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/CMakeLists.txt @@ -0,0 +1,33 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) + +project(UicIncrementalBuild LANGUAGES CXX) + +find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets) + +set(CMAKE_AUTOMOC ON) + +qt_add_executable(example + src/mainwindow.ui + src/widget1.ui + src/widget2.ui + src/mainwindow.h + src/widget1.h + src/widget2.h + src/main.cpp + src/mainwindow.cpp + src/widget1.cpp + src/widget2.cpp +) + +target_link_libraries(example PRIVATE Qt6::Widgets + Qt6::Core + Qt6::Gui) + +qt6_add_ui(example + INCLUDE_PREFIX "src" + SOURCES "src/mainwindow.ui" "src/widget1.ui" "src/widget2.ui" + OPTIONS "$<$<CONFIG:Debug>:-a>") + diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/main.cpp b/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/main.cpp new file mode 100644 index 00000000000..ed46eb370d1 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/main.cpp @@ -0,0 +1,14 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: BSD-3-Clause + +#include <QApplication> + +#include "mainwindow.h" + +int main(int argc, char* argv[]) +{ + QApplication a(argc, argv); + MainWindow w; + w.show(); + return a.exec(); +} diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/mainwindow.cpp b/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/mainwindow.cpp new file mode 100644 index 00000000000..aa913763b02 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/mainwindow.cpp @@ -0,0 +1,28 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: BSD-3-Clause + +#include "mainwindow.h" + +#include <QVBoxLayout> + +#include "src/ui_mainwindow.h" +#include "widget1.h" + +MainWindow::MainWindow(QWidget* parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + auto layout = new QVBoxLayout; + layout->addWidget(new Widget1); + + QWidget* w = new QWidget(this); + w->setLayout(layout); + + setCentralWidget(w); +} + +MainWindow::~MainWindow() +{ + delete ui; +} diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/mainwindow.h b/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/mainwindow.h new file mode 100644 index 00000000000..47f1cdce0f8 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/mainwindow.h @@ -0,0 +1,25 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: BSD-3-Clause + +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include <QMainWindow> + +QT_BEGIN_NAMESPACE +namespace Ui { +class MainWindow; +} +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT +public: + MainWindow(QWidget* parent = nullptr); + ~MainWindow(); + +private: + Ui::MainWindow* ui; +}; +#endif // MAINWINDOW_H diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/mainwindow.ui b/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/mainwindow.ui new file mode 100644 index 00000000000..828d7c1782b --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/mainwindow.ui @@ -0,0 +1,33 @@ +<?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>MainWindow</string> + </property> + <widget class="QWidget" name="centralwidget"> + <layout class="QGridLayout" name="gridLayout"/> + </widget> + <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/test_qt_add_ui_common/UicIncrementalBuild/src/widget1.cpp b/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/widget1.cpp new file mode 100644 index 00000000000..5ba773c529e --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/widget1.cpp @@ -0,0 +1,25 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: BSD-3-Clause + +#include "widget1.h" + +#include "src/ui_widget1.h" + +Widget1::Widget1(QWidget* parent) + : QWidget(parent) + , ui(new Ui::Widget1) +{ + ui->setupUi(this); + connect(ui->lineEdit, SIGNAL(textChanged(const QString&)), this, + SLOT(onTextChanged(const QString&))); +} + +Widget1::~Widget1() +{ + delete ui; +} + +void Widget1::onTextChanged(const QString& text) +{ + ui->OnTextChanged->setText(text); +} diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/widget1.h b/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/widget1.h new file mode 100644 index 00000000000..327949b4d8f --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/widget1.h @@ -0,0 +1,28 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: BSD-3-Clause + +#ifndef WIDGET1_H +#define WIDGET1_H + +#include <QWidget> + +QT_BEGIN_NAMESPACE +namespace Ui { +class Widget1; +} +QT_END_NAMESPACE + +class Widget1 : public QWidget +{ + Q_OBJECT +public: + explicit Widget1(QWidget* parent = nullptr); + ~Widget1(); +public slots: + void onTextChanged(const QString& text); + +private: + Ui::Widget1* ui; +}; + +#endif // WIDGET1_H diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/widget1.ui b/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/widget1.ui new file mode 100644 index 00000000000..db0c58d5df8 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/widget1.ui @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Widget1</class> + <widget class="QWidget" name="Widget1"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QFormLayout" name="formLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Input:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="lineEdit"/> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>OnTextChanged:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLabel" name="OnTextChanged"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>TextLabel</string> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/widget2.cpp b/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/widget2.cpp new file mode 100644 index 00000000000..bb8983c8b56 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/widget2.cpp @@ -0,0 +1,25 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: BSD-3-Clause + +#include "widget2.h" + +#include "src/ui_widget2.h" + +Widget2::Widget2(QWidget* parent) + : QWidget(parent) + , ui(new Ui::Widget2) +{ + ui->setupUi(this); + connect(ui->lineEdit, SIGNAL(textChanged(const QString&)), this, + SLOT(onTextChanged(const QString&))); +} + +Widget2::~Widget2() +{ + delete ui; +} + +void Widget2::onTextChanged(const QString& text) +{ + ui->OnTextChanged->setText(text); +} diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/widget2.h b/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/widget2.h new file mode 100644 index 00000000000..db1c3b7157a --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/widget2.h @@ -0,0 +1,29 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: BSD-3-Clause + +#ifndef WIDGET2_H +#define WIDGET2_H + +#include <QWidget> + +QT_BEGIN_NAMESPACE +namespace Ui { +class Widget2; +} +QT_END_NAMESPACE + +class Widget2 : public QWidget +{ + Q_OBJECT + +public: + explicit Widget2(QWidget* parent = nullptr); + ~Widget2(); +public slots: + void onTextChanged(const QString& text); + +private: + Ui::Widget2* ui; +}; + +#endif // WIDGET2_H diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/widget2.ui b/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/widget2.ui new file mode 100644 index 00000000000..facf4678f2d --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/widget2.ui @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Widget2</class> + <widget class="QWidget" name="Widget2"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QFormLayout" name="formLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Input:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="lineEdit"/> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>OnTextChanged:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLabel" name="OnTextChanged"> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/tests/auto/cmake/test_qt_add_ui_common/functions.cmake b/tests/auto/cmake/test_qt_add_ui_common/functions.cmake new file mode 100644 index 00000000000..efb8b097748 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/functions.cmake @@ -0,0 +1,223 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +function(generate_hash_folder target_name infile out_folder) + get_filename_component(infile_abs "${infile}" ABSOLUTE) + string(SHA1 infile_hash "${target_name}${infile_abs}") + string(SUBSTRING "${infile_hash}" 0 6 short_hash) + set(${out_folder} "${short_hash}" PARENT_SCOPE) +endfunction() + +function(get_latest_vs_generator output) + execute_process(COMMAND ${CMAKE_COMMAND} -G + ERROR_VARIABLE CMAKE_GENERATORS_ERROR + OUTPUT_STRIP_TRAILING_WHITESPACE) + string(REGEX MATCHALL "Visual Studio [0-9]+ [0-9]+" vs_generators + "${CMAKE_GENERATORS_ERROR}") + + if(NOT vs_generators) + message(FATAL_ERROR "No visual studio generators found") + endif() + + set(last_version "0") + set(last_generator "") + foreach(generator IN LISTS vs_generators) + string(REGEX MATCH "Visual Studio ([0-9]+) [0-9]+" unused "${generator}") + if("${CMAKE_MATCH_1}" VERSION_GREATER "${last_version}") + set(last_version "${CMAKE_MATCH_1}") + set(last_generator "${CMAKE_MATCH_0}") + endif() + endforeach() + set(${output} "${last_generator}" PARENT_SCOPE) +endfunction() + +function(check_unwanted_builds_after_first_build cmake_output test_name test_dir + generator) + set(unwanted_builds_success_message + "\"${test_name}\" in \"${test_dir}\" -> No unwanted builds") + set(unwanted_builds_failure_message + "\"${test_name}\" in \"${test_dir}\" -> Unwanted builds found") + if(${generator} MATCHES "Ninja") + expect_string_not_contains(${cmake_output} + "widget2.cpp.o.d|mainwindow.cpp.o.d" + SUCCESS_MESSAGE ${unwanted_builds_success_message} + FAILURE_MESSAGE ${unwanted_builds_failure_message}) + elseif(${generator} MATCHES "Make") + string(CONCAT not_expect_string + "Building CXX object UicIncrementalBuild/CMakeFiles" + "/example.dir/src/widget2.cpp.o|Building CXX object UicIncremental" + "Build/CMakeFiles/example.dir/src/mainwindow.cpp.o") + expect_string_not_contains(${cmake_output} "${not_expect_string}" + SUCCESS_MESSAGE ${unwanted_builds_success_message} + FAILURE_MESSAGE ${unwanted_builds_failure_message}) + elseif(${generator} MATCHES "Visual Studio") + expect_string_not_contains(${cmake_output} "widget2.cpp|mainwindow.cpp" + SUCCESS_MESSAGE ${unwanted_builds_success_message} + FAILURE_MESSAGE ${unwanted_builds_failure_message}) + elseif(${generator} MATCHES "Xcode") + expect_string_not_contains(${cmake_output} "widget2.cpp|mainwindow.cpp" + SUCCESS_MESSAGE ${unwanted_builds_success_message} + FAILURE_MESSAGE ${unwanted_builds_failure_message}) + endif() +endfunction() + +function(check_output_after_second_build cmake_output test_name + test_dir generator) + set(second_build_success_message + "\"${test_name}\" in \"${test_dir}\" -> Generation of UI files were not \ +triggered in the second build") + set(second_build_failure_message + "\"${test_name}\" in \"${test_dir}\" -> Generation of UI files were \ +triggered in the second build") + + if(${generator} MATCHES "Ninja") + expect_string_contains(${cmake_output} "ninja: no work to do." + SUCCESS_MESSAGE ${second_build_success_message} + FAILURE_MESSAGE ${second_build_failure_message}) + elseif(${generator} MATCHES "Visual Studio" OR ${generator} MATCHES "Xcode") + expect_string_not_contains(${cmake_output} "mainwindow" + SUCCESS_MESSAGE + ${second_build_success_message} + FAILURE_MESSAGE + ${second_build_failure_message}) + elseif(${generator} MATCHES "Makefiles") + expect_string_not_contains(${cmake_output} "mainwindow.cpp.o.d -o" + SUCCESS_MESSAGE ${second_build_success_message} + FAILURE_MESSAGE ${second_build_failure_message}) + endif() +endfunction() + +function(incremental_build_test) + set(options CHECK_UNWANTED_BUILDS) + set(oneValueArgs CONFIG TEST_NAME SOURCE_DIR BUILD_DIR FILE_TO_TOUCH + FILE_TO_CHECK FOLDER_TO_CHECK GENERATOR) + set(multiValueArgs ADDITIONAL_ARGS) + + cmake_parse_arguments(arg "${options}" "${oneValueArgs}" "${multiValueArgs}" + ${ARGN}) + + string(REPLACE ";" " " arg_ADDITIONAL_ARGS "${arg_ADDITIONAL_ARGS}") + if ("${arg_SOURCE_DIR}" STREQUAL "") + message(FATAL_ERROR "FAIL: \"${arg_TEST_NAME}\" test failed because " + "SOURCE_DIR is empty") + endif() + + if ("${arg_BUILD_DIR}" STREQUAL "") + message(FATAL_ERROR "FAIL: \"${arg_TEST_NAME}\" test failed because " + "BUILD_DIR is empty") + endif() + + if ("${arg_GENERATOR}" STREQUAL "") + message(FATAL_ERROR "FAIL: \"${arg_TEST_NAME}\" test failed because " + "GENERATOR is empty") + endif() + + run_cmake_configure(SOURCE_DIR "${arg_SOURCE_DIR}" + BUILD_DIR "${arg_BUILD_DIR}" + GENERATOR "${arg_GENERATOR}" + CLEAN_FIRST + ADDITIONAL_ARGS ${arg_ADDITIONAL_ARGS} + OUTPUT_VARIABLE cmake_output + ERROR_VARIABLE cmake_error + RESULT_VARIABLE cmake_result) + + if(${cmake_result} EQUAL 0) + message(STATUS + "PASS: \"${arg_TEST_NAME}\" test in ${arg_BUILD_DIR} was configured " + "successfully") + else() + message(FATAL_ERROR "cmake_output: ${cmake_output}\ncmake_error: " + "${cmake_error}\nFAIL: \"${arg_TEST_NAME}\" test in ${arg_BUILD_DIR}" + " failed to configure") + endif() + + if(NOT "${arg_CONFIG}" STREQUAL "single_config") + set(config_arg "${arg_CONFIG}") + endif() + + run_cmake_build(BUILD_DIR ${arg_BUILD_DIR} + VERBOSE ON + CONFIG ${config_arg} + OUTPUT_VARIABLE cmake_build_output + ERROR_VARIABLE cmake_build_error + RESULT_VARIABLE cmake_build_result) + + if(${cmake_build_result} EQUAL 0) + message(STATUS + "PASS: \"${arg_TEST_NAME}\" test in ${arg_BUILD_DIR} was built " + "successfully") + else() + message(FATAL_ERROR + "cmake_build_output: ${cmake_build_output}\ncmake_build_error: " + "${cmake_build_error}\nFAIL: \"${arg_TEST_NAME}\" test in " + "${arg_BUILD_DIR} failed to build") + endif() + + if(NOT "${arg_FILE_TO_CHECK}" STREQUAL "") + if(NOT EXISTS "${arg_FILE_TO_CHECK}") + message(FATAL_ERROR "FAIL: \"${arg_TEST_NAME}\" ${arg_FILE_TO_CHECK}" + " could not be found") + else() + message(STATUS "PASS: \"${arg_TEST_NAME}\" \"${arg_FILE_TO_CHECK}\" " + "was generated successfully") + endif() + endif() + + if(NOT "${arg_FOLDER_TO_CHECK}" STREQUAL "" AND NOT WIN32) + if(NOT EXISTS "${arg_FOLDER_TO_CHECK}") + message(FATAL_ERROR + "FAIL: \"${arg_TEST_NAME}\" Folder \"${arg_FOLDER_TO_CHECK}\" " + "does not exist") + else() + message(STATUS + "PASS: \"${arg_TEST_NAME}\" Folder \"${arg_FOLDER_TO_CHECK}\" " + "exists") + endif() + endif() + + if(NOT "${arg_FILE_TO_TOUCH}" STREQUAL "") + file(TOUCH "${arg_FILE_TO_TOUCH}") + endif() + + run_cmake_build(BUILD_DIR ${arg_BUILD_DIR} + VERBOSE ON + CONFIG ${arg_CONFIG} + OUTPUT_VARIABLE cmake_build_output) + if(${arg_CHECK_UNWANTED_BUILDS}) + check_unwanted_builds_after_first_build(${cmake_build_output} + ${arg_TEST_NAME} ${arg_BUILD_DIR} ${arg_GENERATOR}) + endif() + + run_cmake_build(BUILD_DIR ${arg_BUILD_DIR} + VERBOSE ON + CONFIG ${arg_CONFIG} + OUTPUT_VARIABLE cmake_build_output) + check_output_after_second_build(${cmake_build_output} + ${arg_TEST_NAME} ${arg_BUILD_DIR} ${arg_GENERATOR}) +endfunction() + +function(get_generators output) + if(${CMAKE_HOST_SYSTEM_NAME} MATCHES "Linux") + set(generators "Unix Makefiles" "Ninja" "Ninja Multi-Config") + elseif(${CMAKE_HOST_SYSTEM_NAME} MATCHES "Windows") + # CI fails with Clang and Visual Studio generators. + # That's why discard that combination. + if (NOT "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" AND + NOT MINGW) + get_latest_vs_generator(latest_vs) + endif() + set(generators "Ninja" "Ninja Multi-Config" "${latest_vs}") + elseif(${CMAKE_HOST_SYSTEM_NAME} MATCHES "Darwin") + # TODO: Add Xcode generator when + # https://gitlab.kitware.com/cmake/cmake/-/issues/25790 is fixed. + # Otherwise, adding Xcode generator might fail CI due to the timeout + # issue. + set(generators "Unix Makefiles" "Ninja" "Ninja Multi-Config") + else() + string(JOIN "" ERROR_MESSAGE + "FAIL: host OS not supported for this test." + "host : ${CMAKE_HOST_SYSTEM_NAME}") + message(FATAL_ERROR "${ERROR_MESSAGE}") + endif() + set(${output} "${generators}" PARENT_SCOPE) +endfunction() diff --git a/tests/auto/cmake/test_qt_add_ui_common/main.cpp b/tests/auto/cmake/test_qt_add_ui_common/main.cpp new file mode 100644 index 00000000000..d3aff6a3c51 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/main.cpp @@ -0,0 +1,4 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: BSD-3-Clause + +int main() { return 0; } diff --git a/tests/auto/cmake/test_qt_add_ui_common/uic_test/CMakeLists.txt b/tests/auto/cmake/test_qt_add_ui_common/uic_test/CMakeLists.txt new file mode 100644 index 00000000000..b05efd5e4d7 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/uic_test/CMakeLists.txt @@ -0,0 +1,65 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) + +project(UicTest LANGUAGES CXX) + +find_package(Qt6 REQUIRED COMPONENTS Core Widgets Gui) + +set(CMAKE_AUTOMOC ON) + +if (NOT DO_NOT_GENERATE_FILE) + file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/mainwindow.cpp" + CONTENT " \ +#include \"${CMAKE_CURRENT_SOURCE_DIR}/../UicBuildFolderLeakageCommon/mainwindow.h\" \n \ +#include \"${MAINWINDOW_UI_PATH}ui_mainwindow.h\" \n \ +MainWindow::MainWindow(QWidget* parent) \n \ + : QMainWindow(parent) \n \ + , ui(new Ui::MainWindow) \n \ +{ \n \ + ui->setupUi(this); \n \ +} \n \ + \n \ +MainWindow::~MainWindow() \n \ +{ \n \ + delete ui; \n \ +} \n \ +") +endif() + +qt_add_executable(example + ../UicBuildFolderLeakageCommon/main.cpp + ../UicBuildFolderLeakageCommon/mainwindow.h + mainwindow.ui +) + +if (${DO_NOT_GENERATE_FILE}) + target_sources(example PRIVATE mainwindow.cpp) +else() + target_sources(example PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/mainwindow.cpp") +endif() + +target_link_libraries(example PRIVATE Qt6::Widgets + Qt6::Core + Qt6::Gui) + +if (NOT UI_NO_CONFIG_OPTIONS) + set(uic_options "$<$<CONFIG:Debug>:-a>") +endif() +qt6_add_ui(example + INCLUDE_PREFIX "${MAINWINDOW_UI_PATH}" + SOURCES "mainwindow.ui" + OPTIONS "${uic_options}") + +if(ADD_NEW_UI) + qt6_add_ui(example INCLUDE_PREFIX "${NEW_UI_PATH}" + SOURCES "subdir/mainwindow.ui" + OPTIONS "${uic_options}") +endif() + +# Enable AUTOUIC after qt6_add_ui() has been called +if (CMAKE_AUTOUIC) + set_property(TARGET example PROPERTY AUTOUIC ON) +endif() + diff --git a/tests/auto/cmake/test_qt_add_ui_common/uic_test/mainwindow.cpp b/tests/auto/cmake/test_qt_add_ui_common/uic_test/mainwindow.cpp new file mode 100644 index 00000000000..de6cfbaa311 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/uic_test/mainwindow.cpp @@ -0,0 +1,17 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: BSD-3-Clause + +#include "../UicBuildFolderLeakageCommon/mainwindow.h" +#include "sub1/sub2/sub3/ui_mainwindow.h" + +MainWindow::MainWindow(QWidget* parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); +} + +MainWindow::~MainWindow() +{ + delete ui; +} diff --git a/tests/auto/cmake/test_qt_add_ui_common/uic_test/mainwindow.ui b/tests/auto/cmake/test_qt_add_ui_common/uic_test/mainwindow.ui new file mode 100644 index 00000000000..4e57b05eac0 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/uic_test/mainwindow.ui @@ -0,0 +1,45 @@ +<?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>MainWindow</string> + </property> + <widget class="QWidget" name="centralwidget"> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QPushButton" name="pushButton"> + <property name="text"> + <string>PushButton</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <widget class="QMenuBar" name="menubar"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>800</width> + <height>23</height> + </rect> + </property> + </widget> + <widget class="QStatusBar" name="statusbar"/> + </widget> + <resources/> + <connections/> +</ui> diff --git a/tests/auto/cmake/test_qt_add_ui_common/uic_test/subdir/mainwindow.ui b/tests/auto/cmake/test_qt_add_ui_common/uic_test/subdir/mainwindow.ui new file mode 100644 index 00000000000..4e57b05eac0 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/uic_test/subdir/mainwindow.ui @@ -0,0 +1,45 @@ +<?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>MainWindow</string> + </property> + <widget class="QWidget" name="centralwidget"> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QPushButton" name="pushButton"> + <property name="text"> + <string>PushButton</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <widget class="QMenuBar" name="menubar"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>800</width> + <height>23</height> + </rect> + </property> + </widget> + <widget class="QStatusBar" name="statusbar"/> + </widget> + <resources/> + <connections/> +</ui> |