-1

I'm new to Qt and I'm trying to do a simple VoIP application. I use CMake and conan to get all packages and build the application. If I place all Qt related class headers and sources files on the same directory, I can compile without any problem, but when I move the header files to another directory I found linker problems. I don't know why because in CMakeLists.txt I declare the include directories (and is working fine for others classes not related to Qt), I feel it's something related to autoMOC.

Project structure

|--include
|     |
|     |--client
|     |    |
|     |    |--views
|     |    |    |
|     |    |    |--loginwindow.ui
|     |    |    |--ui_loginwindow.h
|     |    |    |--mainwindow.ui
|     |    |    |--ui_mainwindow.h
|     |    |--Client.h
|     |    |--loginWindow.h
|     |    |--mainWindow.h
|     |    |--Profile.h
|     |--common
|     |
|     |--server
|     |
|--src
|   |
|   |--client
|   |   |
|   |   |--Controller
|   |   |--Model
|   |   |--View
|   |   |    |
|   |   |    |--loginWindow.cpp
|   |   |    |--mainWindow.cpp
|   |--common
|   |
|   |--server
|   |

I've ommited some directories content because the problem is not found there.

CMakeLists.txt

if (${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR})
    message(FATAL_ERROR "Prevented in-tree built. Please create a build directory outside of the source code and call cmake from there")
endif ()

list(APPEND CMAKE_MODULE_PATH ${CMAKE_BINARY_DIR})
list(APPEND CMAKE_PREFIX_PATH ${CMAKE_BINARY_DIR})

project(babel)
cmake_minimum_required(VERSION 3.17.4)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_definitions("-fPIC")

if(MSVC)
    set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}")
else()
    set(STANDARD_UNIX_CXX_FLAGS "-Wall -g3 -Wextra -Wfatal-errors")
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${STANDARD_UNIX_CXX_FLAGS}")
endif()

if (EXISTS ${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
    include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
else()
    message(FATAL_ERROR "No conanbuildinfo.cmake file found")
endif()

conan_basic_setup(KEEP_RPATHS)

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

find_package(Qt5OpenGL CONFIG REQUIRED)
find_package(Qt5Widgets CONFIG REQUIRED)
find_package(Qt5Core CONFIG REQUIRED)
find_package(Qt5Gui CONFIG REQUIRED)
find_package(Qt5Network CONFIG REQUIRED)
find_package(portaudio REQUIRED)
find_package(Opus REQUIRED)
find_package(asio REQUIRED)

file(
        GLOB_RECURSE
        SOURCES_CLIENT
        ${PROJECT_SOURCE_DIR}/src/*.cpp
        ${PROJECT_SOURCE_DIR}/src/client/*.cpp
        ${PROJECT_SOURCE_DIR}/include/client/*.hpp
        ${PROJECT_SOURCE_DIR}/include/client/*.ui
        ${PROJECT_SOURCE_DIR}/resources.qrc
)
file(
        GLOB_RECURSE
        SOURCES_SERVER
        ${PROJECT_SOURCE_DIR}/src/server/*.cpp
)
file(
        GLOB_RECURSE
        SOURCES_COMMON
        ${PROJECT_SOURCE_DIR}/src/common/*.cpp
        ${PROJECT_SOURCE_DIR}/include/common/*.hpp
)
add_executable(babel_client ${SOURCES_CLIENT} ${SOURCES_COMMON})
install(TARGETS babel_client DESTINATION ${PROJECT_SOURCE_DIR}/bin)
target_link_libraries(
        babel_client
        Qt5::Widgets
        Qt5::Network
        Qt5::OpenGL
        Qt5::Core
        Qt5::Gui
        opus
        portaudio
)
target_include_directories(
        babel_client PRIVATE
        ${CONAN_INCLUDE_LIBS}
        ${PROJECT_SOURCE_DIR}/include/client
        ${PROJECT_SOURCE_DIR}/include/client/views
        ${PROJECT_SOURCE_DIR}/include/common
)

loginWindow.h


#ifndef LOGINWINDOW_H
#define LOGINWINDOW_H

#include <QMainWindow>
#include "mainWindow.h"
#include "views/ui_loginwindow.h"
#include <QKeyEvent>

QT_BEGIN_NAMESPACE
namespace Ui { class LoginWindow; }
QT_END_NAMESPACE

class LoginWindow : public QMainWindow
{
    Q_OBJECT

public:
    LoginWindow(QWidget *parent = nullptr);
    ~LoginWindow();

private slots:
    void on_loginButton_clicked();

protected:
    void keyPressEvent(QKeyEvent *key);

private:
    Ui::LoginWindow *ui;
    MainWindow *mainWindow;
};
#endif // LOGINWINDOW_H

loginWindow.cpp


#include "loginWindow.h"
#include <QPixmap>

LoginWindow::LoginWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::LoginWindow)
{
    int picWidth;
    int picHeight;

    ui->setupUi(this);
    QPixmap pix(":/resources/babel.png");
    picWidth = ui->babelPicture->width();
    picHeight = ui->babelPicture->height();
    ui->babelPicture->setPixmap(pix.scaled(picWidth, picHeight, Qt::KeepAspectRatio));
    ui->statusbar->addPermanentWidget(ui->statusText);
    ui->loginButton->setDefault(true);
}

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

void LoginWindow::keyPressEvent(QKeyEvent *key)
{
    if (key->key() == Qt::Key_Return)
        on_loginButton_clicked();
}

void LoginWindow::on_loginButton_clicked()
{
    QString user = ui->userField->text();
    QString pass = ui->passwordField->text();

    if (user == "test" && pass == "test") {
        ui->statusText->setText("Correct login");
        this->hide();
        mainWindow = new MainWindow(this);
        mainWindow->show();
    } else{
        ui->statusText->setText("Inorrect login");
    }

}

mainWindow.h


#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "views/ui_mainwindow.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

private:
    Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H

mainWindow.cpp


#include "mainWindow.h"

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

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

Linker output

/usr/bin/ld: CMakeFiles/babel_client.dir/src/client/View/loginWindow.cpp.o: in function `LoginWindow::~LoginWindow()':
/home/bltksk/epitech/tek3/CPP/babel/src/client/View/loginWindow.cpp:20: undefined reference to `vtable for LoginWindow'
/usr/bin/ld: CMakeFiles/babel_client.dir/src/client/View/loginWindow.cpp.o: in function `non-virtual thunk to LoginWindow::~LoginWindow()':
/home/bltksk/epitech/tek3/CPP/babel/src/client/View/loginWindow.cpp:23: undefined reference to `vtable for LoginWindow'
/usr/bin/ld: CMakeFiles/babel_client.dir/src/client/View/loginWindow.cpp.o: in function `LoginWindow::LoginWindow(QWidget*)':
/home/bltksk/epitech/tek3/CPP/babel/src/client/View/loginWindow.cpp:6: undefined reference to `vtable for LoginWindow'
/usr/bin/ld: CMakeFiles/babel_client.dir/src/client/View/mainWindow.cpp.o: in function `MainWindow::MainWindow(QWidget*)':
/home/bltksk/epitech/tek3/CPP/babel/src/client/View/mainWindow.cpp:5: undefined reference to `vtable for MainWindow'
/usr/bin/ld: CMakeFiles/babel_client.dir/src/client/View/mainWindow.cpp.o: in function `MainWindow::~MainWindow()':
/home/bltksk/epitech/tek3/CPP/babel/src/client/View/mainWindow.cpp:10: undefined reference to `vtable for MainWindow'
/usr/bin/ld: CMakeFiles/babel_client.dir/src/client/View/mainWindow.cpp.o: in function `non-virtual thunk to MainWindow::~MainWindow()':
/home/bltksk/epitech/tek3/CPP/babel/src/client/View/mainWindow.cpp:13: undefined reference to `vtable for MainWindow'

As I've said on the introduction, if I place xxWindow.h and xxWindow.cpp on the same folder, the problem disappears, but when I place them on the include directory it doesn't link. I'm not a cmake expert, but it seems right to me. Any hint about the solution?

Alvaro
  • 27
  • 1
  • 6
  • 1
    Using GLOB to construct lists of source files is an antipattern. The typical failure pattern is that CMake does not see new files. – Botje Oct 12 '21 at 11:00

1 Answers1

-1

Okay, I achieved to solve the problem.

In order to make AUTOUIC function correctly, we have to add all the headers that includes ui_*.h to the sources target and not only to the include path. This way automoc will correctly process the headers and it will be correctly linked.

In my case it is like this:

file(
        GLOB_RECURSE
        SOURCES_CLIENT
        ${PROJECT_SOURCE_DIR}/src/*.cpp
        ${PROJECT_SOURCE_DIR}/src/client/*.cpp
        ${PROJECT_SOURCE_DIR}/include/client/*.h
        ${PROJECT_SOURCE_DIR}/include/client/resources/resources.qrc
)

Also, remember to set CMAKE_AUTOUIC_SEARCH_PATHS (cmake version >3.9) with the path where .ui files are located, if not, there will be an AutoUic error.

I hope this will help somebody in the future.

Alvaro
  • 27
  • 1
  • 6