Не удается использовать макрос Q_OBJECT в CMake Project

У меня проблемы с мета-объектным компилятором Qt в моем проекте CMake. Разделяемая библиотека, которую я создаю, содержит следующий код и использует идиому 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' не выполнена

Я не понимаю, что я делаю неправильно или как правильно включить файлы src с макросом Q_OBJECT в мой проект. Пожалуйста, помогите =/

GUI / включать / GUI / mainWindow.hpp

#include <QMainWindow>
#include <string>


class MainWindow : public QMainWindow {
  class MainWindowImpl;

 public:
  MainWindow(QWidget* parent = nullptr);

 private:
  MainWindowImpl* pimpl_;
};

гуй / 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, где я ссылаюсь на графический интерфейс

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)

мой CMakeList верхнего уровня проекта выглядит следующим образом

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 ответ

Решение

Это типичная путаница по поводу компиляции приложения Qt с CMake. В основном есть два подхода к запуску moc препроцессор с CMake:

1. Подход CMake с AUTOMOC имущество.

Он очень прост в использовании, но имеет несколько требований, которые упомянуты в документации.

  • Убедитесь, что собственность AUTOMOC включен для цели.

    set_target_properties(${PROJECT_NAME} PROPERTIES AUTOMOC TRUE)
    
  • Если твой .cpp файл содержит Q_OBJECT макрос, то вам нужно включить сгенерированный .moc файл после последнего класса qobject (лучше в конце файла). Для этого шага также необходимо включить 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:

гуй / 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"

гуй /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. Qt подход с QT5_WRAP_CPP

Здесь вам просто нужно "обернуть" все ваши файлы заголовков, которые имеют Q_OBJECT в них и добавьте результат в список исходных файлов.

Или, если у вас есть класс в файле cpp, это становится сложно. Q_OBJECT макрос добавляет функции-члены в класс. Реализация любой функции-члена класса вне тела класса должна знать объявление класса. Эти реализации находятся внутри сгенерированного .moc файл, но они не могут видеть объявление класса. Самый простой способ исправить это было бы разделить ваш .cpp файл на две части:

гуй / SRC /mainWindowImpl.hpp:

#pragma once
#include "gui/mainWindow.hpp"

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

  private:
   MainWindow &parent_;
};

гуй / 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:

гуй /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 и классы, которые используют сложный синтаксис, поскольку существуют ограничения.

Другие вопросы по тегам