PyQt 静态构建在 make 时失败
我拥有 Qt 和 PyQt 的商业许可证,并且正在尝试构建所有必需软件的静态版本,以生成我目前正在使用 Python3 和 PyQt 编写的独立可执行程序.我计划使用 VirtualBox 本地构建,并且可能针对 Linux 和 Windows.我目前正在尝试设置 Linux 构建环境.
I have commercial licenses for Qt and PyQt, and am attempting to build static versions of all required software for generating a standalone, executable program that I'm currently writing using Python3 and PyQt. I plan to build natively using VirtualBox, and will probably target Linux and Windows. I'm currently trying to get the Linux build environment set up.
完成此操作的第一步是构建系统根目录,如下所述:http://pyqt.sourceforge.net/Docs/pyqtdeploy/build_sysroot.html
The first step to accomplish this is to build a system root directory, as outlined here: http://pyqt.sourceforge.net/Docs/pyqtdeploy/build_sysroot.html
我正在使用 build-sysroot.py,这里提供:https://www.riverbankcomputing.com/hg/pyqtdeploy/file/tip/Developers/build-sysroot.py
I am using build-sysroot.py, which is presented here: https://www.riverbankcomputing.com/hg/pyqtdeploy/file/tip/Developers/build-sysroot.py
按照第一个链接中的说明,我创建了一个根目录 (sysroot-linux),其中包含一个名为 src 的目录,其中包含用于 Qt5、PyQt5、sip、PyQtChart 和 Python 的 g 压缩源代码文件夹.
Following the instructions in the first link, I have created a root directory (sysroot-linux) containing a directory named src, which has g-zipped source code folders for Qt5, PyQt5, sip, PyQtChart, and Python.
我在 VBox 中使用 Xubuntu 16.04 64 位,以及商业 PyQt-5.8、商业 PyQtChart-5.8、Python-3.6.1、商业 Qt-5.8.0 和 sip-4.19.2.我将 Qt 许可证信息复制到位于/home/(user)/.qt-license 的文件中.我将 PyQt 许可证文件复制到 g 压缩的 PyQt/sip 目录中.
I am using Xubuntu 16.04 64-bit in a VBox, along with commercial PyQt-5.8, commercial PyQtChart-5.8, Python-3.6.1, commercial Qt-5.8.0, and sip-4.19.2. I copied my Qt license information into a file located at /home/(user)/.qt-license. I copied my PyQt license file into the g-zipped PyQt/sip directory.
当我在终端中运行以下命令时:
When I run the following command in the Terminal:
python3 build-sysroot.py --build qt python sip pyqt5 pyqtchart --sysroot /home/(user)/(path-to)/sysroot-linux
其中 (user) 是用户名,(path-to) 是系统根目录的路径.
Where (user) is user name and (path-to) is the path to the system root directory.
大约 1.5 小时后,我在 PyQt 构建过程中收到一条错误消息
About 1.5 hours into the process, I get an error during the PyQt build that reads
Makefile:953: recipe for target 'sipQtNetworkpart1.o' failed
make[1]: \*** [sipQtNetworkpart1.o] Error 1
make[1]: Leaving directory '/home/(user)/(path-to)/sysroot-linux/build/PyQt5_commercial-5.8/QtNetwork'
Makefile:414: recipe for target 'sub-QtNetwork-make_first-ordered' failed
make: \*** [sub-QtNetwork-make_first-ordered] Error 2
Traceback (most recent call last):
File "build-sysroot.py", line 1073, in <module>
build_pyqt5(host, target, args.all, args.debug)
File "build-sysroot.py", line 735, in build_pyqt5
host.run(host.make)
File "build-sysroot.py", line 313, in run
subprocess.check_call(args)
File "/usr/lib/python3.5/subprocess.py", line 581, in check_call
raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command '('make',)' returned non-zero exit status 2
销售 PyQt 的公司告诉我,我的 Qt 版本不支持 SSL,但我的 PyQt 配置文件启用了它.
The company that sells PyQt advised me that my build of Qt doesn't support SSL but my PyQt configuration file has it enabled.
所以我尝试在 build-sysroot.py 中使用disable=QtNetwork"在 PyQt 配置中禁用 QtNetwork,在args"中使用函数build_pyqt5"(python 列表类型;参见第 1 个链接的第 721 行),但无济于事- 该选项似乎已被忽略,无论如何都会尝试构建 QtNetwork.
So I tried disabling QtNetwork in the PyQt configuration using "disable=QtNetwork" in build-sysroot.py, function "build_pyqt5" in "args" (python list type; see line 721 of 1st link), but to no avail - the option seems to have been ignored and the build of QtNetwork is attempted anyway.
我与销售 PyQt 的公司有联系,但我想联系更广泛的编程社区,看看过去是否有人成功地做到了这一点,也许可以提供见解.非常感谢任何帮助.
I am in touch with the company that sells PyQt, but I wanted to reach out to the broader programming community to see if anyone has successfully done this in the past, and could perhaps offer insight. Any help is much appreciated.
谢谢,克里斯
编辑 1这是完整的错误输出,仅修改了目录路径:
EDIT 1 Here is the complete error output, only directory paths modified:
make[1]: Entering directory '/home/(user)/(path-to)/sysroot-linux/build/PyQt5_commercial-5.8/QtNetwork'
g++ -c -pipe -O2 -fPIC -std=gnu++11 -fvisibility=hidden -fvisibility-inlines-hidden -fno-exceptions -Wall -W -D_REENTRANT -DSIP_PROTECTED_IS_PUBLIC -Dprotected=public -DQT_NO_EXCEPTIONS -DQT_NO_DEBUG -DQT_NETWORK_LIB -DQT_CORE_LIB -I. -I. -I../../../include/python3.6 -I../../../Qt/include -I../../../Qt/include/QtNetwork -I../../../Qt/include/QtCore -I. -I../../../Qt/mkspecs/linux-g++ -o sipQtNetworkpart1.o sipQtNetworkpart1.cpp
/home/(user)/(path-to)/sysroot-linux/build/PyQt5_commercial-5.8/QtNetwork/sipQtNetworkpart1.cpp: In function ‘PyObject* meth_QNetworkReply_sslConfiguration(PyObject*, PyObject*)’:
/home/(user)/(path-to)/sysroot-linux/build/PyQt5_commercial-5.8/QtNetwork/sipQtNetworkpart1.cpp:1094:55: error: ‘const class QNetworkReply’ has no member named ‘sslConfiguration’
sipRes = new ::QSslConfiguration(sipCpp->sslConfiguration());
^
/home/(user)/(path-to)/sysroot-linux/build/PyQt5_commercial-5.8/QtNetwork/sipQtNetworkpart1.cpp:1094:73: error: invalid use of incomplete type ‘class QSslConfiguration’
sipRes = new ::QSslConfiguration(sipCpp->sslConfiguration());
^
In file included from ../../../Qt/include/QtNetwork/QNetworkRequest:1:0,
from ../../../Qt/include/QtNetwork/qnetworkreply.h:48,
from /home/(user)/(path-to)/sysroot-linux/build/PyQt5_commercial-5.8/sip/QtNetwork/qnetworkreply.sip:18:
../../../Qt/include/QtNetwork/qnetworkrequest.h:52:7: note: forward declaration of ‘class QSslConfiguration’
class QSslConfiguration;
^
/home/(user)/(path-to)/sysroot-linux/build/PyQt5_commercial-5.8/QtNetwork/sipQtNetworkpart1.cpp: In function ‘PyObject* meth_QNetworkReply_setSslConfiguration(PyObject*, PyObject*)’:
/home/(user)/(path-to)/sysroot-linux/build/PyQt5_commercial-5.8/QtNetwork/sipQtNetworkpart1.cpp:1120:21: error: ‘class QNetworkReply’ has no member named ‘setSslConfiguration’
sipCpp->setSslConfiguration(*a0);
^
/home/(user)/(path-to)/sysroot-linux/build/PyQt5_commercial-5.8/QtNetwork/sipQtNetworkpart1.cpp: In function ‘PyObject* meth_QNetworkReply_ignoreSslErrors(PyObject*, PyObject*)’:
/home/(user)/(path-to)/sysroot-linux/build/PyQt5_commercial-5.8/QtNetwork/sipQtNetworkpart1.cpp:1162:40: error: no matching function for call to ‘QNetworkReply::ignoreSslErrors(const QList<QSslError>&)’
sipCpp->ignoreSslErrors(*a0);
^
In file included from /home/(user)/(path-to)/sysroot-linux/build/PyQt5_commercial-5.8/sip/QtNetwork/qnetworkreply.sip:18:0:
../../../Qt/include/QtNetwork/qnetworkreply.h:154:18: note: candidate: virtual void QNetworkReply::ignoreSslErrors()
virtual void ignoreSslErrors();
^
../../../Qt/include/QtNetwork/qnetworkreply.h:154:18: note: candidate expects 0 arguments, 1 provided
/home/(user)/(path-to)/sysroot-linux/build/PyQt5_commercial-5.8/QtNetwork/sipQtNetworkpart1.cpp: In function ‘PyObject* meth_QNetworkAccessManager_connectToHostEncrypted(PyObject*, PyObject*, PyObject*)’:
/home/(user)/(path-to)/sysroot-linux/build/PyQt5_commercial-5.8/QtNetwork/sipQtNetworkpart1.cpp:12735:45: error: incomplete type ‘QSslConfiguration’ used in nested name specifier
{sipName_PutOperation, static_cast<int>( ::QNetworkAccessManager::PutOperation), 55},
^
/home/(user)/(path-to)/sysroot-linux/build/PyQt5_commercial-5.8/QtNetwork/sipQtNetworkpart1.cpp:12747:21: error: ‘class QNetworkAccessManager’ has no member named ‘connectToHostEncrypted’
{"encrypted(QNetworkReply*)", "\1encrypted(self, QNetworkReply)", 0, 0},
^
Makefile:953: recipe for target 'sipQtNetworkpart1.o' failed
make[1]: *** [sipQtNetworkpart1.o] Error 1
make[1]: Leaving directory '/home/(user)/(path-to)/sysroot-linux/build/PyQt5_commercial-5.8/QtNetwork'
Makefile:414: recipe for target 'sub-QtNetwork-make_first-ordered' failed
make: *** [sub-QtNetwork-make_first-ordered] Error 2
Traceback (most recent call last):
File "build-sysroot.py", line 1073, in <module>
build_pyqt5(host, target, args.all, args.debug)
File "build-sysroot.py", line 735, in build_pyqt5
host.run(host.make)
File "build-sysroot.py", line 313, in run
subprocess.check_call(args)
File "/usr/lib/python3.5/subprocess.py", line 581, in check_call
raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command '('make',)' returned non-zero exit status 2
我会继续分享我的解决方法,以防其他人遇到此问题.
I'll go ahead and share my workaround, in case someone else encounters this issue.
目前build-sysroot.py脚本使用pyqtdeploycli生成构建PyQt的配置文件,其中包含无法静态构建的Qt模块,如QtWebEngine.无论如何,我最终为 PyQt5 制作了一个配置文件,并将其放入 PyQt5 源代码的顶部 g-zipped 文件夹中:
Currently, the build-sysroot.py script uses pyqtdeploycli to generate a configuration file for building PyQt, which includes Qt modules that cannot be statically built, such as QtWebEngine. At any rate, I ended up making a configuration file for PyQt5, and placing it into the top g-zipped folder for the PyQt5 source:
/home/(user)/(path-to)/sysroot-linux/src/PyQt5_commercial-5.8.2.tar.gz/PyQt5_commercial-5.8.2/pyqt5-linux-64.cfg
PyQt5 配置文件(pyqt5-linux-64.cfg):
PyQt5 configuration file (pyqt5-linux-64.cfg):
# The configuration for building PyQt5 for Linux.
# The target Python installation.
py_platform = linux
py_inc_dir = %(sysroot)/include/python%(py_major).%(py_minor)
py_pylib_dir = %(sysroot)/lib
py_pylib_lib = python%(py_major).%(py_minor)m
# The target PyQt installation.
pyqt_module_dir = %(sysroot)/lib/python%(py_major).%(py_minor)/site-packages
pyqt_sip_dir = %(sysroot)/share/sip/PyQt5
# Qt configuration common to all versions.
qt_shared = False
[Qt 5.6]
pyqt_modules = QtCore QtGui QtWidgets
[Qt 5.5]
pyqt_modules = QtCore QtGui QtWidgets
[Qt 5.4]
pyqt_modules = QtCore QtGui QtWidgets
[Qt 5.3]
pyqt_modules = QtCore QtGui QtWidgets
[Qt 5.2]
pyqt_modules = QtCore QtGui QtWidgets
然后我修改了 build-sysroot.py build_pyqt5 函数,不再调用 pyqtdeploycli,而是直接使用手动创建的配置文件,如下:
I then modified the build-sysroot.py build_pyqt5 function to no longer evoke pyqtdeploycli, and instead just use the manually-created configuration file, as follows:
def build_pyqt5(host, target, optional, debug):
""" Build a target static PyQt5. """
source = host.sysroot.find_source('PyQt5_*', optional=optional)
if source is None:
return
host.sysroot.unpack_source(source)
license_path = os.path.join(host.sysroot.src_dir, 'pyqt-commercial.sip')
if os.path.isfile(license_path):
shutil.copy(license_path, 'sip')
configuration = 'pyqt5-' + target.name + '.cfg'
# Disable the call to pyqtdeploycli here
# host.run(host.pyqtdeploycli, '--package', 'pyqt5', '--output',
# configuration, '--target', target.name, 'configure')
args = [host.interpreter, 'configure.py', '--static', '--qmake',
host.qmake, '--sysroot', str(host.sysroot), '--no-tools',
'--no-qsci-api', '--no-designer-plugin', '--no-python-dbus',
'--no-qml-plugin', '--no-stubs', '--configuration', configuration,
'--sip', host.sip, '--confirm-license', '-c', '-j2']
if debug:
args.append('--debug')
host.run(*args)
host.run(host.make)
host.run(host.make, 'install')
我只让它在 QtCore、QtGui 和 QtWidgets 上工作,但其他可能是可能的.
I have only gotten this to work for QtCore, QtGui, and QtWidgets, but others may be possible.