Linking problem when trying to cross-compile a qt application using cmake
I'm currently switching the build system of my Qt application from qmake to cmake. While trying to cross-compile for Windows with MinGW (static Qt build), I came across a strange problem I cannot explain:
CMakeLists.txt:
project(noble)
cmake_minimum_required(VERSION 2.8.5)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")
find_package(Boost COMPONENTS program_options thread REQUIRED)
find_package(Qt4 REQUIRED QtCore QtGui)
find_package(BLAS REQUIRED)
find_package(LAPACK REQUIRED)
find_package(Armadillo)
find_package(Qwt6 REQUIRED)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR})
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR})
include(${QT_USE_FILE})
set(LIBRARIES
${Qwt6_Qt4_LIBRARY}
${QT_QTCORE_LIBRARY}
${QT_QTGUI_LIBRARY}
${Boost_LIBRARIES}
${BLAS_LIBRARIES}
${LAPACK_LIBRARIES}
)
set(DEFINITIONS
${QT_DEFINITIONS}
${BLAS_LINKER_FLAGS}
${LAPACK_LINKER_FLAGS}
-DBOOST_THREAD_USE_LIB
)
add_subdirectory(src)
add_subdirectory(plugins)
src/CMakeLists.txt:
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(noble_SOURCES
#file names...
)
set(noble_HEADERS
#file names...
)
set(noble_FORMS
#file names...
)
QT4_WRAP_UI(noble_FORMS_HEADERS ${noble_FORMS})
QT4_WRAP_CPP(noble_HEADERS_MOC ${noble_HEADERS})
include_directories(
${Boost_INCLUDE_DIRS}
${ARMADILLO_INCLUDE_DIRS}
${Qwt6_INCLUDE_DIR}
)
add_definitions(${DEFINITIONS})
add_executable(noble
${noble_SOURCES}
${noble_HEADERS_MOC}
${noble_FORMS_HEADERS}
)
target_link_libraries(noble
${LIBRARIES}
-lQtGui -lQtCore
-ljpeg -lpng -ltiff -lmng -lz -limm32 -llcms -lwinmm
-fopenmp
)
I am now trying to get rid of the manually set compiler flags right in the end. If I omit the -lQtGui -lQtCore
I get a whole bunch of undefined references like
/home/mjung/Programme/mingw-cross-env-2.21/usr/lib/gcc/i686-pc-mingw32/4.6.0/../../../../i686-pc-mingw32/lib/libQtGui.a(qapplication.o):qapplication.cpp:(.text+0x2340): undefined reference to `QBasicTimer::stop()'
If i keep these flags, the program compiles fine.
The strange thing now is the following:
cmake's g++ call with -lQtGui -lQtCore
flags:
/home/mjung/Programme/mingw-cross-env-2.21/usr/bin/i686-pc-mingw32-g++
-Wl,--whole-archive
CMakeFiles/noble.dir/objects.a
-Wl,--no-whole-archive
-o ../noble.exe
-Wl,--out-implib,../libnoble.dll.a
-Wl,--major-image-version,0,--minor-image-version,0
/home/mjung/Programme/windows_builds/usr/lib/libqwt.a
-Wl,-Bstatic
-lQtCore -lQtGui
-lboost_program_options-mt
-lboost_thread-mt
-Wl,-Bdynamic
/home/mjung/Programme/windows_builds/usr/lib/libblas.a
/home/mjung/Programme/windows_builds/usr/lib/liblapack.a
/home/mjung/Programme/windows_builds/usr/lib/libblas.a
-lQtGui -lQtCore
-ljpeg -lpng -ltiff -lmng -lz -limm32 -llcms -lwinmm
-fopenmp
/home/mjung/Programme/windows_builds/usr/lib/liblapack.a
-lQtGui -lQtCore
-ljpeg -lpng -ltiff -lmng -lz -limm32 -llcms -lwinmm
-lkernel32 -luser32 -lgdi32 -lwinspool -lshell32 -lole32 -loleaut32 -luuid -lcomdlg32 -ladvapi32
cmake's g++ call without -lQtGui -lQtCore
flags:
/home/mjung/Programme/mingw-cross-env-2.21/usr/bin/i686-pc-mingw32-g++
-Wl,--whole-archive
CMakeFiles/noble.dir/objects.a
-Wl,--no-whole-archive
-o ../noble.exe
-Wl,--out-implib,../libnoble.dll.a
-Wl,--major-image-version,0,--minor-image-version,0
/home/mjung/Programme/windows_builds/usr/lib/libqwt.a
-Wl,-Bstatic
-lQtCore -lQtGui
-lboost_program_options-mt
-lboost_thread-mt
-Wl,-Bdynamic
/home/mjung/Programme/windows_builds/usr/lib/libblas.a
/home/mjung/Programme/windows_builds/usr/lib/liblapack.a
/home/mjung/Programme/windows_builds/usr/lib/libblas.a
-ljpeg -lpng -ltiff -lmng -lz -limm32 -llcms -lwinmm
-fopenmp
/home/mjung/Programme/windows_builds/usr/lib/liblapack.a
-ljpeg -lpng -ltiff -lmng -lz -limm32 -llcms -lwinmm
-lkernel开发者_运维技巧32 -luser32 -lgdi32 -lwinspool -lshell32 -lole32 -loleaut32 -luuid -lcomdlg32 -ladvapi32
Even if I do not set the compiler flags manually the FindQt4.cmake
script still sets them. But this doesn't seem to be enough. I absolutely don't understand why it makes a difference if the flags are set once automatically, or three times, if I add them manually. Does this have something to do with the order of the flags?
Do you have any ideas? Any help is appreciated.
UPDATE:
Nevermind...
I just changed the order of the libraries from
set(LIBRARIES
${Qwt6_Qt4_LIBRARY}
${QT_QTCORE_LIBRARY}
${QT_QTGUI_LIBRARY}
${Boost_LIBRARIES}
${BLAS_LIBRARIES}
${LAPACK_LIBRARIES}
)
to
set(LIBRARIES
${Qwt6_Qt4_LIBRARY}
${QT_QTGUI_LIBRARY}
${QT_QTCORE_LIBRARY}
${Boost_LIBRARIES}
${BLAS_LIBRARIES}
${LAPACK_LIBRARIES}
)
I do not quite understand why it has to be this way round, but now it works...
PS: I would have posted this as an answer, but I am not allowed to do so yet, due to lack of reputation.
Many architectures do a single pass through the library list when linking. i.e. If you have an undefined symbol, a library that's later in the link list must define it. The linker won't search 'backwards' through the library list to resolve the symbol.
In this case, QtGui has a dependency on QtCore. QtCore therefore must appear after QtGui in the link list.
The easiest way around this is to use the ${QT_LIBRARIES} cmake variable that gets magically defined when you do the FIND_PACKAGE(Qt4...). It has all the relevant Qt libs in the right order for the link.
FindQt4 CMake documentation
精彩评论