-
-
Notifications
You must be signed in to change notification settings - Fork 51
/
hifi_qt.py
257 lines (215 loc) · 10.8 KB
/
hifi_qt.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
# Copyright 2013-2019 High Fidelity, Inc.
# Copyright 2020-2022 Vircadia contributors.
# Copyright 2020-2022 Overte e.V.
# SPDX-License-Identifier: Apache-2.0
import hifi_utils
import hifi_android
import hashlib
import os
import platform
import re
import shutil
import tempfile
import json
import xml.etree.ElementTree as ET
import functools
# The way Qt is handled is a bit complicated, so I'm documenting it here.
#
# 1. User runs cmake
# 2. cmake calls prebuild.py, which is referenced in /CMakeLists.txt
# 3. prebuild.py calls this code.
# 4. hifi_qt.py determines how to handle cmake: do we need to download a package, and which?
# 4.a - Using system Qt
# No download, most special paths are turned off.
# We build in the same way a normal Qt program would.
# 4.b - Using an user-provided Qt build in a custom directory.
# We just need to set the cmakePath to the right dir (qt5-install/lib/cmake)
# 4.c - Using a premade package.
# We check the OS and distro and set qtUrl to the URL to download.
# After this, it works on the same pathway as 4.b.
# 5. We write /qt.cmake, which contains paths that are passed down to SetupQt.cmake
# The template for this file is in CMAKE_TEMPLATE just below this comment
# and it sets the QT_CMAKE_PREFIX_PATH variable used by SetupQt.cmake.
# 6. cmake includes /qt.cmake receiving our information
# In the case of system Qt, this step is skipped.
# 7. cmake runs SetupQt.cmake which takes care of the cmake parts of the Qt configuration.
# In the case of system Qt, SetupQt.cmake is a no-op. It runs but exits immediately.
#
# The format for a prebuilt qt is a package containing a top-level directory named
# 'qt5-install', which contains the result of a "make install" from a build of the Qt source.
print = functools.partial(print, flush=True)
# Encapsulates the vcpkg system
class QtDownloader:
CMAKE_TEMPLATE = """
# this file auto-generated by hifi_qt.py
get_filename_component(QT_CMAKE_PREFIX_PATH "{}" ABSOLUTE CACHE)
get_filename_component(QT_CMAKE_PREFIX_PATH_UNCACHED "{}" ABSOLUTE)
# If the cached cmake toolchain path is different from the computed one, exit
if(NOT (QT_CMAKE_PREFIX_PATH_UNCACHED STREQUAL QT_CMAKE_PREFIX_PATH))
message(FATAL_ERROR "QT_CMAKE_PREFIX_PATH has changed, please wipe the build directory and rerun cmake")
endif()
"""
def __init__(self, args):
self.args = args
self.configFilePath = os.path.join(args.build_root, 'qt.cmake')
self.version = os.getenv('OVERTE_USE_QT_VERSION', '5.15.2')
self.assets_url = hifi_utils.readEnviromentVariableFromFile(args.build_root, 'EXTERNAL_BUILD_ASSETS')
# OS dependent information
system = platform.system()
qt_found = False
system_qt = False
# Here we handle the 3 possible cases of dealing with Qt:
if bool(os.getenv('OVERTE_USE_SYSTEM_QT', False)):
# 1. Using the system provided Qt. This is only recommended for Qt 5.15.0 and above,
# as it includes a required fix on Linux.
#
# This path only works on Linux as neither Windows nor OSX ship Qt.
if system != "Linux":
raise Exception("Using the system Qt is only supported on Linux")
self.path = None
self.cmakePath = None
qt_found = True
system_qt = True
if not self.args.quiet:
print("Using system Qt")
elif os.getenv('OVERTE_QT_PATH', "") != "":
# 2. Using an user-provided directory.
# OVERTE_QT_PATH must point to a directory with a Qt install in it.
self.path = os.getenv('OVERTE_QT_PATH')
self.fullPath = self.path
self.cmakePath = os.path.join(self.fullPath, 'lib', 'cmake')
qt_found = True
if not self.args.quiet:
print("Using Qt from " + self.fullPath)
else:
# 3. Using a pre-built Qt.
#
# This works somewhat differently from above, notice how path and fullPath are
# used differently in this case.
#
# In the case of an user-provided directory, we just use the user-supplied directory.
#
# For a pre-built qt, however, we have to unpack it. The archive is required to contain
# a qt5-install directory in it.
self.path = os.path.expanduser("~/overte-files/qt")
self.fullPath = os.path.join(self.path, 'qt5-install')
self.cmakePath = os.path.join(self.fullPath, 'lib', 'cmake')
if (not os.path.isdir(self.path)):
os.makedirs(self.path)
qt_found = os.path.isdir(self.fullPath)
print("Using a packaged Qt")
if not system_qt:
if qt_found:
# Sanity check, ensure we have a good cmake directory
qt5_dir = os.path.join(self.cmakePath, "Qt5")
if not os.path.isdir(qt5_dir):
raise Exception("Failed to find Qt5 directory under " + self.cmakePath + ". There should be a " + qt5_dir)
else:
print("Qt5 check passed, found " + qt5_dir)
# I'm not sure why this is needed. It's used by hifi_singleton.
# Perhaps it stops multiple build processes from interferring?
lockDir, lockName = os.path.split(self.path)
lockName += '.lock'
if not os.path.isdir(lockDir):
os.makedirs(lockDir)
self.lockFile = os.path.join(lockDir, lockName)
if qt_found:
if not self.args.quiet:
print("Found pre-built Qt5")
return
if 'Windows' == system:
self.qtUrl = self.assets_url + '/dependencies/qt5/qt5-install-5.15.10-2023.10.02-windows-x86_64.tar.xz'
elif 'Darwin' == system:
self.qtUrl = self.assets_url + '/dependencies/vcpkg/qt5-install-5.15.2-macos.tar.gz'
elif 'Linux' == system:
import distro
cpu_architecture = platform.machine()
if 'x86_64' == cpu_architecture:
# `major_version()` can return blank string on rolling release distros like arch
# The `or 0` conditional assignment prevents the int parsing error from hiding the useful Qt package error
u_major = int( distro.major_version() or '0' )
if distro.id() == 'ubuntu' or distro.id() == 'linuxmint':
if (distro.id() == 'ubuntu' and u_major == 20) or distro.id() == 'linuxmint' and u_major == 20:
self.qtUrl = self.assets_url + '/dependencies/qt5/qt5-install-5.15.14-2024.06.17-kde_570f5b2105df1ea052bec0d6dbf8a00137274371-ubuntu-20.04-amd64.tar.xz'
elif (distro.id() == 'ubuntu' and u_major > 20) or (distro.id() == 'linuxmint' and u_major > 20):
self.__no_qt_package_error()
else:
self.__unsupported_error()
else:
self.__no_qt_package_error()
elif 'aarch64' == cpu_architecture:
if distro.id() == 'ubuntu':
u_major = int( distro.major_version() )
if u_major == 20:
self.qtUrl = self.assets_url + '/dependencies/qt5/qt5-install-5.15.9-2023.05.21-kde_fb3ec282151b1ee281a24f0545a40ac6438537c2-ubuntu-20.04-aarch64.tar.xz'
elif u_major > 20:
self.__no_qt_package_error()
else:
self.__unsupported_error()
elif distro.id() == 'debian':
u_major = int( distro.major_version() )
if u_major > 10:
self.__no_qt_package_error()
else:
self.__unsupported_error()
else:
self.__no_qt_package_error()
else:
raise Exception('UNKNOWN CPU ARCHITECTURE!!!')
else:
print("System : " + platform.system())
print("Architecture: " + platform.architecture())
print("Machine : " + platform.machine())
raise Exception('UNKNOWN OPERATING SYSTEM!!!')
def showQtBuildInfo(self):
print("")
print("It's also possible to build Qt for your distribution, please see the documentation at:")
print("https://github.com/overte-org/overte/tree/master/tools/qt-builder")
print("")
print("Alternatively, you can try building against the system Qt by setting the OVERTE_USE_SYSTEM_QT environment variable.")
print("You'll need to install the development packages, and to have Qt 5.15.0 or later.")
def writeConfig(self):
print("Writing cmake config to {}".format(self.configFilePath))
# Write out the configuration for use by CMake
cmakeConfig = QtDownloader.CMAKE_TEMPLATE.format(self.cmakePath, self.cmakePath).replace('\\', '/')
with open(self.configFilePath, 'w') as f:
f.write(cmakeConfig)
def installQt(self):
if not os.path.isdir(self.fullPath):
print ('Downloading Qt package')
print('Extracting ' + self.qtUrl + ' to ' + self.path)
hifi_utils.downloadAndExtract(self.qtUrl, self.path)
else:
print ('Qt has already been downloaded')
def __unsupported_error(self):
import distro
cpu_architecture = platform.machine()
print('')
hifi_utils.color('red')
print("Sorry, " + distro.name(pretty=True) + " on " + cpu_architecture + " is too old and won't be officially supported.")
hifi_utils.color('white')
print("Please upgrade to a more recent Linux distribution.")
hifi_utils.color('clear')
print('')
raise hifi_utils.SilentFatalError(3)
def __no_qt_package_error(self):
import distro
cpu_architecture = platform.machine()
print('')
hifi_utils.color('red')
print("Sorry, we don't have a prebuilt Qt package for " + distro.name(pretty=True) + " on " + cpu_architecture + ".")
hifi_utils.color('white')
print('')
print("If this is a recent distribution, dating from 2021 or so, you can try building")
print("against the system Qt by running this command, and trying again:")
print(" export OVERTE_USE_SYSTEM_QT=1")
print("")
hifi_utils.color('clear')
print("If you'd like to try to build Qt from source either for building Overte, or")
print("to contribute a prebuilt package for your distribution, please see the")
print("documentation at: ", end='')
hifi_utils.color('blue')
print("https://github.com/overte-org/overte/tree/master/tools/qt-builder")
hifi_utils.color('clear')
print('')
raise hifi_utils.SilentFatalError(2)