diff --git a/src/corelib/Qt6CoreMacros.cmake b/src/corelib/Qt6CoreMacros.cmake index 84ffa6d2865..486957d7d7e 100644 --- a/src/corelib/Qt6CoreMacros.cmake +++ b/src/corelib/Qt6CoreMacros.cmake @@ -200,7 +200,49 @@ function(qt6_wrap_cpp outfiles ) foreach(it ${moc_files}) get_filename_component(it ${it} ABSOLUTE) - _qt_internal_make_output_file(${it} moc_ cpp outfile) + get_filename_component(it_ext ${it} EXT) + # remove the dot + string(SUBSTRING ${it_ext} 1 -1 it_ext) + set(HEADER_REGEX "(h|hh|h\\+\\+|hm|hpp|hxx|in|txx|inl)$") + + if(it_ext MATCHES "${HEADER_REGEX}") + _qt_internal_make_output_file("${it}" moc_ cpp outfile) + set(is_header_file TRUE) + else() + set(found_source_extension FALSE) + foreach(LANG C CXX OBJC OBJCXX CUDA) + list(FIND CMAKE_${LANG}_SOURCE_FILE_EXTENSIONS "${it_ext}" + index) + if(${index} GREATER -1) + set(found_extension TRUE) + break() + endif() + endforeach() + if(found_extension) + if(TARGET ${moc_target}) + _qt_internal_make_output_file(${it} "" moc outfile) + target_sources(${moc_target} PRIVATE "${outfile}") + target_include_directories("${moc_target}" PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}") + else() + if("${moc_target}" STREQUAL "") + string(JOIN "" err_msg + "qt6_wrap_cpp: TARGET parameter is empty. " + "Since the file ${it} is a source file, " + "the TARGET option must be specified.") + else() + string(JOIN "" err_msg + "qt6_wrap_cpp: TARGET \"${moc_target}\" " + "not found.") + endif() + message(FATAL_ERROR "${err_msg}") + endif() + else() + string(JOIN "" err_msg "qt6_wrap_cpp: Unknown file extension: " + "\"\.${it_ext}\".") + message(FATAL_ERROR "${err_msg}") + endif() + endif() set(out_json_file_var "") if(_WRAP_CPP___QT_INTERNAL_OUTPUT_MOC_JSON_FILES) @@ -215,7 +257,10 @@ function(qt6_wrap_cpp outfiles ) list(APPEND metatypes_json_list "${${out_json_file_var}}") endif() endforeach() - set(${outfiles} ${${outfiles}} PARENT_SCOPE) + + if(is_header_file) + set(${outfiles} "${${outfiles}}" PARENT_SCOPE) + endif() if(metatypes_json_list) set(${_WRAP_CPP___QT_INTERNAL_OUTPUT_MOC_JSON_FILES} diff --git a/src/corelib/doc/snippets/cmake-macros/examples.cmake b/src/corelib/doc/snippets/cmake-macros/examples.cmake index c8d5e710814..b0800d8fe08 100644 --- a/src/corelib/doc/snippets/cmake-macros/examples.cmake +++ b/src/corelib/doc/snippets/cmake-macros/examples.cmake @@ -28,6 +28,11 @@ target_compile_definitions(myapp PRIVATE "$<$:MY_OPTION_FOR_DEBUG> "$<$:DEFINE_CMDLINE_SIGNAL_IN_GENEX=void cmdlineSignal(const QMap int$ &i)>") #! [qt_wrap_cpp_3] +#! [qt_wrap_cpp_4] +qt_add_executable(myapp myapp.cpp main.cpp) +qt_wrap_cpp("" myapp.cpp TARGET myapp) +#! [qt_wrap_cpp_4] + #! [qt_add_resources] set(SOURCES main.cpp) qt_add_resources(SOURCES example.qrc) diff --git a/src/corelib/doc/snippets/cmake-macros/examples.cpp b/src/corelib/doc/snippets/cmake-macros/examples.cpp new file mode 100644 index 00000000000..b17fcd8e77a --- /dev/null +++ b/src/corelib/doc/snippets/cmake-macros/examples.cpp @@ -0,0 +1,16 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +//! [qt_wrap_cpp_4] +// myapp.cpp +#include "myapp.h" +#include + +class MyApp : public QObject { + Q_OBJECT +public: + MyApp() = default; +}; + +#include "myapp.moc" +//! [qt_wrap_cpp_4] diff --git a/src/corelib/doc/src/cmake/qt_wrap_cpp.qdoc b/src/corelib/doc/src/cmake/qt_wrap_cpp.qdoc index 0624651869a..3b298a9d7e1 100644 --- a/src/corelib/doc/src/cmake/qt_wrap_cpp.qdoc +++ b/src/corelib/doc/src/cmake/qt_wrap_cpp.qdoc @@ -40,6 +40,12 @@ You can set an explicit \c{TARGET}. This will make sure that the target properties \c{INCLUDE_DIRECTORIES} and \c{COMPILE_DEFINITIONS} are also used when scanning the source files with \c{moc}. +Since Qt 6.8, when a source file is passed to \c{qt_wrap_cpp} instead of a +header file to generate a \c{.moc} file for a target, the \c{TARGET} parameter +is needed to set the correct include path for the generated \c{.moc} file in +the source file. As generated \c{.moc} files are added to the target's +sources by \c{qt_wrap_cpp}, they are not added to \c{}. + You can set additional \c{OPTIONS} that should be added to the \c{moc} calls. You can find possible options in the \l{moc}{moc documentation}. @@ -69,5 +75,16 @@ avoid syntax errors in the generator expressions. The following example uses \l{https://cmake.org/cmake/help/latest/command/target_compile_definitions.html}{target_compile_definitions} to set \l{https://cmake.org/cmake/help/latest/prop_tgt/COMPILE_DEFINITIONS.html}{COMPILE_DEFINITIONS} which will be added to \c{OPTIONS}. -\snippet cmake-macros/examples.cmake qt_wrap_cpp_3 + +\snippet cmake-macros/examples.cmake qt_wrap_cpp_4 + +\snippet cmake-macros/examples.cpp qt_wrap_cpp_4 + +In the above file, \c{myapp.moc} is included in \c{myapp.cpp}. +To generate the \c{myapp.moc} file, the \c{qt_wrap_cpp} macro is used with the +\c{TARGET} parameter. The first parameter is empty because the \c{.moc} file +and its path will be added to the target's sources and include directories by +the \c{qt_wrap_cpp} macro. + +\snippet cmake-macros/examples.cmake qt_wrap_cpp_4 */ diff --git a/tests/auto/cmake/CMakeLists.txt b/tests/auto/cmake/CMakeLists.txt index 3fe358cd0b9..b8d8f50d6ba 100644 --- a/tests/auto/cmake/CMakeLists.txt +++ b/tests/auto/cmake/CMakeLists.txt @@ -197,6 +197,7 @@ endif() _qt_internal_test_expect_pass(test_add_resource_prefix BINARY test_add_resource_prefix) _qt_internal_test_expect_build_fail(test_add_resource_options) _qt_internal_test_expect_build_fail(test_wrap_cpp_options) +_qt_internal_test_expect_pass(test_wrap_cpp_moc) _qt_internal_test_expect_pass(test_platform_defs_include) _qt_internal_test_expect_pass(test_qtmainwin_library) diff --git a/tests/auto/cmake/test_wrap_cpp_moc/CMakeLists.txt b/tests/auto/cmake/test_wrap_cpp_moc/CMakeLists.txt new file mode 100644 index 00000000000..c00a9d83c50 --- /dev/null +++ b/tests/auto/cmake/test_wrap_cpp_moc/CMakeLists.txt @@ -0,0 +1,19 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) + +project(test_wrap_cpp_and_resources) + +find_package(Qt6Core REQUIRED) + +add_executable(example main.cpp) + +qt_wrap_cpp(moc_files main.cpp TARGET example) + +# expect the generated moc files to be empty when a source file is passed +if (NOT moc_files STREQUAL "") + message(FATAL_ERROR "test_qt_wrap_cpp_moc: moc_files should be empty") +endif() + +target_link_libraries(example PRIVATE Qt::Core) diff --git a/tests/auto/cmake/test_wrap_cpp_moc/main.cpp b/tests/auto/cmake/test_wrap_cpp_moc/main.cpp new file mode 100644 index 00000000000..4be03269712 --- /dev/null +++ b/tests/auto/cmake/test_wrap_cpp_moc/main.cpp @@ -0,0 +1,17 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: BSD-3-Clause + +#include + +class MyObject2 : public QObject { + Q_OBJECT +public: + MyObject2() = default; +}; + +#include "main.moc" + +int main() +{ + return 0; +}