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?