1# This script generates the Makefiles for building PyQt5. 2# 3# Copyright (c) 2021 Riverbank Computing Limited <info@riverbankcomputing.com> 4# 5# This file is part of PyQt5. 6# 7# This file may be used under the terms of the GNU General Public License 8# version 3.0 as published by the Free Software Foundation and appearing in 9# the file LICENSE included in the packaging of this file. Please review the 10# following information to ensure the GNU General Public License version 3.0 11# requirements will be met: http://www.gnu.org/copyleft/gpl.html. 12# 13# If you do not wish to use this file under the terms of the GPL version 3.0 14# then you may purchase a commercial license. For more information contact 15# info@riverbankcomputing.com. 16# 17# This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE 18# WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 19 20 21from distutils import sysconfig 22import glob 23import optparse 24import os 25import shutil 26import stat 27import sys 28 29 30# Initialise the constants. 31PYQT_VERSION_STR = "5.15.4" 32SIP_MIN_VERSION = '4.19.23' 33 34 35class ModuleMetadata: 36 """ This class encapsulates the meta-data about a PyQt5 module. """ 37 38 def __init__(self, qmake_QT=None, qmake_TARGET='', qpy_lib=False, cpp11=False, public=True): 39 """ Initialise the meta-data. """ 40 41 # The values to update qmake's QT variable. 42 self.qmake_QT = [] if qmake_QT is None else qmake_QT 43 44 # The value to set qmake's TARGET variable to. It defaults to the name 45 # of the module. 46 self.qmake_TARGET = qmake_TARGET 47 48 # Set if there is a qpy support library. 49 self.qpy_lib = qpy_lib 50 51 # Set if C++11 support is required. 52 self.cpp11 = cpp11 53 54 # Set if the module is public. 55 self.public = public 56 57 58# The module meta-data. 59MODULE_METADATA = { 60 'dbus': ModuleMetadata(qmake_QT=['-gui'], 61 qmake_TARGET='pyqt5'), 62 'QAxContainer': ModuleMetadata(qmake_QT=['axcontainer']), 63 'Qt': ModuleMetadata(qmake_QT=['-core', '-gui']), 64 'QtAndroidExtras': ModuleMetadata(qmake_QT=['androidextras']), 65 'QtBluetooth': ModuleMetadata(qmake_QT=['bluetooth']), 66 'QtCore': ModuleMetadata(qmake_QT=['-gui'], qpy_lib=True), 67 'QtDBus': ModuleMetadata(qmake_QT=['dbus', '-gui'], 68 qpy_lib=True), 69 'QtDesigner': ModuleMetadata(qmake_QT=['designer'], 70 qpy_lib=True), 71 'Enginio': ModuleMetadata(qmake_QT=['enginio']), 72 'QtGui': ModuleMetadata(qpy_lib=True), 73 'QtHelp': ModuleMetadata(qmake_QT=['help']), 74 'QtLocation': ModuleMetadata(qmake_QT=['location']), 75 'QtMacExtras': ModuleMetadata(qmake_QT=['macextras']), 76 'QtMultimedia': ModuleMetadata(qmake_QT=['multimedia']), 77 'QtMultimediaWidgets': ModuleMetadata( 78 qmake_QT=['multimediawidgets', 79 'multimedia']), 80 'QtNetwork': ModuleMetadata(qmake_QT=['network', '-gui']), 81 'QtNfc': ModuleMetadata(qmake_QT=['nfc', '-gui']), 82 'QtOpenGL': ModuleMetadata(qmake_QT=['opengl']), 83 'QtPositioning': ModuleMetadata(qmake_QT=['positioning']), 84 'QtPrintSupport': ModuleMetadata(qmake_QT=['printsupport']), 85 'QtQml': ModuleMetadata(qmake_QT=['qml'], qpy_lib=True), 86 'QtQuick': ModuleMetadata(qmake_QT=['quick'], qpy_lib=True), 87 'QtQuick3D': ModuleMetadata(qmake_QT=['quick3d']), 88 'QtQuickWidgets': ModuleMetadata(qmake_QT=['quickwidgets']), 89 'QtRemoteObjects': ModuleMetadata(qmake_QT=['remoteobjects', '-gui']), 90 'QtSensors': ModuleMetadata(qmake_QT=['sensors']), 91 'QtSerialPort': ModuleMetadata(qmake_QT=['serialport']), 92 'QtSql': ModuleMetadata(qmake_QT=['sql', 'widgets']), 93 'QtSvg': ModuleMetadata(qmake_QT=['svg']), 94 'QtTest': ModuleMetadata(qmake_QT=['testlib', 'widgets']), 95 'QtTextToSpeech': ModuleMetadata(qmake_QT=['texttospeech', '-gui']), 96 'QtWebChannel': ModuleMetadata( 97 qmake_QT=['webchannel', 'network', 98 '-gui']), 99 'QtWebKit': ModuleMetadata(qmake_QT=['webkit', 'network']), 100 'QtWebKitWidgets': ModuleMetadata( 101 qmake_QT=['webkitwidgets', 102 'printsupport']), 103 'QtWebSockets': ModuleMetadata(qmake_QT=['websockets', '-gui']), 104 'QtWidgets': ModuleMetadata(qmake_QT=['widgets'], qpy_lib=True), 105 'QtWinExtras': ModuleMetadata(qmake_QT=['winextras', 'widgets']), 106 'QtX11Extras': ModuleMetadata(qmake_QT=['x11extras']), 107 'QtXml': ModuleMetadata(qmake_QT=['xml', '-gui']), 108 'QtXmlPatterns': ModuleMetadata( 109 qmake_QT=['xmlpatterns', '-gui', 110 'network']), 111 112 # The OpenGL wrappers. 113 '_QOpenGLFunctions_1_0': ModuleMetadata(public=False), 114 '_QOpenGLFunctions_1_1': ModuleMetadata(public=False), 115 '_QOpenGLFunctions_1_2': ModuleMetadata(public=False), 116 '_QOpenGLFunctions_1_3': ModuleMetadata(public=False), 117 '_QOpenGLFunctions_1_4': ModuleMetadata(public=False), 118 '_QOpenGLFunctions_1_5': ModuleMetadata(public=False), 119 '_QOpenGLFunctions_2_0': ModuleMetadata(public=False), 120 '_QOpenGLFunctions_2_1': ModuleMetadata(public=False), 121 '_QOpenGLFunctions_3_0': ModuleMetadata(public=False), 122 '_QOpenGLFunctions_3_1': ModuleMetadata(public=False), 123 '_QOpenGLFunctions_3_2_Compatibility': ModuleMetadata(public=False), 124 '_QOpenGLFunctions_3_2_Core': ModuleMetadata(public=False), 125 '_QOpenGLFunctions_3_3_Compatibility': ModuleMetadata(public=False), 126 '_QOpenGLFunctions_3_3_Core': ModuleMetadata(public=False), 127 '_QOpenGLFunctions_4_0_Compatibility': ModuleMetadata(public=False), 128 '_QOpenGLFunctions_4_0_Core': ModuleMetadata(public=False), 129 '_QOpenGLFunctions_4_1_Compatibility': ModuleMetadata(public=False), 130 '_QOpenGLFunctions_4_1_Core': ModuleMetadata(public=False), 131 '_QOpenGLFunctions_4_2_Compatibility': ModuleMetadata(public=False), 132 '_QOpenGLFunctions_4_2_Core': ModuleMetadata(public=False), 133 '_QOpenGLFunctions_4_3_Compatibility': ModuleMetadata(public=False), 134 '_QOpenGLFunctions_4_3_Core': ModuleMetadata(public=False), 135 '_QOpenGLFunctions_4_4_Compatibility': ModuleMetadata(public=False), 136 '_QOpenGLFunctions_4_4_Core': ModuleMetadata(public=False), 137 '_QOpenGLFunctions_4_5_Compatibility': ModuleMetadata(public=False), 138 '_QOpenGLFunctions_4_5_Core': ModuleMetadata(public=False), 139 '_QOpenGLFunctions_ES2': ModuleMetadata(public=False), 140 141 # Internal modules. 142 'pylupdate': ModuleMetadata(qmake_QT=['xml', '-gui'], 143 qpy_lib=True, public=False), 144 'pyrcc': ModuleMetadata(qmake_QT=['xml', '-gui'], 145 qpy_lib=True, public=False), 146} 147 148 149# The component modules that make up the composite Qt module. SIP is broken in 150# its handling of composite module in that a component module must be %Included 151# before it is first %Imported. In other words, a module must appear before 152# any modules that depend on it. 153COMPOSITE_COMPONENTS = ( 154 'QtCore', 155 'QtAndroidExtras', 'QtDBus', 'QtGui', 'QtNetwork', 156 'QtSensors', 'QtSerialPort', 'QtMultimedia', 'QtQml', 'QtWebKit', 157 'QtWidgets', 'QtXml', 'QtXmlPatterns', 'QtAxContainer', 'QtDesigner', 158 'QtHelp', 'QtMultimediaWidgets', 'QtOpenGL', 159 'QtPrintSupport', 'QtQuick', 'QtSql', 'QtSvg', 'QtTest', 160 'QtWebKitWidgets', 'QtBluetooth', 'QtMacExtras', 'QtPositioning', 161 'QtWinExtras', 'QtX11Extras', 'QtQuick3D', 'QtQuickWidgets', 162 'QtWebSockets', 'Enginio', 'QtWebChannel', 163 'QtLocation', 'QtNfc', 'QtRemoteObjects', 'QtTextToSpeech' 164) 165 166 167def error(msg): 168 """ Display an error message and terminate. msg is the text of the error 169 message. 170 """ 171 172 sys.stderr.write(format("Error: " + msg) + "\n") 173 sys.exit(1) 174 175 176def inform(msg): 177 """ Display an information message. msg is the text of the error message. 178 """ 179 180 sys.stdout.write(format(msg) + "\n") 181 182 183def format(msg, left_margin=0, right_margin=78): 184 """ Format a message by inserting line breaks at appropriate places. msg 185 is the text of the message. left_margin is the position of the left 186 margin. right_margin is the position of the right margin. Returns the 187 formatted message. 188 """ 189 190 curs = left_margin 191 fmsg = " " * left_margin 192 193 for w in msg.split(): 194 l = len(w) 195 if curs != left_margin and curs + l > right_margin: 196 fmsg = fmsg + "\n" + (" " * left_margin) 197 curs = left_margin 198 199 if curs > left_margin: 200 fmsg = fmsg + " " 201 curs = curs + 1 202 203 fmsg = fmsg + w 204 curs = curs + l 205 206 return fmsg 207 208 209def version_to_sip_tag(version): 210 """ Convert a version number to a SIP tag. version is the version number. 211 """ 212 213 # Anything after Qt v5 is assumed to be Qt v6.0. 214 if version > 0x060000: 215 version = 0x060000 216 217 major = (version >> 16) & 0xff 218 minor = (version >> 8) & 0xff 219 patch = version & 0xff 220 221 # Qt v5.12.4 was the last release where we updated for a patch version. 222 if (major, minor) >= (5, 13): 223 patch = 0 224 elif (major, minor) == (5, 12): 225 if patch > 4: 226 patch = 4 227 228 return 'Qt_%d_%d_%d' % (major, minor, patch) 229 230 231def version_to_string(version, parts=3): 232 """ Convert an n-part version number encoded as a hexadecimal value to a 233 string. version is the version number. Returns the string. 234 """ 235 236 part_list = [str((version >> 16) & 0xff)] 237 238 if parts > 1: 239 part_list.append(str((version >> 8) & 0xff)) 240 241 if parts > 2: 242 part_list.append(str(version & 0xff)) 243 244 return '.'.join(part_list) 245 246 247class ConfigurationFileParser: 248 """ A parser for configuration files. """ 249 250 def __init__(self, config_file): 251 """ Read and parse a configuration file. """ 252 253 self._config = {} 254 self._extrapolating = [] 255 256 cfg = open(config_file) 257 line_nr = 0 258 last_name = None 259 260 section = '' 261 section_config = {} 262 self._config[section] = section_config 263 264 for l in cfg: 265 line_nr += 1 266 267 # Strip comments. 268 l = l.split('#')[0] 269 270 # See if this might be part of a multi-line. 271 multiline = (last_name is not None and len(l) != 0 and l[0] == ' ') 272 273 l = l.strip() 274 275 if l == '': 276 last_name = None 277 continue 278 279 # See if this is a new section. 280 if l[0] == '[' and l[-1] == ']': 281 section = l[1:-1].strip() 282 if section == '': 283 error( 284 "%s:%d: Empty section name." % ( 285 config_file, line_nr)) 286 287 if section in self._config: 288 error( 289 "%s:%d: Section '%s' defined more than once." % ( 290 config_file, line_nr, section)) 291 292 section_config = {} 293 self._config[section] = section_config 294 295 last_name = None 296 continue 297 298 parts = l.split('=', 1) 299 if len(parts) == 2: 300 name = parts[0].strip() 301 value = parts[1].strip() 302 elif multiline: 303 name = last_name 304 value = section_config[last_name] 305 value += ' ' + l 306 else: 307 name = value = '' 308 309 if name == '' or value == '': 310 error("%s:%d: Invalid line." % (config_file, line_nr)) 311 312 section_config[name] = value 313 last_name = name 314 315 cfg.close() 316 317 def sections(self): 318 """ Return the list of sections, excluding the default one. """ 319 320 return [s for s in self._config.keys() if s != ''] 321 322 def preset(self, name, value): 323 """ Add a preset value to the configuration. """ 324 325 self._config[''][name] = value 326 327 def get(self, section, name, default=None): 328 """ Get a configuration value while extrapolating. """ 329 330 # Get the name from the section, or the default section. 331 value = self._config[section].get(name) 332 if value is None: 333 value = self._config[''].get(name) 334 if value is None: 335 if default is None: 336 error( 337 "Configuration file references non-existent name " 338 "'%s'." % name) 339 340 return default 341 342 # Handle any extrapolations. 343 parts = value.split('%(', 1) 344 while len(parts) == 2: 345 prefix, tail = parts 346 347 parts = tail.split(')', 1) 348 if len(parts) != 2: 349 error( 350 "Configuration file contains unterminated " 351 "extrapolated name '%s'." % tail) 352 353 xtra_name, suffix = parts 354 355 if xtra_name in self._extrapolating: 356 error( 357 "Configuration file contains a recursive reference to " 358 "'%s'." % xtra_name) 359 360 self._extrapolating.append(xtra_name) 361 xtra_value = self.get(section, xtra_name) 362 self._extrapolating.pop() 363 364 value = prefix + xtra_value + suffix 365 366 parts = value.split('%(', 1) 367 368 return value 369 370 def getboolean(self, section, name, default): 371 """ Get a boolean configuration value while extrapolating. """ 372 373 value = self.get(section, name, default) 374 375 # In case the default was returned. 376 if isinstance(value, bool): 377 return value 378 379 if value in ('True', 'true', '1'): 380 return True 381 382 if value in ('False', 'false', '0'): 383 return False 384 385 error( 386 "Configuration file contains invalid boolean value for " 387 "'%s'." % name) 388 389 def getlist(self, section, name, default): 390 """ Get a list configuration value while extrapolating. """ 391 392 value = self.get(section, name, default) 393 394 # In case the default was returned. 395 if isinstance(value, list): 396 return value 397 398 return value.split() 399 400 401class HostPythonConfiguration: 402 """ A container for the host Python configuration. """ 403 404 def __init__(self): 405 """ Initialise the configuration. """ 406 407 self.platform = sys.platform 408 self.version = sys.hexversion >> 8 409 410 self.inc_dir = sysconfig.get_python_inc() 411 self.venv_inc_dir = sysconfig.get_python_inc(prefix=sys.prefix) 412 self.module_dir = sysconfig.get_python_lib(plat_specific=1) 413 self.debug = hasattr(sys, 'gettotalrefcount') 414 415 if sys.platform == 'win32': 416 bin_dir = sys.exec_prefix 417 418 try: 419 # Python v3.3 and later. 420 base_prefix = sys.base_prefix 421 422 if sys.exec_prefix != sys.base_exec_prefix: 423 bin_dir += '\\Scripts' 424 425 except AttributeError: 426 try: 427 # virtualenv for Python v2. 428 base_prefix = sys.real_prefix 429 bin_dir += '\\Scripts' 430 431 except AttributeError: 432 # We can't detect the base prefix in Python v3 prior to 433 # v3.3. 434 base_prefix = sys.prefix 435 436 self.bin_dir = bin_dir 437 self.data_dir = sys.prefix 438 self.lib_dir = base_prefix + '\\libs' 439 else: 440 self.bin_dir = sys.exec_prefix + '/bin' 441 self.data_dir = sys.prefix + '/share' 442 self.lib_dir = sys.prefix + '/lib' 443 444 # The name of the interpreter used by the pyuic5 wrapper. 445 if sys.platform == 'darwin': 446 # The installation of MacOS's python is a mess that changes from 447 # version to version and where sys.executable is useless. 448 449 py_major = self.version >> 16 450 py_minor = (self.version >> 8) & 0xff 451 452 # In Python v3.4 and later there is no pythonw. 453 if (py_major == 3 and py_minor >= 4) or py_major >= 4: 454 exe = "python" 455 else: 456 exe = "pythonw" 457 458 self.pyuic_interpreter = '%s%d.%d' % (exe, py_major, py_minor) 459 else: 460 self.pyuic_interpreter = sys.executable 461 462 463class TargetQtConfiguration: 464 """ A container for the target Qt configuration. """ 465 466 def __init__(self, qmake): 467 """ Initialise the configuration. qmake is the full pathname of the 468 qmake executable that will provide the configuration. 469 """ 470 471 inform("Querying qmake about your Qt installation...") 472 473 pipe = os.popen(' '.join([qmake, '-query'])) 474 475 for l in pipe: 476 l = l.strip() 477 478 tokens = l.split(':', 1) 479 if isinstance(tokens, list): 480 if len(tokens) != 2: 481 error("Unexpected output from qmake: '%s'\n" % l) 482 483 name, value = tokens 484 else: 485 name = tokens 486 value = None 487 488 name = name.replace('/', '_') 489 490 setattr(self, name, value) 491 492 pipe.close() 493 494 495class TargetConfiguration: 496 """ A container for configuration information about the target. """ 497 498 def __init__(self): 499 """ Initialise the configuration with default values. """ 500 501 # Values based on the host Python configuration. 502 py_config = HostPythonConfiguration() 503 self.py_debug = py_config.debug 504 self.py_inc_dir = py_config.inc_dir 505 self.py_venv_inc_dir = py_config.venv_inc_dir 506 self.py_lib_dir = py_config.lib_dir 507 self.py_platform = py_config.platform 508 self.py_version = py_config.version 509 self.pyqt_bin_dir = py_config.bin_dir 510 self.pyqt_module_dir = py_config.module_dir 511 self.pyqt_stubs_dir = os.path.join(py_config.module_dir, 'PyQt5') 512 self.pyqt_sip_dir = os.path.join(py_config.data_dir, 'sip', 'PyQt5') 513 self.pyuic_interpreter = py_config.pyuic_interpreter 514 515 # Remaining values. 516 self.abi_version = None 517 self.dbus_inc_dirs = [] 518 self.dbus_lib_dirs = [] 519 self.dbus_libs = [] 520 self.debug = False 521 self.designer_plugin_dir = '' 522 self.license_dir = source_path('sip') 523 self.link_full_dll = False 524 self.no_designer_plugin = False 525 self.no_docstrings = False 526 self.no_pydbus = False 527 self.no_qml_plugin = False 528 self.no_tools = False 529 self.prot_is_public = (self.py_platform.startswith('linux') or self.py_platform.startswith('freebsd') or self.py_platform == 'darwin') 530 self.qmake = self._find_exe('qmake') 531 self.qmake_spec = '' 532 self.qmake_spec_default = '' 533 self.qmake_variables = [] 534 self.qml_debug = False 535 self.py_pylib_dir = '' 536 self.py_pylib_lib = '' 537 self.py_pyshlib = '' 538 self.pydbus_inc_dir = '' 539 self.pydbus_module_dir = '' 540 self.pyqt_disabled_features = [] 541 self.pyqt_modules = [] 542 self.qml_plugin_dir = '' 543 self.qsci_api = False 544 self.qsci_api_dir = '' 545 self.qt_shared = False 546 self.qt_version = 0 547 self.sip = self._find_exe('sip5', 'sip') 548 self.sip_inc_dir = None 549 self.static = False 550 self.sysroot = '' 551 self.vend_enabled = False 552 self.vend_inc_dir = '' 553 self.vend_lib_dir = '' 554 555 def from_configuration_file(self, config_file): 556 """ Initialise the configuration with values from a file. config_file 557 is the name of the configuration file. 558 """ 559 560 inform("Reading configuration from %s..." % config_file) 561 562 parser = ConfigurationFileParser(config_file) 563 564 # Populate some presets from the command line. 565 version = version_to_string(self.py_version).split('.') 566 parser.preset('py_major', version[0]) 567 parser.preset('py_minor', version[1]) 568 569 parser.preset('sysroot', self.sysroot) 570 571 # Find the section corresponding to the version of Qt. 572 qt_major = self.qt_version >> 16 573 section = None 574 latest_section = -1 575 576 for name in parser.sections(): 577 parts = name.split() 578 if len(parts) != 2 or parts[0] != 'Qt': 579 continue 580 581 section_qt_version = version_from_string(parts[1]) 582 if section_qt_version is None: 583 continue 584 585 # Major versions must match. 586 if section_qt_version >> 16 != self.qt_version >> 16: 587 continue 588 589 # It must be no later that the version of Qt. 590 if section_qt_version > self.qt_version: 591 continue 592 593 # Save it if it is the latest so far. 594 if section_qt_version > latest_section: 595 section = name 596 latest_section = section_qt_version 597 598 if section is None: 599 error("%s does not define a section that covers Qt v%s." % (config_file, version_to_string(self.qt_version))) 600 601 self.py_platform = parser.get(section, 'py_platform', self.py_platform) 602 self.py_debug = parser.get(section, 'py_debug', self.py_debug) 603 self.py_inc_dir = parser.get(section, 'py_inc_dir', self.py_inc_dir) 604 self.py_venv_inc_dir = self.py_inc_dir 605 self.py_pylib_dir = parser.get(section, 'py_pylib_dir', 606 self.py_pylib_dir) 607 self.py_pylib_lib = parser.get(section, 'py_pylib_lib', 608 self.py_pylib_lib) 609 self.py_pyshlib = parser.get(section, 'py_pyshlib', self.py_pyshlib) 610 611 self.qt_shared = parser.getboolean(section, 'qt_shared', 612 self.qt_shared) 613 614 self.pyqt_disabled_features = parser.getlist(section, 615 'pyqt_disabled_features', self.pyqt_disabled_features) 616 self.pyqt_modules = parser.getlist(section, 'pyqt_modules', 617 self.pyqt_modules) 618 self.pyqt_module_dir = parser.get(section, 'pyqt_module_dir', 619 self.pyqt_module_dir) 620 self.pyqt_bin_dir = parser.get(section, 'pyqt_bin_dir', 621 self.pyqt_bin_dir) 622 self.pyqt_stubs_dir = parser.get(section, 'pyqt_stubs_dir', 623 self.pyqt_stubs_dir) 624 self.pyqt_sip_dir = parser.get(section, 'pyqt_sip_dir', 625 self.pyqt_sip_dir) 626 self.pyuic_interpreter = parser.get(section, 'pyuic_interpreter', 627 self.pyuic_interpreter) 628 629 def from_introspection(self, verbose, debug): 630 """ Initialise the configuration by introspecting the system. """ 631 632 # Check that the enum module is available. 633 try: 634 import enum 635 except ImportError: 636 error( 637 "Unable to import enum. Please install the enum34 " 638 "package from PyPI.") 639 640 # Get the details of the Python interpreter library. 641 py_major = self.py_version >> 16 642 py_minor = (self.py_version >> 8) & 0x0ff 643 644 if sys.platform == 'win32': 645 debug_suffix = self.get_win32_debug_suffix() 646 647 # See if we are using the limited API. 648 limited = (py_major == 3 and py_minor >= 4) 649 if self.py_debug or self.link_full_dll: 650 limited = False 651 652 if limited: 653 pylib_lib = 'python%d%s' % (py_major, debug_suffix) 654 else: 655 pylib_lib = 'python%d%d%s' % (py_major, py_minor, debug_suffix) 656 657 pylib_dir = self.py_lib_dir 658 659 # Assume Python is a DLL on Windows. 660 pyshlib = pylib_lib 661 else: 662 abi = getattr(sys, 'abiflags', '') 663 pylib_lib = 'python%d.%d%s' % (py_major, py_minor, abi) 664 pylib_dir = pyshlib = '' 665 666 # Use distutils to get the additional configuration. 667 ducfg = sysconfig.get_config_vars() 668 669 config_args = ducfg.get('CONFIG_ARGS', '') 670 671 dynamic_pylib = '--enable-shared' in config_args 672 if not dynamic_pylib: 673 dynamic_pylib = '--enable-framework' in config_args 674 675 if dynamic_pylib: 676 pyshlib = ducfg.get('LDLIBRARY', '') 677 678 exec_prefix = ducfg['exec_prefix'] 679 multiarch = ducfg.get('MULTIARCH', '') 680 libdir = ducfg['LIBDIR'] 681 682 if glob.glob('%s/lib/libpython%d.%d*' % (exec_prefix, py_major, py_minor)): 683 pylib_dir = exec_prefix + '/lib' 684 elif multiarch != '' and glob.glob('%s/lib/%s/libpython%d.%d*' % (exec_prefix, multiarch, py_major, py_minor)): 685 pylib_dir = exec_prefix + '/lib/' + multiarch 686 elif glob.glob('%s/libpython%d.%d*' % (libdir, py_major, py_minor)): 687 pylib_dir = libdir 688 689 self.py_pylib_dir = pylib_dir 690 self.py_pylib_lib = pylib_lib 691 self.py_pyshlib = pyshlib 692 693 # Apply sysroot where necessary. 694 if self.sysroot != '': 695 self.py_inc_dir = self._apply_sysroot(self.py_inc_dir) 696 self.py_venv_inc_dir = self._apply_sysroot(self.py_venv_inc_dir) 697 self.py_pylib_dir = self._apply_sysroot(self.py_pylib_dir) 698 self.pyqt_bin_dir = self._apply_sysroot(self.pyqt_bin_dir) 699 self.pyqt_module_dir = self._apply_sysroot(self.pyqt_module_dir) 700 self.pyqt_stubs_dir = self._apply_sysroot(self.pyqt_stubs_dir) 701 self.pyqt_sip_dir = self._apply_sysroot(self.pyqt_sip_dir) 702 703 inform("Determining the details of your Qt installation...") 704 705 # Compile and run the QtCore test program. 706 test = compile_test_program(self, verbose, 'QtCore', debug=debug) 707 if test is None: 708 error("Failed to determine the detail of your Qt installation. Try again using the --verbose flag to see more detail about the problem.") 709 710 lines = run_test_program('QtCore', test, verbose) 711 712 self.qt_shared = (lines[0] == 'shared') 713 self.pyqt_disabled_features = lines[1:] 714 715 if self.pyqt_disabled_features: 716 inform("Disabled QtCore features: %s" % ', '.join( 717 self.pyqt_disabled_features)) 718 719 def _apply_sysroot(self, dir_name): 720 """ Replace any leading sys.prefix of a directory name with sysroot. 721 """ 722 723 if dir_name.startswith(sys.prefix): 724 dir_name = self.sysroot + dir_name[len(sys.prefix):] 725 726 return dir_name 727 728 def get_win32_debug_suffix(self): 729 """ Return the debug-dependent suffix appended to the name of Windows 730 libraries. 731 """ 732 733 return '_d' if self.py_debug else '' 734 735 def get_qt_configuration(self): 736 """ Get the Qt configuration that can be extracted from qmake. """ 737 738 # Query qmake. 739 qt_config = TargetQtConfiguration(self.qmake) 740 741 self.qt_version = 0 742 try: 743 qt_version_str = qt_config.QT_VERSION 744 for v in qt_version_str.split('.'): 745 self.qt_version *= 256 746 self.qt_version += int(v) 747 except AttributeError: 748 qt_version_str = "3" 749 750 # Check the Qt version number as soon as possible. 751 if self.qt_version < 0x050000: 752 error( 753 "PyQt5 requires Qt v5.0 or later. You seem to be using " 754 "v%s. Use the --qmake flag to specify the correct version " 755 "of qmake." % qt_version_str) 756 757 self.designer_plugin_dir = qt_config.QT_INSTALL_PLUGINS + '/designer' 758 self.qml_plugin_dir = qt_config.QT_INSTALL_PLUGINS + '/PyQt5' 759 760 if self.sysroot == '': 761 self.sysroot = qt_config.QT_SYSROOT 762 763 # By default, install the API file if QScintilla seems to be installed 764 # in the default location. 765 self.qsci_api_dir = os.path.join(qt_config.QT_INSTALL_DATA, 'qsci') 766 self.qsci_api = os.path.isdir(self.qsci_api_dir) 767 768 # Save the default qmake spec. and finalise the value we want to use. 769 self.qmake_spec_default = qt_config.QMAKE_SPEC 770 771 # On Windows for Qt versions prior to v5.9.0 we need to be explicit 772 # about the qmake spec. 773 if self.qt_version < 0x050900 and self.py_platform == 'win32': 774 if self.py_version >= 0x030500: 775 self.qmake_spec = 'win32-msvc2015' 776 elif self.py_version >= 0x030300: 777 self.qmake_spec = 'win32-msvc2010' 778 elif self.py_version >= 0x020600: 779 self.qmake_spec = 'win32-msvc2008' 780 elif self.py_version >= 0x020400: 781 self.qmake_spec = 'win32-msvc.net' 782 else: 783 self.qmake_spec = 'win32-msvc' 784 else: 785 # Otherwise use the default. 786 self.qmake_spec = self.qmake_spec_default 787 788 # The binary OS/X Qt installer used to default to XCode. If so then 789 # use macx-clang. 790 if self.qmake_spec == 'macx-xcode': 791 # This will exist (and we can't check anyway). 792 self.qmake_spec = 'macx-clang' 793 794 def post_configuration(self): 795 """ Handle any remaining default configuration after having read a 796 configuration file or introspected the system. 797 """ 798 799 # The platform may have changed so update the default. 800 if self.py_platform.startswith('linux') or self.py_platform.startswith('freebsd') or self.py_platform == 'darwin': 801 self.prot_is_public = True 802 803 self.vend_inc_dir = self.py_venv_inc_dir 804 self.vend_lib_dir = self.py_lib_dir 805 806 def apply_pre_options(self, opts): 807 """ Apply options from the command line that influence subsequent 808 configuration. opts are the command line options. 809 """ 810 811 # On Windows the interpreter must be a debug build if a debug version 812 # is to be built and vice versa. 813 if sys.platform == 'win32': 814 if opts.debug: 815 if not self.py_debug: 816 error( 817 "A debug version of Python must be used when " 818 "--debug is specified.") 819 elif self.py_debug: 820 error( 821 "--debug must be specified when a debug version of " 822 "Python is used.") 823 824 self.debug = opts.debug 825 826 # Get the target Python version. 827 if opts.target_py_version is not None: 828 self.py_version = opts.target_py_version 829 830 # Get the system root. 831 if opts.sysroot is not None: 832 self.sysroot = opts.sysroot 833 834 # Determine how to run qmake. 835 if opts.qmake is not None: 836 self.qmake = opts.qmake 837 838 # On Windows add the directory that probably contains the Qt DLLs 839 # to PATH. 840 if sys.platform == 'win32': 841 path = os.environ['PATH'] 842 path = os.path.dirname(self.qmake) + ';' + path 843 os.environ['PATH'] = path 844 845 if self.qmake is None: 846 error( 847 "Use the --qmake argument to explicitly specify a working " 848 "Qt qmake.") 849 850 if opts.qmakespec is not None: 851 self.qmake_spec = opts.qmakespec 852 853 if opts.sipincdir is not None: 854 self.sip_inc_dir = opts.sipincdir 855 856 def apply_post_options(self, opts): 857 """ Apply options from the command line that override the previous 858 configuration. opts are the command line options. 859 """ 860 861 self.pyqt_disabled_features.extend(opts.disabled_features) 862 863 if opts.assumeshared: 864 self.qt_shared = True 865 866 if opts.bindir is not None: 867 self.pyqt_bin_dir = opts.bindir 868 869 if opts.licensedir is not None: 870 self.license_dir = opts.licensedir 871 872 if opts.link_full_dll: 873 self.link_full_dll = True 874 875 if opts.designerplugindir is not None: 876 self.designer_plugin_dir = opts.designerplugindir 877 878 if opts.qmlplugindir is not None: 879 self.qml_plugin_dir = opts.qmlplugindir 880 881 if opts.destdir is not None: 882 self.pyqt_module_dir = opts.destdir 883 884 if len(opts.modules) > 0: 885 self.pyqt_modules = opts.modules 886 887 if opts.nodesignerplugin: 888 self.no_designer_plugin = True 889 890 if opts.nodocstrings: 891 self.no_docstrings = True 892 893 if opts.nopydbus: 894 self.no_pydbus = True 895 896 if opts.noqmlplugin: 897 self.no_qml_plugin = True 898 899 if opts.notools: 900 self.no_tools = True 901 902 if opts.protispublic is not None: 903 self.prot_is_public = opts.protispublic 904 905 if opts.pydbusincdir is not None: 906 self.pydbus_inc_dir = opts.pydbusincdir 907 908 if opts.pyuicinterpreter is not None: 909 self.pyuic_interpreter = opts.pyuicinterpreter 910 911 if opts.qml_debug: 912 self.qml_debug = True 913 914 if opts.qsciapidir is not None: 915 self.qsci_api_dir = opts.qsciapidir 916 917 # Assume we want to install the API file if we have provided an 918 # installation directory. 919 self.qsci_api = True 920 921 if opts.qsciapi is not None: 922 self.qsci_api = opts.qsciapi 923 924 if opts.qsciapidir is not None: 925 self.qsci_api_dir = opts.qsciapidir 926 927 if opts.stubsdir is not None: 928 self.pyqt_stubs_dir = opts.stubsdir 929 elif not opts.install_stubs: 930 self.pyqt_stubs_dir = '' 931 932 if opts.sip is not None: 933 self.sip = opts.sip 934 935 if opts.abi_version is not None: 936 if not self.using_sip5(): 937 error("The --abi-version argument can only be used with sip5.") 938 939 self.abi_version = opts.abi_version 940 941 if opts.sipdir is not None: 942 self.pyqt_sip_dir = opts.sipdir 943 elif not opts.install_sipfiles: 944 self.pyqt_sip_dir = '' 945 946 if opts.static: 947 self.static = True 948 949 if opts.vendenabled: 950 self.vend_enabled = True 951 952 if opts.vendincdir is not None: 953 self.vend_inc_dir = opts.vendincdir 954 955 if opts.vendlibdir is not None: 956 self.vend_lib_dir = opts.vendlibdir 957 958 # Handle any conflicts. 959 if not self.qt_shared: 960 if not self.static: 961 error( 962 "Qt has been built as static libraries so the " 963 "--static argument should be used.") 964 965 if self.vend_enabled and self.static: 966 error( 967 "Using the VendorID package when building static " 968 "libraries makes no sense.") 969 970 def get_pylib_link_arguments(self, name=True): 971 """ Return a string to append to qmake's LIBS macro to link against the 972 Python interpreter library. 973 """ 974 975 args = qmake_quote('-L' + self.py_pylib_dir) 976 977 if name: 978 args += ' -l' + self.py_pylib_lib 979 980 return args 981 982 def add_sip_h_directives(self, pro_lines): 983 """ Add the directives required by sip.h to a sequence of .pro file 984 lines. 985 """ 986 987 # Make sure the include directory is searched before the Python include 988 # directory if they are different. 989 pro_lines.append('INCLUDEPATH += %s' % qmake_quote(self.sip_inc_dir)) 990 if self.py_inc_dir != self.sip_inc_dir: 991 pro_lines.append('INCLUDEPATH += %s' % qmake_quote(self.py_inc_dir)) 992 993 # Python.h on Windows seems to embed the need for pythonXY.lib, so tell 994 # it where it is. 995 if not self.static: 996 pro_lines.extend(['win32 {', 997 ' LIBS += ' + self.get_pylib_link_arguments(name=False), 998 '}']) 999 1000 def using_sip5(self): 1001 """ Return True if sip5 is being used. """ 1002 1003 return os.path.basename(self.sip).startswith('sip5') 1004 1005 @staticmethod 1006 def _find_exe(*exes): 1007 """ Find an executable, ie. the first on the path. """ 1008 1009 path_dirs = os.environ.get('PATH', '').split(os.pathsep) 1010 1011 for exe in exes: 1012 # Strip any surrounding quotes. 1013 if exe.startswith('"') and exe.endswith('"'): 1014 exe = exe[1:-1] 1015 1016 if sys.platform == 'win32': 1017 exe = exe + '.exe' 1018 1019 for d in path_dirs: 1020 exe_path = os.path.join(d, exe) 1021 1022 if os.access(exe_path, os.X_OK): 1023 return exe_path 1024 1025 return None 1026 1027 1028def create_optparser(target_config): 1029 """ Create the parser for the command line. target_config is the target 1030 configuration containing default values. 1031 """ 1032 1033 def store_abspath(option, opt_str, value, parser): 1034 setattr(parser.values, option.dest, os.path.abspath(value)) 1035 1036 def store_abspath_dir(option, opt_str, value, parser): 1037 if not os.path.isdir(value): 1038 raise optparse.OptionValueError("'%s' is not a directory" % value) 1039 setattr(parser.values, option.dest, os.path.abspath(value)) 1040 1041 def store_abspath_exe(option, opt_str, value, parser): 1042 if not os.access(value, os.X_OK): 1043 raise optparse.OptionValueError("'%s' is not an executable" % value) 1044 setattr(parser.values, option.dest, os.path.abspath(value)) 1045 1046 def store_abspath_file(option, opt_str, value, parser): 1047 if not os.path.isfile(value): 1048 raise optparse.OptionValueError("'%s' is not a file" % value) 1049 setattr(parser.values, option.dest, os.path.abspath(value)) 1050 1051 def store_version(option, opt_str, value, parser): 1052 version = version_from_string(value) 1053 if version is None: 1054 raise optparse.OptionValueError( 1055 "'%s' is not a valid version number" % value) 1056 setattr(parser.values, option.dest, version) 1057 1058 p = optparse.OptionParser(usage="python %prog [opts] [name=value] " 1059 "[name+=value]", version=PYQT_VERSION_STR) 1060 1061 # Note: we don't use %default to be compatible with Python 2.3. 1062 p.add_option("--abi-version", dest='abi_version', default=None, 1063 metavar="VERSION", 1064 help="the SIP ABI version to use (sip5 only)") 1065 p.add_option("--static", "-k", dest='static', default=False, 1066 action='store_true', 1067 help="build modules as static libraries") 1068 p.add_option("--no-docstrings", dest='nodocstrings', default=False, 1069 action='store_true', 1070 help="disable the generation of docstrings") 1071 p.add_option("--trace", "-r", dest='tracing', default=False, 1072 action='store_true', 1073 help="build modules with tracing enabled") 1074 p.add_option("--debug", "-u", dest='debug', default=False, 1075 action='store_true', 1076 help="build modules with debugging symbols") 1077 p.add_option("--qml-debug", dest='qml_debug', default=False, 1078 action='store_true', 1079 help="enable the QML debugging infrastructure") 1080 p.add_option("--verbose", "-w", dest='verbose', default=False, 1081 action='store_true', 1082 help="enable verbose output during configuration") 1083 1084 p.add_option("--concatenate", "-c", dest='concat', default=False, 1085 action='store_true', 1086 help="concatenate each module's C++ source files") 1087 p.add_option("--concatenate-split", "-j", dest='split', type='int', 1088 default=1, metavar="N", 1089 help="split the concatenated C++ source files into N pieces " 1090 "[default: 1]") 1091 1092 # Configuration. 1093 g = optparse.OptionGroup(p, title="Configuration") 1094 g.add_option("--confirm-license", dest='license_confirmed', default=False, 1095 action='store_true', 1096 help="confirm acceptance of the license") 1097 g.add_option("--license-dir", dest='licensedir', type='string', 1098 default=None, action='callback', callback=store_abspath, 1099 metavar="DIR", 1100 help="the license file can be found in DIR [default: " 1101 "%s]" % target_config.license_dir) 1102 g.add_option("--target-py-version", dest='target_py_version', 1103 type='string', action='callback', callback=store_version, 1104 metavar="VERSION", 1105 help="the major.minor version of the target Python [default: " 1106 "%s]" % version_to_string(target_config.py_version, 1107 parts=2)) 1108 g.add_option("--link-full-dll", dest='link_full_dll', 1109 default=False, action='store_true', 1110 help="on Windows link against the full Python DLL rather than the " 1111 "limited API DLL") 1112 g.add_option("--sysroot", dest='sysroot', type='string', action='callback', 1113 callback=store_abspath_dir, metavar="DIR", 1114 help="DIR is the target system root directory") 1115 g.add_option("--spec", dest='qmakespec', default=None, action='store', 1116 metavar="SPEC", 1117 help="pass -spec SPEC to qmake") 1118 g.add_option("--disable", dest='disabled_modules', default=[], 1119 action='append', metavar="MODULE", 1120 help="disable the specified MODULE [default: checks for all " 1121 "modules will be enabled]") 1122 g.add_option("--disable-feature", dest='disabled_features', default=[], 1123 action='append', metavar="FEATURE", 1124 help="disable the specified FEATURE") 1125 g.add_option("--enable", "-e", dest='modules', default=[], action='append', 1126 metavar="MODULE", 1127 help="enable checks for the specified MODULE [default: checks for " 1128 "all modules will be enabled]") 1129 g.add_option("--no-designer-plugin", dest='nodesignerplugin', 1130 default=False, action='store_true', 1131 help="disable the building of the Python plugin for Qt Designer " 1132 "[default: enabled]") 1133 g.add_option("--no-qml-plugin", dest='noqmlplugin', default=False, 1134 action='store_true', 1135 help="disable the building of the Python plugin for qmlscene " 1136 "[default: enabled]") 1137 g.add_option("--assume-shared", dest='assumeshared', default=False, 1138 action='store_true', 1139 help="assume that the Qt libraries have been built as shared " 1140 "libraries [default: check]") 1141 g.add_option("--no-timestamp", "-T", dest='notimestamp', default=False, 1142 action='store_true', 1143 help="suppress timestamps in the header comments of generated " 1144 "code [default: include timestamps]") 1145 g.add_option("--configuration", dest='config_file', type='string', 1146 action='callback', callback=store_abspath_file, metavar="FILE", 1147 help="FILE contains the target configuration") 1148 1149 g.add_option("--protected-is-public", dest='protispublic', default=None, 1150 action='store_true', 1151 help="enable building with 'protected' redefined as 'public' " 1152 "[default: %s]" % 1153 "enabled" if target_config.prot_is_public 1154 else "disabled") 1155 g.add_option("--protected-not-public", dest='protispublic', default=None, 1156 action='store_false', 1157 help="disable building with 'protected' redefined as 'public'") 1158 1159 g.add_option("--pyuic5-interpreter", dest='pyuicinterpreter', 1160 type='string', default=None, action='callback', 1161 callback=store_abspath_exe, metavar="FILE", 1162 help="the name of the Python interpreter to run the pylupdate5, " 1163 "pyrcc5 and pyuic5 wrappers is FILE [default: %s]" % 1164 target_config.pyuic_interpreter) 1165 1166 g.add_option("--qmake", "-q", dest='qmake', type='string', default=None, 1167 action='callback', callback=store_abspath_exe, metavar="FILE", 1168 help="the pathname of qmake is FILE [default: " 1169 "%s]" % (target_config.qmake or "search PATH")) 1170 1171 g.add_option("--sip", dest='sip', type='string', default=None, 1172 action='callback', callback=store_abspath_exe, metavar="FILE", 1173 help="the pathname of sip is FILE [default: " 1174 "%s]" % (target_config.sip or "None")) 1175 g.add_option("--sip-incdir", dest='sipincdir', type='string', 1176 default=None, action='callback', callback=store_abspath_dir, 1177 metavar="DIR", 1178 help="the directory containing the sip.h header file is DIR " 1179 "[default: %s]" % target_config.sip_inc_dir) 1180 g.add_option("--allow-sip-warnings", dest='fatal_warnings', 1181 default=True, action='store_false', 1182 help="allow sip to issue non-fatal warning messages " 1183 "[default: warning messages are treated as errors]") 1184 1185 g.add_option("--no-python-dbus", dest='nopydbus', 1186 default=False, action='store_true', 1187 help="disable the Qt support for the standard Python DBus " 1188 "bindings [default: enabled]") 1189 g.add_option("--dbus", "-s", dest='pydbusincdir', type='string', 1190 default=None, action='callback', callback=store_abspath_dir, 1191 metavar="DIR", 1192 help="the directory containing the dbus/dbus-python.h header is " 1193 "DIR [default: supplied by pkg-config]") 1194 p.add_option_group(g) 1195 1196 # Installation. 1197 g = optparse.OptionGroup(p, title="Installation") 1198 g.add_option("--bindir", "-b", dest='bindir', type='string', default=None, 1199 action='callback', callback=store_abspath, metavar="DIR", 1200 help="install pyuic5, pyrcc5 and pylupdate5 in DIR [default: " 1201 "%s]" % target_config.pyqt_bin_dir) 1202 g.add_option("--destdir", "-d", dest='destdir', type='string', 1203 default=None, action='callback', callback=store_abspath, 1204 metavar="DIR", 1205 help="install the PyQt5 Python package in DIR [default: " 1206 "%s]" % target_config.pyqt_module_dir) 1207 g.add_option("--designer-plugindir", dest='designerplugindir', 1208 type='string', default=None, action='callback', 1209 callback=store_abspath, metavar="DIR", 1210 help="install the Python plugin for Qt Designer in DIR " 1211 "[default: QT_INSTALL_PLUGINS/designer]") 1212 g.add_option("--qml-plugindir", dest='qmlplugindir', type='string', 1213 default=None, action='callback', callback=store_abspath, 1214 metavar="DIR", 1215 help="install the Python plugin for qmlscene in DIR " 1216 "[default: QT_INSTALL_PLUGINS/PyQt5]") 1217 g.add_option("--no-sip-files", action="store_false", default=True, 1218 dest="install_sipfiles", help="disable the installation of the " 1219 ".sip files [default: enabled]") 1220 g.add_option("--sipdir", "-v", dest='sipdir', type='string', default=None, 1221 action='callback', callback=store_abspath, metavar="DIR", 1222 help="install the PyQt5 .sip files in DIR [default: %s]" % 1223 target_config.pyqt_sip_dir) 1224 g.add_option("--no-dist-info", action="store_false", default=True, 1225 dest="distinfo", 1226 help="do not install the dist-info directory") 1227 g.add_option("--no-stubs", action="store_false", default=True, 1228 dest="install_stubs", help="disable the installation of the PEP " 1229 "484 stub files [default: enabled]") 1230 g.add_option("--stubsdir", dest='stubsdir', type='string', default=None, 1231 action='callback', callback=store_abspath, metavar="DIR", 1232 help="install the PEP 484 stub files in DIR [default: " 1233 "%s]" % target_config.pyqt_stubs_dir) 1234 g.add_option("--no-tools", action="store_true", default=False, 1235 dest="notools", 1236 help="disable the building of pyuic5, pyrcc5 and pylupdate5 " 1237 "[default: enabled]") 1238 p.add_option_group(g) 1239 1240 # Vendor ID. 1241 g = optparse.OptionGroup(p, title="VendorID support") 1242 g.add_option("--vendorid", "-i", dest='vendenabled', default=False, 1243 action='store_true', 1244 help="enable checking of signed interpreters using the VendorID " 1245 "package [default: %s]" % 1246 "enabled" if target_config.vend_enabled else "disabled") 1247 g.add_option("--vendorid-incdir", "-l", dest='vendincdir', type='string', 1248 default=None, action='callback', callback=store_abspath_dir, 1249 metavar="DIR", 1250 help="the VendorID header file is installed in DIR [default: " 1251 "%s]" % target_config.vend_inc_dir) 1252 g.add_option("--vendorid-libdir", "-m", dest='vendlibdir', type='string', 1253 default=None, action='callback', callback=store_abspath_dir, 1254 metavar="DIR", 1255 help="the VendorID library is installed in DIR [default: " 1256 "%s]" % target_config.vend_lib_dir) 1257 p.add_option_group(g) 1258 1259 # QScintilla. 1260 g = optparse.OptionGroup(p, title="QScintilla support") 1261 g.add_option("--qsci-api", "-a", dest='qsciapi', default=None, 1262 action='store_true', 1263 help="always install the PyQt API file for QScintilla [default: " 1264 "install only if QScintilla installed]") 1265 g.add_option("--no-qsci-api", dest='qsciapi', default=None, 1266 action='store_false', 1267 help="do not install the PyQt API file for QScintilla [default: " 1268 "install only if QScintilla installed]") 1269 g.add_option("--qsci-api-destdir", "-n", dest='qsciapidir', type='string', 1270 default=None, action='callback', callback=store_abspath, 1271 metavar="DIR", 1272 help="install the PyQt5 API file for QScintilla in DIR [default: " 1273 "QT_INSTALL_DATA/qsci]") 1274 p.add_option_group(g) 1275 1276 return p 1277 1278 1279def check_modules(target_config, disabled_modules, verbose): 1280 """ Check which modules can be built and update the target configuration 1281 accordingly. target_config is the target configuration. disabled_modules 1282 is the list of modules that have been explicitly disabled. verbose is set 1283 if the output is to be displayed. 1284 """ 1285 1286 target_config.pyqt_modules.append('QtCore') 1287 1288 check_module(target_config, disabled_modules, verbose, 'QtGui') 1289 check_module(target_config, disabled_modules, verbose, 'QtHelp', 1290 'qhelpengine.h', 'new QHelpEngine("foo")') 1291 check_module(target_config, disabled_modules, verbose, 'QtMultimedia', 1292 'QAudioDeviceInfo', 'new QAudioDeviceInfo()') 1293 check_module(target_config, disabled_modules, verbose, 1294 'QtMultimediaWidgets', 'QVideoWidget', 'new QVideoWidget()') 1295 check_module(target_config, disabled_modules, verbose, 'QtNetwork') 1296 check_module(target_config, disabled_modules, verbose, 'QtOpenGL', 'qgl.h', 1297 'new QGLWidget()') 1298 check_module(target_config, disabled_modules, verbose, 'QtPrintSupport') 1299 check_module(target_config, disabled_modules, verbose, 'QtQml', 1300 'qjsengine.h', 'new QJSEngine()') 1301 check_module(target_config, disabled_modules, verbose, 'QtQuick', 1302 'qquickwindow.h', 'new QQuickWindow()') 1303 check_module(target_config, disabled_modules, verbose, 'QtSql', 1304 'qsqldatabase.h', 'new QSqlDatabase()') 1305 check_module(target_config, disabled_modules, verbose, 'QtSvg', 1306 'qsvgwidget.h', 'new QSvgWidget()') 1307 check_module(target_config, disabled_modules, verbose, 'QtTest', 'QtTest', 1308 'QTest::qSleep(0)') 1309 check_module(target_config, disabled_modules, verbose, 'QtWebKit', 1310 'qwebkitglobal.h', 'qWebKitVersion()') 1311 check_module(target_config, disabled_modules, verbose, 'QtWebKitWidgets', 1312 'qwebpage.h', 'new QWebPage()') 1313 check_module(target_config, disabled_modules, verbose, 'QtWidgets', 1314 'qwidget.h', 'new QWidget()') 1315 check_module(target_config, disabled_modules, verbose, 'QtXml', 'qdom.h', 1316 'new QDomDocument()') 1317 check_module(target_config, disabled_modules, verbose, 'QtXmlPatterns', 1318 'qxmlname.h', 'new QXmlName()') 1319 1320 if target_config.qt_shared: 1321 check_module(target_config, disabled_modules, verbose, 'QtDesigner', 1322 ('QExtensionFactory', 'customwidget.h'), 1323 'new QExtensionFactory()') 1324 else: 1325 inform("QtDesigner module disabled with static Qt libraries.") 1326 1327 check_module(target_config, disabled_modules, verbose, 'QAxContainer', 1328 'qaxobject.h', 'new QAxObject()') 1329 1330 check_module(target_config, disabled_modules, verbose, 'QtDBus', 1331 'qdbusconnection.h', 'QDBusConnection::systemBus()') 1332 1333 if target_config.qt_version >= 0x050100: 1334 check_5_1_modules(target_config, disabled_modules, verbose) 1335 1336 if target_config.qt_version >= 0x050200: 1337 check_5_2_modules(target_config, disabled_modules, verbose) 1338 1339 if target_config.qt_version >= 0x050300: 1340 check_5_3_modules(target_config, disabled_modules, verbose) 1341 1342 if target_config.qt_version >= 0x050400: 1343 check_5_4_modules(target_config, disabled_modules, verbose) 1344 1345 if target_config.qt_version >= 0x050500: 1346 check_5_5_modules(target_config, disabled_modules, verbose) 1347 1348 if target_config.qt_version >= 0x050c00: 1349 check_5_12_modules(target_config, disabled_modules, verbose) 1350 1351 if target_config.qt_version >= 0x050f00: 1352 check_5_15_modules(target_config, disabled_modules, verbose) 1353 1354 # QtWebEngine needs to know if QtWebChannel is available. 1355 if 'QtWebChannel' not in target_config.pyqt_modules: 1356 target_config.pyqt_disabled_features.append('PyQt_WebChannel') 1357 1358 1359def check_5_1_modules(target_config, disabled_modules, verbose): 1360 """ Check which modules introduced in Qt v5.1 can be built and update the 1361 target configuration accordingly. target_config is the target 1362 configuration. disabled_modules is the list of modules that have been 1363 explicitly disabled. verbose is set if the output is to be displayed. 1364 """ 1365 1366 # Check the OpenGL functions. 1367 if 'PyQt_OpenGL' in target_config.pyqt_disabled_features: 1368 pass 1369 elif 'PyQt_Desktop_OpenGL' in target_config.pyqt_disabled_features: 1370 check_module(target_config, disabled_modules, verbose, 1371 '_QOpenGLFunctions_ES2', 'qopenglfunctions_es2.h', 1372 'new QOpenGLFunctions_ES2()') 1373 else: 1374 desktop_versions = ( 1375 '1_0', '1_1', '1_2', '1_3', '1_4', '1_5', 1376 '2_0', '2_1', 1377 '3_0', '3_1', 1378 '3_2_Compatibility', '3_2_Core', 1379 '3_3_Compatibility', '3_3_Core', 1380 '4_0_Compatibility', '4_0_Core', 1381 '4_1_Compatibility', '4_1_Core', 1382 '4_2_Compatibility', '4_2_Core', 1383 '4_3_Compatibility', '4_3_Core', 1384 '4_4_Compatibility', '4_4_Core', 1385 '4_5_Compatibility', '4_5_Core') 1386 1387 for ogl in desktop_versions: 1388 ogl_module = '_QOpenGLFunctions_' + ogl 1389 ogl_h = 'qopenglfunctions_' + ogl.lower() + '.h' 1390 ogl_ctor = 'new QOpenGLFunctions_' + ogl + '()' 1391 1392 check_module(target_config, disabled_modules, verbose, ogl_module, 1393 ogl_h, ogl_ctor) 1394 1395 check_module(target_config, disabled_modules, verbose, 'QtSensors', 1396 'qsensor.h', 'new QSensor(QByteArray())') 1397 check_module(target_config, disabled_modules, verbose, 'QtSerialPort', 1398 'qserialport.h', 'new QSerialPort()') 1399 check_module(target_config, disabled_modules, verbose, 'QtX11Extras', 1400 'QX11Info', 'QX11Info::display()') 1401 1402 1403def check_5_2_modules(target_config, disabled_modules, verbose): 1404 """ Check which modules introduced in Qt v5.2 can be built and update the 1405 target configuration accordingly. target_config is the target 1406 configuration. disabled_modules is the list of modules that have been 1407 explicitly disabled. verbose is set if the output is to be displayed. 1408 """ 1409 1410 check_module(target_config, disabled_modules, verbose, 'QtBluetooth', 1411 'qbluetoothaddress.h', 'new QBluetoothAddress()') 1412 check_module(target_config, disabled_modules, verbose, 'QtMacExtras', 1413 'qmacpasteboardmime.h', 'class Foo : public QMacPasteboardMime {}') 1414 check_module(target_config, disabled_modules, verbose, 'QtPositioning', 1415 'qgeoaddress.h', 'new QGeoAddress()') 1416 check_module(target_config, disabled_modules, verbose, 'QtWinExtras', 1417 'QtWin', 'QtWin::isCompositionEnabled()') 1418 1419 1420def check_5_3_modules(target_config, disabled_modules, verbose): 1421 """ Check which modules introduced in Qt v5.3 can be built and update the 1422 target configuration accordingly. target_config is the target 1423 configuration. disabled_modules is the list of modules that have been 1424 explicitly disabled. verbose is set if the output is to be displayed. 1425 """ 1426 1427 check_module(target_config, disabled_modules, verbose, 'QtQuickWidgets', 1428 'qquickwidget.h', 'new QQuickWidget()') 1429 check_module(target_config, disabled_modules, verbose, 'QtWebSockets', 1430 'qwebsocket.h', 'new QWebSocket()') 1431 check_module(target_config, disabled_modules, verbose, 'Enginio', 1432 'enginioclient.h', 'new EnginioClient()') 1433 1434 1435def check_5_4_modules(target_config, disabled_modules, verbose): 1436 """ Check which modules introduced in Qt v5.4 can be built and update the 1437 target configuration accordingly. target_config is the target 1438 configuration. disabled_modules is the list of modules that have been 1439 explicitly disabled. verbose is set if the output is to be displayed. 1440 """ 1441 1442 check_module(target_config, disabled_modules, verbose, 'QtWebChannel', 1443 'qwebchannel.h', 'new QWebChannel()') 1444 1445 1446def check_5_5_modules(target_config, disabled_modules, verbose): 1447 """ Check which modules introduced in Qt v5.5 can be built and update the 1448 target configuration accordingly. target_config is the target 1449 configuration. disabled_modules is the list of modules that have been 1450 explicitly disabled. verbose is set if the output is to be displayed. 1451 """ 1452 1453 check_module(target_config, disabled_modules, verbose, 'QtLocation', 1454 'qplace.h', 'new QPlace()') 1455 check_module(target_config, disabled_modules, verbose, 'QtNfc', 1456 'qnearfieldmanager.h', 'new QNearFieldManager()') 1457 1458 1459def check_5_12_modules(target_config, disabled_modules, verbose): 1460 """ Check which modules introduced in Qt v5.12 can be built and update the 1461 target configuration accordingly. target_config is the target 1462 configuration. disabled_modules is the list of modules that have been 1463 explicitly disabled. verbose is set if the output is to be displayed. 1464 """ 1465 1466 check_module(target_config, disabled_modules, verbose, 'QtRemoteObjects', 1467 'qtremoteobjectsversion.h', 1468 'const char *v = QTREMOTEOBJECTS_VERSION_STR') 1469 1470 1471def check_5_15_modules(target_config, disabled_modules, verbose): 1472 """ Check which modules introduced in Qt v5.15 can be built and update the 1473 target configuration accordingly. target_config is the target 1474 configuration. disabled_modules is the list of modules that have been 1475 explicitly disabled. verbose is set if the output is to be displayed. 1476 """ 1477 1478 check_module(target_config, disabled_modules, verbose, 'QtQuick3D', 1479 'qquick3d.h', 'QQuick3D::idealSurfaceFormat()') 1480 check_module(target_config, disabled_modules, verbose, 'QtTextToSpeech', 1481 'QTextToSpeech', 'new QTextToSpeech()') 1482 1483 1484def generate_makefiles(target_config, verbose, parts, tracing, fatal_warnings, distinfo): 1485 """ Generate the makefiles to build everything. target_config is the 1486 target configuration. verbose is set if the output is to be displayed. 1487 parts is the number of parts the generated code should be split into. 1488 tracing is set if the generated code should include tracing calls. 1489 fatal_warnings is set if warnings are fatal. distinfo is set if a 1490 .dist-info directory should be created. 1491 """ 1492 1493 # For the top-level .pro file. 1494 toplevel_pro = 'PyQt5.pro' 1495 subdirs = [] 1496 1497 # Set the SIP platform, version and feature flags. 1498 sip_flags = get_sip_flags(target_config) 1499 1500 # Go through the modules. 1501 pyqt_modules = list(target_config.pyqt_modules) 1502 1503 # Add the internal modules if they are required. 1504 if not target_config.no_tools: 1505 if "QtXml" in target_config.pyqt_modules: 1506 pyqt_modules.append('pylupdate') 1507 pyqt_modules.append('pyrcc') 1508 1509 for mname in pyqt_modules: 1510 metadata = MODULE_METADATA[mname] 1511 1512 if metadata.qpy_lib: 1513 sp_qpy_dir = source_path('qpy', mname) 1514 1515 qpy_c_sources = [os.path.relpath(f, mname) 1516 for f in matching_files(os.path.join(sp_qpy_dir, '*.c'))] 1517 qpy_cpp_sources = [os.path.relpath(f, mname) 1518 for f in matching_files(os.path.join(sp_qpy_dir, '*.cpp'))] 1519 qpy_headers = [os.path.relpath(f, mname) 1520 for f in matching_files(os.path.join(sp_qpy_dir, '*.h'))] 1521 1522 qpy_sources = qpy_c_sources + qpy_cpp_sources 1523 else: 1524 qpy_sources = [] 1525 qpy_headers = [] 1526 1527 generate_sip_module_code(target_config, verbose, parts, tracing, mname, 1528 fatal_warnings, sip_flags, metadata.public, qpy_sources, 1529 qpy_headers) 1530 subdirs.append(mname) 1531 1532 # Generate the composite module. 1533 qtmod_sipdir = os.path.join('sip', 'Qt') 1534 mk_clean_dir(qtmod_sipdir) 1535 1536 qtmod_sipfile = os.path.join(qtmod_sipdir, 'Qtmod.sip') 1537 f = open_for_writing(qtmod_sipfile) 1538 1539 f.write('''%CompositeModule PyQt5.Qt 1540 1541''') 1542 1543 for mname in COMPOSITE_COMPONENTS: 1544 if mname in target_config.pyqt_modules: 1545 f.write('%%Include %s/%smod.sip\n' % (mname, mname)) 1546 1547 f.close() 1548 1549 generate_sip_module_code(target_config, verbose, parts, tracing, 'Qt', 1550 fatal_warnings, sip_flags, False) 1551 if "QtCore" in target_config.pyqt_modules: 1552 subdirs.append('Qt') 1553 1554 # Generate the top-level __init__.py. 1555 inf = open(source_path('__init__.py')) 1556 contents = inf.read() 1557 inf.close() 1558 1559 inf = open_for_writing('__init__.py') 1560 inf.write(contents) 1561 1562 if target_config.py_platform == 'win32': 1563 # On Windows we try and make sure the Qt DLLs can be found, either any 1564 # bundled copies or an existing installation (using the traditional 1565 # Windows DLL search). We don't raise an exception in case the 1566 # application has already taken steps to resolve this which we don't 1567 # know about. 1568 inf.write(""" 1569 1570def find_qt(): 1571 import os, sys 1572 1573 qtcore_dll = '\\\\Qt5Core.dll' 1574 1575 dll_dir = os.path.dirname(sys.executable) 1576 if not os.path.isfile(dll_dir + qtcore_dll): 1577 path = os.environ['PATH'] 1578 1579 dll_dir = os.path.dirname(__file__) + '\\\\Qt5\\\\bin' 1580 if os.path.isfile(dll_dir + qtcore_dll): 1581 path = dll_dir + ';' + path 1582 os.environ['PATH'] = path 1583 else: 1584 for dll_dir in path.split(';'): 1585 if os.path.isfile(dll_dir + qtcore_dll): 1586 break 1587 else: 1588 return 1589 1590 try: 1591 os.add_dll_directory(dll_dir) 1592 except AttributeError: 1593 pass 1594 1595 1596find_qt() 1597del find_qt 1598""") 1599 1600 inf.close() 1601 1602 # Generate any executable wrappers. 1603 wrappers = [] 1604 if not target_config.no_tools: 1605 if "QtXml" in target_config.pyqt_modules: 1606 # Generate the pylupdate5 and pyrcc5 wrappers. 1607 for tool in ('pylupdate', 'pyrcc'): 1608 wrappers.append((tool, 1609 generate_tool_wrapper(target_config, tool + '5', 1610 'PyQt5.%s_main' % tool))) 1611 1612 if "QtCore" in target_config.pyqt_modules: 1613 # Generate the pyuic5 wrapper. 1614 wrappers.append(('pyuic', 1615 generate_tool_wrapper(target_config, 'pyuic5', 1616 'PyQt5.uic.pyuic'))) 1617 1618 # Generate the Qt Designer plugin. 1619 if not target_config.no_designer_plugin and 'QtDesigner' in target_config.pyqt_modules: 1620 if generate_plugin_makefile(target_config, verbose, 'designer', target_config.designer_plugin_dir, "Qt Designer"): 1621 subdirs.append('designer') 1622 1623 # Generate the qmlscene plugin. 1624 if not target_config.no_qml_plugin and 'QtQml' in target_config.pyqt_modules: 1625 if generate_plugin_makefile(target_config, verbose, 'qmlscene', target_config.qml_plugin_dir, "qmlscene"): 1626 subdirs.append('qmlscene') 1627 1628 rewrite_qmldir(target_config, 'Charts', 1629 source_path('examples', 'quick', 'tutorials', 'extending', 1630 'chapter6-plugins')) 1631 1632 # Generate the Python dbus module. 1633 if target_config.pydbus_module_dir != '': 1634 mname = 'dbus' 1635 1636 mk_dir(mname) 1637 sp_src_dir = source_path(mname) 1638 1639 lib_dirs = ['-L' + l for l in target_config.dbus_lib_dirs] 1640 lib_names = ['-l' + l for l in target_config.dbus_libs] 1641 libs = ' '.join(lib_dirs + lib_names) 1642 1643 generate_module_makefile(target_config, verbose, mname, 1644 include_paths=target_config.dbus_inc_dirs, libs=libs, 1645 install_path=target_config.pydbus_module_dir, 1646 src_dir=sp_src_dir) 1647 1648 subdirs.append(mname) 1649 1650 # Generate the top-level .pro file. 1651 all_installs = [] 1652 1653 inform("Generating the top-level .pro file...") 1654 out_f = open_for_writing(toplevel_pro) 1655 1656 root_dir = qmake_quote(target_config.pyqt_module_dir + '/PyQt5') 1657 1658 for mname in pyqt_modules: 1659 all_installs.append( 1660 root_dir + '/' + module_file_name(target_config, mname)) 1661 1662 # all_installs.append(root_dir + '/' + module_file_name(target_config, 'Qt')) 1663 1664 out_f.write('''TEMPLATE = subdirs 1665CONFIG += ordered nostrip 1666SUBDIRS = %s 1667''' % (' '.join(subdirs))) 1668 1669 if "QtCore" in target_config.pyqt_modules: 1670 out_f.write(''' 1671init_py.files = %s 1672init_py.path = %s 1673INSTALLS += init_py 1674''' % (source_path('__init__.py'), root_dir)) 1675 1676 # all_installs.append(root_dir + '/__init__.py') 1677 1678 if not target_config.no_tools: 1679 # Install the uic module. 1680 out_f.write(''' 1681uic_package.files = %s 1682uic_package.path = %s 1683INSTALLS += uic_package 1684''' % (source_path('pyuic', 'uic'), root_dir)) 1685 1686 # all_installs.append(root_dir + '/uic') 1687 1688 # Install the tool main scripts and wrappers. 1689 if wrappers: 1690 wrapper_exes = [] 1691 for tool, wrapper in wrappers: 1692 if tool != 'pyuic': 1693 tool_main = tool + '_main.py' 1694 1695 out_f.write(''' 1696%s.files = %s 1697%s.path = %s 1698INSTALLS += %s 1699''' % (tool, source_path('sip', tool, tool_main), tool, root_dir, tool)) 1700 1701 all_installs.append(root_dir + '/' + tool_main) 1702 1703 wrapper_exes.append(wrapper) 1704 all_installs.append(target_config.pyqt_bin_dir + '/' + wrapper) 1705 1706 out_f.write(''' 1707tools.files = %s 1708tools.path = %s 1709INSTALLS += tools 1710''' % (' '.join(wrapper_exes), qmake_quote(target_config.pyqt_bin_dir))) 1711 1712 # Install the .sip files. 1713 if target_config.pyqt_sip_dir: 1714 for mname, metadata in MODULE_METADATA.items(): 1715 if mname not in pyqt_modules: 1716 continue 1717 if metadata.public and mname != 'Qt': 1718 sip_files = matching_files(source_path('sip', mname, '*.sip')) 1719 1720 if len(sip_files) != 0: 1721 mdir = target_config.pyqt_sip_dir + '/' + mname 1722 1723 out_f.write(''' 1724sip%s.path = %s 1725sip%s.files = %s 1726INSTALLS += sip%s 1727''' % ( 1728 mname, qmake_quote(mdir), 1729 mname, ' '.join([qmake_quote(s) for s in sip_files]), 1730 mname 1731)) 1732 1733 all_installs.append(mdir) 1734 1735 # Install the stub files. 1736 if target_config.pyqt_stubs_dir: 1737 pyi_names = [mname + '.pyi' 1738 for mname in target_config.pyqt_modules if mname[0] != '_'] 1739 1740 out_f.write(''' 1741pep484_stubs.files = %s 1742pep484_stubs.path = %s 1743INSTALLS += pep484_stubs 1744''' % (' '.join(pyi_names), 1745 qmake_quote(target_config.pyqt_stubs_dir))) 1746 1747 all_installs.extend( 1748 [target_config.pyqt_stubs_dir + '/' + pyi 1749 for pyi in pyi_names]) 1750 1751 # Install the QScintilla .api file. 1752 if target_config.qsci_api: 1753 api_dir = target_config.qsci_api_dir + '/api/python' 1754 api_list = ' '.join(['%s.api' % m for m in target_config.pyqt_modules]) 1755 1756 out_f.write(''' 1757qscintilla_api.files = %s 1758qscintilla_api.path = %s 1759INSTALLS += qscintilla_api 1760''' % (api_list, qmake_quote(api_dir))) 1761 1762 # all_installs.append(api_dir + '/PyQt5.api') 1763 1764 if distinfo: 1765 # The command to run to generate the .dist-info directory. 1766 distinfo_dir = os.path.join(target_config.pyqt_module_dir, 1767 'PyQt5-' + PYQT_VERSION_STR + '.dist-info') 1768 1769 run_mk_distinfo = '%s %s \\"$(INSTALL_ROOT)\\" %s installed.txt' % ( 1770 sys.executable, source_path('mk_distinfo.py'), distinfo_dir) 1771 1772 out_f.write(''' 1773distinfo.extra = %s 1774distinfo.path = %s 1775INSTALLS += distinfo 1776''' % (run_mk_distinfo, root_dir)) 1777 1778 # Create the file containing all installed files. 1779 installed = open('installed.txt', 'w') 1780 1781 for install in all_installs: 1782 installed.write(install + '\n') 1783 1784 installed.close() 1785 1786 out_f.close() 1787 1788 # Make the wrappers executable on platforms that support it. If we did it 1789 # after running qmake then (on Linux) the execute bits would be stripped on 1790 # installation. 1791 if target_config.py_platform != 'win32': 1792 for tool, wrapper in wrappers: 1793 inform("Making the %s wrapper executable..." % wrapper) 1794 1795 sbuf = os.stat(wrapper) 1796 mode = sbuf.st_mode 1797 mode |= (stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH) 1798 os.chmod(wrapper, mode) 1799 1800 # Generate the makefiles. 1801 inform("Generating the Makefiles...") 1802 run_qmake(target_config, verbose, toplevel_pro, recursive=True) 1803 1804 1805def generate_plugin_makefile(target_config, verbose, plugin_dir, install_dir, plugin_name): 1806 """ Generate the makefile for a plugin that embeds the Python interpreter. 1807 target_config is the target configuration. verbose is set if the output is 1808 to be displayed. plugin_dir is the name of the directory containing the 1809 plugin implementation. install_dir is the name of the directory that the 1810 plugin will be installed in. plugin_name is a descriptive name of the 1811 plugin to be used in user messages. Returns True if the makefile could be 1812 generated. 1813 """ 1814 1815 # Check we have a shared interpreter library. 1816 if target_config.py_pyshlib == '': 1817 inform("The %s plugin was disabled because a dynamic Python library couldn't be found." % plugin_name) 1818 return False 1819 1820 # Create the qmake project file. 1821 inform("Generating the %s plugin .pro file..." % plugin_name) 1822 1823 sp_plugin_dir = source_path(plugin_dir) 1824 1825 fin = open(os.path.join(sp_plugin_dir, '%s.pro-in' % plugin_dir)) 1826 prj = fin.read() 1827 fin.close() 1828 1829 prj = prj.replace('@QTCONFIG@', 1830 'debug' if target_config.debug else 'release') 1831 prj = prj.replace('@PYINCDIR@', qmake_quote(target_config.py_inc_dir)) 1832 prj = prj.replace('@SIPINCDIR@', qmake_quote(target_config.sip_inc_dir)) 1833 prj = prj.replace('@PYLINK@', target_config.get_pylib_link_arguments()) 1834 prj = prj.replace('@PYSHLIB@', target_config.py_pyshlib) 1835 prj = prj.replace('@QTPLUGINDIR@', qmake_quote(install_dir)) 1836 1837 pro_name = os.path.join(plugin_dir, '%s.pro' % plugin_dir) 1838 1839 mk_dir(plugin_dir) 1840 fout = open_for_writing(pro_name) 1841 fout.write(prj) 1842 1843 if sp_plugin_dir != plugin_dir: 1844 fout.write(''' 1845INCLUDEPATH += %s 1846VPATH = %s 1847''' % (qmake_quote(sp_plugin_dir), qmake_quote(sp_plugin_dir))) 1848 1849 fout.write('\n'.join(target_config.qmake_variables) + '\n') 1850 1851 fout.close() 1852 1853 return True 1854 1855 1856def pro_sources(src_dir, other_headers=None, other_sources=None): 1857 """ Return the HEADERS, SOURCES and OBJECTIVE_SOURCES variables for a .pro 1858 file by introspecting a directory. src_dir is the name of the directory. 1859 other_headers is an optional list of other header files. other_sources is 1860 an optional list of other source files. 1861 """ 1862 1863 pro_lines = [] 1864 1865 headers = [os.path.basename(f) for f in matching_files('%s/*.h' % src_dir)] 1866 1867 if other_headers is not None: 1868 headers += other_headers 1869 1870 if len(headers) != 0: 1871 pro_lines.append('HEADERS = %s' % ' '.join(headers)) 1872 1873 sources = [os.path.basename(f) for f in matching_files('%s/*.c' % src_dir)] 1874 1875 for f in matching_files('%s/*.cpp' % src_dir): 1876 f = os.path.basename(f) 1877 1878 # Exclude any moc generated C++ files that might be around from a 1879 # previous build. 1880 if not f.startswith('moc_'): 1881 sources.append(f) 1882 1883 if other_sources is not None: 1884 sources += other_sources 1885 1886 if len(sources) != 0: 1887 pro_lines.append('SOURCES = %s' % ' '.join([qmake_quote(s) for s in sources])) 1888 1889 objective_sources = [ 1890 os.path.basename(f) for f in matching_files('%s/*.mm' % src_dir)] 1891 1892 if len(objective_sources) != 0: 1893 pro_lines.append('OBJECTIVE_SOURCES = %s' % ' '.join([qmake_quote(s) for s in objective_sources])) 1894 1895 return pro_lines 1896 1897 1898def module_file_name(target_config, name): 1899 """ Return the name of a file implementing a module. """ 1900 1901 if sys.platform == 'win32': 1902 fs = '{}.lib' if target_config.static else '{}.pyd' 1903 else: 1904 fs = 'lib{}.a' if target_config.static else '{}.so' 1905 1906 return fs.format(name) 1907 1908 1909def generate_tool_wrapper(target_config, wrapper, module): 1910 """ Create a platform dependent executable wrapper for a tool module. 1911 target_config is the target configuration. wrapper is the name of the 1912 wrapper without any extension. module is the tool module. Returns the 1913 platform specific name of the wrapper. 1914 """ 1915 1916 if target_config.py_platform == 'win32': 1917 wrapper += '.bat' 1918 1919 inform("Generating the %s wrapper..." % wrapper) 1920 1921 exe = quote(target_config.pyuic_interpreter) 1922 1923 wf = open_for_writing(wrapper) 1924 1925 if target_config.py_platform == 'win32': 1926 wf.write('@%s -m %s %%1 %%2 %%3 %%4 %%5 %%6 %%7 %%8 %%9\n' % (exe, module)) 1927 else: 1928 wf.write('#!/bin/sh\n') 1929 wf.write('exec %s -m %s ${1+"$@"}\n' % (exe, module)) 1930 1931 wf.close() 1932 1933 return wrapper 1934 1935 1936def rewrite_qmldir(target_config, module, module_dir): 1937 """ Re-write a qmldir file for a module that used the qmlscene plugin. 1938 target_config is the target configuration. module is the name of the QML 1939 module. module_dir is the name of the directory containing the QML module. 1940 """ 1941 1942 qmldir_fn = os.path.join(module_dir, module, 'qmldir') 1943 1944 inform("Re-writing %s..." % qmldir_fn) 1945 1946 qmldir = open_for_writing(qmldir_fn) 1947 qmldir.write('module %s\nplugin pyqt5qmlplugin %s\n' % (module, target_config.qml_plugin_dir)) 1948 qmldir.close() 1949 1950 1951def quote(path): 1952 """ Return a path with quotes added if it contains spaces. path is the 1953 path. 1954 """ 1955 1956 if ' ' in path: 1957 path = '"%s"' % path 1958 1959 return path 1960 1961 1962def qmake_quote(path): 1963 """ Return a path quoted for qmake if it contains spaces. path is the 1964 path. 1965 """ 1966 1967 if ' ' in path: 1968 path = '$$quote(%s)' % path 1969 1970 return path 1971 1972 1973def inform_user(target_config, sip_version): 1974 """ Tell the user the values that are going to be used. target_config is 1975 the target configuration. sip_version is the SIP version string. 1976 """ 1977 1978 inform("Qt v%s is being used." % 1979 version_to_string(target_config.qt_version)) 1980 1981 inform("The qmake executable is %s." % target_config.qmake) 1982 1983 inform( 1984 "Qt is built as a %s library." % ( 1985 "shared" if target_config.qt_shared else "static")) 1986 1987 if target_config.sysroot != '': 1988 inform("The system root directory is %s." % target_config.sysroot) 1989 1990 inform("SIP %s is being used." % sip_version) 1991 inform("The sip executable is %s." % target_config.sip) 1992 inform("These PyQt5 modules will be built: %s." % ', '.join(target_config.pyqt_modules)) 1993 inform("The PyQt5 Python package will be installed in %s." % target_config.pyqt_module_dir) 1994 1995 if target_config.debug: 1996 inform("A debug version of PyQt5 will be built.") 1997 1998 if target_config.py_debug: 1999 inform("A debug build of Python is being used.") 2000 2001 if target_config.no_docstrings: 2002 inform("PyQt5 is being built without generated docstrings.") 2003 else: 2004 inform("PyQt5 is being built with generated docstrings.") 2005 2006 if target_config.prot_is_public: 2007 inform("PyQt5 is being built with 'protected' redefined as 'public'.") 2008 2009 if target_config.no_designer_plugin: 2010 inform("The Designer plugin will not be built.") 2011 else: 2012 inform("The Designer plugin will be installed in %s." % 2013 target_config.designer_plugin_dir) 2014 2015 if target_config.no_qml_plugin: 2016 inform("The qmlscene plugin will not be built.") 2017 else: 2018 inform("The qmlscene plugin will be installed in %s." % 2019 target_config.qml_plugin_dir) 2020 2021 if target_config.qsci_api: 2022 inform( 2023 "The QScintilla API file will be installed in %s." % 2024 os.path.join( 2025 target_config.qsci_api_dir, 'api', 'python')) 2026 2027 if target_config.pyqt_stubs_dir: 2028 inform("The PyQt5 PEP 484 stub files will be installed in %s." % 2029 target_config.pyqt_stubs_dir) 2030 2031 if target_config.pydbus_module_dir: 2032 inform( 2033 "The dbus support module will be installed in %s." % 2034 target_config.pydbus_module_dir) 2035 2036 if target_config.pyqt_sip_dir: 2037 inform("The PyQt5 .sip files will be installed in %s." % 2038 target_config.pyqt_sip_dir) 2039 2040 if target_config.no_tools: 2041 inform("pyuic5, pyrcc5 and pylupdate5 will not be built.") 2042 else: 2043 inform("pyuic5, pyrcc5 and pylupdate5 will be installed in %s." % 2044 target_config.pyqt_bin_dir) 2045 2046 inform("The interpreter used by pyuic5 is %s." % 2047 target_config.pyuic_interpreter) 2048 2049 if target_config.vend_enabled: 2050 inform("PyQt5 will only be usable with signed interpreters.") 2051 2052 2053def run_qmake(target_config, verbose, pro_name, makefile_name='', fatal=True, recursive=False): 2054 """ Run qmake against a .pro file. target_config is the target 2055 configuration. verbose is set if the output is to be displayed. pro_name 2056 is the name of the .pro file. makefile_name is the name of the makefile 2057 to generate (and defaults to Makefile). fatal is set if a qmake failure is 2058 considered a fatal error, otherwise False is returned if qmake fails. 2059 recursive is set to use the -recursive flag. 2060 """ 2061 2062 # qmake doesn't behave consistently if it is not run from the directory 2063 # containing the .pro file - so make sure it is. 2064 pro_dir, pro_file = os.path.split(pro_name) 2065 if pro_dir != '': 2066 cwd = os.getcwd() 2067 os.chdir(pro_dir) 2068 else: 2069 cwd = None 2070 2071 mf = makefile_name if makefile_name != '' else 'Makefile' 2072 2073 remove_file(mf) 2074 2075 args = [quote(target_config.qmake)] 2076 2077 if target_config.qmake_spec != target_config.qmake_spec_default: 2078 args.append('-spec') 2079 args.append(target_config.qmake_spec) 2080 2081 if makefile_name != '': 2082 args.append('-o') 2083 args.append(makefile_name) 2084 2085 if recursive: 2086 args.append('-recursive') 2087 2088 args.append(pro_file) 2089 2090 run_command(' '.join(args), verbose) 2091 2092 if not os.access(mf, os.F_OK): 2093 if fatal: 2094 error( 2095 "%s failed to create a makefile from %s." % 2096 (target_config.qmake, pro_name)) 2097 2098 return False 2099 2100 # Restore the current directory. 2101 if cwd is not None: 2102 os.chdir(cwd) 2103 2104 return True 2105 2106 2107def run_make(target_config, verbose, exe, makefile_name): 2108 """ Run make against a makefile to create an executable. target_config is 2109 the target configuration. verbose is set if the output is to be displayed. 2110 exe is the platform independent name of the executable that will be 2111 created. makefile_name is the name of the makefile. Returns the platform 2112 specific name of the executable, or None if an executable wasn't created. 2113 """ 2114 2115 # Guess the name of make and set the default target and platform specific 2116 # name of the executable. 2117 if target_config.py_platform == 'win32': 2118 if target_config.qmake_spec == 'win32-g++': 2119 make = 'mingw32-make' 2120 else: 2121 make = 'nmake' 2122 2123 if target_config.debug: 2124 makefile_target = 'debug' 2125 platform_exe = os.path.join('debug', exe + '.exe') 2126 else: 2127 makefile_target = 'release' 2128 platform_exe = os.path.join('release', exe + '.exe') 2129 else: 2130 make = 'make' 2131 makefile_target = '' 2132 2133 if target_config.py_platform == 'darwin': 2134 platform_exe = os.path.join(exe + '.app', 'Contents', 'MacOS', exe) 2135 else: 2136 platform_exe = os.path.join('.', exe) 2137 2138 remove_file(platform_exe) 2139 2140 args = [make, '-f', makefile_name] 2141 2142 if makefile_target != '': 2143 args.append(makefile_target) 2144 2145 run_command(' '.join(args), verbose) 2146 2147 return platform_exe if os.access(platform_exe, os.X_OK) else None 2148 2149 2150def run_command(cmd, verbose): 2151 """ Run a command and display the output if requested. cmd is the command 2152 to run. verbose is set if the output is to be displayed. 2153 """ 2154 2155 if verbose: 2156 sys.stdout.write(cmd + "\n") 2157 2158 fout = get_command_output(cmd, and_stderr=True) 2159 2160 # Read stdout and stderr until there is no more output. 2161 lout = fout.readline() 2162 while lout: 2163 if verbose: 2164 if sys.hexversion >= 0x03000000: 2165 sys.stdout.write(str(lout, encoding=sys.stdout.encoding)) 2166 else: 2167 sys.stdout.write(lout) 2168 2169 lout = fout.readline() 2170 2171 close_command_pipe(fout) 2172 2173 2174def remove_file(fname): 2175 """ Remove a file which may or may not exist. fname is the name of the 2176 file. 2177 """ 2178 2179 try: 2180 os.remove(fname) 2181 except OSError: 2182 pass 2183 2184 2185def check_vendorid(target_config): 2186 """ See if the VendorID library and include file can be found. 2187 target_config is the target configuration. 2188 """ 2189 2190 if target_config.py_version >= 0x030000: 2191 # VendorID doesn't support Python v3. 2192 target_config.vend_enabled = False 2193 elif target_config.vend_enabled: 2194 if os.access(os.path.join(target_config.vend_inc_dir, 'vendorid.h'), os.F_OK): 2195 if glob.glob(os.path.join(target_config.vend_lib_dir, '*vendorid*')): 2196 inform("The VendorID package was found.") 2197 else: 2198 target_config.vend_enabled = False 2199 inform( 2200 "The VendorID library could not be found in %s and so " 2201 "signed interpreter checking will be disabled. If the " 2202 "VendorID package is installed then use the " 2203 "--vendorid-libdir argument to explicitly specify the " 2204 "correct directory." % target_config.vend_lib_dir) 2205 else: 2206 target_config.vend_enabled = False 2207 inform( 2208 "vendorid.h could not be found in %s and so signed " 2209 "interpreter checking will be disabled. If the VendorID " 2210 "package is installed then use the --vendorid-incdir " 2211 "argument to explicitly specify the correct directory." % 2212 target_config.vend_inc_dir) 2213 2214 2215def get_command_output(cmd, and_stderr=False): 2216 """ Return a pipe from which a command's output can be read. cmd is the 2217 command. and_stderr is set if the output should include stderr as well as 2218 stdout. 2219 """ 2220 2221 try: 2222 import subprocess 2223 except ImportError: 2224 if and_stderr: 2225 _, sout = os.popen4(cmd) 2226 else: 2227 _, sout, _ = os.popen3(cmd) 2228 2229 return sout 2230 2231 if and_stderr: 2232 stderr = subprocess.STDOUT 2233 else: 2234 stderr = subprocess.PIPE 2235 2236 p = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, 2237 stdout=subprocess.PIPE, stderr=stderr) 2238 2239 return p.stdout 2240 2241 2242def close_command_pipe(pipe): 2243 """ Close the pipe returned by get_command_output(). """ 2244 2245 pipe.close() 2246 2247 try: 2248 os.wait() 2249 except: 2250 pass 2251 2252 2253def source_path(*names): 2254 """ Return the native path for a list of components rooted at the directory 2255 containing this script. names is the sequence of component names. 2256 """ 2257 2258 path = [os.path.dirname(os.path.abspath(__file__))] + list(names) 2259 2260 return os.path.join(*path) 2261 2262 2263def check_dbus(target_config, verbose): 2264 """ See if the DBus support module should be built and update the target 2265 configuration accordingly. target_config is the target configuration. 2266 verbose is set if the output is to be displayed. 2267 """ 2268 2269 if target_config.no_pydbus or not os.path.isdir(source_path('dbus')): 2270 return 2271 2272 inform("Checking to see if the dbus support module should be built...") 2273 2274 cmd = 'pkg-config --cflags-only-I --libs dbus-1' 2275 2276 if verbose: 2277 sys.stdout.write(cmd + "\n") 2278 2279 sout = get_command_output(cmd) 2280 iflags = sout.read().strip() 2281 close_command_pipe(sout) 2282 2283 if not iflags: 2284 inform("DBus v1 does not seem to be installed.") 2285 return 2286 2287 if sys.hexversion >= 0x03000000: 2288 iflags = iflags.decode() 2289 2290 for f in iflags.split(): 2291 if f.startswith('-I'): 2292 target_config.dbus_inc_dirs.append(f[2:]) 2293 elif f.startswith('-L'): 2294 target_config.dbus_lib_dirs.append(f[2:]) 2295 elif f.startswith('-l'): 2296 target_config.dbus_libs.append(f[2:]) 2297 2298 try: 2299 import dbus.mainloop 2300 except: 2301 inform("The Python dbus module doesn't seem to be installed.") 2302 return 2303 2304 target_config.pydbus_module_dir = dbus.mainloop.__path__[0] 2305 2306 # Try and find dbus-python.h. We don't use pkg-config because it is broken 2307 # for dbus-python (at least for versions up to and including v0.81.0). 2308 # Instead we look where DBus itself is installed - which in most cases will 2309 # be where dbus-python is also installed. 2310 if target_config.pydbus_inc_dir != '': 2311 target_config.dbus_inc_dirs = [target_config.pydbus_inc_dir] 2312 2313 for d in target_config.dbus_inc_dirs: 2314 if os.access(os.path.join(d, 'dbus', 'dbus-python.h'), os.F_OK): 2315 break 2316 else: 2317 inform( 2318 "dbus/dbus-python.h could not be found and so the DBus " 2319 "support module will be disabled. If dbus-python v0.80 or " 2320 "later is installed then use the --dbus argument to " 2321 "explicitly specify the directory containing " 2322 "dbus/dbus-python.h.") 2323 target_config.pydbus_module_dir = '' 2324 2325 2326def check_module(target_config, disabled_modules, verbose, mname, incfile=None, test=None): 2327 """ See if a module can be built and, if so, add it to the target 2328 configurations list of modules. target_config is the target configuration. 2329 disabled_modules is the list of modules that have been explicitly disabled. 2330 verbose is set if the output is to be displayed. mname is the name of the 2331 module. incfile is the name of the include file (or sequence of multiple 2332 include files) needed for the test. test is a C++ statement being used for 2333 the test. If either incfile or test are None then there is a test program 2334 that needs to be run and its output captured. 2335 """ 2336 2337 if mname in disabled_modules: 2338 return 2339 2340 # Check the module's main .sip file exists. 2341 if not os.access(source_path('sip', mname, mname + 'mod.sip'), os.F_OK): 2342 return 2343 2344 if verbose: 2345 sys.stdout.write('\n') 2346 2347 inform("Checking to see if the %s module should be built..." % mname) 2348 2349 if incfile is None or test is None: 2350 source = None 2351 else: 2352 if isinstance(incfile, str): 2353 incfile = [incfile] 2354 2355 incfile = ['#include<%s>' % i for i in incfile] 2356 2357 source = '''%s 2358 2359int main(int, char **) 2360{ 2361 %s; 2362} 2363''' % ('\n'.join(incfile), test) 2364 2365 test = compile_test_program(target_config, verbose, mname, source) 2366 if test is None: 2367 return 2368 2369 # If there was an explicit test program then run it to get the disabled 2370 # features. 2371 if source is None: 2372 for disabled in run_test_program(mname, test, verbose): 2373 if disabled: 2374 inform("Disabled %s feature: %s" % (mname, disabled)) 2375 target_config.pyqt_disabled_features.append(disabled) 2376 2377 # Include the module in the build. 2378 target_config.pyqt_modules.append(mname) 2379 2380 2381def compile_test_program(target_config, verbose, mname, source=None, debug=None): 2382 """ Compile the source of a Qt program and return the name of the 2383 executable or None if it couldn't be created. target_config is the target 2384 configuration. verbose is set if the output is to be displayed. mname is 2385 the name of the PyQt module being tested. source is the C++ source of the 2386 program. If it is None then the source is expected to be found in the 2387 config-tests directory. debug is set if debug, rather than release, mode 2388 is to be used. If it is None then the mode is taken from the target 2389 configuration. 2390 """ 2391 2392 metadata = MODULE_METADATA[mname] 2393 2394 # The derived file names. 2395 name = 'cfgtest_' + mname 2396 name_pro = name + '.pro' 2397 name_makefile = name + '.mk' 2398 name_source = name + '.cpp' 2399 2400 # Create the source file if necessary. 2401 if source is None: 2402 name_source = source_path('config-tests', name_source) 2403 else: 2404 f = open_for_writing(name_source) 2405 f.write(source) 2406 f.close() 2407 2408 # Create the .pro file. 2409 pro_lines = [] 2410 pro_add_qt_dependencies(target_config, metadata, pro_lines, debug) 2411 pro_lines.append('TARGET = %s' % name) 2412 2413 pro_lines.append('SOURCES = %s' % qmake_quote(name_source)) 2414 2415 f = open_for_writing(name_pro) 2416 f.write('\n'.join(pro_lines)) 2417 f.close() 2418 2419 if not run_qmake(target_config, verbose, name_pro, name_makefile, fatal=False): 2420 return None 2421 2422 return run_make(target_config, verbose, name, name_makefile) 2423 2424 2425def run_test_program(mname, test, verbose): 2426 """ Run a test program and return the output as a list of lines. mname is 2427 the name of the PyQt module being tested. test is the name of the test 2428 executable. verbose is set if the output is to be displayed. 2429 """ 2430 2431 out_file = 'cfgtest_' + mname + '.out' 2432 2433 # Create the output file, first making sure it doesn't exist. 2434 remove_file(out_file) 2435 run_command(test + ' ' + out_file, verbose) 2436 2437 if not os.access(out_file, os.F_OK): 2438 error("%s failed to create %s. Make sure your Qt installation is correct." % (test, out_file)) 2439 2440 # Read the details. 2441 f = open(out_file) 2442 lines = f.read().strip() 2443 f.close() 2444 2445 return lines.split('\n') if lines else [] 2446 2447 2448def pro_add_qt_dependencies(target_config, metadata, pro_lines, debug=None): 2449 """ Add the Qt dependencies of a module to a .pro file. target_config is 2450 the target configuration. metadata is the module's meta-data. pro_lines 2451 is the list of lines making up the .pro file that is updated. debug is set 2452 if debug, rather than release, mode is to be used. If it is None then the 2453 mode is taken from the target configuration. 2454 """ 2455 2456 if debug is None: 2457 debug = target_config.debug 2458 2459 add = [] 2460 remove = [] 2461 for qt in metadata.qmake_QT: 2462 if qt.startswith('-'): 2463 remove.append(qt[1:]) 2464 else: 2465 add.append(qt) 2466 2467 if len(remove) != 0: 2468 pro_lines.append('QT -= %s' % ' '.join(remove)) 2469 2470 if len(add) != 0: 2471 pro_lines.append('QT += %s' % ' '.join(add)) 2472 2473 pro_lines.append( 2474 'CONFIG += %s' % ('debug' if debug else 'release')) 2475 2476 if metadata.cpp11: 2477 pro_lines.append('CONFIG += c++11') 2478 2479 pro_lines.extend(target_config.qmake_variables) 2480 2481 2482def get_sip_flags(target_config): 2483 """ Return the SIP platform, version and feature flags. target_config is 2484 the target configuration. 2485 """ 2486 2487 sip_flags = ['-n', 'PyQt5.sip'] 2488 2489 # If we don't check for signed interpreters, we exclude the 'VendorID' 2490 # feature 2491 if target_config.py_version < 0x030000 and not target_config.vend_enabled: 2492 sip_flags.append('-x') 2493 sip_flags.append('VendorID') 2494 2495 # Handle Python debug builds. 2496 if target_config.py_debug: 2497 sip_flags.append('-D') 2498 2499 # Handle the platform tag. (Allow for win32-g++.) 2500 if target_config.py_platform.startswith('win32'): 2501 plattag = 'WS_WIN' 2502 elif target_config.py_platform == 'darwin': 2503 plattag = 'WS_MACX' 2504 else: 2505 plattag = 'WS_X11' 2506 2507 sip_flags.append('-t') 2508 sip_flags.append(plattag) 2509 2510 # Handle the Qt version tag. 2511 sip_flags.append('-t') 2512 sip_flags.append(version_to_sip_tag(target_config.qt_version)) 2513 2514 # Handle any feature flags. 2515 for xf in target_config.pyqt_disabled_features: 2516 sip_flags.append('-x') 2517 sip_flags.append(xf) 2518 2519 # Handle the version specific Python features. 2520 if target_config.py_version < 0x030000: 2521 sip_flags.append('-x') 2522 sip_flags.append('Py_v3') 2523 2524 return sip_flags 2525 2526 2527def mk_clean_dir(name): 2528 """ Create a clean (ie. empty) directory. name is the name of the 2529 directory. 2530 """ 2531 2532 try: 2533 shutil.rmtree(name) 2534 except: 2535 pass 2536 2537 try: 2538 os.makedirs(name) 2539 except: 2540 error("Unable to create the %s directory." % name) 2541 2542 2543def mk_dir(name): 2544 """ Ensure a directory exists, creating it if necessary. name is the name 2545 of the directory. 2546 """ 2547 2548 try: 2549 os.makedirs(name) 2550 except: 2551 pass 2552 2553 2554def generate_sip_module_code(target_config, verbose, parts, tracing, mname, fatal_warnings, sip_flags, doc_support, qpy_sources=None, qpy_headers=None): 2555 """ Generate the code for a module. target_config is the target 2556 configuration. verbose is set if the output is to be displayed. parts is 2557 the number of parts the generated code should be split into. tracing is 2558 set if the generated code should include tracing calls. mname is the name 2559 of the module to generate the code for. fatal_warnings is set if warnings 2560 are fatal. sip_flags is the list of flags to pass to sip. doc_support 2561 is set if documentation support is to be generated for the module. 2562 qpy_sources is the optional list of QPy support code source files. 2563 qpy_headers is the optional list of QPy support code header files. 2564 """ 2565 2566 inform("Generating the C++ source for the %s module..." % mname) 2567 2568 mk_clean_dir(mname) 2569 2570 # Build the SIP command line. 2571 argv = [target_config.sip, '-w'] 2572 2573 if target_config.abi_version: 2574 argv.append('--abi-version') 2575 argv.append(target_config.abi_version) 2576 2577 argv.extend(sip_flags) 2578 2579 if fatal_warnings: 2580 argv.append('-f') 2581 2582 if target_config.prot_is_public: 2583 argv.append('-P'); 2584 2585 if parts != 0: 2586 argv.append('-j') 2587 argv.append(str(parts)) 2588 2589 if tracing: 2590 argv.append('-r') 2591 2592 if doc_support: 2593 if not target_config.no_docstrings: 2594 argv.append('-o'); 2595 2596 if target_config.qsci_api: 2597 argv.append('-a') 2598 argv.append(mname + '.api') 2599 2600 if target_config.pyqt_stubs_dir: 2601 argv.append('-y') 2602 argv.append(mname + '.pyi') 2603 2604 # Pass the absolute pathname so that #line files are absolute. 2605 argv.append('-c') 2606 argv.append(os.path.abspath(mname)) 2607 2608 argv.append('-I') 2609 argv.append('sip') 2610 2611 sp_sip_dir = source_path('sip') 2612 if sp_sip_dir != 'sip': 2613 # SIP assumes POSIX style separators. 2614 sp_sip_dir = sp_sip_dir.replace('\\', '/') 2615 argv.append('-I') 2616 argv.append(sp_sip_dir) 2617 2618 # The .sip files for the Qt modules will be in the out-of-tree directory. 2619 if mname == 'Qt': 2620 sip_dir = 'sip' 2621 else: 2622 sip_dir = sp_sip_dir 2623 2624 # Add the name of the .sip file. 2625 argv.append('%s/%s/%smod.sip' % (sip_dir, mname, mname)) 2626 2627 run_command(' '.join([quote(a) for a in argv]), verbose) 2628 2629 # Check the result. 2630 if mname == 'Qt': 2631 file_check = 'sip%scmodule.c' % mname 2632 else: 2633 file_check = 'sipAPI%s.h' % mname 2634 2635 if not os.access(os.path.join(mname, file_check), os.F_OK): 2636 error("Unable to create the C++ code.") 2637 2638 # Embed the sip flags. 2639 if mname == 'QtCore': 2640 inform("Embedding sip flags...") 2641 2642 in_f = open(source_path('qpy', 'QtCore', 'qpycore_post_init.cpp.in')) 2643 out_f = open_for_writing( 2644 os.path.join('QtCore', 'qpycore_post_init.cpp')) 2645 2646 for line in in_f: 2647 line = line.replace('@@PYQT_SIP_FLAGS@@', ' '.join(sip_flags)) 2648 out_f.write(line) 2649 2650 in_f.close() 2651 out_f.close() 2652 2653 # Generate the makefile. 2654 include_paths = [] 2655 libs = '' 2656 2657 if target_config.vend_enabled: 2658 if mname == 'QtCore': 2659 include_paths.append(target_config.vend_inc_dir) 2660 libs = '-L%s -lvendorid' % target_config.vend_lib_dir 2661 2662 generate_module_makefile(target_config, verbose, mname, 2663 include_paths=include_paths, libs=libs, qpy_sources=qpy_sources, 2664 qpy_headers=qpy_headers) 2665 2666 2667def generate_module_makefile(target_config, verbose, mname, include_paths=None, libs='', install_path='', src_dir='', qpy_sources=None, qpy_headers=None): 2668 """ Generate the makefile for a module. target_config is the target 2669 configuration. verbose is set if the output is to be displayed. mname is 2670 the name of the module. include_paths is an optional list of values of 2671 INCLUDEPATH. libs is an optional additional value of LIBS. install_path 2672 is the optional name of the directory that the module will be installed in. 2673 src_dir is the optional source directory (by default the sources are 2674 assumed to be in the module directory). qpy_sources is the optional list 2675 of QPy support code source files. qpy_headers is the optional list of QPy 2676 support code header files. 2677 """ 2678 2679 if verbose: 2680 sys.stdout.write('\n') 2681 2682 inform("Generating the .pro file for the %s module..." % mname) 2683 2684 if src_dir == '': 2685 src_dir = mname 2686 2687 target_name = mname 2688 2689 metadata = MODULE_METADATA[mname] 2690 2691 if metadata.qmake_TARGET != '': 2692 target_name = metadata.qmake_TARGET 2693 2694 pro_lines = ['TEMPLATE = lib'] 2695 2696 # Note some version of Qt5 (probably incorrectly) implements 2697 # 'plugin_bundle' instead of 'plugin' so we specify both. 2698 pro_lines.append('CONFIG += warn_on exceptions_off %s' % ('staticlib hide_symbols' if target_config.static else 'plugin plugin_bundle')) 2699 2700 pro_add_qt_dependencies(target_config, metadata, pro_lines) 2701 2702 if target_config.qml_debug: 2703 pro_lines.append('CONFIG += qml_debug') 2704 2705 # Work around QTBUG-39300. 2706 pro_lines.append('CONFIG -= android_install') 2707 2708 pro_lines.append('TARGET = %s' % target_name) 2709 2710 if not target_config.static: 2711 debug_suffix = target_config.get_win32_debug_suffix() 2712 2713 # For Qt v5.5 make sure these frameworks are already loaded by the time 2714 # the libqcocoa.dylib plugin gets loaded. This problem seems to be 2715 # fixed in Qt v5.6. 2716 extra_lflags = '' 2717 2718 if mname == 'QtGui': 2719 # Note that this workaround is flawed because it looks at the PyQt 2720 # configuration rather than the Qt configuration. It will fail if 2721 # the user is building a PyQt without the QtDBus module against a 2722 # Qt with the QtDBus library. However it will be fine for the 2723 # common case where the PyQt configuration reflects the Qt 2724 # configuration. 2725 fwks = [] 2726 for m in ('QtPrintSupport', 'QtDBus', 'QtWidgets'): 2727 if m in target_config.pyqt_modules: 2728 fwks.append('-framework ' + m) 2729 2730 if len(fwks) != 0: 2731 extra_lflags = 'QMAKE_LFLAGS += "%s"\n ' % ' '.join(fwks) 2732 2733 # Without the 'no_check_exist' magic the target.files must exist when 2734 # qmake is run otherwise the install and uninstall targets are not 2735 # generated. 2736 shared = ''' 2737win32 { 2738 PY_MODULE = %s%s.pyd 2739 PY_MODULE_SRC = $(DESTDIR_TARGET) 2740} else { 2741 PY_MODULE = %s.so 2742 2743 macx { 2744 PY_MODULE_SRC = $(TARGET).plugin/Contents/MacOS/$(TARGET) 2745 2746 QMAKE_LFLAGS += "-undefined dynamic_lookup" 2747 2748 equals(QT_MINOR_VERSION, 5) { 2749 %sQMAKE_RPATHDIR += $$[QT_INSTALL_LIBS] 2750 } 2751 } else { 2752 PY_MODULE_SRC = $(TARGET) 2753 } 2754} 2755 2756QMAKE_POST_LINK = $(COPY_FILE) $$PY_MODULE_SRC $$PY_MODULE 2757 2758target.CONFIG = no_check_exist 2759target.files = $$PY_MODULE 2760''' % (target_name, debug_suffix, target_name, extra_lflags) 2761 2762 pro_lines.extend(shared.split('\n')) 2763 2764 if install_path == '': 2765 install_path = target_config.pyqt_module_dir + '/PyQt5' 2766 2767 install_path = install_path.replace('\\', '/') 2768 2769 pro_lines.append('target.path = %s' % install_path) 2770 pro_lines.append('INSTALLS += target') 2771 2772 # This optimisation could apply to other platforms. 2773 if not target_config.static: 2774 if target_config.py_version >= 0x030000: 2775 entry_point = 'PyInit_%s' % target_name 2776 else: 2777 entry_point = 'init%s' % target_name 2778 2779 exp = open_for_writing(os.path.join(mname, target_name + '.exp')) 2780 exp.write('{ global: %s; local: *; };' % entry_point) 2781 exp.close() 2782 2783 pro_lines.append('QMAKE_LFLAGS += -Wl,--version-script=%s.exp' % target_name) 2784 2785 if target_config.prot_is_public: 2786 pro_lines.append('DEFINES += SIP_PROTECTED_IS_PUBLIC protected=public') 2787 2788 # This is needed for Windows. 2789 pro_lines.append('INCLUDEPATH += .') 2790 2791 target_config.add_sip_h_directives(pro_lines) 2792 2793 if metadata.qpy_lib: 2794 # This is the easiest way to make sure it is set for handwritten code. 2795 if not target_config.py_debug: 2796 pro_lines.append('DEFINES += Py_LIMITED_API=0x03040000') 2797 2798 pro_lines.append('INCLUDEPATH += %s' % 2799 qmake_quote(os.path.relpath(source_path('qpy', mname), mname))) 2800 2801 if include_paths: 2802 pro_lines.append( 2803 'INCLUDEPATH += ' + ' '.join( 2804 [qmake_quote(p) for p in include_paths])) 2805 2806 if libs != '': 2807 pro_lines.append('LIBS += %s' % libs) 2808 2809 if src_dir != mname: 2810 pro_lines.append('INCLUDEPATH += %s' % qmake_quote(src_dir)) 2811 pro_lines.append('VPATH = %s' % qmake_quote(src_dir)) 2812 2813 pro_lines.extend(pro_sources(src_dir, qpy_headers, qpy_sources)) 2814 2815 pro_name = os.path.join(mname, mname + '.pro') 2816 2817 pro = open_for_writing(pro_name) 2818 pro.write('\n'.join(pro_lines)) 2819 pro.write('\n') 2820 pro.close() 2821 2822 2823def fix_license(src_lfile, dst_lfile): 2824 """ Fix the license file, if there is one, so that it conforms to the SIP 2825 v5 syntax. src_lfile is the name of the license file. dst_lfile is the 2826 name of the fixed license file. 2827 """ 2828 2829 f = open(src_lfile) 2830 f5 = open_for_writing(dst_lfile) 2831 2832 for line in f: 2833 if line.startswith('%License'): 2834 anno_start = line.find('/') 2835 anno_end = line.rfind('/') 2836 2837 if anno_start < 0 or anno_end < 0 or anno_start == anno_end: 2838 error("%s has missing annotations." % name) 2839 2840 annos = line[anno_start + 1:anno_end].split(', ') 2841 annos5 = [anno[0].lower() + anno[1:] for anno in annos] 2842 2843 f5.write('%License(') 2844 f5.write(', '.join(annos5)) 2845 f5.write(')\n') 2846 else: 2847 f5.write(line) 2848 2849 f5.close() 2850 f.close() 2851 2852 2853def check_license(target_config, license_confirmed): 2854 """ Handle the validation of the PyQt5 license. target_config is the 2855 target configuration. license_confirmed is set if the user has already 2856 accepted the license. 2857 """ 2858 2859 try: 2860 import license 2861 ltype = license.LicenseType 2862 lname = license.LicenseName 2863 2864 try: 2865 lfile = license.LicenseFile 2866 except AttributeError: 2867 lfile = None 2868 except ImportError: 2869 ltype = None 2870 2871 if ltype is None: 2872 ltype = 'GPL' 2873 lname = "GNU General Public License" 2874 lfile = 'pyqt-gpl.sip' 2875 2876 inform( 2877 "This is the %s version of PyQt %s (licensed under the %s) for " 2878 "Python %s on %s." % 2879 (ltype, PYQT_VERSION_STR, lname, sys.version.split()[0], 2880 sys.platform)) 2881 2882 # Confirm the license if not already done. 2883 if not license_confirmed: 2884 loptions = """ 2885Type 'L' to view the license. 2886""" 2887 2888 sys.stdout.write(loptions) 2889 sys.stdout.write("""Type 'yes' to accept the terms of the license. 2890Type 'no' to decline the terms of the license. 2891 2892""") 2893 2894 while 1: 2895 sys.stdout.write("Do you accept the terms of the license? ") 2896 sys.stdout.flush() 2897 2898 try: 2899 resp = sys.stdin.readline() 2900 except KeyboardInterrupt: 2901 raise SystemExit 2902 except: 2903 resp = "" 2904 2905 resp = resp.strip().lower() 2906 2907 if resp == "yes": 2908 break 2909 2910 if resp == "no": 2911 sys.exit(0) 2912 2913 if resp == 'l': 2914 os.system('more LICENSE') 2915 2916 # Check that the license file exists and fix its syntax. 2917 sip_dir = 'sip' 2918 mk_dir(sip_dir) 2919 2920 src_lfile = os.path.join(target_config.license_dir, lfile) 2921 2922 if os.access(src_lfile, os.F_OK): 2923 inform("Found the license file %s." % lfile) 2924 fix_license(src_lfile, os.path.join(sip_dir, lfile + '5')) 2925 else: 2926 error( 2927 "Please copy the license file %s to %s." % (lfile, 2928 target_config.license_dir)) 2929 2930 2931def check_qt(target_config): 2932 """ Check the Qt installation. target_config is the target configuration. 2933 """ 2934 2935 # Starting with v4.7, Qt (when built with MinGW) assumes that stack frames 2936 # are 16 byte aligned because it uses SSE. However the Python Windows 2937 # installers are built with 4 byte aligned stack frames. We therefore need 2938 # to tweak the g++ flags to deal with it. 2939 if target_config.qmake_spec == 'win32-g++': 2940 target_config.qmake_variables.append('QMAKE_CFLAGS += -mstackrealign') 2941 target_config.qmake_variables.append('QMAKE_CXXFLAGS += -mstackrealign') 2942 2943 2944def check_python(target_config): 2945 """ Check the Python installation. target_config is the target 2946 configuration. 2947 """ 2948 2949 # Check the Python version number. This allows us to assume relative 2950 # imports and ElemenTree are available. 2951 if target_config.py_version < 0x020600: 2952 error("PyQt5 requires Python v2.6 or later.") 2953 2954 2955def check_sip(target_config, verbose): 2956 """ Check that the version of sip is good enough and return its version. 2957 target_config is the target configuration. 2958 """ 2959 2960 if target_config.sip is None: 2961 error( 2962 "Make sure you have a working sip on your PATH or use the " 2963 "--sip argument to explicitly specify a working sip.") 2964 2965 pipe = os.popen(' '.join([quote(target_config.sip), '-V'])) 2966 2967 for l in pipe: 2968 version_str = l.strip() 2969 break 2970 else: 2971 error("'%s -V' did not generate any output." % target_config.sip) 2972 2973 pipe.close() 2974 2975 if '.dev' in version_str or 'snapshot' in version_str: 2976 # We only need to distinguish between sip v4 and sip v5. 2977 if target_config.using_sip5(): 2978 version = 0x050000 2979 else: 2980 version = 0x040000 2981 else: 2982 version = version_from_string(version_str) 2983 if version is None: 2984 error( 2985 "'%s -V' generated unexpected output: '%s'." % ( 2986 target_config.sip, version_str)) 2987 2988 min_version = version_from_string(SIP_MIN_VERSION) 2989 if version < min_version: 2990 error( 2991 "This version of PyQt5 requires sip %s or later." % 2992 SIP_MIN_VERSION) 2993 2994 if version >= 0x050000: 2995 # Install the sip.h file for the private sip module. 2996 if target_config.sip_inc_dir is None: 2997 target_config.sip_inc_dir = os.path.join( 2998 os.path.abspath(os.getcwd()), 'include') 2999 3000 inform("Installing sip.h in %s..." % target_config.sip_inc_dir) 3001 3002 os.makedirs(target_config.sip_inc_dir, exist_ok=True) 3003 3004 argv = ['sip-module', '--sip-h'] 3005 3006 if target_config.abi_version: 3007 argv.append('--abi-version') 3008 argv.append(target_config.abi_version) 3009 3010 argv.append('--target-dir') 3011 argv.append(quote(target_config.sip_inc_dir)), 3012 argv.append('PyQt5.sip') 3013 3014 run_command(' '.join(argv), verbose) 3015 3016 if not os.access(os.path.join(target_config.sip_inc_dir, 'sip.h'), os.F_OK): 3017 error( 3018 "sip-module failed to install sip.h in %s." % 3019 target_config.sip_inc_dir) 3020 else: 3021 if target_config.sip_inc_dir is None: 3022 target_config.sip_inc_dir = target_config.py_venv_inc_dir 3023 3024 return version_str 3025 3026 3027def version_from_string(version_str): 3028 """ Convert a version string of the form m.n or m.n.o to an encoded version 3029 number (or None if it was an invalid format). version_str is the version 3030 string. 3031 """ 3032 3033 parts = version_str.split('.') 3034 if not isinstance(parts, list): 3035 return None 3036 3037 if len(parts) == 2: 3038 parts.append('0') 3039 3040 if len(parts) != 3: 3041 return None 3042 3043 version = 0 3044 3045 for part in parts: 3046 try: 3047 v = int(part) 3048 except ValueError: 3049 return None 3050 3051 version = (version << 8) + v 3052 3053 return version 3054 3055 3056def open_for_writing(fname): 3057 """ Return a file opened for writing while handling the most common problem 3058 of not having write permission on the directory. fname is the name of the 3059 file to open. 3060 """ 3061 try: 3062 return open(fname, 'w') 3063 except IOError: 3064 error( 3065 "There was an error creating %s. Make sure you have write " 3066 "permission on the parent directory." % fname) 3067 3068 3069def matching_files(pattern): 3070 """ Return a reproducable list of files that match a pattern. """ 3071 3072 return sorted(glob.glob(pattern)) 3073 3074 3075def main(argv): 3076 """ Create the configuration module module. argv is the list of command 3077 line arguments. 3078 """ 3079 3080 # Create the default target configuration. 3081 target_config = TargetConfiguration() 3082 3083 # Parse the command line. 3084 parser = create_optparser(target_config) 3085 opts, target_config.qmake_variables = parser.parse_args() 3086 3087 target_config.apply_pre_options(opts) 3088 3089 # Query qmake for the basic configuration information. 3090 target_config.get_qt_configuration() 3091 3092 # Update the target configuration. 3093 if opts.config_file is not None: 3094 target_config.from_configuration_file(opts.config_file) 3095 else: 3096 target_config.from_introspection(opts.verbose, opts.debug) 3097 3098 target_config.post_configuration() 3099 3100 target_config.apply_post_options(opts) 3101 3102 # Check the licenses are compatible. 3103 check_license(target_config, opts.license_confirmed) 3104 3105 # Check Python is what we need. 3106 check_python(target_config) 3107 3108 # Check SIP is what we need. 3109 sip_version = check_sip(target_config, opts.verbose) 3110 3111 # Check Qt is what we need. 3112 check_qt(target_config) 3113 3114 # Check for the VendorID package. 3115 check_vendorid(target_config) 3116 3117 # Check which modules to build if we haven't been told. 3118 if len(target_config.pyqt_modules) == 0: 3119 check_modules(target_config, opts.disabled_modules, opts.verbose) 3120 else: 3121 # Check that the user supplied module names are valid. 3122 for mname in target_config.pyqt_modules: 3123 if mname not in MODULE_METADATA: 3124 error("'%s' is not a valid module name." % mname) 3125 3126 check_dbus(target_config, opts.verbose) 3127 3128 # Tell the user what's been found. 3129 inform_user(target_config, sip_version) 3130 3131 # Generate the makefiles. 3132 generate_makefiles(target_config, opts.verbose, 3133 opts.split if opts.concat else 0, opts.tracing, 3134 opts.fatal_warnings, opts.distinfo) 3135 3136 3137############################################################################### 3138# The script starts here. 3139############################################################################### 3140 3141if __name__ == '__main__': 3142 try: 3143 main(sys.argv) 3144 except SystemExit: 3145 raise 3146 except: 3147 sys.stderr.write( 3148"""An internal error occured. Please report all the output from the program, 3149including the following traceback, to support@riverbankcomputing.com. 3150""") 3151 raise 3152