1、如何理解Qt支持跨平台?
答:我们经常听到这样的说法“如果考虑跨平台的话,qt是一个很好的选择”,感性认识上可能有一个模糊的认识,好像是这么回事,但是往深处想一想,可能又不知道所以然。这里说下我个人的浅浅的见解,不一定正确,希望给大家以启发。如果理解有误,请留言给我,不胜感激。
跨平台一般意义上的理解就是,开发人员写一份代码,理想情况下不需要修改任何代码,在别的平台上重新编译一下就可以运行。这里首先要理解“平台”的概念,什么是平台?平台就是硬件平台+操作系统的组合。例如X86+Windows组合,X86+Linux组合,arm+Linux组合等。硬件平台影响的是指令集,操作系统影响的是底层API接口。对于硬件平台来说,只要使用正确的编译器就可以生成对应的指令集。而操作系统则的不同可能会造成一些底层API的不同。所以说跨平台更多的是讲跨不同的操作系统。QT是如何解决这个问题的呢?QT支持大多数操作系统,并针对支持的操作系统进行了封装处理,将这些操作系统的接口抽象封装为统一接口(相当于一个平台层,该平台层对外呈现统一的接口,但是在不同的操作系统下调用不同的API接口),开发人员只要使用QT提供的类(如QDIR,用户无需关心操作系统的接口什么)进行操作,就能屏蔽底层操作系统差异,实现用统一的接口来实现系统调用的功能。从这个角度来说,qt是跨平台的。
QT支持的操作系统可以通过代码中提供的宏来体现:
在<QtGlobal>中,定义了各个系统的宏定义
Q_OS_AIX
Defined on AIX.
Q_OS_ANDROID
Defined on Android.
Q_OS_BSD4
Defined on Any BSD 4.4 system.
Q_OS_BSDI
Defined on BSD/OS.
Q_OS_CYGWIN
Defined on Cygwin.
Q_OS_DARWIN
Defined on Darwin-based operating systems such as macOS, iOS, watchOS, and tvOS.
Q_OS_DGUX
Defined on DG/UX.
Q_OS_DYNIX
Defined on DYNIX/ptx.
Q_OS_FREEBSD
Defined on FreeBSD.
Q_OS_HPUX
Defined on HP-UX.
Q_OS_HURD
Defined on GNU Hurd.
Q_OS_IOS
Defined on iOS.
Q_OS_IRIX
Defined on SGI Irix.
Q_OS_LINUX
Defined on Linux.
Q_OS_LYNX
Defined on LynxOS.
Q_OS_MAC
Deprecated synonym for Q_OS_DARWIN. Do not use.
Q_OS_MACOS
Defined on macOS.
Q_OS_NETBSD
Defined on NetBSD.
Q_OS_OPENBSD
Defined on OpenBSD.
Q_OS_OSF
Defined on HP Tru64 UNIX.
Q_OS_OSX
Deprecated synonym for Q_OS_MACOS. Do not use.
Q_OS_QNX
Defined on QNX Neutrino.
Q_OS_RELIANT
Defined on Reliant UNIX.
Q_OS_SCO
Defined on SCO OpenServer 5.
Q_OS_SOLARIS
Defined on Sun Solaris.
Q_OS_TVOS
Defined on tvOS.
Q_OS_ULTRIX
Defined on DEC Ultrix.
Q_OS_UNIX
Defined on Any UNIX BSD/SYSV system.
Q_OS_UNIXWARE
Defined on UnixWare 7, Open UNIX 8.
Q_OS_WATCHOS
Defined on watchOS.
Q_OS_WIN32
Defined on 32-bit and 64-bit versions of Windows.
Q_OS_WIN64
Defined on 64-bit versions of Windows.
Q_OS_WIN
Defined on all supported versions of Windows. That is, if Q_OS_WIN32, Q_OS_WIN64, or Q_OS_WINRT is defined.
Q_OS_WINPHONE
Defined on Windows Phone 8.
Q_OS_WINRT
Defined for Windows Runtime (Windows Store apps) on Windows 8, Windows RT, and Windows Phone 8.
2、Qt的是否支持交叉编译?
答:支持。我们大多数使用场景是桌面环境下,安装qt和对应的IDE(qtcreator),通过 IDE编译本平台下的应用程序或者库。那么对于交叉编译场景下(交叉编译使用场景)应该如何实现交叉编译呢?问出这个问题说明对qt的编译原理还是不太清楚的。qt编译过程中使用了qmake工具,通过这个工具可以生成工程项目的makefile文件。
我们都知道编译链接过程是根据makefile来完成的,所以我们若想实现交叉编译,只需要在在makefile中指定好编译要是用的交叉编译工具链即可。这里就涉及到了qmake是如何生成出makefile的,相关资料网上一大堆,这里不进行赘述。着重提一下的是需要修改对应平台的qmake.conf文件,在该文件中通过QMAKE_CC = xxx的方式指定交叉编译工具即可。
上述完成之后可以打开makefile文件查看编译器,若编译器中的CC中的内容为指定的编译器则表示修改成功。
3、qmake.cache的作用
答:在我们的工程目录中不知道大家是否注意过.qmake.cache这个文件,是否思考过这个文件的作用,这个文件是如何产生的?下面我将尽量详细的把这个文件讲明白。
首先要将这个文件不能绕开的是就是qmake,qmake的作用就是通过工程文件(.pro)生成该工程对应的makefile文件,qmake寻找pro文件有其固定的规则,这个不进行展开。更进一步将qmake是一个应用程序,该应用程序中使用c++实现,定义了一些函数接口,这些函数接口可以在pro文件以某种形式使用,达到函数调用的效果。下面介绍几个比较好的介绍qmake的网址:
https://www.cnblogs.com/iriczhao/p/11274598.html
通过上面的介绍对qmake的有一个基本的认识,qmake要生成makefile,首先要有工程文件(.pro),然后是要告知qmake平台+编译器,没有这两项就无法生成对应的makefile。
qmake的工作方式流程默认情况下是首先查找.qmake.cache,若有则首先读取该文件中的配置;若没有找到,则读取工程文件和对应平台的qmake.conf文件,生成makefile。注意若.qmake.cache文件,则该文件中的配置会体现到makefile中,通过前面的讲述我们已经知道qmake整个过程涉及qmake.conf/.pro/.qmake.cache等三个文件,通过这几种类型的文件最终输出该工程对应的makefile文件。如果我们要修改makefile文件,只能从修改这三个文件入手,一般而言qmake.conf文件不进行修改(也可以进行修改,但是修改后会影响所有使用这个conf
的工程,除非重新自定义一个新的qmake.conf文件,方法在上面的链接中有涉及),所以我们一般修改.pro文件或者.qmake.cache文件来实现,从我的角度理解,一些针对某个平台需要的配置可以声明在.qmake.cache中,这是一般的做法,当然也可以将配置写在.pro文件中,使用.qmake.cache的好处是针对一个包含多个子项目(subdir)的工程来说,不需要在每个pro文件中写入同样的配置语句,在这个场景下是很有用处的。如果只有一个pro文件,那么.qmake.cache的必要性就没那么高。
.qmake.cahce的生成方法,在pro文件文件中写入如下例子代码,然后执行qmake就可以生成.qmake.cache:
var = 111
var1 = tt1
vars = gggg
!build_pass:cache(var,set) #向目标路径下的.qmake.cache中以append的方式写入var = 111
!build_pass:cache(var,set) #var与.qmake.cache中的var值一直,不写入
var=222
!build_pass:cache(var,set) #向目标路径下的.qmake.cache中以append的方式写入var = 222
!build_pass:cache(var,add,var1) #向目标路径下的.qmake.cache中以append的方式写入var += tt1
var2=111 zzz
!build_pass:cache(var,add,var2) #向目标路径下的.qmake.cache中以append的方式写入
#var += \
# 111 \
# zzz
var3=111 xxx
!build_pass:cache(var,sub,var3) #向目标路径下的.qmake.cache中以append的方式写入
#var -= \
# 111 \
# zzz
!build_pass:cache(var1,set transient) #为.qmake.cache所在的上下文(QMakeEvaluator)添加变量,但是不写入文件。
!build_pass:cache(vars,set stash) #向目标路径下的.qmake.stash中以append的方式写入var = 111
总结:
(1).qmake.cache文件在包含多个子项目中的工程中很有用,无需在每个pro文件中添加同样的配置代码
(2)qmake执行时首先查找.qmake.cache文件,如果存在则将配置保存起来。然后再解析pro文件
(3)一般而言,我们不会去修改qmake.conf,因为修改该文件,会影响所有使用该配置文件的工程,除非我们自定义一个qmake.conf文件
(4)给出了如何生成.qmake.cache文件的方法
4、代码移植遇到的问题
在代码移植时,遇到过一个问题,x86平台下qt工程代码移植到另外一个平台时提示报错,整个工程是直接拷贝过去的,x86+麒麟操作系统平台依赖的qt库的版本是qt5.9.9,路径是/opt/Qt5.9.9/5.9.9/gcc_64/bin,新平台是飞腾+麒麟平台,但是qt版本换成了5.12,其路径为/usr/lib/qt5/bin,编译过程中总是报错,提示qmake总是依赖x86平台下的qt路径,报错信息如下:
make[1]:/opt/Qt5.9.9/5.9.9/gcc_64/bin/qmake: Command not found
基于此做了如下排查:
- 排查kit(套件)的配置情况
- 排查环境变量是否正确
做了上述排查后,始终没有发现问题,最后想到编译过程中依赖makefile,然后打开makefile发现,makefile文件还是x86平台下的makefile文件,该makefile中指明了QMAKE对应的路径,如下图所示:
至此,找到问题原因,解决办法有两种:
- 删除工程工程中所有的makefile,重新调用qmake生成makefile。(至于qmake在何种情况下才会重新生成makeifle,这个原理我目前也不是很清楚,有知道的朋友可以留言给我,谢谢)
- 重新指定构建目录,重新进行编译
上述两种方法都能触发makefile重新生成,新生成的makefile是基于本平台的。
总结:
在进行代码移植或者跨平台移植时,需要删除原来工程中的makefile,否则会遇到跟我类似的问题。因为在makefile存在的情况下,调用qtcreator执行qmake并不会重新生成新的makefile。
5、Qt构建目录
什么是构建目录?
答:构建目录在qt creator的下含义是就是中间文件和可执行程序的输出目录。默认情况下生成的可执行文件或者库文件在不显式指定生成目录的情况下,都会生成到构建目录下的release或者debug目录;如果在pro文件中通过DESTDIR变量显式指定了生成路径,那么可执行文件都会生成到对应的目录。
构建目录可以通过qt creator设置,设置方法如下:
另外,构建目录还有一个坑,假设在工程文件中有如下设置:
DESTDIR = ./outpput
请思考,生成的可执行文件是生成到pro文件所在目录下的output目录下,还是生成到构建目录的output目录下?
这里就不卖关子了,通过实验观察到现象是生成到了构建目录下的output目录。这个结果跟我之前的理解不一致,说明如果在DESTDIR中使用./这样的符号指定生成路径,这个.(当前路径)表示的是构建目录,并不是pro文件所在目录,这一点尤其要注意。
6、pro文件常用的变量符号
$$PWD表示的是pro文件所在路径
$$OUT_PWD表示的构建目录的路径
message():该接口应用在pro文件中,可以打印变量值,例如message($$PWD)
其余待补充