16

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.

4

3 回答 3

16

如前所述, mocMainWindow.h在您的示例中未处理。强制发生这种情况的一种方法是qt_wrap_cpp()直接调用它(而不是 on MainWindow.cpp),然后将生成的文件包含在对add_executable().

您的顶级 CMakeLists.txt 可能如下所示:

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)

和你的 src 一级像:

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)

附录:

  • 这适用于您的示例,无论是否启用 AUTOMOC。我不确定是否会在将来引起问题。如果你不启用它,你将不得不手动 moc 任何其他东西......尽管它可能都表现得像 MainWindow 在这种情况下你将手动 moc 标题无论如何。
  • 您不必在顶级 CMakeLists.txt 中设置目录变量,但我发现它比这样做更干净qt5_wrap_cpp(hello-world_SRC ../inc/MainWindow.h)
  • 可能有更好的方法来做到这一点。
  • 对于其他有类似问题的人,到目前为止,这个解决方案在我最初遇到的更大的项目中得到了支持。如果它失败了,我会相应地更新。
于 2013-11-07T23:19:48.123 回答
13

我遇到了同样的问题并找到了解决方案。正如 Eric Lemanissier在 GitHub 上的一个问题中评论的那样:

此错误与柯南无关:您需要在add_executable中添加您的头文件,否则moc不会解析它们

add_executable必须使用oradd_library语句将头文件添加到项目中。如果不这样做,automoc 将不会解析文件。

于 2019-03-26T17:41:16.323 回答
3

好吧,也许automoc对你不起作用,我猜这是因为 CMake 没有找到相应的文件。在此处查看文档: http ://www.cmake.org/cmake/help/v2.8.12/cmake.html#prop_tgt:AUTOMOC

在这种情况下,您始终可以在以下位置手动为它们调用 moc 命令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})

注意:您必须确保自己正确使用 list 命令。此代码示例来自我的项目,其中我使用了特定的源列表 ( library_sources)。

这只是一个猜测,但您应该先尝试不使用自动魔法来排除一个可能的错误来源。

还要确保在更改目录结构后完全删除了 CMake 缓存。

于 2013-11-04T05:06:07.873 回答