I have a project which compiles fine when I build it with a .pro
file with QtCreator. I am trying to convert it to cmake
, but have run into an issue.
EDITED WITH MINIMAL REPRODUCIBLE EXAMPLE:
Project folder structure:
src/foo.cpp --> #include "ui_foo.h"
src/bar.cpp --> #include "bar.moc"
inc/foo.h
ui/foo.ui
Expected compilation flow (observed flow when using qmake
):
(1) moc inc/foo.h -o moc_foo.cpp
(2) moc src/bar.cpp -o bar.moc
(3) uic ui/foo.ui -o ui_foo.h
(4) gcc src/foo.cpp -o foo.o
(5) gcc src/bar.cpp -o bar.o
(6) gcc moc_foo.cpp -o moc_foo.o
(7) ld foo.o bar.o moc_foo.o -o foobar
CMakeLists.txt(1):
set(CMAKE_INCLUDE_CURRENT_DIR ON )
set(CMAKE_AUTOMOC ON )
set(CMAKE_AUTOUIC ON )
add_executable(foobar src/foo.cpp src/bar.cpp)
moc_foo.cpp
is not created (step 1 & 6 are missed) and doesn't get added to add_executable
in step 7. Undefined references to vtable occur because of missing object. I think this is because foo.cpp and foo.h are in different folders.
CMakeLists.txt(2):
set(CMAKE_INCLUDE_CURRENT_DIR ON )
qt5_wrap_cpp(foobar_moc inc/foo.h src/bar.cpp)
qt5_wrap_ui (foobar_ui ui/foo.ui)
add_executable(foobar src/foo.cpp src/bar.cpp ${foobar_moc} ${foobar_ui})
moc_bar.cpp
is generated instead of bar.moc
in step 2. I get a compiler error because bar.moc
cannot be found in step 5.
You're making your own life unnecessary hard by trying to moc a .cpp file.
Despite the name
qt5_wrap_cpp
actually takes header files as its argument and invokes the moc to generate matching cpp files:Note that typically you will have a
foo.cpp
source file lying around somewhere as well, which together with the.cpp
generated by the moc provides the full implementation for the class defined infoo.h
.Now, if you really want to invoke the moc on a
.cpp
file, the procedure is slightly different. Here, since the class definition is no longer in a header file, the source file generated by the moc cannot access the class definition it refers to. As a result, the generated source file can no longer be compiled on its own. Instead, you need to pull it into the original source file after the class definition using#include
, which is not very nice.What's worse is that for CMake to invoke the moc command, the generated source file still needs to be part of the dependency chain somewhere, which makes everything quite ugly.
We will use the
qt5_generate_moc
command to invoke the moc onbar.cpp
(asqt5_wrap_cpp
would also add the generated source to the build, which would break):Note that we place the generated file in the binary tree to not pollute the source tree with generated files. If you have set an
AUTOGEN_BUILD_DIR
, you might want to put the generated file there instead. In order to allow the#include
to find the generated file, we add the binary tree to the include directories for our project:And finally, we need a custom target for the build step dependency. Without this, the moc command would never actually get invoked:
Bottom line: Just move all your class definitions to header files and then moc those. There's no disadvantage and you will save yourself lots of ugly boilerplate.