Converting Qt project with a .pro file to project using CMake - Problems with moc

1.8k views Asked by At

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.

2

There are 2 answers

1
ComicSansMS On BEST ANSWER

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:

qt5_wrap_cpp(foobar_moc inc/foo.h)

# ${foobar_moc} will contain the name 
# of the .cpp file created for foo.h
# which we pass to add_executable()

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 in foo.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 on bar.cpp (as qt5_wrap_cpp would also add the generated source to the build, which would break):

qt5_generate_moc(src/bar.cpp ${CMAKE_BINARY_DIR}/bar.moc)

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:

target_include_directories(foobar PUBLIC ${CMAKE_BINARY_DIR})

And finally, we need a custom target for the build step dependency. Without this, the moc command would never actually get invoked:

add_custom_target(moc_dummy DEPENDS ${CMAKE_BINARY_DIR}/bar.moc)
add_dependencies(foobar moc_dummy)

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.

2
cantSleepNow On

The easiest thing to do would be to add AUTOMOC property, and let CMake handle QT mocs automatically

set_target_properties(MyProj PROPERTIES AUTOMOC TRUE)