1# Copyright (c) 2017, Riverbank Computing Limited 2# All rights reserved. 3# 4# Redistribution and use in source and binary forms, with or without 5# modification, are permitted provided that the following conditions are met: 6# 7# 1. Redistributions of source code must retain the above copyright notice, 8# this list of conditions and the following disclaimer. 9# 10# 2. Redistributions in binary form must reproduce the above copyright notice, 11# this list of conditions and the following disclaimer in the documentation 12# and/or other materials provided with the distribution. 13# 14# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 18# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24# POSSIBILITY OF SUCH DAMAGE. 25 26# This is v2.3 of this boilerplate. 27 28 29from distutils import sysconfig 30import glob 31import os 32import optparse 33import sys 34 35 36############################################################################### 37# You shouldn't need to modify anything above this line. 38############################################################################### 39 40 41class QtAVConfiguration(object): 42 """ This class encapsulates all the module specific information needed by 43 the rest of this script to implement a configure.py script for modules that 44 build on top of PyQt. Functions implemented by the rest of this script 45 that begin with an underscore are considered internal and shouldn't be 46 called from here. 47 """ 48 49 # The name of the module as it would be used in an import statement. 50 name = 'QtAV' 51 52 # Set if support for C++ exceptions can be disabled. 53 no_exceptions = True 54 55 # The name (without the .pyi extension) of the name of the PEP 484 stub 56 # file to be generated. If it is None or an empty string then a stub file 57 # is not generated. 58 pep484_stub_file = 'QtAV' 59 60 @staticmethod 61 def get_sip_file(target_configuration): 62 """ Return the name of the module's .sip file. target_configuration is 63 the target configuration. 64 """ 65 66 return 'sip/QtAV/QtAVmod.sip' 67 68 @staticmethod 69 def get_sip_installs(target_configuration): 70 """ Return a tuple of the installation directory of the module's .sip 71 files and a sequence of the names of each of the .sip files relative to 72 the directory containing this configuration script. None is returned 73 if the module's .sip files are not to be installed. 74 target_configuration is the target configuration. 75 """ 76 77 if target_configuration.qtav_sip_dir == '': 78 return None 79 80 path = os.path.join(target_configuration.qtav_sip_dir, 'QtAV') 81 files = glob.glob('sip/QtAV/*.sip') 82 83 return path, files 84 85 @staticmethod 86 def get_qmake_configuration(target_configuration): 87 """ Return a dict of qmake configuration values for CONFIG, DEFINES, 88 INCLUDEPATH, LIBS and QT. If value names (i.e. dict keys) have either 89 'Qt4' or 'Qt5' prefixes then they are specific to the corresponding 90 version of Qt. target_configuration is the target configuration. 91 """ 92 libs = r"-L%s/lib " % (target_configuration.qtav_base_dir) 93 if sys.platform == "win32": 94 libs += "-lQtAV1 -lQtAVWidgets1" 95 else: 96 libs += "-lQtAV -lQtAVWidgets" 97 98 return {'QT': 'widgets opengl', 99 'INCLUDEPATH': os.path.join(target_configuration.qtav_base_dir, "include"), 100 'LIBS': libs} 101 102 @staticmethod 103 def get_mac_wrapped_library_file(target_configuration): 104 """ Return the full pathname of the file that implements the library 105 being wrapped by the module as it would be called on OS/X so that the 106 module will reference it explicitly without DYLD_LIBRARY_PATH being 107 set. If it is None or an empty string then the default is used. 108 target_configuration is the target configuration. 109 """ 110 111 return None 112 113 114class QtAVWidgetsConfiguration(object): 115 """ This class encapsulates all the module specific information needed by 116 the rest of this script to implement a configure.py script for modules that 117 build on top of PyQt. Functions implemented by the rest of this script 118 that begin with an underscore are considered internal and shouldn't be 119 called from here. 120 """ 121 122 # The name of the module as it would be used in an import statement. 123 name = 'QtAVWidgets' 124 125 # Set if support for C++ exceptions can be disabled. 126 no_exceptions = True 127 128 # The name (without the .pyi extension) of the name of the PEP 484 stub 129 # file to be generated. If it is None or an empty string then a stub file 130 # is not generated. 131 pep484_stub_file = 'QtAVWidgets' 132 133 @staticmethod 134 def get_sip_file(target_configuration): 135 """ Return the name of the module's .sip file. target_configuration is 136 the target configuration. 137 """ 138 139 return 'sip/QtAVWidgets/QtAVWidgetsmod.sip' 140 141 @staticmethod 142 def get_sip_installs(target_configuration): 143 """ Return a tuple of the installation directory of the module's .sip 144 files and a sequence of the names of each of the .sip files relative to 145 the directory containing this configuration script. None is returned 146 if the module's .sip files are not to be installed. 147 target_configuration is the target configuration. 148 """ 149 150 if target_configuration.qtav_sip_dir == '': 151 return None 152 153 path = os.path.join(target_configuration.qtav_sip_dir, 'QtAVWidgets') 154 files = glob.glob('sip/QtAVWidgets/*.sip') 155 156 return path, files 157 158 @staticmethod 159 def get_qmake_configuration(target_configuration): 160 """ Return a dict of qmake configuration values for CONFIG, DEFINES, 161 INCLUDEPATH, LIBS and QT. If value names (i.e. dict keys) have either 162 'Qt4' or 'Qt5' prefixes then they are specific to the corresponding 163 version of Qt. target_configuration is the target configuration. 164 """ 165 libs = r"-L%s/lib " % (target_configuration.qtav_base_dir) 166 if sys.platform == "win32": 167 libs += "-lQtAV1 -lQtAVWidgets1" 168 else: 169 libs += "-lQtAV -lQtAVWidgets" 170 171 return {'QT': 'widgets opengl', 172 'INCLUDEPATH': os.path.join(target_configuration.qtav_base_dir, "include"), 173 'LIBS': libs} 174 175 @staticmethod 176 def get_mac_wrapped_library_file(target_configuration): 177 """ Return the full pathname of the file that implements the library 178 being wrapped by the module as it would be called on OS/X so that the 179 module will reference it explicitly without DYLD_LIBRARY_PATH being 180 set. If it is None or an empty string then the default is used. 181 target_configuration is the target configuration. 182 """ 183 184 return None 185 186 187class PackageConfiguration(object): 188 """ This class encapsulates all the package specific information needed by 189 the rest of this script to implement a configure.py script for modules that 190 build on top of PyQt. Functions implemented by the rest of this script 191 that begin with an underscore are considered internal and shouldn't be 192 called from here. 193 """ 194 195 # The descriptive name of the module. This is used in help text and error 196 # messages. 197 descriptive_name = "PyQtAV" 198 199 # The version of the module as a string. Set it to None if you don't 200 # provide version information. 201 version = '1.12.0' 202 203 # The sequence of module configurations that make up the package. 204 modules = (QtAVConfiguration(), QtAVWidgetsConfiguration()) 205 206 # Set if a configuration script is provided that handles versions of PyQt4 207 # prior to v4.10 (i.e. versions where the pyqtconfig.py module is 208 # available). If provided the script must be called configure-old.py and 209 # be in the same directory as this script. 210 legacy_configuration_script = False 211 212 # The minimum version of SIP that is required. This should be a 213 # dot-separated string of two or three integers (e.g. '1.0', '4.10.3'). If 214 # it is None or an empty string then the version is not checked. 215 minimum_sip_version = '4.19.1' 216 217 # Set if the module supports redefining 'protected' as 'public'. 218 protected_is_public_is_supported = True 219 220 # Set if the module supports PyQt4. 221 pyqt4_is_supported = False 222 223 # Set if the module supports PyQt5. 224 pyqt5_is_supported = True 225 226 # Set if the PyQt5 support is the default. It is ignored unless both 227 # 'pyqt4_is_supported' and 'pyqt5_is_supported' are set. 228 pyqt5_is_default = True 229 230 # The name (without the .api extension) of the name of the QScintilla API 231 # file to be generated. If it is None or an empty string then an API file 232 # is not generated. 233 qscintilla_api_file = 'PyQtAV' 234 235 # The email address that will be included when an error in the script is 236 # detected. Leave it blank if you don't want to include an address. 237 support_email_address = 'support@riverbankcomputing.com' 238 239 # Set if the user can provide a configuration file. It is normally only 240 # used if cross-compilation is supported. 241 user_configuration_file_is_supported = True 242 243 # Set if the user is allowed to pass PyQt sip flags on the command line. 244 # It is normally only used if cross-compilation is supported. It is 245 # ignored unless at least one of 'pyqt4_is_supported' or 246 # 'pyqt5_is_supported' is set. 247 user_pyqt_sip_flags_is_supported = True 248 249 @staticmethod 250 def init_target_configuration(target_configuration): 251 """ Perform any module specific initialisation of the target 252 target configuration. Typically this is the initialisation of module 253 specific attributes. To avoid name clashes attributes should be given 254 a module specific prefix. target_configuration is the target 255 configuration. 256 """ 257 258 target_configuration.qtav_version = None 259 target_configuration.qtav_base_dir = None 260 target_configuration.qtav_sip_dir = None 261 262 @staticmethod 263 def init_optparser(optparser, target_configuration): 264 """ Perform any module specific initialisation of the command line 265 option parser. To avoid name clashes destination attributes should be 266 given a module specific prefix. optparser is the option parser. 267 target_configuration is the target configuration. 268 """ 269 270 optparser.add_option('--qtav-version', dest='qtav_version', 271 type='string', metavar="VERSION", 272 help="the QtAV version number (eg. 1.12.0)") 273 274 optparser.add_option('--qtav-base-dir', '-v', 275 dest='qtav_base_dir', type='string', default=None, 276 action='callback', callback=optparser_store_abspath_dir, 277 metavar="DIR", 278 help="the base directory that contains QtAV installation ") 279 280 optparser.add_option("--no-sip-files", action="store_true", 281 default=False, dest="qtav_no_sip_files", 282 help="disable the installation of the .sip files " 283 "[default: enabled]") 284 285 @staticmethod 286 def apply_options(target_configuration, options): 287 """ Apply the module specific command line options to the target 288 configuration. target_configuration is the target configuration. 289 options are the parsed options. 290 """ 291 292 # Historically Digia have been incapable of remembering to update the 293 # version number in a new release of add-on libraries so we allow the 294 # user to specify it while defaulting to the Qt version. 295 qtav_version_str = options.qtav_version 296 if qtav_version_str is None: 297 qtav_version_str = target_configuration.qt_version_str 298 299 target_configuration.qtav_version = version_from_string( 300 qtav_version_str) 301 302 if target_configuration.qtav_version is None: 303 error("%s is not a valid QtAV version." % qtav_version_str) 304 305 if target_configuration.qtav_version >= 0x020000 and target_configuration.pyqt_package != 'PyQt5': 306 error("QtAV v%s requires PyQt5." % options.qtav_version) 307 308 if options.qtav_base_dir is not None: 309 target_configuration.qtav_base_dir = options.qtav_base_dir 310 else: 311 error("--qtav-base-dir must be spcified") 312 313 if options.qtav_no_sip_files: 314 target_configuration.qtav_sip_dir = '' 315 else: 316 target_configuration.qtav_sip_dir = os.path.join(target_configuration.qtav_base_dir, 'share', 'sip') 317 318 @staticmethod 319 def check_package(target_configuration): 320 """ Perform any package specific checks now that the target 321 configuration is complete. target_configuration is the target 322 configuration. 323 """ 324 325 # Nothing to do. 326 327 @staticmethod 328 def inform_user(target_configuration): 329 """ Inform the user about module specific configuration information. 330 target_configuration is the target configuration. 331 """ 332 333 major = (target_configuration.qtav_version >> 16) & 0xff 334 minor = (target_configuration.qtav_version >> 8) & 0xff 335 patch = target_configuration.qtav_version & 0xff 336 version = '%d.%d.%d' % (major, minor, patch) 337 338 inform("QtAV %s is being used." % version) 339 340 if target_configuration.qtav_sip_dir != '': 341 inform( 342 "The QtAV .sip files will be installed in %s." % 343 target_configuration.qtav_sip_dir) 344 345 @staticmethod 346 def pre_code_generation(target_config): 347 """ Perform any module specific initialisation prior to generating the 348 code. target_config is the target configuration. 349 """ 350 351 # Nothing to do. 352 353 @staticmethod 354 def get_sip_flags(target_configuration): 355 """ Return the list of module-specific flags to pass to SIP. 356 target_configuration is the target configuration. 357 """ 358 359 major = (target_configuration.qtav_version >> 16) & 0xff 360 minor = (target_configuration.qtav_version >> 8) & 0xff 361 patch = target_configuration.qtav_version & 0xff 362 version_tag = 'QtAV_%d_%d_%d' % (major, minor, patch) 363 364 return ['-t', version_tag] 365 366 367############################################################################### 368# You shouldn't need to modify anything below this line. 369############################################################################### 370 371 372def error(msg): 373 """ Display an error message and terminate. msg is the text of the error 374 message. 375 """ 376 377 sys.stderr.write(_format("Error: " + msg) + "\n") 378 sys.exit(1) 379 380 381def inform(msg): 382 """ Display an information message. msg is the text of the error message. 383 """ 384 385 sys.stdout.write(_format(msg) + "\n") 386 387 388def quote(path): 389 """ Return a path with quotes added if it contains spaces. path is the 390 path. 391 """ 392 393 if ' ' in path: 394 path = '"%s"' % path 395 396 return path 397 398 399def optparser_store_abspath(option, opt_str, value, parser): 400 """ An optparser callback that saves an option as an absolute pathname. """ 401 402 setattr(parser.values, option.dest, os.path.abspath(value)) 403 404 405def optparser_store_abspath_dir(option, opt_str, value, parser): 406 """ An optparser callback that saves an option as the absolute pathname 407 of an existing directory. 408 """ 409 410 if not os.path.isdir(value): 411 raise optparse.OptionValueError("'%s' is not a directory" % value) 412 413 setattr(parser.values, option.dest, os.path.abspath(value)) 414 415 416def optparser_store_abspath_exe(option, opt_str, value, parser): 417 """ An optparser callback that saves an option as the absolute pathname 418 of an existing executable. 419 """ 420 421 if not os.access(value, os.X_OK): 422 raise optparse.OptionValueError("'%s' is not an executable" % value) 423 424 setattr(parser.values, option.dest, os.path.abspath(value)) 425 426 427def read_define(filename, define): 428 """ Read the value of a #define from a file. filename is the name of the 429 file. define is the name of the #define. None is returned if there was no 430 such #define. 431 """ 432 433 f = open(filename) 434 435 for l in f: 436 wl = l.split() 437 if len(wl) >= 3 and wl[0] == "#define" and wl[1] == define: 438 # Take account of embedded spaces. 439 value = ' '.join(wl[2:])[1:-1] 440 break 441 else: 442 value = None 443 444 f.close() 445 446 return value 447 448 449def version_from_string(version_str): 450 """ Convert a version string of the form m, m.n or m.n.o to an encoded 451 version number (or None if it was an invalid format). version_str is the 452 version string. 453 """ 454 455 parts = version_str.split('.') 456 if not isinstance(parts, list): 457 return None 458 459 if len(parts) == 1: 460 parts.append('0') 461 462 if len(parts) == 2: 463 parts.append('0') 464 465 if len(parts) != 3: 466 return None 467 468 version = 0 469 470 for part in parts: 471 try: 472 v = int(part) 473 except ValueError: 474 return None 475 476 version = (version << 8) + v 477 478 return version 479 480 481def _format(msg, left_margin=0, right_margin=78): 482 """ Format a message by inserting line breaks at appropriate places. msg 483 is the text of the message. left_margin is the position of the left 484 margin. right_margin is the position of the right margin. Returns the 485 formatted message. 486 """ 487 488 curs = left_margin 489 fmsg = " " * left_margin 490 491 for w in msg.split(): 492 l = len(w) 493 if curs != left_margin and curs + l > right_margin: 494 fmsg = fmsg + "\n" + (" " * left_margin) 495 curs = left_margin 496 497 if curs > left_margin: 498 fmsg = fmsg + " " 499 curs = curs + 1 500 501 fmsg = fmsg + w 502 curs = curs + l 503 504 return fmsg 505 506 507class _ConfigurationFileParser: 508 """ A parser for configuration files. """ 509 510 def __init__(self, config_file): 511 """ Read and parse a configuration file. """ 512 513 self._config = {} 514 self._extrapolating = [] 515 516 cfg = open(config_file) 517 line_nr = 0 518 last_name = None 519 520 section = '' 521 section_config = {} 522 self._config[section] = section_config 523 524 for l in cfg: 525 line_nr += 1 526 527 # Strip comments. 528 l = l.split('#')[0] 529 530 # See if this might be part of a multi-line. 531 multiline = (last_name is not None and len(l) != 0 and l[0] == ' ') 532 533 l = l.strip() 534 535 if l == '': 536 last_name = None 537 continue 538 539 # See if this is a new section. 540 if l[0] == '[' and l[-1] == ']': 541 section = l[1:-1].strip() 542 if section == '': 543 error( 544 "%s:%d: Empty section name." % ( 545 config_file, line_nr)) 546 547 if section in self._config: 548 error( 549 "%s:%d: Section '%s' defined more than once." % ( 550 config_file, line_nr, section)) 551 552 section_config = {} 553 self._config[section] = section_config 554 555 last_name = None 556 continue 557 558 parts = l.split('=', 1) 559 if len(parts) == 2: 560 name = parts[0].strip() 561 value = parts[1].strip() 562 elif multiline: 563 name = last_name 564 value = section_config[last_name] 565 value += ' ' + l 566 else: 567 name = value = '' 568 569 if name == '' or value == '': 570 error("%s:%d: Invalid line." % (config_file, line_nr)) 571 572 section_config[name] = value 573 last_name = name 574 575 cfg.close() 576 577 def sections(self): 578 """ Return the list of sections, excluding the default one. """ 579 580 return [s for s in self._config.keys() if s != ''] 581 582 def preset(self, name, value): 583 """ Add a preset value to the configuration. """ 584 585 self._config[''][name] = value 586 587 def get(self, section, name, default=None): 588 """ Get a configuration value while extrapolating. """ 589 590 # Get the name from the section, or the default section. 591 value = self._config[section].get(name) 592 if value is None: 593 value = self._config[''].get(name) 594 if value is None: 595 if default is None: 596 error( 597 "Configuration file references non-existent name " 598 "'%s'." % name) 599 600 return default 601 602 # Handle any extrapolations. 603 parts = value.split('%(', 1) 604 while len(parts) == 2: 605 prefix, tail = parts 606 607 parts = tail.split(')', 1) 608 if len(parts) != 2: 609 error( 610 "Configuration file contains unterminated " 611 "extrapolated name '%s'." % tail) 612 613 xtra_name, suffix = parts 614 615 if xtra_name in self._extrapolating: 616 error( 617 "Configuration file contains a recursive reference to " 618 "'%s'." % xtra_name) 619 620 self._extrapolating.append(xtra_name) 621 xtra_value = self.get(section, xtra_name) 622 self._extrapolating.pop() 623 624 value = prefix + xtra_value + suffix 625 626 parts = value.split('%(', 1) 627 628 return value 629 630 def getboolean(self, section, name, default): 631 """ Get a boolean configuration value while extrapolating. """ 632 633 value = self.get(section, name, default) 634 635 # In case the default was returned. 636 if isinstance(value, bool): 637 return value 638 639 if value in ('True', 'true', '1'): 640 return True 641 642 if value in ('False', 'false', '0'): 643 return False 644 645 error( 646 "Configuration file contains invalid boolean value for " 647 "'%s'." % name) 648 649 def getlist(self, section, name, default): 650 """ Get a list configuration value while extrapolating. """ 651 652 value = self.get(section, name, default) 653 654 # In case the default was returned. 655 if isinstance(value, list): 656 return value 657 658 return value.split() 659 660 661class _HostPythonConfiguration: 662 """ A container for the host Python configuration. """ 663 664 def __init__(self): 665 """ Initialise the configuration. """ 666 667 self.platform = sys.platform 668 self.version = sys.hexversion >> 8 669 670 self.inc_dir = sysconfig.get_python_inc() 671 self.venv_inc_dir = sysconfig.get_python_inc(prefix=sys.prefix) 672 self.module_dir = sysconfig.get_python_lib(plat_specific=1) 673 self.debug = hasattr(sys, 'gettotalrefcount') 674 675 if sys.platform == 'win32': 676 try: 677 # Python v3.3 and later. 678 base_prefix = sys.base_prefix 679 680 except AttributeError: 681 try: 682 # virtualenv for Python v2. 683 base_prefix = sys.real_prefix 684 685 except AttributeError: 686 # We can't detect the base prefix in Python v3 prior to 687 # v3.3. 688 base_prefix = sys.prefix 689 690 self.data_dir = sys.prefix 691 self.lib_dir = base_prefix + '\\libs' 692 else: 693 self.data_dir = sys.prefix + '/share' 694 self.lib_dir = sys.prefix + '/lib' 695 696 697class _TargetQtConfiguration: 698 """ A container for the target Qt configuration. """ 699 700 def __init__(self, qmake): 701 """ Initialise the configuration. qmake is the full pathname of the 702 qmake executable that will provide the configuration. 703 """ 704 705 pipe = os.popen(quote(qmake) + ' -query') 706 707 for l in pipe: 708 l = l.strip() 709 710 tokens = l.split(':', 1) 711 if isinstance(tokens, list): 712 if len(tokens) != 2: 713 error("Unexpected output from qmake: '%s'\n" % l) 714 715 name, value = tokens 716 else: 717 name = tokens 718 value = None 719 720 name = name.replace('/', '_') 721 722 setattr(self, name, value) 723 724 pipe.close() 725 726 727class _TargetConfiguration: 728 """ A container for the target configuration. """ 729 730 def __init__(self, pkg_config): 731 """ Initialise the configuration with default values. pkg_config is 732 the package configuration. 733 """ 734 735 # Values based on the host Python configuration. 736 py_config = _HostPythonConfiguration() 737 self.py_debug = py_config.debug 738 self.py_platform = py_config.platform 739 self.py_version = py_config.version 740 self.py_module_dir = py_config.module_dir 741 self.py_inc_dir = py_config.inc_dir 742 self.py_venv_inc_dir = py_config.venv_inc_dir 743 self.py_pylib_dir = py_config.lib_dir 744 self.py_sip_dir = os.path.join(py_config.data_dir, 'sip') 745 self.sip_inc_dir = py_config.venv_inc_dir 746 747 # Remaining values. 748 self.debug = False 749 self.pyqt_sip_flags = None 750 self.pyqt_version_str = '' 751 self.qmake = self._find_exe('qmake') 752 self.qmake_spec = '' 753 self.qt_version = 0 754 self.qt_version_str = '' 755 self.sip = self._find_exe('sip5', 'sip') 756 self.sip_version = None 757 self.sip_version_str = None 758 self.sysroot = '' 759 self.stubs_dir = '' 760 761 self.prot_is_public = (self.py_platform.startswith('linux') or self.py_platform == 'darwin') 762 763 if pkg_config.pyqt5_is_supported and pkg_config.pyqt4_is_supported: 764 pyqt = 'PyQt5' if pkg_config.pyqt5_is_default else 'PyQt4' 765 elif pkg_config.pyqt5_is_supported and not pkg_config.pyqt4_is_supported: 766 pyqt = 'PyQt5' 767 elif not pkg_config.pyqt5_is_supported and pkg_config.pyqt4_is_supported: 768 pyqt = 'PyQt4' 769 else: 770 pyqt = None 771 772 if pyqt is not None: 773 self.module_dir = os.path.join(py_config.module_dir, pyqt) 774 self.pyqt_sip_dir = os.path.join(self.py_sip_dir, pyqt) 775 else: 776 self.module_dir = py_config.module_dir 777 self.pyqt_sip_dir = None 778 779 self.pyqt_package = pyqt 780 781 pkg_config.init_target_configuration(self) 782 783 def update_from_configuration_file(self, config_file): 784 """ Update the configuration with values from a file. config_file 785 is the name of the configuration file. 786 """ 787 788 inform("Reading configuration from %s..." % config_file) 789 790 parser = _ConfigurationFileParser(config_file) 791 792 # Populate some presets from the command line. 793 parser.preset('py_major', str(self.py_version >> 16)) 794 parser.preset('py_minor', str((self.py_version >> 8) & 0xff)) 795 parser.preset('sysroot', self.sysroot) 796 797 if self.pyqt_package is None: 798 section = '' 799 else: 800 # At the moment we only need to distinguish between PyQt4 and 801 # PyQt5. If that changes we may need a --target-pyqt-version 802 # command line option. 803 pyqt_version = 0x050000 if self.pyqt_package == 'PyQt5' else 0x040000 804 805 # Find the section corresponding to the version of PyQt. 806 section = None 807 latest_section = -1 808 809 for name in parser.sections(): 810 parts = name.split() 811 if len(parts) != 2 or parts[0] != 'PyQt': 812 continue 813 814 section_pyqt_version = version_from_string(parts[1]) 815 if section_pyqt_version is None: 816 continue 817 818 # Major versions must match. 819 if section_pyqt_version >> 16 != pyqt_version >> 16: 820 continue 821 822 # It must be no later that the version of PyQt. 823 if section_pyqt_version > pyqt_version: 824 continue 825 826 # Save it if it is the latest so far. 827 if section_pyqt_version > latest_section: 828 section = name 829 latest_section = section_pyqt_version 830 831 if section is None: 832 error( 833 "%s does not define a section that covers PyQt " 834 "v%s." % (config_file, self.pyqt_version_str)) 835 836 self.py_platform = parser.get(section, 'py_platform', self.py_platform) 837 self.py_inc_dir = parser.get(section, 'py_inc_dir', self.py_inc_dir) 838 self.py_venv_inc_dir = self.py_inc_dir 839 self.py_pylib_dir = parser.get(section, 'py_pylib_dir', 840 self.py_pylib_dir) 841 842 self.sip_inc_dir = self.py_venv_inc_dir 843 844 self.module_dir = parser.get(section, 'module_dir', self.module_dir) 845 846 if self.pyqt_package is not None: 847 self.py_sip_dir = parser.get(section, 'py_sip_dir', 848 self.py_sip_dir) 849 850 # Construct the SIP flags. 851 flags = [] 852 853 flags.append('-t') 854 flags.append(self._get_platform_tag()) 855 856 if self.pyqt_package == 'PyQt5': 857 if self.qt_version < 0x050000: 858 error("PyQt5 requires Qt v5.0 or later.") 859 860 if self.qt_version > 0x060000: 861 self.qt_version = 0x060000 862 else: 863 if self.qt_version > 0x050000: 864 self.qt_version = 0x050000 865 866 major = (self.qt_version >> 16) & 0xff 867 minor = (self.qt_version >> 8) & 0xff 868 patch = self.qt_version & 0xff 869 870 flags.append('-t') 871 flags.append('Qt_%d_%d_%d' % (major, minor, patch)) 872 873 for feat in parser.getlist(section, 'pyqt_disabled_features', []): 874 flags.append('-x') 875 flags.append(feat) 876 877 self.pyqt_sip_flags = ' '.join(flags) 878 879 def _get_platform_tag(self): 880 """ Return the tag for the target platform. """ 881 882 # This replicates the logic in PyQt's configure scripts. 883 if self.py_platform == 'win32': 884 plattag = 'WS_WIN' 885 elif self.py_platform == 'darwin': 886 plattag = 'WS_MACX' 887 else: 888 plattag = 'WS_X11' 889 890 return plattag 891 892 def introspect_pyqt(self, pkg_config): 893 """ Introspect PyQt to determine the sip flags required. pkg_config 894 is the package configuration. 895 """ 896 897 if self.pyqt_package == 'PyQt5': 898 try: 899 from PyQt5 import QtCore 900 except ImportError: 901 error( 902 "Unable to import PyQt5.QtCore. Make sure PyQt5 is " 903 "installed.") 904 else: 905 try: 906 from PyQt4 import QtCore 907 except ImportError: 908 error( 909 "Unable to import PyQt4.QtCore. Make sure PyQt4 is " 910 "installed.") 911 912 self.pyqt_version_str = QtCore.PYQT_VERSION_STR 913 self.qt_version_str = QtCore.qVersion() 914 915 # See if we have a PyQt that embeds its configuration. 916 try: 917 pyqt_config = QtCore.PYQT_CONFIGURATION 918 except AttributeError: 919 pyqt_config = None 920 921 if pyqt_config is None: 922 if pkg_config.legacy_configuration_script: 923 # Fallback to the old configuration script. 924 config_script = sys.argv[0].replace('configure', 'configure-old') 925 args = [sys.executable, config_script] + sys.argv[1:] 926 927 try: 928 os.execv(sys.executable, args) 929 except OSError: 930 pass 931 932 error("Unable to execute '%s'" % config_script) 933 934 error("PyQt v4.10 or later is required.") 935 936 self.pyqt_sip_flags = pyqt_config['sip_flags'] 937 938 def apply_sysroot(self): 939 """ Apply sysroot where necessary. """ 940 941 if self.sysroot != '': 942 self.py_inc_dir = self._apply_sysroot(self.py_inc_dir) 943 self.py_venv_inc_dir = self._apply_sysroot(self.py_venv_inc_dir) 944 self.py_pylib_dir = self._apply_sysroot(self.py_pylib_dir) 945 self.py_sip_dir = self._apply_sysroot(self.py_sip_dir) 946 self.module_dir = self._apply_sysroot(self.module_dir) 947 self.sip_inc_dir = self._apply_sysroot(self.sip_inc_dir) 948 949 def _apply_sysroot(self, dir_name): 950 """ Replace any leading sys.prefix of a directory name with sysroot. 951 """ 952 953 if dir_name.startswith(sys.prefix): 954 dir_name = self.sysroot + dir_name[len(sys.prefix):] 955 956 return dir_name 957 958 def get_qt_configuration(self, opts): 959 """ Get the Qt configuration that can be extracted from qmake. opts 960 are the command line options. 961 """ 962 963 # Query qmake. 964 qt_config = _TargetQtConfiguration(self.qmake) 965 966 self.qt_version_str = getattr(qt_config, 'QT_VERSION', '') 967 self.qt_version = version_from_string(self.qt_version_str) 968 if self.qt_version is None: 969 error("Unable to determine the version of Qt.") 970 971 # On Windows for Qt versions prior to v5.9.0 we need to be explicit 972 # about the qmake spec. 973 if self.qt_version < 0x050900 and self.py_platform == 'win32': 974 if self.py_version >= 0x030500: 975 self.qmake_spec = 'win32-msvc2015' 976 elif self.py_version >= 0x030300: 977 self.qmake_spec = 'win32-msvc2010' 978 elif self.py_version >= 0x020600: 979 self.qmake_spec = 'win32-msvc2008' 980 elif self.py_version >= 0x020400: 981 self.qmake_spec = 'win32-msvc.net' 982 else: 983 self.qmake_spec = 'win32-msvc' 984 else: 985 # Otherwise use the default. 986 self.qmake_spec = '' 987 988 # The binary MacOS/X Qt installer used to default to XCode. If so then 989 # use macx-clang (Qt v5) or macx-g++ (Qt v4). 990 if sys.platform == 'darwin': 991 try: 992 # Qt v5. 993 if qt_config.QMAKE_SPEC == 'macx-xcode': 994 # This will exist (and we can't check anyway). 995 self.qmake_spec = 'macx-clang' 996 else: 997 # No need to explicitly name the default. 998 self.qmake_spec = '' 999 except AttributeError: 1000 # Qt v4. 1001 self.qmake_spec = 'macx-g++' 1002 1003 self.api_dir = os.path.join(qt_config.QT_INSTALL_DATA, 'qsci') 1004 self.qt_inc_dir = qt_config.QT_INSTALL_HEADERS 1005 self.qt_lib_dir = qt_config.QT_INSTALL_LIBS 1006 1007 if self.sysroot == '': 1008 self.sysroot = getattr(qt_config, 'QT_SYSROOT', '') 1009 1010 def apply_pre_options(self, opts): 1011 """ Apply options from the command line that influence subsequent 1012 configuration. opts are the command line options. 1013 """ 1014 1015 # On Windows the interpreter must be a debug build if a debug version 1016 # is to be built and vice versa. 1017 if sys.platform == 'win32': 1018 if opts.debug: 1019 if not self.py_debug: 1020 error( 1021 "A debug version of Python must be used when " 1022 "--debug is specified.") 1023 elif self.py_debug: 1024 error( 1025 "--debug must be specified when a debug version of " 1026 "Python is used.") 1027 1028 self.debug = opts.debug 1029 1030 # Get the system root. 1031 if opts.sysroot is not None: 1032 self.sysroot = opts.sysroot 1033 1034 # Determine how to run qmake. 1035 if opts.qmake is not None: 1036 self.qmake = opts.qmake 1037 1038 # On Windows add the directory that probably contains the Qt DLLs 1039 # to PATH. 1040 if sys.platform == 'win32': 1041 path = os.environ['PATH'] 1042 path = os.path.dirname(self.qmake) + ';' + path 1043 os.environ['PATH'] = path 1044 1045 if self.qmake is None: 1046 error( 1047 "Use the --qmake argument to explicitly specify a working " 1048 "Qt qmake.") 1049 1050 if opts.qmakespec is not None: 1051 self.qmake_spec = opts.qmakespec 1052 1053 if self.pyqt_package is not None: 1054 try: 1055 self.pyqt_package = opts.pyqt_package 1056 except AttributeError: 1057 # Multiple PyQt versions are not supported. 1058 pass 1059 1060 self.module_dir = os.path.join(self.py_module_dir, 1061 self.pyqt_package) 1062 1063 def apply_post_options(self, opts, pkg_config): 1064 """ Apply options from the command line that override the previous 1065 configuration. opts are the command line options. pkg_config is the 1066 package configuration. 1067 """ 1068 1069 if self.pyqt_package is not None: 1070 if pkg_config.user_pyqt_sip_flags_is_supported: 1071 if opts.pyqt_sip_flags is not None: 1072 self.pyqt_sip_flags = opts.pyqt_sip_flags 1073 1074 if opts.pyqt_sip_dir is not None: 1075 self.pyqt_sip_dir = opts.pyqt_sip_dir 1076 else: 1077 self.pyqt_sip_dir = os.path.join(self.py_sip_dir, 1078 self.pyqt_package) 1079 1080 if _has_stubs(pkg_config): 1081 if opts.stubsdir is not None: 1082 self.stubs_dir = opts.stubsdir 1083 1084 if opts.no_stubs: 1085 self.stubs_dir = '' 1086 elif self.stubs_dir == '': 1087 self.stubs_dir = self.module_dir 1088 1089 if pkg_config.qscintilla_api_file: 1090 if opts.apidir is not None: 1091 self.api_dir = opts.apidir 1092 1093 if opts.no_qsci_api: 1094 self.api_dir = '' 1095 1096 if opts.destdir is not None: 1097 self.module_dir = opts.destdir 1098 1099 if pkg_config.protected_is_public_is_supported: 1100 if opts.prot_is_public is not None: 1101 self.prot_is_public = opts.prot_is_public 1102 else: 1103 self.prot_is_public = False 1104 1105 if opts.sip_inc_dir is not None: 1106 self.sip_inc_dir = opts.sip_inc_dir 1107 1108 if opts.sip is not None: 1109 self.sip = opts.sip 1110 1111 pkg_config.apply_options(self, opts) 1112 1113 @staticmethod 1114 def _find_exe(*exes): 1115 """ Find an executable, ie. the first on the path. """ 1116 1117 path_dirs = os.environ.get('PATH', '').split(os.pathsep) 1118 1119 for exe in exes: 1120 # Strip any surrounding quotes. 1121 if exe.startswith('"') and exe.endswith('"'): 1122 exe = exe[1:-1] 1123 1124 if sys.platform == 'win32': 1125 exe = exe + '.exe' 1126 1127 for d in path_dirs: 1128 exe_path = os.path.join(d, exe) 1129 1130 if os.access(exe_path, os.X_OK): 1131 return exe_path 1132 1133 return None 1134 1135 1136def _create_optparser(target_config, pkg_config): 1137 """ Create the parser for the command line. target_config is the target 1138 configuration containing default values. pkg_config is the package 1139 configuration. 1140 """ 1141 1142 pkg_name = pkg_config.descriptive_name 1143 1144 p = optparse.OptionParser(usage="python %prog [options]", 1145 version=pkg_config.version) 1146 1147 p.add_option('--spec', dest='qmakespec', default=None, action='store', 1148 metavar="SPEC", 1149 help="pass -spec SPEC to qmake") 1150 1151 if _has_stubs(pkg_config): 1152 p.add_option('--stubsdir', dest='stubsdir', type='string', 1153 default=None, action='callback', 1154 callback=optparser_store_abspath, metavar="DIR", 1155 help="the PEP 484 stubs will be installed in DIR [default: " 1156 "with the module]") 1157 p.add_option('--no-stubs', dest='no_stubs', default=False, 1158 action='store_true', 1159 help="disable the installation of the PEP 484 stubs " 1160 "[default: enabled]") 1161 1162 if pkg_config.qscintilla_api_file: 1163 p.add_option('--apidir', '-a', dest='apidir', type='string', 1164 default=None, action='callback', 1165 callback=optparser_store_abspath, metavar="DIR", 1166 help="the QScintilla API file will be installed in DIR " 1167 "[default: QT_INSTALL_DATA/qsci]") 1168 p.add_option('--no-qsci-api', dest='no_qsci_api', default=False, 1169 action='store_true', 1170 help="disable the installation of the QScintilla API file " 1171 "[default: enabled]") 1172 1173 if pkg_config.user_configuration_file_is_supported: 1174 p.add_option('--configuration', dest='config_file', type='string', 1175 default=None, action='callback', 1176 callback=optparser_store_abspath, metavar="FILE", 1177 help="FILE defines the target configuration") 1178 1179 p.add_option('--destdir', '-d', dest='destdir', type='string', 1180 default=None, action='callback', callback=optparser_store_abspath, 1181 metavar="DIR", 1182 help="install %s in DIR [default: %s]" % 1183 (pkg_name, target_config.module_dir)) 1184 1185 if pkg_config.protected_is_public_is_supported: 1186 p.add_option('--protected-is-public', dest='prot_is_public', 1187 default=None, action='store_true', 1188 help="enable building with 'protected' redefined as 'public' " 1189 "[default: %s]" % target_config.prot_is_public) 1190 p.add_option('--protected-not-public', dest='prot_is_public', 1191 action='store_false', 1192 help="disable building with 'protected' redefined as 'public'") 1193 1194 if target_config.pyqt_package is not None: 1195 pyqt = target_config.pyqt_package 1196 1197 if pkg_config.pyqt5_is_supported and pkg_config.pyqt4_is_supported: 1198 p.add_option('--pyqt', dest='pyqt_package', type='choice', 1199 choices=['PyQt4', 'PyQt5'], default=pyqt, 1200 action='store', metavar="PyQtn", 1201 help="configure for PyQt4 or PyQt5 [default: %s]" % pyqt) 1202 1203 if pkg_config.user_pyqt_sip_flags_is_supported: 1204 p.add_option('--pyqt-sip-flags', dest='pyqt_sip_flags', 1205 default=None, action='store', metavar="FLAGS", 1206 help="the sip flags used to build PyQt [default: query PyQt]") 1207 1208 p.add_option('--qmake', '-q', dest='qmake', type='string', default=None, 1209 action='callback', callback=optparser_store_abspath_exe, 1210 metavar="FILE", 1211 help="the pathname of qmake is FILE [default: %s]" % ( 1212 target_config.qmake or "search PATH")) 1213 1214 p.add_option('--sip', dest='sip', type='string', default=None, 1215 action='callback', callback=optparser_store_abspath_exe, 1216 metavar="FILE", 1217 help="the pathname of sip is FILE [default: " 1218 "%s]" % (target_config.sip or "None")) 1219 p.add_option('--sip-incdir', dest='sip_inc_dir', type='string', 1220 default=None, action='callback', 1221 callback=optparser_store_abspath_dir, metavar="DIR", 1222 help="the directory containing the sip.h header file file is DIR " 1223 "[default: %s]" % target_config.sip_inc_dir) 1224 1225 if target_config.pyqt_package is not None: 1226 p.add_option('--pyqt-sipdir', dest='pyqt_sip_dir', type='string', 1227 default=None, action='callback', 1228 callback=optparser_store_abspath_dir, metavar="DIR", 1229 help="the directory containing the PyQt .sip files is DIR " 1230 "[default: %s]" % target_config.pyqt_sip_dir) 1231 1232 p.add_option('--concatenate', '-c', dest='concat', default=False, 1233 action='store_true', 1234 help="concatenate the C++ source files") 1235 p.add_option('--concatenate-split', '-j', dest='split', type='int', 1236 default=1, metavar="N", 1237 help="split the concatenated C++ source files into N pieces " 1238 "[default: 1]") 1239 p.add_option('--static', '-k', dest='static', default=False, 1240 action='store_true', 1241 help="build a static %s" % pkg_name) 1242 p.add_option("--sysroot", dest='sysroot', type='string', action='callback', 1243 callback=optparser_store_abspath_dir, metavar="DIR", 1244 help="DIR is the target system root directory") 1245 p.add_option('--no-docstrings', dest='no_docstrings', default=False, 1246 action='store_true', 1247 help="disable the generation of docstrings") 1248 p.add_option('--trace', '-r', dest='tracing', default=False, 1249 action='store_true', 1250 help="build %s with tracing enabled" % pkg_name) 1251 p.add_option('--debug', '-u', default=False, action='store_true', 1252 help="build %s with debugging symbols" % pkg_name) 1253 p.add_option('--verbose', '-w', dest='verbose', default=False, 1254 action='store_true', 1255 help="enable verbose output during configuration") 1256 1257 pkg_config.init_optparser(p, target_config) 1258 1259 return p 1260 1261 1262def _has_stubs(pkg_config): 1263 """ See if a stub file for any of the modules will be generated. 1264 pkg_config is the package configuration. 1265 """ 1266 1267 for module_config in pkg_config.modules: 1268 if module_config.pep484_stub_file: 1269 return True 1270 1271 return False 1272 1273 1274def _inform_user(target_config, pkg_config): 1275 """ Tell the user the values that are going to be used. target_config is 1276 the target configuration. pkg_config is the package configuration. 1277 """ 1278 1279 pkg_name = pkg_config.descriptive_name 1280 1281 inform("Configuring %s %s..." % (pkg_name, pkg_config.version)) 1282 1283 pkg_config.inform_user(target_config) 1284 1285 inform("%s will be installed in %s." % 1286 (pkg_name, target_config.module_dir)) 1287 1288 if target_config.debug: 1289 inform("A debug version of %s will be built." % pkg_name) 1290 1291 if target_config.py_debug: 1292 inform("A debug build of Python is being used.") 1293 1294 if target_config.pyqt_version_str != '': 1295 inform("PyQt %s is being used." % target_config.pyqt_version_str) 1296 else: 1297 inform("%s is being used." % target_config.pyqt_package) 1298 1299 if target_config.qt_version_str != '': 1300 inform("Qt %s is being used." % target_config.qt_version_str) 1301 1302 if target_config.sysroot != '': 1303 inform("The system root directory is %s." % target_config.sysroot) 1304 1305 inform("sip %s is being used." % target_config.sip_version_str) 1306 inform("The sip executable is %s." % target_config.sip) 1307 1308 if target_config.prot_is_public: 1309 inform("%s is being built with 'protected' redefined as 'public'." % 1310 pkg_name) 1311 1312 if target_config.stubs_dir != '': 1313 inform("The PEP 484 stubs will be installed in %s." % 1314 target_config.stubs_dir) 1315 1316 if pkg_config.qscintilla_api_file and target_config.api_dir != '': 1317 inform("The QScintilla API file will be installed in %s." % 1318 os.path.join(target_config.api_dir, 'api', 'python')) 1319 1320 1321def _generate_code(target_config, opts, pkg_config, module_config): 1322 """ Generate the code for the module. target_config is the target 1323 configuration. opts are the command line options. pkg_config is the 1324 package configuration. module_config is the module configuration. 1325 """ 1326 1327 inform( 1328 "Generating the C++ source for the %s module..." % 1329 module_config.name) 1330 1331 # Generate the code in a module-specific sub-directory. 1332 try: 1333 os.mkdir(module_config.name) 1334 except: 1335 pass 1336 1337 # Build the SIP command line. 1338 argv = [quote(target_config.sip)] 1339 1340 # Tell SIP if this is a debug build of Python (SIP v4.19.1 and later). 1341 if target_config.sip_version >= 0x041301 and target_config.py_debug: 1342 argv.append('-D') 1343 1344 # Add the module-specific flags. 1345 argv.extend(pkg_config.get_sip_flags(target_config)) 1346 1347 if target_config.pyqt_package is not None: 1348 # Get the flags used for the main PyQt module. 1349 argv.extend(target_config.pyqt_sip_flags.split()) 1350 1351 # Add the backstop version. 1352 argv.append('-B') 1353 argv.append('Qt_6_0_0' if target_config.pyqt_package == 'PyQt5' 1354 else 'Qt_5_0_0') 1355 1356 # Add PyQt's .sip files to the search path. 1357 argv.append('-I') 1358 argv.append(quote(target_config.pyqt_sip_dir)) 1359 1360 if target_config.stubs_dir != '': 1361 # Generate the stub file. 1362 argv.append('-y') 1363 argv.append(quote(module_config.pep484_stub_file + '.pyi')) 1364 1365 if pkg_config.qscintilla_api_file and target_config.api_dir != '': 1366 # Generate the API file. 1367 argv.append('-a') 1368 argv.append(quote(module_config.name + '.api')) 1369 1370 if target_config.prot_is_public: 1371 argv.append('-P'); 1372 1373 if not opts.no_docstrings: 1374 argv.append('-o'); 1375 1376 if opts.concat: 1377 argv.append('-j') 1378 argv.append(str(opts.split)) 1379 1380 if opts.tracing: 1381 argv.append('-r') 1382 1383 argv.append('-c') 1384 argv.append(os.path.abspath(module_config.name)) 1385 1386 # This assumes that, for multi-module packages, all modules's .sip files 1387 # will be rooted in a common root directory. 1388 sip_file = module_config.get_sip_file(target_config) 1389 1390 head, tail = os.path.split(sip_file) 1391 while head: 1392 head, tail = os.path.split(head) 1393 1394 if tail != sip_file: 1395 argv.append('-I') 1396 argv.append(quote(tail)) 1397 1398 argv.append(sip_file) 1399 1400 check_file = os.path.join(module_config.name, 1401 'sipAPI%s.h' % module_config.name) 1402 _remove_file(check_file) 1403 1404 _run_command(' '.join(argv), opts.verbose) 1405 1406 if not os.access(check_file, os.F_OK): 1407 error("Unable to create the C++ code.") 1408 1409 # Generate the .pro file. 1410 _generate_pro(target_config, opts, module_config) 1411 1412 1413def _get_qt_qmake_config(qmake_config, qt_version): 1414 """ Return a dict of qmake configuration values for a specific Qt version. 1415 """ 1416 1417 qt_qmake_config = {} 1418 1419 for name, value in qmake_config.items(): 1420 name_parts = name.split(':') 1421 if len(name_parts) == 2 and name_parts[0] == qt_version: 1422 qt_qmake_config[name_parts[1]] = value 1423 1424 return qt_qmake_config 1425 1426 1427def _write_qt_qmake_config(qt_qmake_config, pro): 1428 """ Write the qmake configuration values to a .pro file. """ 1429 1430 for name in ('QT', 'CONFIG', 'DEFINES', 'INCLUDEPATH', 'LIBS'): 1431 value = qt_qmake_config.get(name) 1432 if value: 1433 pro.write(' %s += %s\n' % (name, value)) 1434 1435 1436def _generate_pro(target_config, opts, module_config): 1437 """ Generate the .pro file for the module. target_config is the target 1438 configuration. opts are the command line options. module_config is the 1439 module configuration. 1440 """ 1441 1442 inform("Generating the .pro file for the %s module..." % module_config.name) 1443 1444 # Without the 'no_check_exist' magic the target.files must exist when qmake 1445 # is run otherwise the install and uninstall targets are not generated. 1446 1447 qmake_config = module_config.get_qmake_configuration(target_config) 1448 1449 pro = open(os.path.join(module_config.name, module_config.name + '.pro'), 1450 'w') 1451 1452 pro.write('TEMPLATE = lib\n') 1453 1454 qt = qmake_config.get('QT') 1455 if qt: 1456 pro.write('QT += %s\n' % qt) 1457 1458 pro.write('CONFIG += %s\n' % ('debug' if target_config.debug else 'release')) 1459 pro.write('CONFIG += %s\n' % ('staticlib' if opts.static else 'plugin plugin_bundle')) 1460 1461 config = qmake_config.get('CONFIG') 1462 if config: 1463 pro.write('CONFIG += %s\n' % config) 1464 1465 # Work around QTBUG-39300. 1466 pro.write('CONFIG -= android_install\n') 1467 1468 qt5_qmake_config = _get_qt_qmake_config(qmake_config, 'Qt5') 1469 qt4_qmake_config = _get_qt_qmake_config(qmake_config, 'Qt4') 1470 1471 if qt5_qmake_config or qt4_qmake_config: 1472 pro.write(''' 1473greaterThan(QT_MAJOR_VERSION, 4) { 1474''') 1475 1476 if qt5_qmake_config: 1477 _write_qt_qmake_config(qt5_qmake_config, pro) 1478 1479 if qt4_qmake_config: 1480 pro.write('} else {\n') 1481 _write_qt_qmake_config(qt4_qmake_config, pro) 1482 1483 pro.write('}\n') 1484 1485 mname = module_config.name 1486 1487 pro.write('TARGET = %s\n' % mname) 1488 1489 if not opts.static: 1490 pro.write(''' 1491win32 { 1492 PY_MODULE = %s.pyd 1493 PY_MODULE_SRC = $(DESTDIR_TARGET) 1494 1495 LIBS += -L%s 1496} else { 1497 PY_MODULE = %s.so 1498 1499 macx { 1500 PY_MODULE_SRC = $(TARGET).plugin/Contents/MacOS/$(TARGET) 1501 1502 QMAKE_LFLAGS += "-undefined dynamic_lookup" 1503 1504 equals(QT_MAJOR_VERSION, 5) { 1505 equals(QT_MINOR_VERSION, 5) { 1506 QMAKE_RPATHDIR += $$[QT_INSTALL_LIBS] 1507 } 1508 } 1509 } else { 1510 PY_MODULE_SRC = $(TARGET) 1511 } 1512} 1513 1514QMAKE_POST_LINK = $(COPY_FILE) $$PY_MODULE_SRC $$PY_MODULE 1515 1516target.CONFIG = no_check_exist 1517target.files = $$PY_MODULE 1518''' % (mname, quote(target_config.py_pylib_dir), mname)) 1519 1520 pro.write(''' 1521target.path = %s 1522INSTALLS += target 1523''' % quote(target_config.module_dir)) 1524 1525 sip_installs = module_config.get_sip_installs(target_config) 1526 if sip_installs is not None: 1527 path, files = sip_installs 1528 1529 pro.write(''' 1530sip.path = %s 1531sip.files =''' % quote(path)) 1532 1533 for f in files: 1534 pro.write(' \\\n ../%s' % f) 1535 1536 pro.write(''' 1537INSTALLS += sip 1538''') 1539 1540 pro.write('\n') 1541 1542 # These optimisations could apply to other platforms. 1543 if module_config.no_exceptions: 1544 if target_config.py_platform.startswith('linux') or target_config.py_platform == 'darwin': 1545 pro.write('QMAKE_CXXFLAGS += -fno-exceptions\n') 1546 1547 if target_config.py_platform.startswith('linux') and not opts.static: 1548 if target_config.py_version >= 0x030000: 1549 entry_point = 'PyInit_%s' % mname 1550 else: 1551 entry_point = 'init%s' % mname 1552 1553 exp = open(os.path.join(mname, mname + '.exp'), 'wt') 1554 exp.write('{ global: %s; local: *; };' % entry_point) 1555 exp.close() 1556 1557 pro.write('QMAKE_LFLAGS += -Wl,--version-script=%s.exp\n' % mname) 1558 1559 if target_config.prot_is_public: 1560 pro.write('DEFINES += SIP_PROTECTED_IS_PUBLIC protected=public\n') 1561 1562 defines = qmake_config.get('DEFINES') 1563 if defines: 1564 pro.write('DEFINES += %s\n' % defines) 1565 1566 includepath = qmake_config.get('INCLUDEPATH') 1567 if includepath: 1568 pro.write('INCLUDEPATH += %s\n' % includepath) 1569 1570 # Make sure the SIP include directory is searched before the Python include 1571 # directory if they are different. 1572 pro.write('INCLUDEPATH += %s\n' % quote(target_config.sip_inc_dir)) 1573 if target_config.py_inc_dir != target_config.sip_inc_dir: 1574 pro.write('INCLUDEPATH += %s\n' % quote(target_config.py_inc_dir)) 1575 1576 libs = qmake_config.get('LIBS') 1577 if libs: 1578 pro.write('LIBS += %s\n' % libs) 1579 1580 if not opts.static: 1581 dylib = module_config.get_mac_wrapped_library_file(target_config) 1582 1583 if dylib: 1584 pro.write(''' 1585macx { 1586 QMAKE_POST_LINK = $$QMAKE_POST_LINK$$escape_expand(\\\\n\\\\t)$$quote(install_name_tool -change %s %s $$PY_MODULE) 1587} 1588''' % (os.path.basename(dylib), dylib)) 1589 1590 pro.write('\n') 1591 pro.write('HEADERS = sipAPI%s.h\n' % mname) 1592 1593 pro.write('SOURCES =') 1594 for s in os.listdir(module_config.name): 1595 if s.endswith('.cpp'): 1596 pro.write(' \\\n %s' % s) 1597 pro.write('\n') 1598 1599 pro.close() 1600 1601 1602def _run_qmake(target_config, verbose, pro_name): 1603 """ Run qmake against a .pro file. target_config is the target 1604 configuration. verbose is set if the output is to be displayed. pro_name 1605 is the name of the .pro file. 1606 """ 1607 1608 inform("Generating the Makefiles...") 1609 1610 # qmake doesn't behave consistently if it is not run from the directory 1611 # containing the .pro file - so make sure it is. 1612 pro_dir, pro_file = os.path.split(pro_name) 1613 if pro_dir != '': 1614 cwd = os.getcwd() 1615 os.chdir(pro_dir) 1616 else: 1617 cwd = None 1618 1619 mf = 'Makefile' 1620 1621 _remove_file(mf) 1622 1623 args = [quote(target_config.qmake)] 1624 1625 # Make sure all Makefiles are generated now in case qmake has been 1626 # configured with environment variables. 1627 args.append('-recursive') 1628 1629 if target_config.qmake_spec != '': 1630 args.append('-spec') 1631 args.append(target_config.qmake_spec) 1632 1633 args.append(pro_file) 1634 1635 _run_command(' '.join(args), verbose) 1636 1637 if not os.access(mf, os.F_OK): 1638 error( 1639 "%s failed to create a Makefile from %s." % 1640 (target_config.qmake, pro_name)) 1641 1642 # Restore the current directory. 1643 if cwd is not None: 1644 os.chdir(cwd) 1645 1646 1647def _run_command(cmd, verbose): 1648 """ Run a command and display the output if requested. cmd is the command 1649 to run. verbose is set if the output is to be displayed. 1650 """ 1651 1652 if verbose: 1653 sys.stdout.write(cmd + "\n") 1654 1655 fout = _get_command_output(cmd) 1656 1657 # Read stdout and stderr until there is no more output. 1658 lout = fout.readline() 1659 while lout: 1660 if verbose: 1661 if sys.hexversion >= 0x03000000: 1662 sys.stdout.write(str(lout, encoding=sys.stdout.encoding)) 1663 else: 1664 sys.stdout.write(lout) 1665 1666 lout = fout.readline() 1667 1668 fout.close() 1669 1670 try: 1671 os.wait() 1672 except: 1673 pass 1674 1675 1676def _get_command_output(cmd): 1677 """ Return a pipe from which a command's output can be read. cmd is the 1678 command. 1679 """ 1680 1681 try: 1682 import subprocess 1683 except ImportError: 1684 _, sout = os.popen4(cmd) 1685 1686 return sout 1687 1688 p = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, 1689 stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 1690 1691 return p.stdout 1692 1693 1694def _remove_file(fname): 1695 """ Remove a file which may or may not exist. fname is the name of the 1696 file. 1697 """ 1698 1699 try: 1700 os.remove(fname) 1701 except OSError: 1702 pass 1703 1704 1705def _check_sip(target_config, pkg_config): 1706 """ Check that the version of sip is good enough. target_config is the 1707 target configuration. pkg_config is the package configuration. 1708 """ 1709 1710 if target_config.sip is None: 1711 error( 1712 "Make sure you have a working sip on your PATH or use the " 1713 "--sip argument to explicitly specify a working sip.") 1714 1715 pipe = os.popen(' '.join([quote(target_config.sip), '-V'])) 1716 1717 for l in pipe: 1718 version_str = l.strip() 1719 break 1720 else: 1721 error("'%s -V' did not generate any output." % target_config.sip) 1722 1723 pipe.close() 1724 1725 if '.dev' in version_str or 'snapshot' in version_str: 1726 version = 0 1727 else: 1728 version = version_from_string(version_str) 1729 if version is None: 1730 error( 1731 "'%s -V' generated unexpected output: '%s'." % ( 1732 target_config.sip, version_str)) 1733 1734 min_sip_version = pkg_config.minimum_sip_version 1735 if min_sip_version: 1736 min_version = version_from_string(min_sip_version) 1737 if version < min_version: 1738 error( 1739 "This version of %s requires sip %s or later." % 1740 (pkg_config.descriptive_name, min_sip_version)) 1741 1742 target_config.sip_version = version 1743 target_config.sip_version_str = version_str 1744 1745 1746def _main(argv, pkg_config): 1747 """ Create the configured package. argv is the list of command line 1748 arguments. pkg_config is the package configuration. 1749 """ 1750 1751 # Create the default target configuration. 1752 target_config = _TargetConfiguration(pkg_config) 1753 1754 # Parse the command line. 1755 p = _create_optparser(target_config, pkg_config) 1756 opts, args = p.parse_args() 1757 1758 if args: 1759 p.print_help() 1760 sys.exit(2) 1761 1762 target_config.apply_pre_options(opts) 1763 1764 # Query qmake for the basic configuration information. 1765 target_config.get_qt_configuration(opts) 1766 1767 # Update the target configuration. 1768 if pkg_config.user_configuration_file_is_supported: 1769 config_file = opts.config_file 1770 else: 1771 config_file = None 1772 1773 if config_file is not None: 1774 target_config.update_from_configuration_file(config_file) 1775 else: 1776 target_config.apply_sysroot() 1777 1778 target_config.apply_post_options(opts, pkg_config) 1779 1780 if target_config.pyqt_package is not None: 1781 if target_config.pyqt_sip_flags is None: 1782 target_config.introspect_pyqt(pkg_config) 1783 1784 # Check SIP is new enough. 1785 _check_sip(target_config, pkg_config) 1786 1787 # Perform any package specific checks now that all other information has 1788 # been captured. 1789 pkg_config.check_package(target_config) 1790 1791 # Tell the user what's been found. 1792 _inform_user(target_config, pkg_config) 1793 1794 # Allow for module specific hacks. 1795 pkg_config.pre_code_generation(target_config) 1796 1797 # Generate the code. 1798 for module_config in pkg_config.modules: 1799 _generate_code(target_config, opts, pkg_config, module_config) 1800 1801 # Concatenate any .api files. 1802 if pkg_config.qscintilla_api_file and target_config.api_dir != '': 1803 inform("Generating the QScintilla API file...") 1804 f = open(pkg_config.qscintilla_api_file + '.api', 'w') 1805 1806 for module_config in pkg_config.modules: 1807 api = open(module_config.name + '.api') 1808 1809 for l in api: 1810 if target_config.pyqt_package is not None: 1811 l = target_config.pyqt_package + '.' + l 1812 1813 f.write(l) 1814 1815 api.close() 1816 os.remove(module_config.name + '.api') 1817 1818 f.close() 1819 1820 # Generate the top-level .pro file. 1821 inform("Generating the top-level .pro file...") 1822 1823 pro_name = pkg_config.descriptive_name + '.pro' 1824 pro = open(pro_name, 'w') 1825 1826 pro.write('''TEMPLATE = subdirs 1827CONFIG += ordered nostrip 1828SUBDIRS = %s 1829''' % ' '.join([module.name for module in pkg_config.modules])) 1830 1831 if target_config.stubs_dir != '': 1832 stubs = [module.pep484_stub_file + '.pyi' for module in pkg_config.modules if module.pep484_stub_file] 1833 1834 if stubs: 1835 pro.write(''' 1836pep484_stubs.path = %s 1837pep484_stubs.files = %s 1838INSTALLS += pep484_stubs 1839''' % (target_config.stubs_dir, ' '.join(stubs))) 1840 1841 if pkg_config.qscintilla_api_file and target_config.api_dir != '': 1842 pro.write(''' 1843api.path = %s/api/python 1844api.files = %s.api 1845INSTALLS += api 1846''' % (target_config.api_dir, pkg_config.qscintilla_api_file)) 1847 1848 pro.close() 1849 1850 # Generate the Makefile. 1851 _run_qmake(target_config, opts.verbose, pro_name) 1852 1853 1854############################################################################### 1855# The script starts here. 1856############################################################################### 1857 1858if __name__ == '__main__': 1859 # Assume the product is a package containing multiple modules. If it isn't 1860 # then create a dummy package containing the single module. 1861 try: 1862 pkg_config_type = PackageConfiguration 1863 except NameError: 1864 pkg_config_type = type('PackageConfiguration', (object, ), {}) 1865 1866 if not hasattr(pkg_config_type, 'modules'): 1867 mod_config_type = ModuleConfiguration 1868 1869 # Extract the package-specific attributes and methods. 1870 pkg_config_type.descriptive_name = mod_config_type.descriptive_name 1871 pkg_config_type.legacy_configuration_script = mod_config_type.legacy_configuration_script 1872 pkg_config_type.minimum_sip_version = mod_config_type.minimum_sip_version 1873 pkg_config_type.protected_is_public_is_supported = mod_config_type.protected_is_public_is_supported 1874 pkg_config_type.pyqt4_is_supported = mod_config_type.pyqt4_is_supported 1875 pkg_config_type.pyqt5_is_supported = mod_config_type.pyqt5_is_supported 1876 pkg_config_type.pyqt5_is_default = mod_config_type.pyqt5_is_default 1877 pkg_config_type.qscintilla_api_file = mod_config_type.qscintilla_api_file 1878 pkg_config_type.support_email_address = mod_config_type.support_email_address 1879 pkg_config_type.user_configuration_file_is_supported = mod_config_type.user_configuration_file_is_supported 1880 pkg_config_type.user_pyqt_sip_flags_is_supported = mod_config_type.user_pyqt_sip_flags_is_supported 1881 pkg_config_type.version = mod_config_type.version 1882 1883 pkg_config_type.init_target_configuration = staticmethod( 1884 mod_config_type.init_target_configuration) 1885 pkg_config_type.init_optparser = staticmethod( 1886 mod_config_type.init_optparser) 1887 pkg_config_type.apply_options = staticmethod( 1888 mod_config_type.apply_options) 1889 pkg_config_type.inform_user = staticmethod( 1890 mod_config_type.inform_user) 1891 pkg_config_type.pre_code_generation = staticmethod( 1892 mod_config_type.pre_code_generation) 1893 pkg_config_type.get_sip_flags = staticmethod( 1894 mod_config_type.get_sip_flags) 1895 1896 # Note the name change. 1897 pkg_config_type.check_package = staticmethod( 1898 mod_config_type.check_module) 1899 1900 pkg_config_type.modules = [mod_config_type()] 1901 1902 pkg_config = pkg_config_type() 1903 1904 try: 1905 _main(sys.argv, pkg_config) 1906 except SystemExit: 1907 raise 1908 except: 1909 if pkg_config.support_email_address: 1910 sys.stderr.write( 1911"""An internal error occured. Please report all the output from the program, 1912including the following traceback, to %s. 1913""" % pkg_config.support_email_address) 1914 1915 raise 1916