在CMake项目中无法使用Q_OBJECT宏

11

在我的CMake项目中,我正在使用Qt的元对象编译器遇到了问题。我正在构建一个包含以下代码并采用pimpl习语的共享库。在调用CMake并进行编译后,我得到:

  

AUTOGEN:错误:~/tools/Project/gui/src/mainWindow.cpp:该文件包含Q_OBJECT宏,但未包括“mainWindow.moc”! gui/CMakeFiles/gui_automoc.dir/build.make:57:目标‘gui/CMakeFiles/gui_automoc’失败   make [2]:*** [gui/CMakeFiles/gui_automoc] 错误1   CMakeFiles / Makefile2:234:目标‘gui/CMakeFiles/gui_automoc.dir/all’失败

我不知道自己做错了什么或者在项目中如何正确地结合带有Q_OBJECT宏的src文件。请帮忙 = /

gui/include/gui/mainWindow.hpp

#include <QMainWindow>
#include <string>


class MainWindow : public QMainWindow {
  class MainWindowImpl;

 public:
  MainWindow(QWidget* parent = nullptr);

 private:
  MainWindowImpl* pimpl_;
};

gui/src/mainWindow.cpp

#include "gui/mainWindow.hpp"

class MainWindow::MainWindowImpl : public QWidget{
 Q_OBJECT
  public:
   explicit MainWindowImpl(MainWindow *parent);

  private:
   MainWindow &parent_;
};

MainWindow::MainWindowImpl::MainWindowImpl(MainWindow *parent)
    : QWidget{parent}, parent_(*parent) {}

MainWindow::MainWindow(QWidget *parent) : QMainWindow{parent} {
    pimpl_ = new MainWindowImpl{this};
    setCentralWidget(pimpl_);
}

我这样编译库:

cmake_minimum_required(VERSION 3.5.1 FATAL_ERROR)
project(gui)

QT5_WRAP_CPP(MOC_Files
include/gui/mainWindow.hpp
)

add_library(${PROJECT_NAME}
  SHARED
   src/mainWindow.cpp
   ${MOC_Files}
)
add_library(gui::gui ALIAS ${PROJECT_NAME})

target_include_directories(${PROJECT_NAME} 
  PUBLIC 
   ${PROJECT_SOURCE_DIR}/include
)

set_target_properties(${PROJECT_NAME} PROPERTIES AUTOMOC TRUE)

target_link_libraries(${PROJECT_NAME}
  PUBLIC
   Qt5::Widgets
   Qt5::Core
   Qt5::Xml
   Qt5::OpenGL
   Qt5::Gui
)

install(TARGETS ${PROJECT_NAME} DESTINATION lib)
现在我想将这个库链接到我的可执行文件中。 应用程序 / main.cpp
#include <QApplication>
#include "gui/mainWindow.hpp"

int main(int argc, char *argv[]) {

QApplication app{argc, argv};

MainWindow gui{};
gui.show();

return app.exec();
}

使用下面的 CMakelists.txt 文件,在其中链接到 gui 库。

cmake_minimum_required (VERSION 3.5.1 FATAL_ERROR)
project (app)

add_executable(${PROJECT_NAME}
  main.cpp
)

target_include_directories(${PROJECT_NAME}
    PUBLIC ${PROJECT_BINARY_DIR}
)

target_link_libraries(${PROJECT_NAME}
  PRIVATE
   gui::gui
   Qt5::Widgets
   Qt5::Core
   Qt5::Xml
   Qt5::OpenGL
   Qt5::Gui
 )

 install(TARGETS ${PROJECT_NAME}
         DESTINATION bin)

我的项目的顶层CMakeLists文件如下所示

cmake_minimum_required (VERSION 3.5.1 FATAL_ERROR)
project(project)

set(CMAKE_INSTALL_DIR ${PROJECT_SOURCE_DIR}/obj)
set(CMAKE_INSTALL_PREFIX  ${CMAKE_INSTALL_DIR})
# add our local path to the runtime path
SET(CMAKE_INSTALL_RPATH "$ORIGIN:${CMAKE_INSTALL_PREFIX}/lib")
# also add the link paths to the runtime paths
SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)

find_package(Qt5 COMPONENTS Core Widgets Xml OpenGL Gui REQUIRED)

## --> Build libraries and applications  <--
add_subdirectory(gui)
add_subdirectory(apps)

1
你尝试过按照错误信息提示,在源代码gui/include/gui/mainWindow.cpp中添加一行 #include "mainWindow.moc"吗? - Tsyvarev
我执行了以下命令并得到了错误信息:~/tools/Project/gui/src/mainWindow.cpp:74:26: 致命错误: mainWindow.moc: 没有那个文件或目录,编译终止。gui/CMakeFiles/gui.dir/build.make:62: 用于目标 'gui/CMakeFiles/gui.dir/src/mainWindow.cpp.o' 的规则失败。make[2]: *** [gui/CMakeFiles/gui.dir/src/mainWindow.cpp.o] 错误 1。 - CD86
1
你是否尝试过在mainWindow.cpp中按照这里建议的方式使用#include "gui/mainWindow.moc" - Steve Lorimer
我个人发现AUTOMOC有点棘手,最终决定手动调用moc,使用qt5_wrap_cpp(MOC_OUT ${INPUT}),然后将${MOC_OUT}传递给我的库或二进制文件的源列表。你可能想尝试一下这种方法。 - Steve Lorimer
我也试过像上面那样包含它,但没有帮助。 - CD86
@Tsyvarev,您能否澄清为什么需要这样做? - Hareen Laks
1个回答

15

使用CMake编译Qt应用程序时常常会遇到的一个问题是混淆。通常有两种方法在CMake中运行moc预处理器:

1. 使用CMake的AUTOMOC属性方法。

这种方法非常简单易用,但是在文档中提到了一些要求。

  • 确保目标启用了AUTOMOC属性。

set_target_properties(${PROJECT_NAME} PROPERTIES AUTOMOC TRUE)
如果你的.cpp文件包含Q_OBJECT宏,那么你需要在最后一个QObject类之后(最好在文件末尾)包含生成的.moc文件。为此步骤,您还需要启用CMAKE_INCLUDE_CURRENT_DIR,但这是任何CMake + Qt构建的通用建议。

  • 如果你的头文件包含Q_OBJECT,请确保CMake知道它。最简单的方法是将其与源文件一起传递:

  • add_library(${PROJECT_NAME}
      SHARED
       include/mainWindow.hpp
       src/mainWindow.cpp
    )
    
    最后,链接所有所需的Qt库。
    target_link_libraries(${PROJECT_NAME}
      PUBLIC
       Qt5::Widgets
       Qt5::Core
    )
    

    因此,按照CMake的方式修复您的代码:

    gui/src/mainWindow.cpp:

    #include "gui/mainWindow.hpp"
    
    class MainWindow::MainWindowImpl : public QWidget{
     Q_OBJECT
      public:
       explicit MainWindowImpl(MainWindow *parent);
    
      private:
       MainWindow &parent_;
    };
    
    MainWindow::MainWindowImpl::MainWindowImpl(MainWindow *parent)
        : QWidget{parent}, parent_(*parent) {}
    
    MainWindow::MainWindow(QWidget *parent) : QMainWindow{parent} {
        pimpl_ = new MainWindowImpl{this};
        setCentralWidget(pimpl_);
    }
    
    #include "mainWindow.moc"
    

    gui/CMakeLists.txt:

    project(gui)
    
    set(CMAKE_INCLUDE_CURRENT_DIR YES)
    
    add_library(${PROJECT_NAME}
      SHARED
      include/gui/mainWindow.hpp
      src/mainWindow.cpp
    )
    add_library(gui::gui ALIAS ${PROJECT_NAME})
    
    target_include_directories(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/include)
    
    set_target_properties(${PROJECT_NAME} PROPERTIES AUTOMOC TRUE)
    
    target_link_libraries(${PROJECT_NAME}
      PUBLIC
       Qt5::Widgets
       Qt5::Core
    )
    

    2. 使用QT5_WRAP_CPP的Qt方法

    在这里,您只需要“包装”所有具有Q_OBJECT的头文件,并将结果添加到源文件列表中。

    如果您的 cpp 文件中有一个类,则会变得棘手。 Q_OBJECT 宏向该类添加成员函数。在类外实现任何成员函数的实现均需要知道类的声明。这些实现位于生成的 .moc 文件中,但无法看到类的声明。修复它的最简单方法是将您的 .cpp 文件拆分为两个文件:

    gui/src/mainWindowImpl.hpp:

    #pragma once
    #include "gui/mainWindow.hpp"
    
    class MainWindow::MainWindowImpl : public QWidget{
     Q_OBJECT
      public:
       explicit MainWindowImpl(MainWindow *parent);
    
      private:
       MainWindow &parent_;
    };
    

    gui/src/mainWindow.cpp:

    #include "gui/mainWindow.hpp"
    #include "mainWindowImpl.hpp"
    
    MainWindow::MainWindowImpl::MainWindowImpl(MainWindow *parent)
        : QWidget{parent}, parent_(*parent) {}
    
    MainWindow::MainWindow(QWidget *parent) : QMainWindow{parent} {
        pimpl_ = new MainWindowImpl{this};
        setCentralWidget(pimpl_);
    }
    

    QT5_WRAP_CPP中包含附加的头文件:

    gui/CMakeLists.txt:

    project(gui)
    
    QT5_WRAP_CPP(MOC_Files
      include/mainWindow.hpp
      src/mainWindowImpl.hpp
    )
    
    add_library(${PROJECT_NAME}
      SHARED
       src/mainWindow.cpp
       ${MOC_Files}
    )
    add_library(gui::gui ALIAS ${PROJECT_NAME})
    
    target_include_directories(${PROJECT_NAME} 
      PUBLIC 
       ${PROJECT_SOURCE_DIR}/include
    )
    
    target_link_libraries(${PROJECT_NAME}
      PUBLIC
       Qt5::Widgets
       Qt5::Core
    )
    

    注意!在使用 moc 和使用复杂语法的类时要小心,因为存在限制


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接