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 == '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 == '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 pyqt_modules.append('pylupdate') 1506 pyqt_modules.append('pyrcc') 1507 1508 for mname in pyqt_modules: 1509 metadata = MODULE_METADATA[mname] 1510 1511 if metadata.qpy_lib: 1512 sp_qpy_dir = source_path('qpy', mname) 1513 1514 qpy_c_sources = [os.path.relpath(f, mname) 1515 for f in matching_files(os.path.join(sp_qpy_dir, '*.c'))] 1516 qpy_cpp_sources = [os.path.relpath(f, mname) 1517 for f in matching_files(os.path.join(sp_qpy_dir, '*.cpp'))] 1518 qpy_headers = [os.path.relpath(f, mname) 1519 for f in matching_files(os.path.join(sp_qpy_dir, '*.h'))] 1520 1521 qpy_sources = qpy_c_sources + qpy_cpp_sources 1522 else: 1523 qpy_sources = [] 1524 qpy_headers = [] 1525 1526 generate_sip_module_code(target_config, verbose, parts, tracing, mname, 1527 fatal_warnings, sip_flags, metadata.public, qpy_sources, 1528 qpy_headers) 1529 subdirs.append(mname) 1530 1531 # Generate the composite module. 1532 qtmod_sipdir = os.path.join('sip', 'Qt') 1533 mk_clean_dir(qtmod_sipdir) 1534 1535 qtmod_sipfile = os.path.join(qtmod_sipdir, 'Qtmod.sip') 1536 f = open_for_writing(qtmod_sipfile) 1537 1538 f.write('''%CompositeModule PyQt5.Qt 1539 1540''') 1541 1542 for mname in COMPOSITE_COMPONENTS: 1543 if mname in target_config.pyqt_modules: 1544 f.write('%%Include %s/%smod.sip\n' % (mname, mname)) 1545 1546 f.close() 1547 1548 generate_sip_module_code(target_config, verbose, parts, tracing, 'Qt', 1549 fatal_warnings, sip_flags, False) 1550 subdirs.append('Qt') 1551 1552 # Generate the top-level __init__.py. 1553 inf = open(source_path('__init__.py')) 1554 contents = inf.read() 1555 inf.close() 1556 1557 inf = open_for_writing('__init__.py') 1558 inf.write(contents) 1559 1560 if target_config.py_platform == 'win32': 1561 # On Windows we try and make sure the Qt DLLs can be found, either any 1562 # bundled copies or an existing installation (using the traditional 1563 # Windows DLL search). We don't raise an exception in case the 1564 # application has already taken steps to resolve this which we don't 1565 # know about. 1566 inf.write(""" 1567 1568def find_qt(): 1569 import os, sys 1570 1571 qtcore_dll = '\\\\Qt5Core.dll' 1572 1573 dll_dir = os.path.dirname(sys.executable) 1574 if not os.path.isfile(dll_dir + qtcore_dll): 1575 path = os.environ['PATH'] 1576 1577 dll_dir = os.path.dirname(__file__) + '\\\\Qt5\\\\bin' 1578 if os.path.isfile(dll_dir + qtcore_dll): 1579 path = dll_dir + ';' + path 1580 os.environ['PATH'] = path 1581 else: 1582 for dll_dir in path.split(';'): 1583 if os.path.isfile(dll_dir + qtcore_dll): 1584 break 1585 else: 1586 return 1587 1588 try: 1589 os.add_dll_directory(dll_dir) 1590 except AttributeError: 1591 pass 1592 1593 1594find_qt() 1595del find_qt 1596""") 1597 1598 inf.close() 1599 1600 # Generate any executable wrappers. 1601 wrappers = [] 1602 if not target_config.no_tools: 1603 # Generate the pylupdate5 and pyrcc5 wrappers. 1604 for tool in ('pylupdate', 'pyrcc'): 1605 wrappers.append((tool, 1606 generate_tool_wrapper(target_config, tool + '5', 1607 'PyQt5.%s_main' % tool))) 1608 1609 # Generate the pyuic5 wrapper. 1610 wrappers.append(('pyuic', 1611 generate_tool_wrapper(target_config, 'pyuic5', 1612 'PyQt5.uic.pyuic'))) 1613 1614 # Generate the Qt Designer plugin. 1615 if not target_config.no_designer_plugin and 'QtDesigner' in target_config.pyqt_modules: 1616 if generate_plugin_makefile(target_config, verbose, 'designer', target_config.designer_plugin_dir, "Qt Designer"): 1617 subdirs.append('designer') 1618 1619 # Generate the qmlscene plugin. 1620 if not target_config.no_qml_plugin and 'QtQml' in target_config.pyqt_modules: 1621 if generate_plugin_makefile(target_config, verbose, 'qmlscene', target_config.qml_plugin_dir, "qmlscene"): 1622 subdirs.append('qmlscene') 1623 1624 rewrite_qmldir(target_config, 'Charts', 1625 source_path('examples', 'quick', 'tutorials', 'extending', 1626 'chapter6-plugins')) 1627 1628 # Generate the QScintilla API file. 1629 if target_config.qsci_api: 1630 inform("Generating the QScintilla API file...") 1631 f = open_for_writing('PyQt5.api') 1632 1633 for mname in target_config.pyqt_modules: 1634 if MODULE_METADATA[mname].public: 1635 api = open(mname + '.api') 1636 1637 for l in api: 1638 f.write('PyQt5.' + l) 1639 1640 api.close() 1641 os.remove(mname + '.api') 1642 1643 f.close() 1644 1645 # Generate the Python dbus module. 1646 if target_config.pydbus_module_dir != '': 1647 mname = 'dbus' 1648 1649 mk_dir(mname) 1650 sp_src_dir = source_path(mname) 1651 1652 lib_dirs = ['-L' + l for l in target_config.dbus_lib_dirs] 1653 lib_names = ['-l' + l for l in target_config.dbus_libs] 1654 libs = ' '.join(lib_dirs + lib_names) 1655 1656 generate_module_makefile(target_config, verbose, mname, 1657 include_paths=target_config.dbus_inc_dirs, libs=libs, 1658 install_path=target_config.pydbus_module_dir, 1659 src_dir=sp_src_dir) 1660 1661 subdirs.append(mname) 1662 1663 # Generate the top-level .pro file. 1664 all_installs = [] 1665 1666 inform("Generating the top-level .pro file...") 1667 out_f = open_for_writing(toplevel_pro) 1668 1669 root_dir = qmake_quote(target_config.pyqt_module_dir + '/PyQt5') 1670 1671 for mname in pyqt_modules: 1672 all_installs.append( 1673 root_dir + '/' + module_file_name(target_config, mname)) 1674 1675 all_installs.append(root_dir + '/' + module_file_name(target_config, 'Qt')) 1676 1677 out_f.write('''TEMPLATE = subdirs 1678CONFIG += ordered nostrip 1679SUBDIRS = %s 1680 1681init_py.files = __init__.py 1682init_py.path = %s 1683INSTALLS += init_py 1684''' % (' '.join(subdirs), root_dir)) 1685 1686 all_installs.append(root_dir + '/__init__.py') 1687 1688 # Install the uic module. 1689 out_f.write(''' 1690uic_package.files = %s 1691uic_package.path = %s 1692INSTALLS += uic_package 1693''' % (source_path('pyuic', 'uic'), root_dir)) 1694 1695 all_installs.append(root_dir + '/uic') 1696 1697 # Install the tool main scripts and wrappers. 1698 if wrappers: 1699 wrapper_exes = [] 1700 for tool, wrapper in wrappers: 1701 if tool != 'pyuic': 1702 tool_main = tool + '_main.py' 1703 1704 out_f.write(''' 1705%s.files = %s 1706%s.path = %s 1707INSTALLS += %s 1708''' % (tool, source_path('sip', tool, tool_main), tool, root_dir, tool)) 1709 1710 all_installs.append(root_dir + '/' + tool_main) 1711 1712 wrapper_exes.append(wrapper) 1713 all_installs.append(target_config.pyqt_bin_dir + '/' + wrapper) 1714 1715 out_f.write(''' 1716tools.files = %s 1717tools.path = %s 1718INSTALLS += tools 1719''' % (' '.join(wrapper_exes), qmake_quote(target_config.pyqt_bin_dir))) 1720 1721 # Install the .sip files. 1722 if target_config.pyqt_sip_dir: 1723 for mname, metadata in MODULE_METADATA.items(): 1724 if metadata.public and mname != 'Qt': 1725 sip_files = matching_files(source_path('sip', mname, '*.sip')) 1726 1727 if len(sip_files) != 0: 1728 mdir = target_config.pyqt_sip_dir + '/' + mname 1729 1730 out_f.write(''' 1731sip%s.path = %s 1732sip%s.files = %s 1733INSTALLS += sip%s 1734''' % ( 1735 mname, qmake_quote(mdir), 1736 mname, ' '.join([qmake_quote(s) for s in sip_files]), 1737 mname 1738)) 1739 1740 all_installs.append(mdir) 1741 1742 # Install the stub files. 1743 if target_config.py_version >= 0x030500 and target_config.pyqt_stubs_dir: 1744 pyi_names = [mname + '.pyi' 1745 for mname in target_config.pyqt_modules if mname[0] != '_'] 1746 1747 out_f.write(''' 1748pep484_stubs.files = %s 1749pep484_stubs.path = %s 1750INSTALLS += pep484_stubs 1751''' % (' '.join(pyi_names), 1752 qmake_quote(target_config.pyqt_stubs_dir))) 1753 1754 all_installs.extend( 1755 [target_config.pyqt_stubs_dir + '/' + pyi 1756 for pyi in pyi_names]) 1757 1758 # Install the QScintilla .api file. 1759 if target_config.qsci_api: 1760 api_dir = target_config.qsci_api_dir + '/api/python' 1761 1762 out_f.write(''' 1763qscintilla_api.files = PyQt5.api 1764qscintilla_api.path = %s 1765INSTALLS += qscintilla_api 1766''' % qmake_quote(api_dir)) 1767 1768 all_installs.append(api_dir + '/PyQt5.api') 1769 1770 if distinfo: 1771 # The command to run to generate the .dist-info directory. 1772 distinfo_dir = os.path.join(target_config.pyqt_module_dir, 1773 'PyQt5-' + PYQT_VERSION_STR + '.dist-info') 1774 1775 run_mk_distinfo = '%s %s \\"$(INSTALL_ROOT)\\" %s installed.txt' % ( 1776 sys.executable, source_path('mk_distinfo.py'), distinfo_dir) 1777 1778 out_f.write(''' 1779distinfo.extra = %s 1780distinfo.path = %s 1781INSTALLS += distinfo 1782''' % (run_mk_distinfo, root_dir)) 1783 1784 # Create the file containing all installed files. 1785 installed = open('installed.txt', 'w') 1786 1787 for install in all_installs: 1788 installed.write(install + '\n') 1789 1790 installed.close() 1791 1792 out_f.close() 1793 1794 # Make the wrappers executable on platforms that support it. If we did it 1795 # after running qmake then (on Linux) the execute bits would be stripped on 1796 # installation. 1797 if target_config.py_platform != 'win32': 1798 for tool, wrapper in wrappers: 1799 inform("Making the %s wrapper executable..." % wrapper) 1800 1801 sbuf = os.stat(wrapper) 1802 mode = sbuf.st_mode 1803 mode |= (stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH) 1804 os.chmod(wrapper, mode) 1805 1806 # Generate the makefiles. 1807 inform("Generating the Makefiles...") 1808 run_qmake(target_config, verbose, toplevel_pro, recursive=True) 1809 1810 1811def generate_plugin_makefile(target_config, verbose, plugin_dir, install_dir, plugin_name): 1812 """ Generate the makefile for a plugin that embeds the Python interpreter. 1813 target_config is the target configuration. verbose is set if the output is 1814 to be displayed. plugin_dir is the name of the directory containing the 1815 plugin implementation. install_dir is the name of the directory that the 1816 plugin will be installed in. plugin_name is a descriptive name of the 1817 plugin to be used in user messages. Returns True if the makefile could be 1818 generated. 1819 """ 1820 1821 # Check we have a shared interpreter library. 1822 if target_config.py_pyshlib == '': 1823 inform("The %s plugin was disabled because a dynamic Python library couldn't be found." % plugin_name) 1824 return False 1825 1826 # Create the qmake project file. 1827 inform("Generating the %s plugin .pro file..." % plugin_name) 1828 1829 sp_plugin_dir = source_path(plugin_dir) 1830 1831 fin = open(os.path.join(sp_plugin_dir, '%s.pro-in' % plugin_dir)) 1832 prj = fin.read() 1833 fin.close() 1834 1835 prj = prj.replace('@QTCONFIG@', 1836 'debug' if target_config.debug else 'release') 1837 prj = prj.replace('@PYINCDIR@', qmake_quote(target_config.py_inc_dir)) 1838 prj = prj.replace('@SIPINCDIR@', qmake_quote(target_config.sip_inc_dir)) 1839 prj = prj.replace('@PYLINK@', target_config.get_pylib_link_arguments()) 1840 prj = prj.replace('@PYSHLIB@', target_config.py_pyshlib) 1841 prj = prj.replace('@QTPLUGINDIR@', qmake_quote(install_dir)) 1842 1843 pro_name = os.path.join(plugin_dir, '%s.pro' % plugin_dir) 1844 1845 mk_dir(plugin_dir) 1846 fout = open_for_writing(pro_name) 1847 fout.write(prj) 1848 1849 if sp_plugin_dir != plugin_dir: 1850 fout.write(''' 1851INCLUDEPATH += %s 1852VPATH = %s 1853''' % (qmake_quote(sp_plugin_dir), qmake_quote(sp_plugin_dir))) 1854 1855 fout.write('\n'.join(target_config.qmake_variables) + '\n') 1856 1857 fout.close() 1858 1859 return True 1860 1861 1862def pro_sources(src_dir, other_headers=None, other_sources=None): 1863 """ Return the HEADERS, SOURCES and OBJECTIVE_SOURCES variables for a .pro 1864 file by introspecting a directory. src_dir is the name of the directory. 1865 other_headers is an optional list of other header files. other_sources is 1866 an optional list of other source files. 1867 """ 1868 1869 pro_lines = [] 1870 1871 headers = [os.path.basename(f) for f in matching_files('%s/*.h' % src_dir)] 1872 1873 if other_headers is not None: 1874 headers += other_headers 1875 1876 if len(headers) != 0: 1877 pro_lines.append('HEADERS = %s' % ' '.join(headers)) 1878 1879 sources = [os.path.basename(f) for f in matching_files('%s/*.c' % src_dir)] 1880 1881 for f in matching_files('%s/*.cpp' % src_dir): 1882 f = os.path.basename(f) 1883 1884 # Exclude any moc generated C++ files that might be around from a 1885 # previous build. 1886 if not f.startswith('moc_'): 1887 sources.append(f) 1888 1889 if other_sources is not None: 1890 sources += other_sources 1891 1892 if len(sources) != 0: 1893 pro_lines.append('SOURCES = %s' % ' '.join([qmake_quote(s) for s in sources])) 1894 1895 objective_sources = [ 1896 os.path.basename(f) for f in matching_files('%s/*.mm' % src_dir)] 1897 1898 if len(objective_sources) != 0: 1899 pro_lines.append('OBJECTIVE_SOURCES = %s' % ' '.join([qmake_quote(s) for s in objective_sources])) 1900 1901 return pro_lines 1902 1903 1904def module_file_name(target_config, name): 1905 """ Return the name of a file implementing a module. """ 1906 1907 if sys.platform == 'win32': 1908 fs = '{}.lib' if target_config.static else '{}.pyd' 1909 else: 1910 fs = 'lib{}.a' if target_config.static else '{}.so' 1911 1912 return fs.format(name) 1913 1914 1915def generate_tool_wrapper(target_config, wrapper, module): 1916 """ Create a platform dependent executable wrapper for a tool module. 1917 target_config is the target configuration. wrapper is the name of the 1918 wrapper without any extension. module is the tool module. Returns the 1919 platform specific name of the wrapper. 1920 """ 1921 1922 if target_config.py_platform == 'win32': 1923 wrapper += '.bat' 1924 1925 inform("Generating the %s wrapper..." % wrapper) 1926 1927 exe = quote(target_config.pyuic_interpreter) 1928 1929 wf = open_for_writing(wrapper) 1930 1931 if target_config.py_platform == 'win32': 1932 wf.write('@%s -m %s %%1 %%2 %%3 %%4 %%5 %%6 %%7 %%8 %%9\n' % (exe, module)) 1933 else: 1934 wf.write('#!/bin/sh\n') 1935 wf.write('exec %s -m %s ${1+"$@"}\n' % (exe, module)) 1936 1937 wf.close() 1938 1939 return wrapper 1940 1941 1942def rewrite_qmldir(target_config, module, module_dir): 1943 """ Re-write a qmldir file for a module that used the qmlscene plugin. 1944 target_config is the target configuration. module is the name of the QML 1945 module. module_dir is the name of the directory containing the QML module. 1946 """ 1947 1948 qmldir_fn = os.path.join(module_dir, module, 'qmldir') 1949 1950 inform("Re-writing %s..." % qmldir_fn) 1951 1952 qmldir = open_for_writing(qmldir_fn) 1953 qmldir.write('module %s\nplugin pyqt5qmlplugin %s\n' % (module, target_config.qml_plugin_dir)) 1954 qmldir.close() 1955 1956 1957def quote(path): 1958 """ Return a path with quotes added if it contains spaces. path is the 1959 path. 1960 """ 1961 1962 if ' ' in path: 1963 path = '"%s"' % path 1964 1965 return path 1966 1967 1968def qmake_quote(path): 1969 """ Return a path quoted for qmake if it contains spaces. path is the 1970 path. 1971 """ 1972 1973 if ' ' in path: 1974 path = '$$quote(%s)' % path 1975 1976 return path 1977 1978 1979def inform_user(target_config, sip_version): 1980 """ Tell the user the values that are going to be used. target_config is 1981 the target configuration. sip_version is the SIP version string. 1982 """ 1983 1984 inform("Qt v%s is being used." % 1985 version_to_string(target_config.qt_version)) 1986 1987 inform("The qmake executable is %s." % target_config.qmake) 1988 1989 inform( 1990 "Qt is built as a %s library." % ( 1991 "shared" if target_config.qt_shared else "static")) 1992 1993 if target_config.sysroot != '': 1994 inform("The system root directory is %s." % target_config.sysroot) 1995 1996 inform("SIP %s is being used." % sip_version) 1997 inform("The sip executable is %s." % target_config.sip) 1998 inform("These PyQt5 modules will be built: %s." % ', '.join(target_config.pyqt_modules)) 1999 inform("The PyQt5 Python package will be installed in %s." % target_config.pyqt_module_dir) 2000 2001 if target_config.debug: 2002 inform("A debug version of PyQt5 will be built.") 2003 2004 if target_config.py_debug: 2005 inform("A debug build of Python is being used.") 2006 2007 if target_config.no_docstrings: 2008 inform("PyQt5 is being built without generated docstrings.") 2009 else: 2010 inform("PyQt5 is being built with generated docstrings.") 2011 2012 if target_config.prot_is_public: 2013 inform("PyQt5 is being built with 'protected' redefined as 'public'.") 2014 2015 if target_config.no_designer_plugin: 2016 inform("The Designer plugin will not be built.") 2017 else: 2018 inform("The Designer plugin will be installed in %s." % 2019 target_config.designer_plugin_dir) 2020 2021 if target_config.no_qml_plugin: 2022 inform("The qmlscene plugin will not be built.") 2023 else: 2024 inform("The qmlscene plugin will be installed in %s." % 2025 target_config.qml_plugin_dir) 2026 2027 if target_config.qsci_api: 2028 inform( 2029 "The QScintilla API file will be installed in %s." % 2030 os.path.join( 2031 target_config.qsci_api_dir, 'api', 'python')) 2032 2033 if target_config.py_version >= 0x030500 and target_config.pyqt_stubs_dir: 2034 inform("The PyQt5 PEP 484 stub files will be installed in %s." % 2035 target_config.pyqt_stubs_dir) 2036 2037 if target_config.pydbus_module_dir: 2038 inform( 2039 "The dbus support module will be installed in %s." % 2040 target_config.pydbus_module_dir) 2041 2042 if target_config.pyqt_sip_dir: 2043 inform("The PyQt5 .sip files will be installed in %s." % 2044 target_config.pyqt_sip_dir) 2045 2046 if target_config.no_tools: 2047 inform("pyuic5, pyrcc5 and pylupdate5 will not be built.") 2048 else: 2049 inform("pyuic5, pyrcc5 and pylupdate5 will be installed in %s." % 2050 target_config.pyqt_bin_dir) 2051 2052 inform("The interpreter used by pyuic5 is %s." % 2053 target_config.pyuic_interpreter) 2054 2055 if target_config.vend_enabled: 2056 inform("PyQt5 will only be usable with signed interpreters.") 2057 2058 2059def run_qmake(target_config, verbose, pro_name, makefile_name='', fatal=True, recursive=False): 2060 """ Run qmake against a .pro file. target_config is the target 2061 configuration. verbose is set if the output is to be displayed. pro_name 2062 is the name of the .pro file. makefile_name is the name of the makefile 2063 to generate (and defaults to Makefile). fatal is set if a qmake failure is 2064 considered a fatal error, otherwise False is returned if qmake fails. 2065 recursive is set to use the -recursive flag. 2066 """ 2067 2068 # qmake doesn't behave consistently if it is not run from the directory 2069 # containing the .pro file - so make sure it is. 2070 pro_dir, pro_file = os.path.split(pro_name) 2071 if pro_dir != '': 2072 cwd = os.getcwd() 2073 os.chdir(pro_dir) 2074 else: 2075 cwd = None 2076 2077 mf = makefile_name if makefile_name != '' else 'Makefile' 2078 2079 remove_file(mf) 2080 2081 args = [quote(target_config.qmake)] 2082 2083 if target_config.qmake_spec != target_config.qmake_spec_default: 2084 args.append('-spec') 2085 args.append(target_config.qmake_spec) 2086 2087 if makefile_name != '': 2088 args.append('-o') 2089 args.append(makefile_name) 2090 2091 if recursive: 2092 args.append('-recursive') 2093 2094 args.append(pro_file) 2095 2096 run_command(' '.join(args), verbose) 2097 2098 if not os.access(mf, os.F_OK): 2099 if fatal: 2100 error( 2101 "%s failed to create a makefile from %s." % 2102 (target_config.qmake, pro_name)) 2103 2104 return False 2105 2106 # Restore the current directory. 2107 if cwd is not None: 2108 os.chdir(cwd) 2109 2110 return True 2111 2112 2113def run_make(target_config, verbose, exe, makefile_name): 2114 """ Run make against a makefile to create an executable. target_config is 2115 the target configuration. verbose is set if the output is to be displayed. 2116 exe is the platform independent name of the executable that will be 2117 created. makefile_name is the name of the makefile. Returns the platform 2118 specific name of the executable, or None if an executable wasn't created. 2119 """ 2120 2121 # Guess the name of make and set the default target and platform specific 2122 # name of the executable. 2123 if target_config.py_platform == 'win32': 2124 if target_config.qmake_spec == 'win32-g++': 2125 make = 'mingw32-make' 2126 else: 2127 make = 'nmake' 2128 2129 if target_config.debug: 2130 makefile_target = 'debug' 2131 platform_exe = os.path.join('debug', exe + '.exe') 2132 else: 2133 makefile_target = 'release' 2134 platform_exe = os.path.join('release', exe + '.exe') 2135 else: 2136 make = 'make' 2137 makefile_target = '' 2138 2139 if target_config.py_platform == 'darwin': 2140 platform_exe = os.path.join(exe + '.app', 'Contents', 'MacOS', exe) 2141 else: 2142 platform_exe = os.path.join('.', exe) 2143 2144 remove_file(platform_exe) 2145 2146 args = [make, '-f', makefile_name] 2147 2148 if makefile_target != '': 2149 args.append(makefile_target) 2150 2151 run_command(' '.join(args), verbose) 2152 2153 return platform_exe if os.access(platform_exe, os.X_OK) else None 2154 2155 2156def run_command(cmd, verbose): 2157 """ Run a command and display the output if requested. cmd is the command 2158 to run. verbose is set if the output is to be displayed. 2159 """ 2160 2161 if verbose: 2162 sys.stdout.write(cmd + "\n") 2163 2164 fout = get_command_output(cmd, and_stderr=True) 2165 2166 # Read stdout and stderr until there is no more output. 2167 lout = fout.readline() 2168 while lout: 2169 if verbose: 2170 if sys.hexversion >= 0x03000000: 2171 sys.stdout.write(str(lout, encoding=sys.stdout.encoding)) 2172 else: 2173 sys.stdout.write(lout) 2174 2175 lout = fout.readline() 2176 2177 close_command_pipe(fout) 2178 2179 2180def remove_file(fname): 2181 """ Remove a file which may or may not exist. fname is the name of the 2182 file. 2183 """ 2184 2185 try: 2186 os.remove(fname) 2187 except OSError: 2188 pass 2189 2190 2191def check_vendorid(target_config): 2192 """ See if the VendorID library and include file can be found. 2193 target_config is the target configuration. 2194 """ 2195 2196 if target_config.py_version >= 0x030000: 2197 # VendorID doesn't support Python v3. 2198 target_config.vend_enabled = False 2199 elif target_config.vend_enabled: 2200 if os.access(os.path.join(target_config.vend_inc_dir, 'vendorid.h'), os.F_OK): 2201 if glob.glob(os.path.join(target_config.vend_lib_dir, '*vendorid*')): 2202 inform("The VendorID package was found.") 2203 else: 2204 target_config.vend_enabled = False 2205 inform( 2206 "The VendorID library could not be found in %s and so " 2207 "signed interpreter checking will be disabled. If the " 2208 "VendorID package is installed then use the " 2209 "--vendorid-libdir argument to explicitly specify the " 2210 "correct directory." % target_config.vend_lib_dir) 2211 else: 2212 target_config.vend_enabled = False 2213 inform( 2214 "vendorid.h could not be found in %s and so signed " 2215 "interpreter checking will be disabled. If the VendorID " 2216 "package is installed then use the --vendorid-incdir " 2217 "argument to explicitly specify the correct directory." % 2218 target_config.vend_inc_dir) 2219 2220 2221def get_command_output(cmd, and_stderr=False): 2222 """ Return a pipe from which a command's output can be read. cmd is the 2223 command. and_stderr is set if the output should include stderr as well as 2224 stdout. 2225 """ 2226 2227 try: 2228 import subprocess 2229 except ImportError: 2230 if and_stderr: 2231 _, sout = os.popen4(cmd) 2232 else: 2233 _, sout, _ = os.popen3(cmd) 2234 2235 return sout 2236 2237 if and_stderr: 2238 stderr = subprocess.STDOUT 2239 else: 2240 stderr = subprocess.PIPE 2241 2242 p = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, 2243 stdout=subprocess.PIPE, stderr=stderr) 2244 2245 return p.stdout 2246 2247 2248def close_command_pipe(pipe): 2249 """ Close the pipe returned by get_command_output(). """ 2250 2251 pipe.close() 2252 2253 try: 2254 os.wait() 2255 except: 2256 pass 2257 2258 2259def source_path(*names): 2260 """ Return the native path for a list of components rooted at the directory 2261 containing this script. names is the sequence of component names. 2262 """ 2263 2264 path = [os.path.dirname(os.path.abspath(__file__))] + list(names) 2265 2266 return os.path.join(*path) 2267 2268 2269def check_dbus(target_config, verbose): 2270 """ See if the DBus support module should be built and update the target 2271 configuration accordingly. target_config is the target configuration. 2272 verbose is set if the output is to be displayed. 2273 """ 2274 2275 if target_config.no_pydbus or not os.path.isdir(source_path('dbus')): 2276 return 2277 2278 inform("Checking to see if the dbus support module should be built...") 2279 2280 cmd = 'pkg-config --cflags-only-I --libs dbus-1' 2281 2282 if verbose: 2283 sys.stdout.write(cmd + "\n") 2284 2285 sout = get_command_output(cmd) 2286 iflags = sout.read().strip() 2287 close_command_pipe(sout) 2288 2289 if not iflags: 2290 inform("DBus v1 does not seem to be installed.") 2291 return 2292 2293 if sys.hexversion >= 0x03000000: 2294 iflags = iflags.decode() 2295 2296 for f in iflags.split(): 2297 if f.startswith('-I'): 2298 target_config.dbus_inc_dirs.append(f[2:]) 2299 elif f.startswith('-L'): 2300 target_config.dbus_lib_dirs.append(f[2:]) 2301 elif f.startswith('-l'): 2302 target_config.dbus_libs.append(f[2:]) 2303 2304 try: 2305 import dbus.mainloop 2306 except: 2307 inform("The Python dbus module doesn't seem to be installed.") 2308 return 2309 2310 target_config.pydbus_module_dir = dbus.mainloop.__path__[0] 2311 2312 # Try and find dbus-python.h. We don't use pkg-config because it is broken 2313 # for dbus-python (at least for versions up to and including v0.81.0). 2314 # Instead we look where DBus itself is installed - which in most cases will 2315 # be where dbus-python is also installed. 2316 if target_config.pydbus_inc_dir != '': 2317 target_config.dbus_inc_dirs = [target_config.pydbus_inc_dir] 2318 2319 for d in target_config.dbus_inc_dirs: 2320 if os.access(os.path.join(d, 'dbus', 'dbus-python.h'), os.F_OK): 2321 break 2322 else: 2323 inform( 2324 "dbus/dbus-python.h could not be found and so the DBus " 2325 "support module will be disabled. If dbus-python v0.80 or " 2326 "later is installed then use the --dbus argument to " 2327 "explicitly specify the directory containing " 2328 "dbus/dbus-python.h.") 2329 target_config.pydbus_module_dir = '' 2330 2331 2332def check_module(target_config, disabled_modules, verbose, mname, incfile=None, test=None): 2333 """ See if a module can be built and, if so, add it to the target 2334 configurations list of modules. target_config is the target configuration. 2335 disabled_modules is the list of modules that have been explicitly disabled. 2336 verbose is set if the output is to be displayed. mname is the name of the 2337 module. incfile is the name of the include file (or sequence of multiple 2338 include files) needed for the test. test is a C++ statement being used for 2339 the test. If either incfile or test are None then there is a test program 2340 that needs to be run and its output captured. 2341 """ 2342 2343 if mname in disabled_modules: 2344 return 2345 2346 # Check the module's main .sip file exists. 2347 if not os.access(source_path('sip', mname, mname + 'mod.sip'), os.F_OK): 2348 return 2349 2350 if verbose: 2351 sys.stdout.write('\n') 2352 2353 inform("Checking to see if the %s module should be built..." % mname) 2354 2355 if incfile is None or test is None: 2356 source = None 2357 else: 2358 if isinstance(incfile, str): 2359 incfile = [incfile] 2360 2361 incfile = ['#include<%s>' % i for i in incfile] 2362 2363 source = '''%s 2364 2365int main(int, char **) 2366{ 2367 %s; 2368} 2369''' % ('\n'.join(incfile), test) 2370 2371 test = compile_test_program(target_config, verbose, mname, source) 2372 if test is None: 2373 return 2374 2375 # If there was an explicit test program then run it to get the disabled 2376 # features. 2377 if source is None: 2378 for disabled in run_test_program(mname, test, verbose): 2379 if disabled: 2380 inform("Disabled %s feature: %s" % (mname, disabled)) 2381 target_config.pyqt_disabled_features.append(disabled) 2382 2383 # Include the module in the build. 2384 target_config.pyqt_modules.append(mname) 2385 2386 2387def compile_test_program(target_config, verbose, mname, source=None, debug=None): 2388 """ Compile the source of a Qt program and return the name of the 2389 executable or None if it couldn't be created. target_config is the target 2390 configuration. verbose is set if the output is to be displayed. mname is 2391 the name of the PyQt module being tested. source is the C++ source of the 2392 program. If it is None then the source is expected to be found in the 2393 config-tests directory. debug is set if debug, rather than release, mode 2394 is to be used. If it is None then the mode is taken from the target 2395 configuration. 2396 """ 2397 2398 metadata = MODULE_METADATA[mname] 2399 2400 # The derived file names. 2401 name = 'cfgtest_' + mname 2402 name_pro = name + '.pro' 2403 name_makefile = name + '.mk' 2404 name_source = name + '.cpp' 2405 2406 # Create the source file if necessary. 2407 if source is None: 2408 name_source = source_path('config-tests', name_source) 2409 else: 2410 f = open_for_writing(name_source) 2411 f.write(source) 2412 f.close() 2413 2414 # Create the .pro file. 2415 pro_lines = [] 2416 pro_add_qt_dependencies(target_config, metadata, pro_lines, debug) 2417 pro_lines.append('TARGET = %s' % name) 2418 2419 pro_lines.append('SOURCES = %s' % qmake_quote(name_source)) 2420 2421 f = open_for_writing(name_pro) 2422 f.write('\n'.join(pro_lines)) 2423 f.close() 2424 2425 if not run_qmake(target_config, verbose, name_pro, name_makefile, fatal=False): 2426 return None 2427 2428 return run_make(target_config, verbose, name, name_makefile) 2429 2430 2431def run_test_program(mname, test, verbose): 2432 """ Run a test program and return the output as a list of lines. mname is 2433 the name of the PyQt module being tested. test is the name of the test 2434 executable. verbose is set if the output is to be displayed. 2435 """ 2436 2437 out_file = 'cfgtest_' + mname + '.out' 2438 2439 # Create the output file, first making sure it doesn't exist. 2440 remove_file(out_file) 2441 run_command(test + ' ' + out_file, verbose) 2442 2443 if not os.access(out_file, os.F_OK): 2444 error("%s failed to create %s. Make sure your Qt installation is correct." % (test, out_file)) 2445 2446 # Read the details. 2447 f = open(out_file) 2448 lines = f.read().strip() 2449 f.close() 2450 2451 return lines.split('\n') if lines else [] 2452 2453 2454def pro_add_qt_dependencies(target_config, metadata, pro_lines, debug=None): 2455 """ Add the Qt dependencies of a module to a .pro file. target_config is 2456 the target configuration. metadata is the module's meta-data. pro_lines 2457 is the list of lines making up the .pro file that is updated. debug is set 2458 if debug, rather than release, mode is to be used. If it is None then the 2459 mode is taken from the target configuration. 2460 """ 2461 2462 if debug is None: 2463 debug = target_config.debug 2464 2465 add = [] 2466 remove = [] 2467 for qt in metadata.qmake_QT: 2468 if qt.startswith('-'): 2469 remove.append(qt[1:]) 2470 else: 2471 add.append(qt) 2472 2473 if len(remove) != 0: 2474 pro_lines.append('QT -= %s' % ' '.join(remove)) 2475 2476 if len(add) != 0: 2477 pro_lines.append('QT += %s' % ' '.join(add)) 2478 2479 pro_lines.append( 2480 'CONFIG += %s' % ('debug' if debug else 'release')) 2481 2482 if metadata.cpp11: 2483 pro_lines.append('CONFIG += c++11') 2484 2485 pro_lines.extend(target_config.qmake_variables) 2486 2487 2488def get_sip_flags(target_config): 2489 """ Return the SIP platform, version and feature flags. target_config is 2490 the target configuration. 2491 """ 2492 2493 sip_flags = ['-n', 'PyQt5.sip'] 2494 2495 # If we don't check for signed interpreters, we exclude the 'VendorID' 2496 # feature 2497 if target_config.py_version < 0x030000 and not target_config.vend_enabled: 2498 sip_flags.append('-x') 2499 sip_flags.append('VendorID') 2500 2501 # Handle Python debug builds. 2502 if target_config.py_debug: 2503 sip_flags.append('-D') 2504 2505 # Handle the platform tag. (Allow for win32-g++.) 2506 if target_config.py_platform.startswith('win32'): 2507 plattag = 'WS_WIN' 2508 elif target_config.py_platform == 'darwin': 2509 plattag = 'WS_MACX' 2510 else: 2511 plattag = 'WS_X11' 2512 2513 sip_flags.append('-t') 2514 sip_flags.append(plattag) 2515 2516 # Handle the Qt version tag. 2517 sip_flags.append('-t') 2518 sip_flags.append(version_to_sip_tag(target_config.qt_version)) 2519 2520 # Handle any feature flags. 2521 for xf in target_config.pyqt_disabled_features: 2522 sip_flags.append('-x') 2523 sip_flags.append(xf) 2524 2525 # Handle the version specific Python features. 2526 if target_config.py_version < 0x030000: 2527 sip_flags.append('-x') 2528 sip_flags.append('Py_v3') 2529 2530 return sip_flags 2531 2532 2533def mk_clean_dir(name): 2534 """ Create a clean (ie. empty) directory. name is the name of the 2535 directory. 2536 """ 2537 2538 try: 2539 shutil.rmtree(name) 2540 except: 2541 pass 2542 2543 try: 2544 os.makedirs(name) 2545 except: 2546 error("Unable to create the %s directory." % name) 2547 2548 2549def mk_dir(name): 2550 """ Ensure a directory exists, creating it if necessary. name is the name 2551 of the directory. 2552 """ 2553 2554 try: 2555 os.makedirs(name) 2556 except: 2557 pass 2558 2559 2560def generate_sip_module_code(target_config, verbose, parts, tracing, mname, fatal_warnings, sip_flags, doc_support, qpy_sources=None, qpy_headers=None): 2561 """ Generate the code for a module. target_config is the target 2562 configuration. verbose is set if the output is to be displayed. parts is 2563 the number of parts the generated code should be split into. tracing is 2564 set if the generated code should include tracing calls. mname is the name 2565 of the module to generate the code for. fatal_warnings is set if warnings 2566 are fatal. sip_flags is the list of flags to pass to sip. doc_support 2567 is set if documentation support is to be generated for the module. 2568 qpy_sources is the optional list of QPy support code source files. 2569 qpy_headers is the optional list of QPy support code header files. 2570 """ 2571 2572 inform("Generating the C++ source for the %s module..." % mname) 2573 2574 mk_clean_dir(mname) 2575 2576 # Build the SIP command line. 2577 argv = [target_config.sip, '-w'] 2578 2579 if target_config.abi_version: 2580 argv.append('--abi-version') 2581 argv.append(target_config.abi_version) 2582 2583 argv.extend(sip_flags) 2584 2585 if fatal_warnings: 2586 argv.append('-f') 2587 2588 if target_config.prot_is_public: 2589 argv.append('-P'); 2590 2591 if parts != 0: 2592 argv.append('-j') 2593 argv.append(str(parts)) 2594 2595 if tracing: 2596 argv.append('-r') 2597 2598 if doc_support: 2599 if not target_config.no_docstrings: 2600 argv.append('-o'); 2601 2602 if target_config.qsci_api: 2603 argv.append('-a') 2604 argv.append(mname + '.api') 2605 2606 if target_config.py_version >= 0x030500 and target_config.pyqt_stubs_dir: 2607 argv.append('-y') 2608 argv.append(mname + '.pyi') 2609 2610 # Pass the absolute pathname so that #line files are absolute. 2611 argv.append('-c') 2612 argv.append(os.path.abspath(mname)) 2613 2614 argv.append('-I') 2615 argv.append('sip') 2616 2617 sp_sip_dir = source_path('sip') 2618 if sp_sip_dir != 'sip': 2619 # SIP assumes POSIX style separators. 2620 sp_sip_dir = sp_sip_dir.replace('\\', '/') 2621 argv.append('-I') 2622 argv.append(sp_sip_dir) 2623 2624 # The .sip files for the Qt modules will be in the out-of-tree directory. 2625 if mname == 'Qt': 2626 sip_dir = 'sip' 2627 else: 2628 sip_dir = sp_sip_dir 2629 2630 # Add the name of the .sip file. 2631 argv.append('%s/%s/%smod.sip' % (sip_dir, mname, mname)) 2632 2633 run_command(' '.join([quote(a) for a in argv]), verbose) 2634 2635 # Check the result. 2636 if mname == 'Qt': 2637 file_check = 'sip%scmodule.c' % mname 2638 else: 2639 file_check = 'sipAPI%s.h' % mname 2640 2641 if not os.access(os.path.join(mname, file_check), os.F_OK): 2642 error("Unable to create the C++ code.") 2643 2644 # Embed the sip flags. 2645 if mname == 'QtCore': 2646 inform("Embedding sip flags...") 2647 2648 in_f = open(source_path('qpy', 'QtCore', 'qpycore_post_init.cpp.in')) 2649 out_f = open_for_writing( 2650 os.path.join('QtCore', 'qpycore_post_init.cpp')) 2651 2652 for line in in_f: 2653 line = line.replace('@@PYQT_SIP_FLAGS@@', ' '.join(sip_flags)) 2654 out_f.write(line) 2655 2656 in_f.close() 2657 out_f.close() 2658 2659 # Generate the makefile. 2660 include_paths = [] 2661 libs = '' 2662 2663 if target_config.vend_enabled: 2664 if mname == 'QtCore': 2665 include_paths.append(target_config.vend_inc_dir) 2666 libs = '-L%s -lvendorid' % target_config.vend_lib_dir 2667 2668 generate_module_makefile(target_config, verbose, mname, 2669 include_paths=include_paths, libs=libs, qpy_sources=qpy_sources, 2670 qpy_headers=qpy_headers) 2671 2672 2673def generate_module_makefile(target_config, verbose, mname, include_paths=None, libs='', install_path='', src_dir='', qpy_sources=None, qpy_headers=None): 2674 """ Generate the makefile for a module. target_config is the target 2675 configuration. verbose is set if the output is to be displayed. mname is 2676 the name of the module. include_paths is an optional list of values of 2677 INCLUDEPATH. libs is an optional additional value of LIBS. install_path 2678 is the optional name of the directory that the module will be installed in. 2679 src_dir is the optional source directory (by default the sources are 2680 assumed to be in the module directory). qpy_sources is the optional list 2681 of QPy support code source files. qpy_headers is the optional list of QPy 2682 support code header files. 2683 """ 2684 2685 if verbose: 2686 sys.stdout.write('\n') 2687 2688 inform("Generating the .pro file for the %s module..." % mname) 2689 2690 if src_dir == '': 2691 src_dir = mname 2692 2693 target_name = mname 2694 2695 metadata = MODULE_METADATA[mname] 2696 2697 if metadata.qmake_TARGET != '': 2698 target_name = metadata.qmake_TARGET 2699 2700 pro_lines = ['TEMPLATE = lib'] 2701 2702 # Note some version of Qt5 (probably incorrectly) implements 2703 # 'plugin_bundle' instead of 'plugin' so we specify both. 2704 pro_lines.append('CONFIG += warn_on exceptions_off %s' % ('staticlib hide_symbols' if target_config.static else 'plugin plugin_bundle')) 2705 2706 pro_add_qt_dependencies(target_config, metadata, pro_lines) 2707 2708 if target_config.qml_debug: 2709 pro_lines.append('CONFIG += qml_debug') 2710 2711 # Work around QTBUG-39300. 2712 pro_lines.append('CONFIG -= android_install') 2713 2714 pro_lines.append('TARGET = %s' % target_name) 2715 2716 if not target_config.static: 2717 debug_suffix = target_config.get_win32_debug_suffix() 2718 2719 # For Qt v5.5 make sure these frameworks are already loaded by the time 2720 # the libqcocoa.dylib plugin gets loaded. This problem seems to be 2721 # fixed in Qt v5.6. 2722 extra_lflags = '' 2723 2724 if mname == 'QtGui': 2725 # Note that this workaround is flawed because it looks at the PyQt 2726 # configuration rather than the Qt configuration. It will fail if 2727 # the user is building a PyQt without the QtDBus module against a 2728 # Qt with the QtDBus library. However it will be fine for the 2729 # common case where the PyQt configuration reflects the Qt 2730 # configuration. 2731 fwks = [] 2732 for m in ('QtPrintSupport', 'QtDBus', 'QtWidgets'): 2733 if m in target_config.pyqt_modules: 2734 fwks.append('-framework ' + m) 2735 2736 if len(fwks) != 0: 2737 extra_lflags = 'QMAKE_LFLAGS += "%s"\n ' % ' '.join(fwks) 2738 2739 # Without the 'no_check_exist' magic the target.files must exist when 2740 # qmake is run otherwise the install and uninstall targets are not 2741 # generated. 2742 shared = ''' 2743win32 { 2744 PY_MODULE = %s%s.pyd 2745 PY_MODULE_SRC = $(DESTDIR_TARGET) 2746} else { 2747 PY_MODULE = %s.so 2748 2749 macx { 2750 PY_MODULE_SRC = $(TARGET).plugin/Contents/MacOS/$(TARGET) 2751 2752 QMAKE_LFLAGS += "-undefined dynamic_lookup" 2753 2754 equals(QT_MINOR_VERSION, 5) { 2755 %sQMAKE_RPATHDIR += $$[QT_INSTALL_LIBS] 2756 } 2757 } else { 2758 PY_MODULE_SRC = $(TARGET) 2759 } 2760} 2761 2762QMAKE_POST_LINK = $(COPY_FILE) $$PY_MODULE_SRC $$PY_MODULE 2763 2764target.CONFIG = no_check_exist 2765target.files = $$PY_MODULE 2766''' % (target_name, debug_suffix, target_name, extra_lflags) 2767 2768 pro_lines.extend(shared.split('\n')) 2769 2770 if install_path == '': 2771 install_path = target_config.pyqt_module_dir + '/PyQt5' 2772 2773 install_path = install_path.replace('\\', '/') 2774 2775 pro_lines.append('target.path = %s' % install_path) 2776 pro_lines.append('INSTALLS += target') 2777 2778 # This optimisation could apply to other platforms. 2779 if 'linux' in target_config.qmake_spec and not target_config.static: 2780 if target_config.py_version >= 0x030000: 2781 entry_point = 'PyInit_%s' % target_name 2782 else: 2783 entry_point = 'init%s' % target_name 2784 2785 exp = open_for_writing(os.path.join(mname, target_name + '.exp')) 2786 exp.write('{ global: %s; local: *; };' % entry_point) 2787 exp.close() 2788 2789 pro_lines.append('QMAKE_LFLAGS += -Wl,--version-script=%s.exp' % target_name) 2790 2791 if target_config.prot_is_public: 2792 pro_lines.append('DEFINES += SIP_PROTECTED_IS_PUBLIC protected=public') 2793 2794 # This is needed for Windows. 2795 pro_lines.append('INCLUDEPATH += .') 2796 2797 target_config.add_sip_h_directives(pro_lines) 2798 2799 if metadata.qpy_lib: 2800 # This is the easiest way to make sure it is set for handwritten code. 2801 if not target_config.py_debug: 2802 pro_lines.append('DEFINES += Py_LIMITED_API=0x03040000') 2803 2804 pro_lines.append('INCLUDEPATH += %s' % 2805 qmake_quote(os.path.relpath(source_path('qpy', mname), mname))) 2806 2807 if include_paths: 2808 pro_lines.append( 2809 'INCLUDEPATH += ' + ' '.join( 2810 [qmake_quote(p) for p in include_paths])) 2811 2812 if libs != '': 2813 pro_lines.append('LIBS += %s' % libs) 2814 2815 if src_dir != mname: 2816 pro_lines.append('INCLUDEPATH += %s' % qmake_quote(src_dir)) 2817 pro_lines.append('VPATH = %s' % qmake_quote(src_dir)) 2818 2819 pro_lines.extend(pro_sources(src_dir, qpy_headers, qpy_sources)) 2820 2821 pro_name = os.path.join(mname, mname + '.pro') 2822 2823 pro = open_for_writing(pro_name) 2824 pro.write('\n'.join(pro_lines)) 2825 pro.write('\n') 2826 pro.close() 2827 2828 2829def fix_license(src_lfile, dst_lfile): 2830 """ Fix the license file, if there is one, so that it conforms to the SIP 2831 v5 syntax. src_lfile is the name of the license file. dst_lfile is the 2832 name of the fixed license file. 2833 """ 2834 2835 f = open(src_lfile) 2836 f5 = open_for_writing(dst_lfile) 2837 2838 for line in f: 2839 if line.startswith('%License'): 2840 anno_start = line.find('/') 2841 anno_end = line.rfind('/') 2842 2843 if anno_start < 0 or anno_end < 0 or anno_start == anno_end: 2844 error("%s has missing annotations." % name) 2845 2846 annos = line[anno_start + 1:anno_end].split(', ') 2847 annos5 = [anno[0].lower() + anno[1:] for anno in annos] 2848 2849 f5.write('%License(') 2850 f5.write(', '.join(annos5)) 2851 f5.write(')\n') 2852 else: 2853 f5.write(line) 2854 2855 f5.close() 2856 f.close() 2857 2858 2859def check_license(target_config, license_confirmed): 2860 """ Handle the validation of the PyQt5 license. target_config is the 2861 target configuration. license_confirmed is set if the user has already 2862 accepted the license. 2863 """ 2864 2865 try: 2866 import license 2867 ltype = license.LicenseType 2868 lname = license.LicenseName 2869 2870 try: 2871 lfile = license.LicenseFile 2872 except AttributeError: 2873 lfile = None 2874 except ImportError: 2875 ltype = None 2876 2877 if ltype is None: 2878 ltype = 'GPL' 2879 lname = "GNU General Public License" 2880 lfile = 'pyqt-gpl.sip' 2881 2882 inform( 2883 "This is the %s version of PyQt %s (licensed under the %s) for " 2884 "Python %s on %s." % 2885 (ltype, PYQT_VERSION_STR, lname, sys.version.split()[0], 2886 sys.platform)) 2887 2888 # Confirm the license if not already done. 2889 if not license_confirmed: 2890 loptions = """ 2891Type 'L' to view the license. 2892""" 2893 2894 sys.stdout.write(loptions) 2895 sys.stdout.write("""Type 'yes' to accept the terms of the license. 2896Type 'no' to decline the terms of the license. 2897 2898""") 2899 2900 while 1: 2901 sys.stdout.write("Do you accept the terms of the license? ") 2902 sys.stdout.flush() 2903 2904 try: 2905 resp = sys.stdin.readline() 2906 except KeyboardInterrupt: 2907 raise SystemExit 2908 except: 2909 resp = "" 2910 2911 resp = resp.strip().lower() 2912 2913 if resp == "yes": 2914 break 2915 2916 if resp == "no": 2917 sys.exit(0) 2918 2919 if resp == 'l': 2920 os.system('more LICENSE') 2921 2922 # Check that the license file exists and fix its syntax. 2923 sip_dir = 'sip' 2924 mk_dir(sip_dir) 2925 2926 src_lfile = os.path.join(target_config.license_dir, lfile) 2927 2928 if os.access(src_lfile, os.F_OK): 2929 inform("Found the license file %s." % lfile) 2930 fix_license(src_lfile, os.path.join(sip_dir, lfile + '5')) 2931 else: 2932 error( 2933 "Please copy the license file %s to %s." % (lfile, 2934 target_config.license_dir)) 2935 2936 2937def check_qt(target_config): 2938 """ Check the Qt installation. target_config is the target configuration. 2939 """ 2940 2941 # Starting with v4.7, Qt (when built with MinGW) assumes that stack frames 2942 # are 16 byte aligned because it uses SSE. However the Python Windows 2943 # installers are built with 4 byte aligned stack frames. We therefore need 2944 # to tweak the g++ flags to deal with it. 2945 if target_config.qmake_spec == 'win32-g++': 2946 target_config.qmake_variables.append('QMAKE_CFLAGS += -mstackrealign') 2947 target_config.qmake_variables.append('QMAKE_CXXFLAGS += -mstackrealign') 2948 2949 2950def check_python(target_config): 2951 """ Check the Python installation. target_config is the target 2952 configuration. 2953 """ 2954 2955 # Check the Python version number. This allows us to assume relative 2956 # imports and ElemenTree are available. 2957 if target_config.py_version < 0x020600: 2958 error("PyQt5 requires Python v2.6 or later.") 2959 2960 2961def check_sip(target_config, verbose): 2962 """ Check that the version of sip is good enough and return its version. 2963 target_config is the target configuration. 2964 """ 2965 2966 if target_config.sip is None: 2967 error( 2968 "Make sure you have a working sip on your PATH or use the " 2969 "--sip argument to explicitly specify a working sip.") 2970 2971 pipe = os.popen(' '.join([quote(target_config.sip), '-V'])) 2972 2973 for l in pipe: 2974 version_str = l.strip() 2975 break 2976 else: 2977 error("'%s -V' did not generate any output." % target_config.sip) 2978 2979 pipe.close() 2980 2981 if '.dev' in version_str or 'snapshot' in version_str: 2982 # We only need to distinguish between sip v4 and sip v5. 2983 if target_config.using_sip5(): 2984 version = 0x050000 2985 else: 2986 version = 0x040000 2987 else: 2988 version = version_from_string(version_str) 2989 if version is None: 2990 error( 2991 "'%s -V' generated unexpected output: '%s'." % ( 2992 target_config.sip, version_str)) 2993 2994 min_version = version_from_string(SIP_MIN_VERSION) 2995 if version < min_version: 2996 error( 2997 "This version of PyQt5 requires sip %s or later." % 2998 SIP_MIN_VERSION) 2999 3000 if version >= 0x050000: 3001 # Install the sip.h file for the private sip module. 3002 if target_config.sip_inc_dir is None: 3003 target_config.sip_inc_dir = os.path.join( 3004 os.path.abspath(os.getcwd()), 'include') 3005 3006 inform("Installing sip.h in %s..." % target_config.sip_inc_dir) 3007 3008 os.makedirs(target_config.sip_inc_dir, exist_ok=True) 3009 3010 argv = ['sip-module', '--sip-h'] 3011 3012 if target_config.abi_version: 3013 argv.append('--abi-version') 3014 argv.append(target_config.abi_version) 3015 3016 argv.append('--target-dir') 3017 argv.append(quote(target_config.sip_inc_dir)), 3018 argv.append('PyQt5.sip') 3019 3020 run_command(' '.join(argv), verbose) 3021 3022 if not os.access(os.path.join(target_config.sip_inc_dir, 'sip.h'), os.F_OK): 3023 error( 3024 "sip-module failed to install sip.h in %s." % 3025 target_config.sip_inc_dir) 3026 else: 3027 if target_config.sip_inc_dir is None: 3028 target_config.sip_inc_dir = target_config.py_venv_inc_dir 3029 3030 return version_str 3031 3032 3033def version_from_string(version_str): 3034 """ Convert a version string of the form m.n or m.n.o to an encoded version 3035 number (or None if it was an invalid format). version_str is the version 3036 string. 3037 """ 3038 3039 parts = version_str.split('.') 3040 if not isinstance(parts, list): 3041 return None 3042 3043 if len(parts) == 2: 3044 parts.append('0') 3045 3046 if len(parts) != 3: 3047 return None 3048 3049 version = 0 3050 3051 for part in parts: 3052 try: 3053 v = int(part) 3054 except ValueError: 3055 return None 3056 3057 version = (version << 8) + v 3058 3059 return version 3060 3061 3062def open_for_writing(fname): 3063 """ Return a file opened for writing while handling the most common problem 3064 of not having write permission on the directory. fname is the name of the 3065 file to open. 3066 """ 3067 try: 3068 return open(fname, 'w') 3069 except IOError: 3070 error( 3071 "There was an error creating %s. Make sure you have write " 3072 "permission on the parent directory." % fname) 3073 3074 3075def matching_files(pattern): 3076 """ Return a reproducable list of files that match a pattern. """ 3077 3078 return sorted(glob.glob(pattern)) 3079 3080 3081def main(argv): 3082 """ Create the configuration module module. argv is the list of command 3083 line arguments. 3084 """ 3085 3086 # Create the default target configuration. 3087 target_config = TargetConfiguration() 3088 3089 # Parse the command line. 3090 parser = create_optparser(target_config) 3091 opts, target_config.qmake_variables = parser.parse_args() 3092 3093 target_config.apply_pre_options(opts) 3094 3095 # Query qmake for the basic configuration information. 3096 target_config.get_qt_configuration() 3097 3098 # Update the target configuration. 3099 if opts.config_file is not None: 3100 target_config.from_configuration_file(opts.config_file) 3101 else: 3102 target_config.from_introspection(opts.verbose, opts.debug) 3103 3104 target_config.post_configuration() 3105 3106 target_config.apply_post_options(opts) 3107 3108 # Check the licenses are compatible. 3109 check_license(target_config, opts.license_confirmed) 3110 3111 # Check Python is what we need. 3112 check_python(target_config) 3113 3114 # Check SIP is what we need. 3115 sip_version = check_sip(target_config, opts.verbose) 3116 3117 # Check Qt is what we need. 3118 check_qt(target_config) 3119 3120 # Check for the VendorID package. 3121 check_vendorid(target_config) 3122 3123 # Check which modules to build if we haven't been told. 3124 if len(target_config.pyqt_modules) == 0: 3125 check_modules(target_config, opts.disabled_modules, opts.verbose) 3126 else: 3127 # Check that the user supplied module names are valid. 3128 for mname in target_config.pyqt_modules: 3129 if mname not in MODULE_METADATA: 3130 error("'%s' is not a valid module name." % mname) 3131 3132 check_dbus(target_config, opts.verbose) 3133 3134 # Tell the user what's been found. 3135 inform_user(target_config, sip_version) 3136 3137 # Generate the makefiles. 3138 generate_makefiles(target_config, opts.verbose, 3139 opts.split if opts.concat else 0, opts.tracing, 3140 opts.fatal_warnings, opts.distinfo) 3141 3142 3143############################################################################### 3144# The script starts here. 3145############################################################################### 3146 3147if __name__ == '__main__': 3148 try: 3149 main(sys.argv) 3150 except SystemExit: 3151 raise 3152 except: 3153 sys.stderr.write( 3154"""An internal error occured. Please report all the output from the program, 3155including the following traceback, to support@riverbankcomputing.com. 3156""") 3157 raise 3158