user3877872 user3877872 - 15 days ago 5
C++ Question

Implementing Qt project through CMake

I am trying to build and run very simple and basic example of Qt through Cmake, removing the .pro file.
The following is the code for Qt project(the directory structure for the Qt project automatically generated is

Cmake (my project name)
├── headers
│   └── mainwindow.h
├── sources
│   ├── main.cpp
│   └── mainwindow.cpp
└── forms
└── mainwindow.ui


mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
Q_OBJECT

public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();

private:
Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H


mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}

MainWindow::~MainWindow()
{
delete ui;
}


main.cpp

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();

return a.exec();
}


This is my CmakeLists.txt

project(Cmake)

find_package(Qt5Widgets)

set(CMAKE_AUTOMOC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)

QT5_WRAP_CPP(Cmake_hdr_moc mainwindow.h)
QT5_WRAP_UI(Cmake_form_hdr mainwindow.ui)

add_library(mainwindow ${Cmake_hdr_moc} ${Cmake_form_hdr})
qt5_use_modules(mainwindow Widgets)

add_executable(Cmake main.cpp mainwindow)
qt5_use_modules(Cmake Core Gui Widgets)


mainwindow.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralWidget"/>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>29</height>
</rect>
</property>
</widget>
<widget class="QToolBar" name="mainToolBar">
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
</widget>
<widget class="QStatusBar" name="statusBar"/>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
</ui>


When I build the project and run Cmake, it points to file mainwindow.h indicating 'ui_mainwindow.h' no such file or directory.

Answer

Your script has several errors, also a few things can be improved. After changes it will be looks like:

cmake_minimum_required(VERSION 3.0.2)
project(MyProject)

find_package(Qt5Widgets)

set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)

add_library(mainwindow mainwindow.cpp)
target_link_libraries (mainwindow Qt5::Widgets)

add_executable(MyProject main.cpp)
target_link_libraries (MyProject mainwindow)

Errors:

  1. Wrong add_executable directive. You try to add library, but for this purpose you need to use target_link_libraries. So instead of:

    add_executable(Cmake main.cpp mainwindow)
    

    You need:

    add_executable(Cmake main.cpp)
    target_link_libraries (Cmake mainwindow)
    
  2. And one more mistake is missing *.cpp files in the add_library directive:

    add_library(mainwindow mainwindow.cpp ${Cmake_hdr_moc} ${Cmake_form_hdr})

Recomendations:

  1. Also setting version of CMake would be appropriate. If you use automoc you need version not less that 2.8.6, and if you use autouic you need version not less that 3.0.2:

    cmake_minimum_required(VERSION 3.0.2)
    
  2. Using qt5_wrap_cpp with automock isn't necessary.

  3. When you use automock usage autouic instead of qt5_wrap_ui will be more appropriate.

  4. This script is correct for project with the following structure in the file system:

    Project
    ├── CMakeLists.txt
    ├── main.cpp
    ├── mainwindow.cpp
    ├── mainwindow.h
    └── mainwindow.ui
    

    If you have another structure you should use include_directories as was mentioned by @steveire.

  5. (UPD) Since, I've written this answer, I suggested it several times for beginners who try meet with Qt through CMake. They complain of inappropriate name of the project - "Cmake". For beginners who just meet with CMake is difficult to realize where cmake - is just part of the project name (and isn't mandatory) and where cmake is part of directive (and is mandatory). So I'd like to replace name of the project from "Cmake" to "MyProject". This is reduce connection between question and answer, but on the other hand this makes the answer more friendly for beginners.

  6. (UPD) As was mentioned by @Erik Sjölund qt5_use_modules is obsolete and target_link_libraries should be used instead.

Note: Personally I have had unsuccessful experience with automock; it's good for simple project with plain structure. I've had problems with case when my include files was stored into separate directory:

.
├── include
│   └── QtClass.h
└── src
    └── QtClass.cpp

And when files with the same name were into different subdirectories:

.
├── NamespaceA
│   ├── QtClass.cpp
│   └── QtClass.h
└── NamespaceB
    ├── QtClass.cpp
    └── QtClass.h

Finally: This is suggestion based on my personal opinion, but I'd like propose more explicit version of script without usage of automock and autouic, it's more verbose but in other hand you have more control:

cmake_minimum_required (VERSION 2.8.12)
project (MyProject)

find_package (Qt5Widgets)

set (MyProjectLib_src ${PROJECT_SOURCE_DIR}/mainwindow.cpp)
set (MyProjectLib_hdr ${PROJECT_SOURCE_DIR}/mainwindow.h)
set (MyProjectLib_ui  ${PROJECT_SOURCE_DIR}/mainwindow.ui)
set (MyProjectBin_src ${PROJECT_SOURCE_DIR}/main.cpp)

qt5_wrap_cpp(MyProjectLib_hdr_moc ${MyProjectLib_hdr})
qt5_wrap_ui (MyProjectLib_ui_moc  ${MyProjectLib_ui})

include_directories (${PROJECT_SOURCE_DIR})
include_directories (${PROJECT_BINARY_DIR})

add_library (MyProjectLib SHARED 
    ${MyProjectLib_src}
    ${MyProjectLib_hdr_moc}
    ${MyProjectLib_ui_moc}
)
target_link_libraries (MyProjectLib Qt5::Widgets)

add_executable(MyProject ${MyProjectBin_src})
target_link_libraries (MyProject MyProjectLib)

Complete version of the projects source code is available at GitLab.