Windows/Build/Common problems
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 ;-)
DLLImport of static data member, and lots of "inconsistent DLL linkage" warnings
This is a slight variation case of the above "definition of dll import not allowed" problem. It manifests itself at the core by a "definition of dllimport static data member not allowed" linker error, but before the error (often even in a different source file) there are lots of warnings about inconsistent DLL linkage. Also, the error normally occurs on some Qt internal object in a _moc file (like Foo::staticMetaObject)
Example:
c:\kde\git\calligra\plan\plugins\schedulers\tj\taskjuggler\CoreAttributes.cpp(27) : warning C4273: 'TJ::CoreAttributes::CoreAttributes' : inconsistent dll linkage c:\kde\git\calligra\plan\plugins\schedulers\tj\taskjuggler\CoreAttributes.h(40) : see previous definition of '{ctor}' c:\kde\git\calligra\plan\plugins\schedulers\tj\taskjuggler\CoreAttributes.cpp(50) : warning C4273: 'TJ::CoreAttributes::~CoreAttributes' : inconsistent dll linkage c:\kde\git\calligra\plan\plugins\schedulers\tj\taskjuggler\CoreAttributes.h(43) : see previous definition of '{dtor}' ..... C:\kde\build\testing\calligra-20101207\work\msvc2010-RelWithDebInfo-gitHEAD\plan\plugins\schedulers\tj\Project.moc(45) : warning C4273: 'staticMetaObject' : inconsistent dll linkage C:/kde/git/calligra/plan/plugins/schedulers/tj/taskjuggler/Project.h(59) : see previous definition of 'public: static QMetaObject const TJ::Project::staticMetaObject' C:\kde\build\testing\calligra-20101207\work\msvc2010-RelWithDebInfo-gitHEAD\plan\plugins\schedulers\tj\Project.moc(45) : error C2491: 'TJ::Project::staticMetaObject' : definition of dllimport static data member not allowed
Solution: none yet. It has to do with the FOO_EXPORT macro magic not working 100% correctly, but I have not yet found a solution for that particular problem. The "inconsistent DLL linkage" means that the compiler sees one symbol once as DLLImport and then again as a (regular) definition, or even as DLLExport. This (understandably) confuses the compiler...
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
In most of the cases, 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...
Note: This is of course not the only reason why you might get a missing symbol error. It is, however, probably the most common.
Missing symbols (and you're *sure* that every required library is linked against)
Okay, so you still got a "missing symbols" error, but now you're sure that the library you need is properly referenced in target_link_libraries. Like the following:
Linking CXX shared module ..\..\..\..\bin\plantjscheduler.dll Creating library ..\..\..\..\bin\plantjscheduler.lib and object ..\..\..\..\bin\plantjscheduler.exp PlanTJScheduler.obj : error LNK2019: unresolved external symbol "public: __thiscall KPlato::Schedule::Log::Log(class KPlato::Node const *,class KPlato::Resource const *,int,class QString const &,int)" (??0Log@Schedule@KPlato@@QAE@PBVNode@2@PBVResource@2@HABVQString@@H@Z) referenced in function "public: void __thiscall PlanTJScheduler::slotMessage(int,class QString const &,class TJ::CoreAttributes*)" (?slotMessage@PlanTJScheduler@@QAEXHABVQString@@PAVCoreAttributes@TJ@@@Z) PlanTJScheduler.obj : error LNK2019: unresolved external symbol "public: __thiscall KPlato::Schedule::Log::Log(class KPlato::Node const *,int,class QString const &,int)" (??0Log@Schedule@KPlato@@QAE@PBVNode@2@HABVQString@@H@Z) referenced in function "public: void __thiscall PlanTJScheduler::slotMessage(int,class QString const &,class TJ::CoreAttributes *)" (?slotMessage@PlanTJScheduler@@QAEXHABVQString@@PAVCoreAttributes@TJ@@@Z) ..\..\..\..\bin\plantjscheduler.dll : fatal error LNK1120: 2 unresolved externals LINK failed. with 1120
Okay, this looks a bit cluttered so let's clean it up:
unresolved external symbol ...KPlato::Schedule::Log::Log(class KPlato::Node const *,int,class QString const &,int)
That's basically the gist of it. No templates involved. Looks like you're missing the library that exports KPlato::Schedule::Log::Log(). But if you check where KPlato::Schedule is defined, you see it's inside libs\kernel\, so the library is kplatokernel. Look in CMakeLists.txt for the project which throws the error above and you see
target_link_libraries(plantjscheduler kplatokernel koodf)
So, the library is actually there. Which explains why you only got 1 missing symbol and not a lot more. So why are you still missing that one member function?
The answer: Nested classes. In a nested class, it is not sufficient to declare the outermost class as DLLExport. You have to mark every nested class as DLLExport as well. Let's look inside plan/libs/kernel/kptschedule.h:
class KPLATOKERNEL_EXPORT Schedule
{
public:
enum Type { .... };
Schedule();
....
class Log {
public:
enum Type { ... };
Log() : { ... }
....
}
}
So there is your problem.
Solution: change the code above in the following way and you're good to go:
class KPLATOKERNEL_EXPORT Schedule
{
public:
enum Type { .... };
Schedule();
....
class KPLATOKERNEL_EXPORT Log {
public:
enum Type { ... };
Log() : { ... }
....
}
}
Invalid preprocessor macro
This is an easy one. Consider the following compilation error:
[ 28%] Building CXX object kexi/plugins/forms/widgets/mapbrowser/CMakeFiles/kformdesigner_mapbrowser.dir/MapBrowserWidget.obj MapBrowserWidget.cpp c:\kde\git\calligra\kexi\plugins\forms\widgets\mapbrowser\MapBrowserWidget.cpp(33) : fatal error C1021: invalid preprocessor command 'warning' NMAKE : fatal error U1077: 'c:\PROGRA~2\MICROS~2.0\VC\bin\cl.exe' : return code '0x2' Stop. NMAKE : fatal error U1077: '"c:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\BIN\nmake.exe"' : return code '0x2' Stop. NMAKE : fatal error U1077: '"c:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\BIN\nmake.exe"' : return code '0x2' Stop. emerge fatal error: while running make cmd: nmake /NOLOGO emerge fatal error: running python c:\kde\emerge\portage\testing\calligra\calligra-20101207.py compile emerge debug: Task: Emerge stopped after: 0:34:35.562000
Solution: MSVC doesn't understand the #warning
directive and issues an error. The best way to get rid of that error is to surround the #warning
by #ifndef Q_OS_WIN
or #ifndef Q_CC_MSVC
:
MapBrowserWidget::MapBrowserWidget(QWidget *parent)
: Marble::MarbleWidget(parent),
KFormDesigner::FormWidgetInterface(),
KexiFormDataItemInterface(),
m_slotMapChanged_enabled(true),
m_internalReadOnly(false)
{
#ifndef Q_CC_MSVC
#warning this id could be invalid; try to use Marble::MapThemeManager::mapThemes() and get proper Marble::GeoSceneDocument::head()->mapThemeId()
#endif
//Marble::GeoSceneDocument::head()->mapThemeId()
setMapThemeId("earth/srtm/srtm.dgml");
connect( this, SIGNAL(visibleLatLonAltBoxChanged(const GeoDataLatLonAltBox &)), this , SLOT(slotMapChanged()));
}