如何在Windows下构建ARM Linux QT开发环境

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何在Windows下构建ARM Linux QT开发环境相关的知识,希望对你有一定的参考价值。

准备工作:
首先,最不可思议的,是要在Linux下把QT编译一遍,因为库都是一样的,需要的就是一些Windows下的qmake、moc、uic之类的工具而已。因为QT源码很多地方不能在Windows下面交叉编译通过,虽然我改了一些代码和配置(一会儿我贴出补丁来),但我只用它编译了qtbase、qtdeclarative这两个模块和qttools模块中的一部分。
Linux下的编译可以参照我之前写的这篇文章。参考配置:
开发包:
./configure -extprefix /opt/qt/5.2.1/arm -prefix /usr -plugindir /usr/lib/qt/plugins -importdir /usr/lib/qt/imports -qmldir /usr/lib/qt/qml -make libs -xplatform linux-arm-gnueabi-g++ -opengl es2 -confirm-license -opensource -xcb -xinput2 -nomake examples -nomake tests -qt-zlib -qt-xcb -dbus -largefile -cups -no-fontconfig -glib -gtkstyle -qt-freetype -sysroot /opt/sysroot-arm -mysql_config /opt/sysroot-arm/usr/bin/mysql_config -v
运行库:
./configure -prefix /usr -plugindir /usr/lib/qt/plugins -importdir /usr/lib/qt/imports -qmldir /usr/lib/qt/qml -make libs -xplatform linux-arm-gnueabi-g++ -opengl es2 -confirm-license -opensource -xcb -xinput2 -nomake examples -nomake tests -qt-zlib -qt-xcb -dbus -largefile -cups -no-fontconfig -glib -gtkstyle -qt-freetype -sysroot /opt/sysroot-arm -mysql_config /opt/sysroot-arm/usr/bin/mysql_config -v
做完这一步,你获得两样东西,sysroot和linux下的ARM QT开发文件。sysroot是编译QT之前,用Buildroot做的开发用根目录。这两个东西都要拷贝到Windows里,因为Windows不支持符号连接,拷贝需要需要去掉这些连接,这么做:
cp [源目录] [目标目录] -Lr
第二,需要一个Windows下模拟Linux环境的东西和编译器,我用的是MSYS和MinGW,因为他们编译出来的程序比Cygwin快。在这里可以找到:http://www.mingw.org/。
第三,需要Linaro ARM GCC编译器,Windows版本的。在这里可以找到:http://www.linaro.org/downloads/
第四,需要Python,Windows版本的。在这里可以找到:https://www.python.org/downloads/
下载、安装,然后在MSYS根目录的/etc/profile里面export PATH=$PATH:[Python安装目录]
第五,需要pkg-config,Windows版本的,这个比较麻烦,需要下载以下三个文件,并提取出我们需要的东西:
http://ftp.acc.umu.se/pub/gnome/binaries/win32/dependencies/pkg-config_0.26-1_win32.zip
(提取pkg-config.exe)
http://ftp.gnome.org/pub/gnome/binaries/win32/glib/2.28/glib_2.28.8-1_win32.zip
(提取libglib-2.0-0.dll)
http://ftp.acc.umu.se/pub/gnome/binaries/win32/dependencies/gettext-runtime_0.18.1.1-2_win32.zip (提取intl.dll)
把他们都放到MSYS的bin目录下,然后给pkg-config.exe做一个脚本pkg-config,因为下载的pkg-config.exe比较蠢,在同时指定PKG_CONFIG_SYSROOT_DIR和PKG_CONFIG_LIBDIR这两个环境变量的时候,第一个cflags会输出两次PKG_CONFIG_SYSROOT_DIR。这么做这个脚本:
#!/bin/sh
pushd / > /dev/null
ROOTDIR=`pwd -W 2>/dev/null`
popd > /dev/null
SYSROOT=$PKG_CONFIG_SYSROOT_DIR
pkg-config.exe "$@" | sed "s#$SYSROOT$SYSROOT#$SYSROOT#g" | sed "s#$ROOTDIR##g"
最后去掉$ROOTDIR前缀是为了和Linux Makefile兼容,同时也不会影响在make中的地址转换,最后,QT源码和我的补丁。
我的补丁如下:
diff -Naur qt-everywhere-opensource-src-5.2.1-old/qtbase/configure qt-everywhere-opensource-src-5.2.1/qtbase/configure
--- qt-everywhere-opensource-src-5.2.1-old/qtbase/configure 2014-02-02 04:37:23 +0800
+++ qt-everywhere-opensource-src-5.2.1/qtbase/configure 2014-08-27 22:34:47 +0800
@@ -4022,6 +4022,10 @@
done

(cd "$outpath/qmake"; "$MAKE") || exit 2
+ if [ -e "$outpath/bin/qmake.exe" ]; then
+ echo '#!/bin/sh' > "$outpath/bin/qmake"
+ echo "$outpath/bin/qmake.exe" '"$@"' "-unix" >> "$outpath/bin/qmake"
+ fi
fi # Build qmake

echo "Running configuration tests..."
@@ -4091,9 +4095,9 @@
# when xcompiling, check environment to see if it's actually usable
if [ -z "$PKG_CONFIG_LIBDIR" ]; then
if [ -n "$CFG_SYSROOT" ] && [ -d "$CFG_SYSROOT/usr/lib/pkgconfig" ]; then
- PKG_CONFIG_LIBDIR=$CFG_SYSROOT/usr/lib/pkgconfig:$CFG_SYSROOT/usr/share/pkgconfig
+ PKG_CONFIG_LIBDIR=$CFG_SYSROOT/usr/lib/pkgconfig\;$CFG_SYSROOT/usr/share/pkgconfig
if [ -n "$GCC_MACHINE_DUMP" ]; then
- PKG_CONFIG_LIBDIR=$PKG_CONFIG_LIBDIR:$CFG_SYSROOT/usr/lib/$GCC_MACHINE_DUMP/pkgconfig
+ PKG_CONFIG_LIBDIR=$PKG_CONFIG_LIBDIR\;$CFG_SYSROOT/usr/lib/$GCC_MACHINE_DUMP/pkgconfig
fi
export PKG_CONFIG_LIBDIR
echo >&2 "Note: PKG_CONFIG_LIBDIR automatically set to $PKG_CONFIG_LIBDIR"
diff -Naur qt-everywhere-opensource-src-5.2.1-old/qtbase/mkspecs/linux-arm-gnueabi-g++/qmake.conf qt-everywhere-opensource-src-5.2.1/qtbase/mkspecs/linux-arm-gnueabi-g++/qmake.conf
--- qt-everywhere-opensource-src-5.2.1-old/qtbase/mkspecs/linux-arm-gnueabi-g++/qmake.conf 2014-02-02 04:37:37 +0800
+++ qt-everywhere-opensource-src-5.2.1/qtbase/mkspecs/linux-arm-gnueabi-g++/qmake.conf 2014-08-28 00:08:34 +0800
@@ -11,14 +11,21 @@
include(../common/g++-unix.conf)

# modifications to g++.conf
-QMAKE_CC = arm-linux-gnueabi-gcc
-QMAKE_CXX = arm-linux-gnueabi-g++
-QMAKE_LINK = arm-linux-gnueabi-g++
-QMAKE_LINK_SHLIB = arm-linux-gnueabi-g++
+QMAKE_CC = arm-linux-gnueabihf-gcc
+QMAKE_CXX = arm-linux-gnueabihf-g++
+QMAKE_LINK = arm-linux-gnueabihf-g++
+QMAKE_LINK_SHLIB = arm-linux-gnueabihf-g++

# modifications to linux.conf
-QMAKE_AR = arm-linux-gnueabi-ar cqs
-QMAKE_OBJCOPY = arm-linux-gnueabi-objcopy
-QMAKE_NM = arm-linux-gnueabi-nm -P
-QMAKE_STRIP = arm-linux-gnueabi-strip
+QMAKE_AR = arm-linux-gnueabihf-ar cqs
+QMAKE_OBJCOPY = arm-linux-gnueabihf-objcopy
+QMAKE_NM = arm-linux-gnueabihf-nm -P
+QMAKE_STRIP = arm-linux-gnueabihf-strip
+
+# support for OpenGL
+QMAKE_LIBS_EGL = -lEGL
+QMAKE_LIBS_OPENGL_ES1 = -lGLES_CM
+QMAKE_LIBS_OPENGL_ES2 = -lGLESv2
+#QMAKE_LIBS +=
+
load(qt_config)
diff -Naur qt-everywhere-opensource-src-5.2.1-old/qtbase/qmake/generators/makefile.cpp qt-everywhere-opensource-src-5.2.1/qtbase/qmake/generators/makefile.cpp
--- qt-everywhere-opensource-src-5.2.1-old/qtbase/qmake/generators/makefile.cpp 2014-02-02 04:37:29 +0800
+++ qt-everywhere-opensource-src-5.2.1/qtbase/qmake/generators/makefile.cpp 2014-08-26 13:53:15 +0800
@@ -1161,8 +1161,8 @@

QString srcf = (*sit).toQString();
QString dstf = (*oit).toQString();
- t << escapeDependencyPath(dstf) << ": " << escapeDependencyPath(srcf)
- << " " << escapeDependencyPaths(findDependencies(srcf)).join(" \\\n\t\t");
+ t << escapeDependencyPath(dstf).replace(QRegExp("\\\\"), "/") << ": " << escapeDependencyPath(srcf).replace(QRegExp("\\\\"), "/")
+ << " " << escapeDependencyPaths(findDependencies(srcf)).replaceInStrings(QRegExp("\\\\"), "/").join(" \\\n\t\t");

ProKey comp, cimp;
for(QStringList::Iterator cppit = Option::cpp_ext.begin(); cppit != Option::cpp_ext.end(); ++cppit)
@@ -3346,6 +3346,8 @@
QString MakefileGenerator::installMetaFile(const ProKey &replace_rule, const QString &src, const QString &dst)

QString ret;
+ QString src_p = src;
+ QString dst_p = dst;
if (project->isEmpty(replace_rule)
|| project->isActiveConfig("no_sed_meta_install"))
ret += "-$(INSTALL_FILE) \"" + src + "\" \"" + dst + "\"";
@@ -3362,7 +3364,7 @@
+ "," + windowsifyPath(replace.toQString()) + ",gi");


- ret += " \"" + src + "\" >\"" + dst + "\"";
+ ret += " \"" + src_p.replace(QRegExp("\\\\"), "/") + "\" >\"" + dst_p.replace(QRegExp("\\\\"), "/") + "\"";

return ret;


struct TermChain
TermChain(PatternTerm term)
diff -Naur qt-everywhere-opensource-src-5.2.1-old/qttools/src/linguist/lrelease/lrelease.pro qt-everywhere-opensource-src-5.2.1/qttools/src/linguist/lrelease/lrelease.pro
--- qt-everywhere-opensource-src-5.2.1-old/qttools/src/linguist/lrelease/lrelease.pro 2014-02-02 04:37:57 +0800
+++ qt-everywhere-opensource-src-5.2.1/qttools/src/linguist/lrelease/lrelease.pro 2014-08-28 10:42:55 +0800
@@ -1,4 +1,7 @@
option(host_build)
+
+win32-g++*:QMAKE_CXXFLAGS_CXX11 = -std=gnu++0x
+
QT = core-private
DEFINES += QT_NO_CAST_FROM_ASCII QT_NO_CAST_TO_ASCII

diff -Naur qt-everywhere-opensource-src-5.2.1-old/qttools/src/linguist/lupdate/lupdate.pro qt-everywhere-opensource-src-5.2.1/qttools/src/linguist/lupdate/lupdate.pro
--- qt-everywhere-opensource-src-5.2.1-old/qttools/src/linguist/lupdate/lupdate.pro 2014-02-02 04:37:57 +0800
+++ qt-everywhere-opensource-src-5.2.1/qttools/src/linguist/lupdate/lupdate.pro 2014-08-28 10:46:59 +0800
@@ -1,4 +1,7 @@
option(host_build)
+
+win32-g++*:QMAKE_CXXFLAGS_CXX11 = -std=gnu++0x
+
QT = core-private

qtHaveModule(qmldevtools-private)
接下来开始配置:

其中-extprefix定义安装位置,在编译完以后可以改,一会儿说;-prefix、-plugindir、-importdir、-qmldir定义的位置是目标板上的位置,加双斜杠是为了防止MSYS翻译这些路径成MSYS的路径,其他的设定与Linux下的编译没有不同。Linux下编译的sysroot可以拷贝到例如:E:/MinGW/opt/sysroot-arm。
然后编译
make module-qtbase
make module-qtdeclarative
cd qttools/src/linguist
../../../qtbase/bin/qmake.exe -unix linguist.pro
make
编译的时候可能会有几个库有链接错误,找不到一大堆gl、egl打头的函数,这是因为相应的Makefile里面的LIBS没有自动加上-lEGL -lGLES_CM -lGLESv2;但是正式使用qmake的时候不会,很奇怪;因为也就几个地方,出问题了手工加一下吧,我没去查原因改代码。
编译linguist的时候可能会遇到这个问题:http://qt-project.org/forums/viewthread/33370,按里面说的处理。
编译完了以后,把下列文件拷贝到Linux下编译的ARM QT开发包的bin目录中去:

然后,删掉对应的ARM QT开发包的bin目录中没有exe后缀的文件,那些是Linux下的。
最后一步,确保安装路径正确,也就是说,如果配置Windows下QT的时候设定-extprefix E:/MinGW/opt/qt/5.2.1/arm,那就要把替换过exe文件的ARM QT开发包放到这个位置,如果路径改了,可以用二进制搜索工具去qmake.exe中替换这个字串。
补充一下关于调试的问题,其实不是很关键。
在使用Debug模式编译的时候,最后会出现如下提示:
warning: A handler for the OS ABI "GNU/Linux" is not built into this configuration
of GDB. Attempting to continue with the default i386 settings.
这是因为在mkspecs/features/unix/gdb_dwarf_index.prf中,有这样一段:
QMAKE_GDB_INDEX += \
test \$\$(gdb --version | sed -e \'s,[^0-9][^0-9]*\\([0-9]\\)\\.\\([0-9]\\).*,\\1\\2,;q\') -gt 72 && \
gdb --nx --batch --quiet -ex \'set confirm off\' -ex \"save gdb-index $$QMAKE_GDB_DIR\" -ex quit \'$(TARGET)\' && \
test -f $(TARGET).gdb-index && \
$$QMAKE_OBJCOPY --add-section \'.gdb_index=$(TARGET).gdb-index\' --set-section-flags \'.gdb_index=readonly\' \'$(TARGET)\' \'$(TARGET)\' && \
$$QMAKE_DEL_FILE $(TARGET).gdb-index || true

很显然,这段代码把调试用的GDB默认为“gdb”了,所以应该改成你用的gdb,比如arm-linux-gnueabihf-gdb。另外,这里的sed对GDB版本的判断,无法识别像“GNU gdb (Sourcery CodeBench Lite 2014.05-29) 7.7.50.20140217-cvs”这样的版本信息的,只能识别像“GNU gdb (GDB) 7.6.1”这样的版本信息,所以你有可能看不到刚才那段提示。想解决,要么重新写一段sed的正则表达式,要么直接就把这个test ... -gt 72删掉。
参考技术A 如何在Windows上建立arm-linux开发环境 FriendlyARM 目 前,有很多地方介绍了如何在cygwin上为ARM7建立arm-elf-gcc交叉编译工具链,但是对ARM9却鲜有介绍,为了方便大家更好在 Windows上进行嵌入式li

如何编译可以在Windows下运行的带有Python支持的ARM Linux GDB

参考技术A 做这件事情的目的是为了在QtCreator里调试ARM Linux程序的时候,能看清楚QString、QList这些Qt特有的对象的内容,而不是一个完全看不懂的结构体。
目前(2014年8月)Linaro、CodeSourcery的GCC工具链里的GDB都不支持Python。想知道你用的GDB支持不支持,试一试就行,这样表示不支持:
(gdb) python
>print 'Hello GDB!'
>(按Ctrl+D)Python scripting is not supported in this copy of GDB.
这样表示支持:
(gdb) python
>print 'Hello GDB!'
>(按Ctrl+D)Hello GDB!
这件事情乍一看也很简单,只要把GDB源码下载下来,然后再配置,打开Python支持就行了。实际上会遇到的问题是,在MinGW下,又要与“\”和“:”这两个Windows路径里的刺头斗争了。我觉得我之前挺傻,编译MinGW下Qt的时候,就去硬磕源码和configure脚本去了。这次GDB的configure是自动生成的,不是给人看的,configure.ac看起来也很费劲,根本磕不下去,于是我换了个思路,在ubuntu下交叉编译吧,sudo apt-get install mingw32,这是Ubuntu下的MinGW交叉编译器。
然后是依赖,这样的GDB要依赖expat和python的开发版本。如果是ubuntu底下直接编译,apt-cache search一下他们的开发版本,然后sudo apt-get install一下就好了;给MinGW交叉编译就麻烦了。先说expat,这个好办,把http://downloads.sourceforge.net/project/expat/expat/2.1.0/expat-2.1.0.tar.gz下载下来,然后:
./configure --prefix=[安装目录,如/home/cdu/mingw-gdb/expat] --host=i586-mingw32msvc
make
make install
会提示一些警告,无视即可。
Python就无语了,目前的GDB貌似最高支持Python 2.7,而2.7版本的Python本身不支持MinGW…… 好在有高手做了Patch,也写了说明,可以参考这文章:http://mdqinc.com/blog/2011/10/cross-compiling-python-for-windows-with-mingw32/
但是,就算这样,编译也充满挑战,要修复很多问题,出来的Python还少“nt”模块。就在我觉得没办法的时候,突然发现Windows版Qt提供的MinGW居然内置了Python开发包,位置在Tools/mingw48_32/opt,赶紧把它拷贝到Linux下,比如/home/cdu/mingw-gdb/python。当然,你也必须确保ubuntu下有可用的python。
然后,给GDB打一个补丁:
--- gdb-7.8/gdb/configure 2014-07-29 20:37:42.000000000 +0800
+++ gdb-7.8-old/gdb/configure 2014-08-30 00:08:27.122042706 +0800
@@ -8263,21 +8263,22 @@
# We have a python program to use, but it may be too old.
# Don't flag an error for --with-python=auto (the default).
have_python_config=yes
- python_includes=`$python_prog $srcdir/python/python-config.py --includes`
+ python_config_tool=`echo $python_prog | sed "s#python.exe#python-config#g"`
+ python_includes=`$python_config_tool --includes`
if test $? != 0; then
have_python_config=failed
if test "$with_python" != auto; then
as_fn_error "failure running python-config --includes" "$LINENO" 5
fi
fi
- python_libs=`$python_prog $srcdir/python/python-config.py --ldflags`
+ python_libs=`$python_config_tool --ldflags`
if test $? != 0; then
have_python_config=failed
if test "$with_python" != auto; then
as_fn_error "failure running python-config --ldflags" "$LINENO" 5
fi
fi
- python_prefix=`$python_prog $srcdir/python/python-config.py --exec-prefix`
+ python_prefix=`$python_config_tool --exec-prefix`
if test $? != 0; then
have_python_config=failed
if test "$with_python" != auto; then
@@ -8343,12 +8344,12 @@
return 0;

_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+#if ac_fn_c_try_link "$LINENO"; then :
have_libpython=$version
found_usable_python=yes
PYTHON_CPPFLAGS=$new_CPPFLAGS
PYTHON_LIBS=$new_LIBS
-fi
+#fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
CPPFLAGS=$save_CPPFLAGS
这个补丁的目的是强制为检测到python。
然后给拷贝到Linux下的python开发包打一个补丁:
--- python-old/bin/python-config 2013-04-18 02:43:01.000000000 +0800
+++ python/bin/python-config 2014-08-30 00:53:16.630060288 +0800
@@ -1,4 +1,4 @@
-#!/temp/x32-480-posix-dwarf-r2/mingw32/opt/bin/python2.7.exe
+#!/usr/bin/python

import sys
import os
@@ -31,26 +31,23 @@

for opt in opt_flags:
if opt == '--prefix':
- print sysconfig.PREFIX
+ print '../python'

elif opt == '--exec-prefix':
- print sysconfig.EXEC_PREFIX
+ print '../python'

elif opt in ('--includes', '--cflags'):
- flags = ['-I' + sysconfig.get_python_inc(),
- '-I' + sysconfig.get_python_inc(plat_specific=True)]
+ flags = ['-I' + os.path.split(os.path.realpath(__file__))[0] + '/../include/python2.7']
if opt == '--cflags':
- flags.extend(getvar('CFLAGS').split())
+ flags += ['-fno-strict-aliasing -DMS_WIN32 -DMS_WINDOWS -DHAVE_USABLE_WCHAR_T -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes']
print ' '.join(flags)

elif opt in ('--libs', '--ldflags'):
- libs = getvar('LIBS').split() + getvar('SYSLIBS').split()
- libs.append('-lpython'+pyver)
+ libs = ['-lm -lpython2.7 -Wl,--out-implib=libpython2.7.dll.a']
# add the prefix/lib/pythonX.Y/config dir, but only if there is no
# shared library in prefix/lib/.
if opt == '--ldflags':
if not getvar('Py_ENABLE_SHARED'):
- libs.insert(0, '-L' + getvar('LIBPL'))
- libs.extend(getvar('LINKFORSHARED').split())
+ libs.insert(0, '-L' + os.path.split(os.path.realpath(__file__))[0] + '/../lib/python2.7/config')
print ' '.join(libs)

因为Linux下是无法运行开发包中的python.exe的,所以这个补丁借用了ubuntu的python。里面的cflags和ldflags都是在Windows底下运行原始python-config获得的。prefix和exec-prefix设成“../python”,可以在编译完以后,把python开发包拷贝到gdb安装目录里面的python子目录,这样运行GDB的时候就不需要设定PYTHONHOME环境变量了。
最后一个事情,确保你的Linux下有arm交叉编译器,我的是arm-linux-gnueabihf,是啥target就写啥。
准备工作做完了,开始配置和编译:
./configure --with-expat --host=i586-mingw32msvc --target=arm-linux-gnueabihf --with-libexpat-prefix=[expat安装位置] --with-python=[python开发包安装位置/bin/python.exe]
make
make DESTDIR=[GDB安装位置] install
然后把GDB安装位置下面的所有文件拷贝到Windows下,再把python开发包拷贝到同目录下的python子目录,大功告成。
如果提示没找到libpython2.7.dll,那就把GDB安装目录的python/bin下的拷贝到bin下。
如果发现生成的exe文件太大了,那就strip一下。
2015年9月12日追加:
在windows下调试时,一般会提示说加载不了共享库,让你用"set sysroot"或"set solib-search-path"之类设定路径的。这个问题可以通过.gdbinit文件,用上面这两条命令来设定路径解决,如果想一劳永逸,可以在编译的时候加上host_configargs环境变量来解决这个问题:
host_configargs=--with-sysroot=E:\MinGW\opt\sysroot-arm ./configure ...
或者
export host_configargs=--with-sysroot=E:\MinGW\opt\sysroot-arm
./configure ...

后面的路径是你放在windows下的sysroot的位置。

以上是关于如何在Windows下构建ARM Linux QT开发环境的主要内容,如果未能解决你的问题,请参考以下文章

用于arm linux的QT构建应用程序

如何编译可以在Windows下运行的带有Python支持的ARM Linux GDB

Linux系统上用QT编写ARM9继电器控制程序的问题。 想写个QT界面程序到arm板子上,通过界面的按钮来控制继电

如何在ubuntu下搭建qt交叉编译环境

17.3 构建LinuxPC端QT软件上的ARM编译套件并进行测试

linux下arm平台Qt编译环境搭建与解析