How to create layered project structure in c++ using qmake
Asked Answered
M

1

8

Under windows using MinGW, C++11, Qt 5 and qmake I have following project structure:

/my-project
   my-project.pro
   /my-app
      my-app.pro
      main.cpp
   /module-a
      module-a.pro
      modulea.h
      modulea.cpp
   /module-b
      module-b.pro
      moduleb.h
      moduleb.cpp

The dependencies between modules should look like this:

my-app ==> module-a ==> module-b

What I want to achieve is that my-app uses module-a, module-a uses module-b and my-app doesn't know anything about module-b. The module-a references module-b only through its implementation (the #include for module-b is located in .cpp of module-a).

I've tried to implement this by configuring module-a and module-b as static libraries in qmake. Unfortunately while compiling I receive linker error saying 'undefined reference to ModuleB::doSmthB()'

I understand the reason of this linking problem, my question is if it is somehow possible to achieve something similar to the proposed layered structure?

Sources:

my-project.pro:

TEMPLATE = subdirs
SUBDIRS += module-b
SUBDIRS += module-a
SUBDIRS += my-app
my-app.depends = module-a
module-a.depends = module-b

my-app.pro:

QT += widgets
TARGET = my-app
TEMPLATE = app
CONFIG += c++11
SOURCES += *.cpp
win32 {
    INCLUDEPATH += $$clean_path($$PWD/../module-a)
    DEPENDPATH += $$clean_path($$PWD/../module-a)
    LIBS += $$clean_path($$OUT_PWD/../module-a/debug/libmodule-a.a)
    PRE_TARGETDEPS += $$clean_path($$OUT_PWD/../module-a/debug/libmodule-a.a)
}

main.cpp:

#include <QApplication>
#include <QGraphicsView>
#include <QGraphicsScene>

#include "modulea.h"

int main(int argc, char *args[])
{
    QApplication app(argc, args);
    QGraphicsView view;
    QGraphicsScene *scene = new QGraphicsScene(0, 0, 300, 300, &view);
    ModuleA moduleA;
    scene->addText(QString::number(moduleA.doSmthA())); // undefined reference to ModuleB::doSmthB()
    view.setScene(scene);
    view.show();
    return app.exec();
}

module-a.pro:

QT -= core gui
TARGET = module-a
TEMPLATE = lib
CONFIG += staticlib
CONFIG += c++11
HEADERS += *.h
SOURCES += *.cpp
win32 {
    INCLUDEPATH += $$clean_path($$PWD/../module-b)
    DEPENDPATH += $$clean_path($$PWD/../module-b)
    LIBS += $$clean_path($$OUT_PWD/../module-b/debug/libmodule-b.a)
    PRE_TARGETDEPS += $$clean_path($$OUT_PWD/../module-b/debug/libmodule-b.a)
}

modulea.h:

#ifndef MODULEA_H
#define MODULEA_H

struct ModuleA
{
    int doSmthA();
};

#endif // MODULEA_H

modulea.cpp:

#include "modulea.h"
#include "moduleb.h"

int ModuleA::doSmthA() {
    ModuleB other;
    return other.doSmthB();
}

module-b.pro:

QT -= core gui
TARGET = module-b
TEMPLATE = lib
CONFIG += staticlib
CONFIG += c++11
HEADERS += *.h
SOURCES += *.cpp

moduleb.h:

#ifndef MODULEB_H
#define MODULEB_H

struct ModuleB
{
    int doSmthB();
};

#endif // MODULEB_H

moduleb.cpp:

#include "moduleb.h"

int ModuleB::doSmthB() {
    return 12345;
}
Maura answered 19/4, 2016 at 8:47 Comment(0)
M
4

To fix my example following changes are required:

1) Add CONFIG += create_prl to the .pro files of all app's direct dependencies (in my case module-a). It won't hurt to modify all modules like this.

Explanation here: http://doc.qt.io/qt-5/qmake-advanced-usage.html#library-dependencies

2) In the main .pro (in my example the my-project.pro) the subdir declaration of app (SUBDIRS += my-app) has to be placed AFTER the direct dependencies of app (after SUBDIRS += module-a).

A hint for the second point I've found here: https://mcmap.net/q/294302/-how-to-use-qmake-39-s-subdirs-template

3) The LIBS property have to be defined using -L and -l options (at least under windows), e.g. instead of:

LIBS += $$clean_path($$OUT_PWD/../module-a/debug/libmodule-a.a)

use following:

LIBS += -L$$clean_path($$OUT_PWD/../module-a/debug/) -lmodule-a

The fixed solution looks like this (modified files only):

my-project.pro:

TEMPLATE = subdirs
SUBDIRS += module-b
SUBDIRS += module-a
SUBDIRS += my-app
my-app.depends = module-a
module-a.depends = module-b

my-app.pro:

QT += widgets
TARGET = my-app
TEMPLATE = app
CONFIG += c++11
SOURCES += *.cpp
win32 {
    INCLUDEPATH += $$clean_path($$PWD/../module-a)
    DEPENDPATH += $$clean_path($$PWD/../module-a)
    LIBS += -L$$clean_path($$OUT_PWD/../module-a/debug/) -lmodule-a
    PRE_TARGETDEPS += $$clean_path($$OUT_PWD/../module-a/debug/libmodule-a.a)
}

module-a.pro:

QT -= core gui
TARGET = module-a
TEMPLATE = lib
CONFIG += staticlib
CONFIG += c++11
CONFIG += create_prl
HEADERS += *.h
SOURCES += *.cpp
win32 {
    INCLUDEPATH += $$clean_path($$PWD/../module-b)
    DEPENDPATH += $$clean_path($$PWD/../module-b)
    LIBS += -L$$clean_path($$OUT_PWD/../module-b/debug/) -lmodule-b
    PRE_TARGETDEPS += $$clean_path($$OUT_PWD/../module-b/debug/libmodule-b.a)
}
Maura answered 25/4, 2016 at 12:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.