Search code examples
qtqt6

QtCreator subdirs project linker error for library dependency


  • Platform: Windows 11
  • Qt Version: 6.5.1
  • QtCreator Version: 10.0.2

I'm playing around with Qt Creator and QMake project structures and am running into a linker issue (undefined reference) when compiling an app that attempts to make use of items defined in a cpp in a library. I'm using the subdirs project type to house related subprojects with the following structure:

parentProject
   |- parentProject.pro
   |- coreApp
   |     |- coreApp.pro
   |     |- main.cpp
   |     |- mainwindow.cpp
   |- coreLib
         |-coreLib.pro
         |- corelib.h
         |- corelib.cpp
         |- coreLib_global.h
         |- Temp.h
         |- temp2.h
         |- temp2.cpp

Parent project being the subdirs project maintains references to the various subprojects

#parentProject.pro

TEMPLATE = subdirs

SUBDIRS += \
    coreApp \
    coreLib

coreApp.depends+=coreLib

The coreLib was created using the QtCreator New Project -> Library -> C++ Library wizard and virtually nothing was changed. Only the temp files were added to play around with things (posted below).

#coreLib.pro

QT -= gui

TEMPLATE = lib
DEFINES += CORELIB_LIBRARY

CONFIG += c++17

# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
    corelib.cpp \
    temp2.cpp

HEADERS += \
    Temp.h \
    coreLib_global.h \
    corelib.h \
    temp2.h

# Default rules for deployment.
unix {
    target.path = /usr/lib
}
!isEmpty(target.path): INSTALLS += target

The coreApp was created using the QtCreator New Project -> Application (Qt) -> Qt Widgets Application and essentially left untouched. The only changes I'd made are as follows:

  • coreApp project -> Add Library -> Internal Library -> select coreLib with everything else in that dialog left at the default
  • main.cpp updated to make call to my test classes in coreLib
#coreApp.pro

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++17

# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
    main.cpp \
    mainwindow.cpp

HEADERS += \
    mainwindow.h

FORMS += \
    mainwindow.ui

TRANSLATIONS += \
    coreApp_en_US.ts
CONFIG += lrelease
CONFIG += embed_translations

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../coreLib/release/ -lcoreLib
else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../coreLib/debug/ -lcoreLib
else:unix: LIBS += -L$$OUT_PWD/../coreLib/ -lcoreLib

INCLUDEPATH += $$PWD/../coreLib
DEPENDPATH += $$PWD/../coreLib

And the updated to main.cpp can literally be written out as:

//main.cpp

//snip
#include "corelib.h"
#include "temp2.h"
#include <iostream>

int main(int argc, char *argv[])
{
    CoreLib();
    std::cout << "RESULT: " << Temp2().sub(1, 2) << std::endl;
    // snip
}

The files in coreLib are as rudimentary as it gets

//corelib.h - as per what was generated
#ifndef CORELIB_H
#define CORELIB_H

#include "coreLib_global.h"

class CORELIB_EXPORT CoreLib
{
public:
    CoreLib();
};

#endif // CORELIB_H
//corelib.cpp - as per generated just with the cout added
#include "corelib.h"
#include <iostream>

CoreLib::CoreLib()
{
    std::cout << "NOT HERE!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl;
}

//Temp.h
#ifndef TEMP_H
#define TEMP_H

inline int add(int x, int y) {
    return x + y;
}

#endif // TEMP_H
//temp2.h
#ifndef TEMP2_H
#define TEMP2_H


class Temp2
{
public:
    Temp2();

    int sub(int x, int y);
};

#endif // TEMP2_H
//temp2.cpp
#include "temp2.h"

Temp2::Temp2()
{

}

int Temp2::sub(int x, int y) {
    return x - y;
}

What I'm Experiencing:

  • changes I make to corelib.cpp can be access and changes are reflected (i.e.: if I change the cout in the constructor I can see the change when I launch coreApp)
  • changes in the header files are seen in coreApp and the Temp::add() function works as expected
  • if I try to move the implementation of Temp::add() to Temp.cpp I get a linker error relating to unresolved reference to Temp::add()
  • if I try to do anything with Temp2 I get an unresolved linker error to Temp2::Temp2()

My confusion:

  • the fact that corelib.h and corelib.cpp look to be working indicates that the coreLib.dll is created and accessed by coreApp
  • the headers can be seen/accessed since Temp::add() works when I leave it implemented in Temp.h
  • temp2.cpp is getting compiled (I can see temp2.o in the build directory with the expected timestamp)

It feels like other cpp files while compiled are not included in the generated dll. Why would this be?


Solution

  • Turns out it's the coreLib_global.h that is the crux of the matter. If the class/header definition makes use of that it will be available for use outside of the library. Meaning temp2.h becomes:

    //temp2.h
    #ifndef TEMP2_H
    #define TEMP2_H
    
    #include "coreLib_global.h"
    
    class CORELIB_EXPORT Temp2
    {
    public:
        Temp2();
    
        int sub(int x, int y);
    };
    
    #endif // TEMP2_H
    

    And by marking the class as a CORELIB_EXPORT it becomes accessible to outside of the library. Without it, it can only be used internally (or purely as a header).

    More detail can be found in the QT Docs.