-
Bug
-
Resolution: Unresolved
-
P2: Important
-
None
-
6.9.1
-
None
Let's say you have a top level CMakeLists.txt file that looks like this:
cmake_minimum_required(VERSION 3.31) project(qt_automoc) find_package(Qt6 REQUIRED COMPONENTS Core) qt_standard_project_setup() qt_add_executable(MyQtApp main.cpp) target_link_libraries(MyQtApp PRIVATE Qt6::Core) # Process moc manually rather than using AUTOMOC set_target_properties(MyQtApp PROPERTIES AUTOMOC FALSE) add_subdirectory(src)
Then in src/CMakeLists.txt we have this:
qt_wrap_cpp(MyQtApp MyClass.cpp) target_sources(MyQtApp PRIVATE MyClass.cpp)
The contents of main.cpp and src/MyClass.cpp are not all that important, other than that src/MyClass.cpp defines a class that is a subclass of QObject and it uses the Q_OBJECT macro in its class definition.
The implementation of qt_wrap_cpp() forwards to _qt_internal_wrap_cpp(), which calls target_sources() to add the moc-generated file to the MyQtApp target. However, the custom command that produces that moc-generated file is defined in the src directory scope by a call to add_custom_command(OUTPUT ...) (that's buried in a further call to _qt_internal_set_source_file_generated() near the end of the foreach loop in _qt_internal_wrap_cpp()), but the target is defined in the top level scope. The CMake documentation for add_custom_command(OUTPUT) explicitly states the following:
A target created in the same directory (CMakeLists.txt file) that specifies any output of the custom command as a source file is given a rule to generate the file using the command at build time.
That's perhaps not the best way to draw attention to the real, underlying requirement, which is that a target defined in the same directory scope as the add_custom_command(OUTPUT) call must depend on the output(s) of that command. Otherwise, no build rule is created for generating the output file, and the build will be missing dependencies. The result will be that moc won't be run and the output files won't be produced.
In practice, this means currently you can't call qt_wrap_cpp() from anywhere other than the same CMakeLists.txt file as you call qt_add_executable() or qt_add_library(). The normal pattern for working around this won't work here. If you want to call it in a subdirectory, the usual pattern would be to create an extra custom target to "own" the moc-generated file and then make your real target depend on it. That would look something like this:
qt_wrap_cpp(MyQtApp MyClass.cpp) target_sources(MyQtApp PRIVATE MyClass.cpp) add_custom_target(MyQtApp_gen DEPENDS <what-goes-here?>) add_dependencies(MyQtApp MyQtApp_gen)
As you can see though, the new target form of qt_wrap_cpp() doesn't give us the list of generated files, so we can't make our MyQtApp_gen custom target depend on them.
It's no better with the old form of qt_wrap_cpp() which supposedly should be giving us the list of files:
qt_wrap_cpp(genMocs MyClass.cpp TARGET MyQtApp) target_sources(MyQtApp PRIVATE MyClass.cpp) add_custom_target(MyQtApp_gen DEPENDS ${genMocs}) add_dependencies(MyQtApp MyQtApp_gen)
A couple of observations on this latest example:
- qt_wrap_cpp() already adds the generated files to MyQtApp for us. It didn't do that before Qt 6.8.
- The contents of the genMocs variable will be empty! It only gets populated if we pass at least one header file in our list of files. This aspect was also mentioned in QTBUG-139212.
At the very least, this current restriction should be highlighted in the documentation for qt_wrap_cpp(). I don't recall if the workaround for restoring robust dependencies used to work before Qt 6.8, so I can't comment on whether this is also a regression.
- relates to
-
QTBUG-139212 New target form of qt6_wrap_cpp() doesn't work if given header files
-
- Reported
-
-
QTBUG-130757 Calling find_pacakge(Qt) in a specific directory scope and using the targets in sibling scopes can lead to issues
-
- Open
-