Building Qt app with CONFIG += staticlib causes "undefined reference to vtable" errors

c++, gcc, linux, qt, qt-creator

Solution

The answer is disappointingly simple: the libraries were linked in the wrong order.

I looked at the command that invoked the linker (right above the linker error):

g++ [...] -lStadium [...] -lFootball 

I also looked into the code: The Football subproject refers to the Stadium subproject, so the libraries are in the wrong order, see for example the accepted answer of GCC C++ Linker errors: Undefined reference to 'vtable for XXX', Undefined reference to 'ClassName::ClassName()' for explanation.

Indeed, if I swap the two libraries in the Server.pro file (derived from commit `09836f9`, irrelevant win32 specific details deleted for brevity):

[...]

SOURCES += main.cpp

LIBS += -L$$OUT_PWD/../Football/ -lFootball
INCLUDEPATH += $$PWD/../Football
DEPENDPATH += $$PWD/../Football
PRE_TARGETDEPS += $$OUT_PWD/../Football/libFootball.a

LIBS += -L$$OUT_PWD/../Stadium/ -lStadium
INCLUDEPATH += $$PWD/../Stadium
DEPENDPATH += $$PWD/../Stadium
PRE_TARGETDEPS += $$OUT_PWD/../Stadium/libStadium.a

Now the command line looks like:

g++ [...] -lFootball [...] -lStadium

It compiles and runs just fine on my Linux machine.

Problem

EDIT: I have heavily edited this post to strip the project down to its essentials. I have also added a Github repository, including the files which are not referenced in this post. I have a Qt Creator project (qmake, Qt 5.2.0, Creator 3.0.0) that uses the `subdirs` template. There are three subprojects: - Stadium - library that is configured as `TEMPLATE = lib` and `CONFIG += staticlib`. - Football - library that is configured as `TEMPLATE = lib` and `CONFIG += staticlib` and uses the `Field` library. - Server - a QML application that uses both Stadium and Football libraries. I'm building this application on both Windows 8.1 (MSVC2012) and Linux (gcc 4.8.1). It works without issue on Windows, but the Linux build behaves strangely. The errors I get look like this: ``` undefined reference to 'vtable for Stadium::Engine' ``` I've rewritten this project to a set of bare files that displays the error. You can find it on Github here: Football. Feel free to clone it and see all the errors for yourself. The `661441c` commit solves the problem, and the `09836f9` commit contains the errors. The Stadium Engine.h file is an abstract class. It looks like this: ``` #ifndef STADIUM_ENGINE_H #define STADIUM_ENGINE_H #include <QObject> namespace Stadium { class Engine : public QObject { Q_OBJECT public slots: virtual void executeCommand() = 0; }; } // namespace Stadium #endif // STADIUM_ENGINE_H ``` Here is the Football Engine.h file, which inherits from the Stadium Engine.h file above: ``` #ifndef FOOTBALL_ENGINE_H #define FOOTBALL_ENGINE_H #include <QObject> #include "../Stadium/Engine.h" namespace Football { class Engine : public Stadium::Engine { Q_OBJECT public: Engine(); ~Engine() {} public slots: void executeCommand(); }; } // namespace Football #endif // FOOTBALL_ENGINE_H ``` And the Football Engine.cpp file: ``` #include "Engine.h" #include <QDebug> Football::Engine::Engine() { qDebug() << "[Football::Engine] Created."; } void Football::Engine::executeCommand() { qDebug() << "[Football::Engine] The command was executed."; } ``` If I move the constructor definition from the cpp to the header file, it builds without error. Below is the the Server.pro file. It's indicative of all my other pro files, in that the static linking descriptions (auto-generated by Qt Creator) look the same. ``` QT += core QT -= gui TARGET = Server CONFIG += console CONFIG -= app_bundle TEMPLATE = app SOURCES += main.cpp win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../Stadium/release/ -lStadium else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../Stadium/debug/ -lStadium else:unix: LIBS += -L$$OUT_PWD/../Stadium/ -lStadium INCLUDEPATH += $$PWD/../Stadium DEPENDPATH += $$PWD/../Stadium win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../Stadium/release/libStadium.a else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../Stadium/debug/libStadium.a else:win32:!win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../Stadium/release/Stadium.lib else:win32:!win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../Stadium/debug/Stadium.lib else:unix: PRE_TARGETDEPS += $$OUT_PWD/../Stadium/libStadium.a win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../Football/release/ -lFootball else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../Football/debug/ -lFootball else:unix: LIBS += -L$$OUT_PWD/../Football/ -lFootball INCLUDEPATH += $$PWD/../Football DEPENDPATH += $$PWD/../Football win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../Football/release/libFootball.a else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../Football/debug/libFootball.a else:win32:!win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../Football/release/Football.lib else:win32:!win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../Football/debug/Football.lib else:unix: PRE_TARGETDEPS += $$OUT_PWD/../Football/libFootball.a ``` I've tried cleaning, re-running qmake, removing the build directory, and rebuilding. The only way to get this project build in Linux is to remove the `CONFIG += staticlib` line in the .pro file of the Stadium library (and the corresponding `else:unix: PRE_TARGETDEPS += $$OUT_PWD/../stadium/libstadium.a` line in the Game.pro too, of course). This builds the project successfully and runs without issue. But I just don't understand why. I also don't understand why it matters where the constructor definition is defined. Any ideas?

Original source

Related problems