44

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.

O'Neil
  • 3,790
  • 4
  • 16
  • 30
user3877872
  • 809
  • 2
  • 9
  • 16

3 Answers3

70

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 a 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})

Recommendations:

  1. Also setting version of CMake would be appropriate. If you use CMAKE_AUTOMOC you need a version not less than 2.8.6, and if you use CMAKE_AUTOUIC you need a version not less than 3.0.2:

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

  3. When you use CMAKE_AUTOMOC usage CMAKE_AUTOUIC instead of qt5_wrap_ui will be more appropriate.

  4. This script is correct for the 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 to meet with Qt through CMake. They complain of an 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 a directive (and is mandatory). So I'd like to replace the name of the project from "Cmake" to "MyProject". This reduces 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 CMAKE_AUTOMOC; it's good for a simple project with plain structure. I've had problems with a case when my include files were stored into a 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 a suggestion based on my personal opinion, but I'd like to propose more explicit version of the script without usage of CMAKE_AUTOMOC and CMAKE_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)

The complete version of the projects source code is available at GitLab.

Gluttton
  • 5,739
  • 3
  • 31
  • 58
  • Yes, it worked out, I exactly used the above script, finding out the differences I would like to ask what does include_directories (${PROJECT_BINARY_DIR}) mean? What does it do? And I removed add_library too. – user3877872 Sep 23 '14 at 08:42
  • `include_directories (${PROJECT_BINARY_DIR})` says to `CMake` find include files in the build directory. All generated files are placed into build directory. – Gluttton Sep 23 '14 at 08:43
  • @user3877872, I have found out the source of problems, look my post please. – Gluttton Sep 23 '14 at 08:53
  • @Gluttton: legend! This is the kind of response that really accelerates progress with CMake & Qt :-) – ssc Jun 20 '15 at 06:43
  • qt5_use_modules is deprecated. For more information see this related question: http://stackoverflow.com/questions/31172156/what-to-use-instead-of-qt5-use-modules – Erik Sjölund Sep 15 '15 at 10:34
  • @ErikSjölund , Thanks! I've updated my answer (frankly, I was very surprised). – Gluttton Sep 16 '15 at 10:34
  • 1
    @Gluttton automoc is nice for small project, but we had to gut it from everywhere because it grinds the speed to a halt when project size grows. Better to explicitly call qt5_wrap_cpp – peter karasev Sep 17 '16 at 06:57
  • 1
    @peterkarasev, Thanks for sharing your experience! – Gluttton Sep 17 '16 at 14:44
0

You didn't show us where the CMakeLists.txt is in the directory structure. If it's at top-level then you would have

add_executable(Cmake sources/main.cpp sources/mainwindow.cpp)

and you would need

include_directories(${CMAKE_CURRENT_SOURCE_DIR}/headers)

Anyway, find out where the ui_*.h file is being generated, and add the directory with include_directories.

steveire
  • 10,694
  • 1
  • 37
  • 48
0

At the time of writing this, this is the recommend Qt cmake usage for Qt applications. Just a few gotchas:

  1. Use automoc. This reduces the maintenance overhead significantly. It is also fast enough even for large projects so that you do not need to care about build-time slow-down.

  2. Use versionless cmake targets so that they are compatible across Qt versions.

  3. You do not need to create a library for the mainwindow since it is part of the application.

  4. If you can, take advantage of the QT_DISABLE_DEPRECATED_BEFORE variable.

  5. As a bonus, enable some usual warning, error and sanitizer detections if you aim for high quality.

    project(Application VERSION 1.0.0 LANGUAGES CXX)                                   
    
    set(CMAKE_CXX_STANDARD 23)                                                      
    set(CMAKE_CXX_STANDARD_REQUIRED ON)                                             
    
    set(CMAKE_AUTOMOC ON)                                                           
    set(CMAKE_AUTORCC ON)                                                           
    set(CMAKE_AUTOUIC ON)                                                           
    
    find_package(Qt6 COMPONENTS Widgets)                  
    if (NOT Qt6_FOUND)                                                              
      find_package(Qt5 5.15 REQUIRED COMPONENTS Widgets)                
    endif()
    
    if (CMAKE_CXX_COMPILER_ID MATCHES "(Clang|GNU)")                              
      add_compile_options(-Wall -Wpedantic -Wextra -Werror)                       
      add_compile_options(-fsanitize=address -fsanitize=undefined -fno-omit-frame-pointer)
      add_link_options(-fsanitize=address -fsanitize=undefined -fno-omit-frame-pointer)
    elseif (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")                                 
      set_property(DIRECTORY APPEND PROPERTY COMPILE_OPTIONS "/w")                
    endif()                                                                       
    
    add_compile_definitions(QT_DISABLE_DEPRECATED_BEFORE=0xFFFFFF)
    
    add_executable(Application                                                                                                                     
      mainwindow.cpp                                                                
      main.cpp                                                                                                                              
    )                                                                               
    
    target_link_libraries(Application Qt::Widgets)
    
    
László Papp
  • 51,870
  • 39
  • 111
  • 135