Windows/Build/Common problems

From KDE Community Wiki
< Windows‎ | Build
Revision as of 21:49, 15 December 2011 by *>Darkstar (→‎Common problems: regular missing symbol error)

Rationale

I tend to re-build KDE from scratch from time to time using Microsoft Visual Studio. And all the time, the same problems prevent MSVC from correctly compiling some source files.

I decided to put these problems (and a solution) into the Wiki because I'm probably not the only one having these problems (or is everyone else building on mingw? ;-)) so this page not only serves as a reference for myself but might even benefit others.

It might even encourage developers to avoid these problems in the first place

Common problems

DLL-Export of templated functions/classes

Consider the following piece of code (taken from calligra/kexi/kexiutils/utils.h, rev. 6c7551c)

//! A helper for automatic deleting of contents of containers.
template <typename Container>
class KEXIUTILS_EXPORT ContainerDeleter
{
public:
    ContainerDeleter(Container& container) : m_container(container) {}
    .....
}

looks like a pretty simple, templated class which will be exported in the DLL file. It even builds correctly on MSVC, and the result is a working DLL file.

However, some other project using this DLL will throw errors like the following on link (note that this is for a different function, not the one above, but I couldn't find the corresponding error amongst the thousands that were thrown):

field.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: class QList<class KexiDB::Field *>::const_iterator __thiscall QList<class KexiDB::Field *>::constEnd(void)const " (__imp_?constEnd@?$QList@PAVField@KexiDB@@@@QBE?AVconst_iterator@1@XZ)

The problem is that there is no such thing as a generic DLL export. Each function a DLL exports must be completely specified. This makes sense, since it makes quite a difference whether the function accepts a list of ints or a list of QObjects. That is why GCC ignores the "dllimport" in this case, and just instantiates the template in the compiled object.

However, MSVC tries to honor the DLLImport directive and thus cannot find the exact template specification that the user of the class needs. How could it, since at the time the DLL got compiled, the compiler can't possibly know if you'll ever need a "ContainerDeleter<int>" or maybe a "ContainerDeleter<std::string>". How about "ContainerDeleter<QObject>"? Or "ContainerDeleter<std::pair<std::list<int>, std::list<int> > >"?

That's why you get the error. And you can't even blame MSVC for that, it is perfectly possible that your DLL exports 20 different versions of the "ContainerDeleter<foo>" class, and there might be one for exactly the case you need. It probably should try and look for an import, and if it finds none, instantiate the template again. Feel free to file a feature request with Microsoft for that :)

Solution: simple. Just remove the FOO_EXPORT macro on all templated classes/functions in the .h file(s) and recompile the libraries (shouldn't be needed but just to make sure)

This problem might turn up slightly different, in form of the MSVC compiler error C2492:

error C2491: 'foo' : definition of dllimport function not allowed

This means that you have a function declaration similar to

int KDEFOO_EXPORT foo(int bar, int baz);

in your .h file, and (in the same file) the definition of that function

int foo(int bar, int baz)
{
  return bar+baz;
}

The compiler knows it has to import the foo function from a DLL, but then you tell it "hey, this btw. is the definition of foo". No wonder the poor compiler gets confused ;-)

Missing symbols on link

Sometimes, you're getting a pretty normal "missing symbol" error from the linker, even if no templated class is involved. Like this one (also from kexi, no offense intended but that's simply because I'm compiling it right now)

Linking CXX shared module ..\..\..\bin\kformdesigner_containers.dll
   Creating library ..\..\..\bin\kformdesigner_containers.lib and object ..\..\..\bin\kformdesigner_containers.exp
containerfactory.obj : error LNK2001: unresolved external symbol "public: virtual bool __thiscall KUndo2Command::mergeWith(class KUndo2Command const *)" (?mergeWith@KUndo2Command@@UAE_NPBV1@@Z)
..\..\..\bin\kformdesigner_containers.dll : fatal error LNK1120: 1 unresolved externals
LINK failed. with 1120

This simply means that you have to add a missing library to the CMakeLists.txt file for that project. First, find out in what library the missing function is supposed to be. grep over the source tree or something. You'll notice that in this case the library is called "kundo2" and is in calligra/libs/kundo2. So let's look at the CMakeList.txt for the kformdesigner_containers library (it's ion calligra\kexi\formeditor\factories\, slightly modified for readability):

include_directories( ${CMAKE_SOURCE_DIR}/kexi ${CMAKE_SOURCE_DIR}/kexi/formeditor  [...] )
add_definitions(-DKEXI_FORMS_NO_LIST_WIDGET)

########### next target ###############

set(kformdesigner_containers_PART_SRCS containerfactory.cpp )
kde4_add_plugin(kformdesigner_containers ${kformdesigner_containers_PART_SRCS})

target_link_libraries(
    kformdesigner_containers
    kformdesigner
    ${KDE4_KDECORE_LIBS}
    ${KDE4_KDEUI_LIBS}
    ${KDE4_KDE3SUPPORT_LIBS}
    ${QT_QTCORE_LIBRARY}
    ${QT_QTGUI_LIBRARY}
    ${QT_QTXML_LIBRARY}
    ${QT_QT3SUPPORT_LIBRARY}
    )

install(TARGETS kformdesigner_containers  DESTINATION ${PLUGIN_INSTALL_DIR})

Solution: Simply put "kundo2" in the list of libraries for the "target_link_libraries" statement.

I think this error doesn't occur on GCC because GCC knows that some library or other (in this case for example kformdesigner) has been linked against kundo2 already, so everything that links against kformdesigner must also link against kundo2. MSVC, apparently, is not so smart...