PDA

View Full Version : Porting code to Windows and getting "error LNK2019: unresolved external symbol ...."



redBeard
3rd January 2011, 23:26
I have a small project running on Linux and Mac and I'm now porting it to Windows (Vista). Qt version is 4.7. I'm using VSE 2008 for an IDE.

I did have one small sample working a couple of months ago so I know my environment is set up correctly.

I've been able to easily/quickly resolve most of the issues with just some .pro file tweaking, but I can't resolve this last one (last problem, ha!).

The program structure consists of multiple subdirectories and each subdir creates a shared library of the associated object files (e.g., .so on linux, .dylib on mac, .dll on Win32).

However, when I link a subdirectory of object files into a DLL on windows and code in that directory references classes/functions in another (unbuilt) library, I get 'error LNK2019: unresolved external symbol...'

The .pro file in each subdirectory basically has 'CONFIG += shared' in it (included in a common file, actually).

Each subdirectory also defines their own namespace.

The link line generated to link a DLL looks like:



link /LIBPATH:"c:\Programs\Qt\4.7.0\lib" /NOLOGO /DEBUG /DLL /MANIFEST /MANIFESTFILE:"../../build/element\element.intermediate.manifest" /OUT:..\..\lib\element.dll @C:\XXXXX\AppData\Local\Temp\nmDF6C.tmp


The 'nmDF6C.tmp' file is deleted at the end of the build command so I can't see what it contains.

The errors:



Element.obj : error LNK2019: unresolved external symbol "public: virtual __thiscall exceptions::DuplicateException::~DuplicateExceptio n(void)" (??1DuplicateException@exceptions@@UAE@XZ) referenced in function "protected: void __thiscall element::Element::add(class core::DataChannel *)" (?add@Element@element@@IAEXPAVDataChannel@core@345 6@@Z)
Element.obj : error LNK2019: unresolved external symbol "public: __thiscall exceptions::DuplicateException::DuplicateException (class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)" (??0DuplicateException@exceptions@@QAE@V?$basic_st ring@DU?$char_traits@D@std@@V?$allocator@D@2@@std@ @@Z) referenced in function "protected: void __thiscall element::Element::add(class core::DataChannel *)" (?add@Element@element@@IAEXPAVDataChannel@core@345 6@@Z)


I'm a rather newbie on developing C++ apps on Windows (I spend most of my time on Linux/Mac)... Do I need some sort of '__declspec()' foo on the function definitions or declarations or?

Thanks.

ChrisW67
4th January 2011, 00:33
However, when I link a subdirectory of object files into a DLL on windows and code in that directory references classes/functions in another (unbuilt) library, I get 'error LNK2019: unresolved external symbol...'
You need to make sure that libraries are built the correct order to ensure this doesn't happen. Look at the:


TEMPLATE = subdirs
CONFIG += ordered
SUBDIRS = g a b c d e f


Similar problems arise if the library search path is wrong and the library, which exists, cannot be found.

redBeard
4th January 2011, 16:28
You need to make sure that libraries are built the correct order to ensure this doesn't happen.

Similar problems arise if the library search path is wrong and the library, which exists, cannot be found.
I do have the 'TEMPLATE = subdirs' and 'CONFIG += ordered' in my project-level .pro file and then the list of subdirs to make.

I also have the common (and self-contained) directories built first. I still have the problem with 'unresolved external symbol' when building the shared libraries of sub-directories that reference these common directories.

Do I need to specify the common libs in each of the sub-dir .pro files (as in 'LIBS += ../../core ../../exceptions')? That seems strange as I don't need to do this for either Linux or Mac builds.

Also, I have some classes that have a bi-directional relationship that span sub-directories. One contains a container of pointers to another (e.g., 'map<string, Element *> elements;') and the constructor of the Element class includes a point to the container class (e.g., 'new Element (Container *)'). The Element and Container classes are in separate sub-directories.

Sooo, I really couldn't build one before the other.

Still searching....

ChrisW67
4th January 2011, 22:19
I also have the common (and self-contained) directories built first. I still have the problem with 'unresolved external symbol' when building the shared libraries of sub-directories that reference these common directories.

Do I need to specify the common libs in each of the sub-dir .pro files (as in 'LIBS += ../../core ../../exceptions')? That seems strange as I don't need to do this for either Linux or Mac builds.

Yes. In order to link anything, whether an application, shared, or static library, the linker must be able to find the necessary libraries. It does this by looking for each named library in the current directory and then along a library search path until it is found (or not as in your case). This search behaviour is more-or-less universal. Each linker has a a default search path that can be supplemented. In Qt PRO files you add to the search path with -L options in LIBS. You also name the libraries to search for in LIBS with -l (lower case L) options:


LIBS += -L../../common/lib -lcool -lfroody -ldude

as an alternative you can explicitly provide the full path to each library file:


LIBS += ../../common/lib/libcool.a ../../common/lib/libfroody.a ../../common/lib/libdude.a

but this approach has the disadvantage of needing adjustment by platform/linker for different library file naming systems. It may have worked on Linux/Mac because the common library directory was on the default search path for the linker (/usr/local/lib perhaps).



Also, I have some classes that have a bi-directional relationship that span sub-directories. One contains a container of pointers to another (e.g., 'map<string, Element *> elements;') and the constructor of the Element class includes a point to the container class (e.g., 'new Element (Container *)'). The Element and Container classes are in separate sub-directories.

Sooo, I really couldn't build one before the other.

There is no facility to automatically break the circular dependencies you have built. One approach is to avoid separating dependent items between libraries. Another is to avoid doing things with B in A that require knowing more than that B exists (i.e. more than storing a pointer to a B instance). Exactly how you fix this is specific to your code and not really Qt specific.

redBeard
5th January 2011, 15:51
In Qt PRO files you add to the search path with -L options in LIBS. You also name the libraries to search for in LIBS with -l (lower case L) options:
Yep. I was typing tooo fast (too much coffee?) in my earlier post and mistyped what I specified for the LIBS variable.


It may have worked on Linux/Mac because the common library directory was on the default search path for the linker (/usr/local/lib perhaps).
While anything is possible, I'm not so sure about that on *nix-based systems. For example, here's my compile and link lines of one of the directories into a shared object library. The code in here references another (not yet built) library. Note I use other 3rd-party packages, which I refer to at compile time with the '-I' compiler option (for the include files). Also note the link only refers to Qt libraries (which are in the default search path of /usr/lib).

I also made sure my system had no files named 'libelement.so' or 'libcore.so' before starting, including the Trash, before I started this compile.


cd src/element/ && make -f Makefile.linux all
make[1]: Entering directory `/home/project/src/element'
g++ -c -pipe -g -Wall -W -D_REENTRANT -fPIC -DQT_GUI_LIB -DQT_CORE_LIB -I/usr/lib/qt4/mkspecs/linux-g++ -I. -I/usr/include/QtCore -I/usr/include/QtGui -I/usr/include -I.. -I/usr/share/xsd-3.3.0/libxsd -Imocs -o ../../build/element/Element.o Element.cpp
. . .
/usr/lib/qt4/bin/moc -DQT_GUI_LIB -DQT_CORE_LIB -I/usr/lib/qt4/mkspecs/linux-g++ -I. -I/usr/include/QtCore -I/usr/include/QtGui -I/usr/include -I.. -I/usr/share/xsd-3.3.0/libxsd -Imocs Element.h -o mocs/moc_Element.cpp

g++ -c -pipe -g -Wall -W -D_REENTRANT -fPIC -DQT_GUI_LIB -DQT_CORE_LIB -I/usr/lib/qt4/mkspecs/linux-g++ -I. -I/usr/include/QtCore -I/usr/include/QtGui -I/usr/include -I.. -I/usr/share/xsd-3.3.0/libxsd -Imocs -o ../../build/element/moc_Element.o mocs/moc_Element.cpp

g++ -shared -Wl,-soname,libelement.so.1 -o libelement.so.1.0.0 ../../build/element/Element.o ../../build/element/moc_Element.o -lQtGui -lQtCore -lpthread

mv -f libelement.so.1.0.0 libelement.so libelement.so.1 libelement.so.1.0 ../../lib/

Then, the 'core' directory includes a class that holds a container of pointers to 'Elements' (map<string, Element *>):


make[1]: Leaving directory `/home/project/src/element'
cd src/core/ && make -f Makefile.linux all
make[1]: Entering directory `/home/project/src/core'
g++ -c -pipe -g -Wall -W -D_REENTRANT -fPIC -DQT_GUI_LIB -DQT_CORE_LIB -I/usr/lib/qt4/mkspecs/linux-g++ -I. -I/usr/include/QtCore -I/usr/include/QtGui -I/usr/include -I.. -I/usr/share/xsd-3.3.0/libxsd -Imocs -o ../../build/core/Engine.o Engine.cpp

. . .

g++ -shared -Wl,-soname,libcore.so.1 -o libcore.so.1.0.0 ../../build/core/Engine.o -lQtGui -lQtCore -lpthread
mv -f libcore.so.1.0.0 libcore.so libcore.so.1 libcore.so.1.0 ../../lib/

And the link of the application. Note the 3rd-party libraries are in the normal search path (/usr/lib):


g++ -o ../../bin/instrumentPanel.debug ../../build/main/main.o ../../build/main/qrc_InstrumentPanel.o -llog4cxx -laprutil-1 -lapr-1 -lxerces-c -L../../lib -lelement -ldisplay -lcore -lxsd -lexceptions -lmodel -lQtGui -lQtCore -lpthread
make[1]: Leaving directory `/home/project/src/main'

Then to run this application, I set an environment variable to point to the directories that include the shared libraries needed to run the application:


LD_LIBRARY_PATH=lib
./bin/instrumentPanel.debug


There is no facility to automatically break the circular dependencies you have built. One approach is to avoid separating dependent items between libraries. Another is to avoid doing things with B in A that require knowing more than that B exists (i.e. more than storing a pointer to a B instance). Exactly how you fix this is specific to your code and not really Qt specific.
Bummer. Fortunately, there are ways around this. I can think of two:
1) Change the code directory structure (not my first choice but definitely doable)
2) Change the build structure to not build separate DLLs of each directory but maybe just one (e.g., libcore.dll) and have the main application just reference that library.

Anyway, I've got other problems of trying to get the other 3rd-party packages to work on Windows. Gonna be a loooong day..

Thanks for your help.