18

Update 2

After messing around a bit (and some editing of the generated Makefiles), it looks like what is happening is that moc is not properly processing MainWindow.h (included by main.cpp and MainWindow.cpp unless it is in the same folder as the source files which included it.

Moc runs on MainWindow.cpp, doesn't process the include and thus doesn't see the Q_OBJECT macro so proceeds to produce an empty output file. I'm not sure whether moc usually processes includes or if it just scans the directory, but either way, headers that need mocing but are in other directories are not being processed!

Update

The problem appears to be related to the output produced by moc. In the first case (the one that compiles), hello-world_automoc.cpp and moc_MainWindow.cpp are generated. hello-world_automoc.cpp looks like

/* This file is autogenerated, do not edit*/
#include "moc_MainWindow.cpp"

In the second case, a hello-world_automoc.cpp is produced that looks like

/* This file is autogenerated, do not edit*/
enum some_compilers { need_more_than_nothing };

and there is no moc_MainWindow.cpp at all. If I manually call moc in from cmake instead of using automoc in the broken case, I do get moc_MainWindow.cpp but it is empty.

Original Status

Firstly, no, I haven't forgotten to set(CMAKE_AUTOMOC ON). Also note that MainWindow's destructor is declared and implemented.

When my directory structure looks like:

CMakeLists.txt
|__ main.cpp
|__ MainWindow.cpp
|__ MainWindow.h
|__ MainWindow.ui

compiling works just fine.

However, when it looks like:

helloworld/
|__ CMakeLists.txt
|__ src/
|   |__ CMakeLists.txt
|   |__ main.cpp
|   |__ MainWindow.cpp
|
|__ inc/
|   |__ MainWindow.h
|
|__ gui/
    |__ MainWindow.ui

I get linking errors:

Linking CXX executable hello-world
CMakeFiles/hello-world.dir/MainWindow.cpp.o: In function `MainWindow::MainWindow()':
MainWindow.cpp:(.text+0x3b): undefined reference to `vtable for MainWindow'
MainWindow.cpp:(.text+0x4d): undefined reference to `vtable for MainWindow'
CMakeFiles/hello-world.dir/MainWindow.cpp.o: In function `MainWindow::~MainWindow()':
MainWindow.cpp:(.text+0xaf): undefined reference to `vtable for MainWindow'
MainWindow.cpp:(.text+0xc1): undefined reference to `vtable for MainWindow'
collect2: error: ld returned 1 exit status
make[2]: *** [src/hello-world] Error 1
make[1]: *** [src/CMakeFiles/hello-world.dir/all] Error 2

I'd really like to have my sources and headers in proper subdirectories, but I'm not quite sure how to fix this.

This is actually the simplest identifiable case of an error from a much larger project, so I'm really not all that keen to flatten the project directories just because I'm adding a Qt GUI to it.

Community
  • 1
  • 1
Iskar Jarak
  • 5,136
  • 4
  • 38
  • 60

3 Answers3

16

As noted, moc is not processing MainWindow.h in your example. One way to force this to happen is to call qt_wrap_cpp() on it directly (instead of on MainWindow.cpp) and then include the resulting file in the call to add_executable().

Your top level CMakeLists.txt might look like:

cmake_minimum_required(VERSION 2.8.9)

#set(CMAKE_AUTOMOC ON)

set(CMAKE_PREFIX_PATH "/opt/Qt/5.1.1/gcc_64")
set(CMAKE_INCLUDE_CURRENT_DIR ON)

project(hello-world)

find_package(Qt5Widgets REQUIRED)

set(HW_HEADER_DIR ${CMAKE_CURRENT_SOURCE_DIR}/inc)
set(HW_GUI_DIR ${CMAKE_CURRENT_SOURCE_DIR}/gui)

include_directories(${HW_HEADER_DIR})

subdirs(src)

and your src level one like:

qt5_wrap_cpp(hello-world_SRC ${HW_HEADER_DIR}/MainWindow.h)
qt5_wrap_ui(hello-world_UI ${HW_GUI_DIR}/MainWindow.ui)

add_executable(hello-world MainWindow.cpp main.cpp
               ${hello-world_UI} ${hello-world_SRC})
qt5_use_modules(hello-world Widgets)

Addendum:

  • This works in your example with and without AUTOMOC enabled. I don't know for sure if having it on will cause issues in the future. If you don't enable it you'll have to manually moc any other stuff... although it might all behave like MainWindow in which case you'll be manually mocing the headers regardless.
  • You don't have to set the directory variables in the top level CMakeLists.txt but I find it cleaner than doing qt5_wrap_cpp(hello-world_SRC ../inc/MainWindow.h)
  • There might be a better way of doing this.
  • For anybody else with similar issues, so far this solution has held up in the larger project that I initially encountered this in. I will update accordingly if it fails.
Iskar Jarak
  • 5,136
  • 4
  • 38
  • 60
15

I had the same problem and found a solution. As Eric Lemanissier commented in an issue on GitHub:

This error is not related to conan: you need to add your header files in add_executable, otherwise the moc won't parse them

The header files have to be added to the project using an add_executable or add_library statement. If this is not done, automoc won't parse the files.

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
Christian
  • 151
  • 1
  • 2
3

Well, maybe automoc does not work for you, I would guess it's because CMake does not find the corresponding files. Check the documentation here: http://www.cmake.org/cmake/help/v2.8.12/cmake.html#prop_tgt:AUTOMOC

In this case, you can always call the moc command manually for them in your CMakeLists.txt:

qt5_wrap_cpp(moc_sources src/MainWindow.cpp)
qt5_wrap_ui(uic_sources src/MainWindow.cpp)

list(APPEND library_sources ${moc_sources} ${uic_sources})

Note: you have to make sure you use the list command correctly yourself. This code example is from my project where I use a specific list of sources (library_sources).

It is just a guess but you should try without the automagic first to rule out one possible source of error.

Also make sure that you fully deleted the CMake cache after changing your directory structure.

ypnos
  • 50,202
  • 14
  • 95
  • 141
  • 1) I definitely fully deleted the CMake cache. 2) Surely neither version would compile if automoc was not working for me? I'm also fairly certain I can see automoc running in the Make output and the build tree contains the generated files. I will try running moc manually and get back to you, though. – Iskar Jarak Nov 04 '13 at 05:41
  • Just confirming I get the same error when manually calling moc. – Iskar Jarak Nov 06 '13 at 00:01
  • To be a little clearer, I get the same error when manually calling moc _only_ when the subdirectory structure is used - manually calling moc works fine in the flat directory case where automoc works fine too. – Iskar Jarak Nov 06 '13 at 00:17
  • This is weird. I use subdirectories as well but in my case headers are always in the same place. I would file a bug report with the Qt project. Maybe try this simple example with qmake first to see if this is even related to CMake. – ypnos Nov 06 '13 at 01:18
  • 1
    I have news. I belatedly thought to look at the moc files being generated. In the case where things break and I use automoc, `hello-world_automoc.cpp` contains only an enum (`enum some_compilers { need_more_than_nothing };`) and nothing else, and `moc_MainWindow.cpp` is not generated. **When I manually run moc, I get an empty `moc_MainWindow.cpp`**. In the case where it does work, `hello-world_automoc.cpp` contains `#include "moc_MainWindow.cpp"` and `moc_MainWindow.cpp` contains the proper generated methods. – Iskar Jarak Nov 06 '13 at 02:50
  • Please see my new update. If this is an issue, I think it's more likely a moc issue than a cmake one. – Iskar Jarak Nov 06 '13 at 23:11
  • How do you include the header file? Do you use a relative path? I would expect you do .. Maybe playing around with this helps finding the issue. – ypnos Nov 07 '13 at 05:06
  • Correct, I do indeed do plain old `#include "MainWindow.h"`. I'll give playing with the include path a go but I consider things like `#include "foodir/MainWindow.h"` to be rather messy and worth avoiding where possible. – Iskar Jarak Nov 07 '13 at 09:58
  • Well, if it is not in the same directory but in the search path you should use brackets. Just nitpicking, but who knows how Moc thinks about this.. – ypnos Nov 07 '13 at 12:46
  • Further nitpick: the standard requires that if an `#include "x"` search fails then it falls back to an `#include ` search (6.10.2) so I don't really agree with _should_ when all my headers are totally separate from my source files, but yeah, who knows how moc handles this, so I'll give it a whirl. – Iskar Jarak Nov 07 '13 at 20:49
  • Update: twiddling with how the includes are done makes no difference. – Iskar Jarak Nov 07 '13 at 22:56
  • Well I guess it was worth a try. And yes, I know the standard ;-). – ypnos Nov 07 '13 at 23:43