configure.py 63 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874
  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. # This is v2.1 of this boilerplate.
  26. from distutils import sysconfig
  27. import glob
  28. import os
  29. import optparse
  30. import sys
  31. ###############################################################################
  32. # You shouldn't need to modify anything above this line.
  33. ###############################################################################
  34. # This must be kept in sync with Python/configure-old.py, qscintilla.pro,
  35. # example-Qt4Qt5/application.pro and designer-Qt4Qt5/designer.pro.
  36. QSCI_API_MAJOR = 13
  37. class ModuleConfiguration(object):
  38. """ This class encapsulates all the module specific information needed by
  39. the rest of this script to implement a configure.py script for modules that
  40. build on top of PyQt. Functions implemented by the rest of this script
  41. that begin with an underscore are considered internal and shouldn't be
  42. called from here.
  43. """
  44. # The name of the module as it would be used in an import statement.
  45. name = 'Qsci'
  46. # The descriptive name of the module. This is used in help text and error
  47. # messages.
  48. descriptive_name = "QScintilla"
  49. # The version of the module as a string. Set it to None if you don't
  50. # provide version information.
  51. version = '2.10.1'
  52. # Set if a configuration script is provided that handles versions of PyQt4
  53. # prior to v4.10 (i.e. versions where the pyqtconfig.py module is
  54. # available). If provided the script must be called configure-old.py and
  55. # be in the same directory as this script.
  56. legacy_configuration_script = True
  57. # The minimum version of SIP that is required. This should be a
  58. # dot-separated string of two or three integers (e.g. '1.0', '4.10.3'). If
  59. # it is None or an empty string then the version is not checked.
  60. minimum_sip_version = '4.19'
  61. # Set if support for C++ exceptions can be disabled.
  62. no_exceptions = True
  63. # The name (without the .pyi extension) of the name of the PEP 484 stub
  64. # file to be generated. If it is None or an empty string then a stub file
  65. # is not generated.
  66. pep484_stub_file = 'Qsci'
  67. # Set if the module supports redefining 'protected' as 'public'.
  68. protected_is_public_is_supported = True
  69. # Set if the module supports PyQt4.
  70. pyqt4_is_supported = True
  71. # Set if the module supports PyQt5.
  72. pyqt5_is_supported = True
  73. # Set if the PyQt5 support is the default. It is ignored unless both
  74. # 'pyqt4_is_supported' and 'pyqt5_is_supported' are set.
  75. pyqt5_is_default = False
  76. # The name (without the .api extension) of the name of the QScintilla API
  77. # file to be generated. If it is None or an empty string then an API file
  78. # is not generated.
  79. qscintilla_api_file = 'QScintilla2'
  80. # The email address that will be included when an error in the script is
  81. # detected. Leave it blank if you don't want to include an address.
  82. support_email_address = 'support@riverbankcomputing.com'
  83. # Set if the user can provide a configuration file. It is normally only
  84. # used if cross-compilation is supported.
  85. user_configuration_file_is_supported = True
  86. # Set if the user is allowed to pass PyQt sip flags on the command line.
  87. # It is normally only used if cross-compilation is supported. It is
  88. # ignored unless at least one of 'pyqt4_is_supported' or
  89. # 'pyqt5_is_supported' is set.
  90. user_pyqt_sip_flags_is_supported = True
  91. @staticmethod
  92. def init_target_configuration(target_configuration):
  93. """ Perform any module specific initialisation of the target
  94. target configuration. Typically this is the initialisation of module
  95. specific attributes. To avoid name clashes attributes should be given
  96. a module specific prefix. target_configuration is the target
  97. configuration.
  98. """
  99. target_configuration.qsci_version = None
  100. target_configuration.qsci_features_dir = None
  101. target_configuration.qsci_inc_dir = None
  102. target_configuration.qsci_lib_dir = None
  103. target_configuration.qsci_sip_dir = None
  104. @staticmethod
  105. def init_optparser(optparser, target_configuration):
  106. """ Perform any module specific initialisation of the command line
  107. option parser. To avoid name clashes destination attributes should be
  108. given a module specific prefix. optparser is the option parser.
  109. target_configuration is the target configuration.
  110. """
  111. optparser.add_option('--qsci-incdir', '-n', dest='qsci_inc_dir',
  112. type='string', default=None, action='callback',
  113. callback=optparser_store_abspath_dir, metavar="DIR",
  114. help="the directory containing the QScintilla Qsci header "
  115. "file directory is DIR [default: QT_INSTALL_HEADERS]")
  116. optparser.add_option('--qsci-featuresdir', dest='qsci_features_dir',
  117. type='string', default=None, action='callback',
  118. callback=optparser_store_abspath_dir, metavar="DIR",
  119. help="the directory containing the qscintilla2.prf features "
  120. "file is DIR [default: "
  121. "QT_INSTALL_PREFIX/mkspecs/features]")
  122. optparser.add_option('--qsci-libdir', '-o', dest='qsci_lib_dir',
  123. type='string', default=None, action='callback',
  124. callback=optparser_store_abspath_dir, metavar="DIR",
  125. help="the directory containing the QScintilla library is DIR "
  126. "[default: QT_INSTALL_LIBS]")
  127. optparser.add_option('--qsci-sipdir', '-v', dest='qsci_sip_dir',
  128. type='string', default=None, action='callback',
  129. callback=optparser_store_abspath_dir, metavar="DIR",
  130. help="the QScintilla .sip files will be installed in DIR "
  131. "[default: %s]" % target_configuration.pyqt_sip_dir)
  132. optparser.add_option("--no-sip-files", action="store_true",
  133. default=False, dest="qsci_no_sip_files",
  134. help="disable the installation of the .sip files "
  135. "[default: enabled]")
  136. @staticmethod
  137. def apply_options(target_configuration, options):
  138. """ Apply the module specific command line options to the target
  139. configuration. target_configuration is the target configuration.
  140. options are the parsed options.
  141. """
  142. if options.qsci_features_dir is not None:
  143. target_configuration.qsci_features_dir = options.qsci_features_dir
  144. if options.qsci_inc_dir is not None:
  145. target_configuration.qsci_inc_dir = options.qsci_inc_dir
  146. if options.qsci_lib_dir is not None:
  147. target_configuration.qsci_lib_dir = options.qsci_lib_dir
  148. if options.qsci_sip_dir is not None:
  149. target_configuration.qsci_sip_dir = options.qsci_sip_dir
  150. else:
  151. target_configuration.qsci_sip_dir = target_configuration.pyqt_sip_dir
  152. if options.qsci_no_sip_files:
  153. target_configuration.qsci_sip_dir = ''
  154. @staticmethod
  155. def check_module(target_configuration):
  156. """ Perform any module specific checks now that the target
  157. configuration is complete. target_configuration is the target
  158. configuration.
  159. """
  160. # Find the QScintilla header files.
  161. inc_dir = target_configuration.qsci_inc_dir
  162. if inc_dir is None:
  163. inc_dir = target_configuration.qt_inc_dir
  164. sciglobal = os.path.join(inc_dir, 'Qsci', 'qsciglobal.h')
  165. if not os.access(sciglobal, os.F_OK):
  166. error(
  167. "Qsci/qsciglobal.h could not be found in %s. If "
  168. "QScintilla is installed then use the --qsci-incdir "
  169. "argument to explicitly specify the correct "
  170. "directory." % inc_dir)
  171. # Get the QScintilla version string.
  172. qsci_version = read_define(sciglobal, 'QSCINTILLA_VERSION_STR')
  173. if qsci_version is None:
  174. error(
  175. "The QScintilla version number could not be determined by "
  176. "reading %s." % sciglobal)
  177. lib_dir = target_configuration.qsci_lib_dir
  178. if lib_dir is None:
  179. lib_dir = target_configuration.qt_lib_dir
  180. if not glob.glob(os.path.join(lib_dir, '*qscintilla2_qt*')):
  181. error(
  182. "The QScintilla library could not be found in %s. If "
  183. "QScintilla is installed then use the --qsci-libdir "
  184. "argument to explicitly specify the correct "
  185. "directory." % lib_dir)
  186. # Because we include the Python bindings with the C++ code we can
  187. # reasonably force the same version to be used and not bother about
  188. # versioning in the .sip files.
  189. if qsci_version != ModuleConfiguration.version:
  190. error(
  191. "QScintilla %s is being used but the Python bindings %s "
  192. "are being built. Please use matching "
  193. "versions." % (qsci_version, ModuleConfiguration.version))
  194. target_configuration.qsci_version = qsci_version
  195. @staticmethod
  196. def inform_user(target_configuration):
  197. """ Inform the user about module specific configuration information.
  198. target_configuration is the target configuration.
  199. """
  200. inform("QScintilla %s is being used." %
  201. target_configuration.qsci_version)
  202. if target_configuration.qsci_sip_dir != '':
  203. inform("The QScintilla .sip files will be installed in %s." %
  204. target_configuration.qsci_sip_dir)
  205. @staticmethod
  206. def pre_code_generation(target_config):
  207. """ Perform any module specific initialisation prior to generating the
  208. code. target_config is the target configuration.
  209. """
  210. # Nothing to do.
  211. @staticmethod
  212. def get_sip_flags(target_configuration):
  213. """ Return the list of module-specific flags to pass to SIP.
  214. target_configuration is the target configuration.
  215. """
  216. # Nothing to do.
  217. return []
  218. @staticmethod
  219. def get_sip_file(target_configuration):
  220. """ Return the name of the module's .sip file. target_configuration is
  221. the target configuration.
  222. """
  223. return 'sip/qscimod5.sip' if target_configuration.pyqt_package == 'PyQt5' else 'sip/qscimod4.sip'
  224. @staticmethod
  225. def get_sip_installs(target_configuration):
  226. """ Return a tuple of the installation directory of the module's .sip
  227. files and a sequence of the names of each of the .sip files relative to
  228. the directory containing this configuration script. None is returned
  229. if the module's .sip files are not to be installed.
  230. target_configuration is the target configuration.
  231. """
  232. if target_configuration.qsci_sip_dir == '':
  233. return None
  234. path = os.path.join(target_configuration.qsci_sip_dir, 'Qsci')
  235. files = glob.glob('sip/*.sip')
  236. return path, files
  237. @staticmethod
  238. def get_qmake_configuration(target_configuration):
  239. """ Return a dict of qmake configuration values for CONFIG, DEFINES,
  240. INCLUDEPATH, LIBS and QT. If value names (i.e. dict keys) have either
  241. 'Qt4' or 'Qt5' prefixes then they are specific to the corresponding
  242. version of Qt. target_configuration is the target configuration.
  243. """
  244. qmake = {'CONFIG': 'qscintilla2'}
  245. if target_configuration.qsci_inc_dir is not None:
  246. qmake['INCLUDEPATH'] = quote(target_configuration.qsci_inc_dir)
  247. if target_configuration.qsci_lib_dir is not None:
  248. qmake['LIBS'] = '-L%s' % quote(target_configuration.qsci_lib_dir)
  249. if target_configuration.qsci_features_dir is not None:
  250. os.environ['QMAKEFEATURES'] = target_configuration.qsci_features_dir
  251. return qmake
  252. @staticmethod
  253. def get_mac_wrapped_library_file(target_configuration):
  254. """ Return the full pathname of the file that implements the library
  255. being wrapped by the module as it would be called on OS/X so that the
  256. module will reference it explicitly without DYLD_LIBRARY_PATH being
  257. set. If it is None or an empty string then the default is used.
  258. target_configuration is the target configuration.
  259. """
  260. lib_dir = target_configuration.qsci_lib_dir
  261. if lib_dir is None:
  262. lib_dir = target_configuration.qt_lib_dir
  263. debug = '_debug' if target_configuration.debug else ''
  264. return os.path.join(lib_dir,
  265. 'libqscintilla2_qt%s%s.%s.dylib' % (
  266. target_configuration.qt_version_str[0], debug,
  267. QSCI_API_MAJOR))
  268. ###############################################################################
  269. # You shouldn't need to modify anything below this line.
  270. ###############################################################################
  271. def error(msg):
  272. """ Display an error message and terminate. msg is the text of the error
  273. message.
  274. """
  275. sys.stderr.write(_format("Error: " + msg) + "\n")
  276. sys.exit(1)
  277. def inform(msg):
  278. """ Display an information message. msg is the text of the error message.
  279. """
  280. sys.stdout.write(_format(msg) + "\n")
  281. def quote(path):
  282. """ Return a path with quotes added if it contains spaces. path is the
  283. path.
  284. """
  285. if ' ' in path:
  286. path = '"%s"' % path
  287. return path
  288. def optparser_store_abspath(option, opt_str, value, parser):
  289. """ An optparser callback that saves an option as an absolute pathname. """
  290. setattr(parser.values, option.dest, os.path.abspath(value))
  291. def optparser_store_abspath_dir(option, opt_str, value, parser):
  292. """ An optparser callback that saves an option as the absolute pathname
  293. of an existing directory.
  294. """
  295. if not os.path.isdir(value):
  296. raise optparse.OptionValueError("'%s' is not a directory" % value)
  297. setattr(parser.values, option.dest, os.path.abspath(value))
  298. def optparser_store_abspath_exe(option, opt_str, value, parser):
  299. """ An optparser callback that saves an option as the absolute pathname
  300. of an existing executable.
  301. """
  302. if not os.access(value, os.X_OK):
  303. raise optparse.OptionValueError("'%s' is not an executable" % value)
  304. setattr(parser.values, option.dest, os.path.abspath(value))
  305. def read_define(filename, define):
  306. """ Read the value of a #define from a file. filename is the name of the
  307. file. define is the name of the #define. None is returned if there was no
  308. such #define.
  309. """
  310. f = open(filename)
  311. for l in f:
  312. wl = l.split()
  313. if len(wl) >= 3 and wl[0] == "#define" and wl[1] == define:
  314. # Take account of embedded spaces.
  315. value = ' '.join(wl[2:])[1:-1]
  316. break
  317. else:
  318. value = None
  319. f.close()
  320. return value
  321. def version_from_string(version_str):
  322. """ Convert a version string of the form m, m.n or m.n.o to an encoded
  323. version number (or None if it was an invalid format). version_str is the
  324. version string.
  325. """
  326. parts = version_str.split('.')
  327. if not isinstance(parts, list):
  328. return None
  329. if len(parts) == 1:
  330. parts.append('0')
  331. if len(parts) == 2:
  332. parts.append('0')
  333. if len(parts) != 3:
  334. return None
  335. version = 0
  336. for part in parts:
  337. try:
  338. v = int(part)
  339. except ValueError:
  340. return None
  341. version = (version << 8) + v
  342. return version
  343. def _format(msg, left_margin=0, right_margin=78):
  344. """ Format a message by inserting line breaks at appropriate places. msg
  345. is the text of the message. left_margin is the position of the left
  346. margin. right_margin is the position of the right margin. Returns the
  347. formatted message.
  348. """
  349. curs = left_margin
  350. fmsg = " " * left_margin
  351. for w in msg.split():
  352. l = len(w)
  353. if curs != left_margin and curs + l > right_margin:
  354. fmsg = fmsg + "\n" + (" " * left_margin)
  355. curs = left_margin
  356. if curs > left_margin:
  357. fmsg = fmsg + " "
  358. curs = curs + 1
  359. fmsg = fmsg + w
  360. curs = curs + l
  361. return fmsg
  362. class _ConfigurationFileParser:
  363. """ A parser for configuration files. """
  364. def __init__(self, config_file):
  365. """ Read and parse a configuration file. """
  366. self._config = {}
  367. self._extrapolating = []
  368. cfg = open(config_file)
  369. line_nr = 0
  370. last_name = None
  371. section = ''
  372. section_config = {}
  373. self._config[section] = section_config
  374. for l in cfg:
  375. line_nr += 1
  376. # Strip comments.
  377. l = l.split('#')[0]
  378. # See if this might be part of a multi-line.
  379. multiline = (last_name is not None and len(l) != 0 and l[0] == ' ')
  380. l = l.strip()
  381. if l == '':
  382. last_name = None
  383. continue
  384. # See if this is a new section.
  385. if l[0] == '[' and l[-1] == ']':
  386. section = l[1:-1].strip()
  387. if section == '':
  388. error(
  389. "%s:%d: Empty section name." % (
  390. config_file, line_nr))
  391. if section in self._config:
  392. error(
  393. "%s:%d: Section '%s' defined more than once." % (
  394. config_file, line_nr, section))
  395. section_config = {}
  396. self._config[section] = section_config
  397. last_name = None
  398. continue
  399. parts = l.split('=', 1)
  400. if len(parts) == 2:
  401. name = parts[0].strip()
  402. value = parts[1].strip()
  403. elif multiline:
  404. name = last_name
  405. value = section_config[last_name]
  406. value += ' ' + l
  407. else:
  408. name = value = ''
  409. if name == '' or value == '':
  410. error("%s:%d: Invalid line." % (config_file, line_nr))
  411. section_config[name] = value
  412. last_name = name
  413. cfg.close()
  414. def sections(self):
  415. """ Return the list of sections, excluding the default one. """
  416. return [s for s in self._config.keys() if s != '']
  417. def preset(self, name, value):
  418. """ Add a preset value to the configuration. """
  419. self._config[''][name] = value
  420. def get(self, section, name, default=None):
  421. """ Get a configuration value while extrapolating. """
  422. # Get the name from the section, or the default section.
  423. value = self._config[section].get(name)
  424. if value is None:
  425. value = self._config[''].get(name)
  426. if value is None:
  427. if default is None:
  428. error(
  429. "Configuration file references non-existent name "
  430. "'%s'." % name)
  431. return default
  432. # Handle any extrapolations.
  433. parts = value.split('%(', 1)
  434. while len(parts) == 2:
  435. prefix, tail = parts
  436. parts = tail.split(')', 1)
  437. if len(parts) != 2:
  438. error(
  439. "Configuration file contains unterminated "
  440. "extrapolated name '%s'." % tail)
  441. xtra_name, suffix = parts
  442. if xtra_name in self._extrapolating:
  443. error(
  444. "Configuration file contains a recursive reference to "
  445. "'%s'." % xtra_name)
  446. self._extrapolating.append(xtra_name)
  447. xtra_value = self.get(section, xtra_name)
  448. self._extrapolating.pop()
  449. value = prefix + xtra_value + suffix
  450. parts = value.split('%(', 1)
  451. return value
  452. def getboolean(self, section, name, default):
  453. """ Get a boolean configuration value while extrapolating. """
  454. value = self.get(section, name, default)
  455. # In case the default was returned.
  456. if isinstance(value, bool):
  457. return value
  458. if value in ('True', 'true', '1'):
  459. return True
  460. if value in ('False', 'false', '0'):
  461. return False
  462. error(
  463. "Configuration file contains invalid boolean value for "
  464. "'%s'." % name)
  465. def getlist(self, section, name, default):
  466. """ Get a list configuration value while extrapolating. """
  467. value = self.get(section, name, default)
  468. # In case the default was returned.
  469. if isinstance(value, list):
  470. return value
  471. return value.split()
  472. class _HostPythonConfiguration:
  473. """ A container for the host Python configuration. """
  474. def __init__(self):
  475. """ Initialise the configuration. """
  476. self.platform = sys.platform
  477. self.version = sys.hexversion >> 8
  478. self.inc_dir = sysconfig.get_python_inc()
  479. self.venv_inc_dir = sysconfig.get_python_inc(prefix=sys.prefix)
  480. self.module_dir = sysconfig.get_python_lib(plat_specific=1)
  481. self.debug = hasattr(sys, 'gettotalrefcount')
  482. if sys.platform == 'win32':
  483. self.data_dir = sys.prefix
  484. self.lib_dir = sys.prefix + '\\libs'
  485. else:
  486. self.data_dir = sys.prefix + '/share'
  487. self.lib_dir = sys.prefix + '/lib'
  488. class _TargetQtConfiguration:
  489. """ A container for the target Qt configuration. """
  490. def __init__(self, qmake):
  491. """ Initialise the configuration. qmake is the full pathname of the
  492. qmake executable that will provide the configuration.
  493. """
  494. pipe = os.popen(quote(qmake) + ' -query')
  495. for l in pipe:
  496. l = l.strip()
  497. tokens = l.split(':', 1)
  498. if isinstance(tokens, list):
  499. if len(tokens) != 2:
  500. error("Unexpected output from qmake: '%s'\n" % l)
  501. name, value = tokens
  502. else:
  503. name = tokens
  504. value = None
  505. name = name.replace('/', '_')
  506. setattr(self, name, value)
  507. pipe.close()
  508. class _TargetConfiguration:
  509. """ A container for the target configuration. """
  510. def __init__(self, pkg_config):
  511. """ Initialise the configuration with default values. pkg_config is
  512. the package configuration.
  513. """
  514. # Values based on the host Python configuration.
  515. py_config = _HostPythonConfiguration()
  516. self.py_debug = py_config.debug
  517. self.py_platform = py_config.platform
  518. self.py_version = py_config.version
  519. self.py_module_dir = py_config.module_dir
  520. self.py_inc_dir = py_config.inc_dir
  521. self.py_venv_inc_dir = py_config.venv_inc_dir
  522. self.py_pylib_dir = py_config.lib_dir
  523. self.py_sip_dir = os.path.join(py_config.data_dir, 'sip')
  524. self.sip_inc_dir = py_config.venv_inc_dir
  525. # Remaining values.
  526. self.debug = False
  527. self.pyqt_sip_flags = None
  528. self.pyqt_version_str = ''
  529. self.qmake = self._find_exe('qmake')
  530. self.qmake_spec = ''
  531. self.qt_version = 0
  532. self.qt_version_str = ''
  533. self.sip = self._find_exe('sip5', 'sip')
  534. self.sip_version = None
  535. self.sip_version_str = None
  536. self.sysroot = ''
  537. self.stubs_dir = ''
  538. self.prot_is_public = (self.py_platform.startswith('linux') or self.py_platform == 'darwin')
  539. if pkg_config.pyqt5_is_supported and pkg_config.pyqt4_is_supported:
  540. pyqt = 'PyQt5' if pkg_config.pyqt5_is_default else 'PyQt4'
  541. elif pkg_config.pyqt5_is_supported and not pkg_config.pyqt4_is_supported:
  542. pyqt = 'PyQt5'
  543. elif not pkg_config.pyqt5_is_supported and pkg_config.pyqt4_is_supported:
  544. pyqt = 'PyQt4'
  545. else:
  546. pyqt = None
  547. if pyqt is not None:
  548. self.module_dir = os.path.join(py_config.module_dir, pyqt)
  549. self.pyqt_sip_dir = os.path.join(self.py_sip_dir, pyqt)
  550. else:
  551. self.module_dir = py_config.module_dir
  552. self.pyqt_sip_dir = None
  553. self.pyqt_package = pyqt
  554. pkg_config.init_target_configuration(self)
  555. def update_from_configuration_file(self, config_file):
  556. """ Update the configuration with values from a file. config_file
  557. is the name of the configuration file.
  558. """
  559. inform("Reading configuration from %s..." % config_file)
  560. parser = _ConfigurationFileParser(config_file)
  561. # Populate some presets from the command line.
  562. parser.preset('py_major', str(self.py_version >> 16))
  563. parser.preset('py_minor', str((self.py_version >> 8) & 0xff))
  564. parser.preset('sysroot', self.sysroot)
  565. if self.pyqt_package is None:
  566. section = ''
  567. else:
  568. # At the moment we only need to distinguish between PyQt4 and
  569. # PyQt5. If that changes we may need a --target-pyqt-version
  570. # command line option.
  571. pyqt_version = 0x050000 if self.pyqt_package == 'PyQt5' else 0x040000
  572. # Find the section corresponding to the version of PyQt.
  573. section = None
  574. latest_section = -1
  575. for name in parser.sections():
  576. parts = name.split()
  577. if len(parts) != 2 or parts[0] != 'PyQt':
  578. continue
  579. section_pyqt_version = version_from_string(parts[1])
  580. if section_pyqt_version is None:
  581. continue
  582. # Major versions must match.
  583. if section_pyqt_version >> 16 != pyqt_version >> 16:
  584. continue
  585. # It must be no later that the version of PyQt.
  586. if section_pyqt_version > pyqt_version:
  587. continue
  588. # Save it if it is the latest so far.
  589. if section_pyqt_version > latest_section:
  590. section = name
  591. latest_section = section_pyqt_version
  592. if section is None:
  593. error(
  594. "%s does not define a section that covers PyQt "
  595. "v%s." % (config_file, self.pyqt_version_str))
  596. self.py_platform = parser.get(section, 'py_platform', self.py_platform)
  597. self.py_inc_dir = parser.get(section, 'py_inc_dir', self.py_inc_dir)
  598. self.py_venv_inc_dir = self.py_inc_dir
  599. self.py_pylib_dir = parser.get(section, 'py_pylib_dir',
  600. self.py_pylib_dir)
  601. self.sip_inc_dir = self.py_venv_inc_dir
  602. self.module_dir = parser.get(section, 'module_dir', self.module_dir)
  603. if self.pyqt_package is not None:
  604. self.py_sip_dir = parser.get(section, 'py_sip_dir',
  605. self.py_sip_dir)
  606. # Construct the SIP flags.
  607. flags = []
  608. flags.append('-t')
  609. flags.append(self._get_platform_tag())
  610. if self.pyqt_package == 'PyQt5':
  611. if self.qt_version < 0x050000:
  612. error("PyQt5 requires Qt v5.0 or later.")
  613. if self.qt_version > 0x060000:
  614. self.qt_version = 0x060000
  615. else:
  616. if self.qt_version > 0x050000:
  617. self.qt_version = 0x050000
  618. major = (self.qt_version >> 16) & 0xff
  619. minor = (self.qt_version >> 8) & 0xff
  620. patch = self.qt_version & 0xff
  621. flags.append('-t')
  622. flags.append('Qt_%d_%d_%d' % (major, minor, patch))
  623. for feat in parser.getlist(section, 'pyqt_disabled_features', []):
  624. flags.append('-x')
  625. flags.append(feat)
  626. self.pyqt_sip_flags = ' '.join(flags)
  627. def _get_platform_tag(self):
  628. """ Return the tag for the target platform. """
  629. # This replicates the logic in PyQt's configure scripts.
  630. if self.py_platform == 'win32':
  631. plattag = 'WS_WIN'
  632. elif self.py_platform == 'darwin':
  633. plattag = 'WS_MACX'
  634. else:
  635. plattag = 'WS_X11'
  636. return plattag
  637. def introspect_pyqt(self, pkg_config):
  638. """ Introspect PyQt to determine the sip flags required. pkg_config
  639. is the package configuration.
  640. """
  641. if self.pyqt_package == 'PyQt5':
  642. try:
  643. from PyQt5 import QtCore
  644. except ImportError:
  645. error(
  646. "Unable to import PyQt5.QtCore. Make sure PyQt5 is "
  647. "installed.")
  648. else:
  649. try:
  650. from PyQt4 import QtCore
  651. except ImportError:
  652. error(
  653. "Unable to import PyQt4.QtCore. Make sure PyQt4 is "
  654. "installed.")
  655. self.pyqt_version_str = QtCore.PYQT_VERSION_STR
  656. self.qt_version_str = QtCore.qVersion()
  657. # See if we have a PyQt that embeds its configuration.
  658. try:
  659. pyqt_config = QtCore.PYQT_CONFIGURATION
  660. except AttributeError:
  661. pyqt_config = None
  662. if pyqt_config is None:
  663. if pkg_config.legacy_configuration_script:
  664. # Fallback to the old configuration script.
  665. config_script = sys.argv[0].replace('configure', 'configure-old')
  666. args = [sys.executable, config_script] + sys.argv[1:]
  667. try:
  668. os.execv(sys.executable, args)
  669. except OSError:
  670. pass
  671. error("Unable to execute '%s'" % config_script)
  672. error("PyQt v4.10 or later is required.")
  673. self.pyqt_sip_flags = pyqt_config['sip_flags']
  674. def apply_sysroot(self):
  675. """ Apply sysroot where necessary. """
  676. if self.sysroot != '':
  677. self.py_inc_dir = self._apply_sysroot(self.py_inc_dir)
  678. self.py_venv_inc_dir = self._apply_sysroot(self.py_venv_inc_dir)
  679. self.py_pylib_dir = self._apply_sysroot(self.py_pylib_dir)
  680. self.py_sip_dir = self._apply_sysroot(self.py_sip_dir)
  681. self.module_dir = self._apply_sysroot(self.module_dir)
  682. self.sip_inc_dir = self._apply_sysroot(self.sip_inc_dir)
  683. def _apply_sysroot(self, dir_name):
  684. """ Replace any leading sys.prefix of a directory name with sysroot.
  685. """
  686. if dir_name.startswith(sys.prefix):
  687. dir_name = self.sysroot + dir_name[len(sys.prefix):]
  688. return dir_name
  689. def get_qt_configuration(self, opts):
  690. """ Get the Qt configuration that can be extracted from qmake. opts
  691. are the command line options.
  692. """
  693. # Query qmake.
  694. qt_config = _TargetQtConfiguration(self.qmake)
  695. self.qt_version_str = getattr(qt_config, 'QT_VERSION', '')
  696. self.qt_version = version_from_string(self.qt_version_str)
  697. if self.qt_version is None:
  698. error("Unable to determine the version of Qt.")
  699. # On Windows for Qt versions prior to v5.9.0 we need to be explicit
  700. # about the qmake spec.
  701. if self.qt_version < 0x050900 and self.py_platform == 'win32':
  702. if self.py_version >= 0x030500:
  703. self.qmake_spec = 'win32-msvc2015'
  704. elif self.py_version >= 0x030300:
  705. self.qmake_spec = 'win32-msvc2010'
  706. elif self.py_version >= 0x020600:
  707. self.qmake_spec = 'win32-msvc2008'
  708. elif self.py_version >= 0x020400:
  709. self.qmake_spec = 'win32-msvc.net'
  710. else:
  711. self.qmake_spec = 'win32-msvc'
  712. else:
  713. # Otherwise use the default.
  714. self.qmake_spec = ''
  715. # The binary MacOS/X Qt installer used to default to XCode. If so then
  716. # use macx-clang (Qt v5) or macx-g++ (Qt v4).
  717. if sys.platform == 'darwin':
  718. try:
  719. # Qt v5.
  720. if qt_config.QMAKE_SPEC == 'macx-xcode':
  721. # This will exist (and we can't check anyway).
  722. self.qmake_spec = 'macx-clang'
  723. else:
  724. # No need to explicitly name the default.
  725. self.qmake_spec = ''
  726. except AttributeError:
  727. # Qt v4.
  728. self.qmake_spec = 'macx-g++'
  729. self.api_dir = os.path.join(qt_config.QT_INSTALL_DATA, 'qsci')
  730. self.qt_inc_dir = qt_config.QT_INSTALL_HEADERS
  731. self.qt_lib_dir = qt_config.QT_INSTALL_LIBS
  732. if self.sysroot == '':
  733. self.sysroot = getattr(qt_config, 'QT_SYSROOT', '')
  734. def apply_pre_options(self, opts):
  735. """ Apply options from the command line that influence subsequent
  736. configuration. opts are the command line options.
  737. """
  738. # On Windows the interpreter must be a debug build if a debug version
  739. # is to be built and vice versa.
  740. if sys.platform == 'win32':
  741. if opts.debug:
  742. if not self.py_debug:
  743. error(
  744. "A debug version of Python must be used when "
  745. "--debug is specified.")
  746. elif self.py_debug:
  747. error(
  748. "--debug must be specified when a debug version of "
  749. "Python is used.")
  750. self.debug = opts.debug
  751. # Get the system root.
  752. if opts.sysroot is not None:
  753. self.sysroot = opts.sysroot
  754. # Determine how to run qmake.
  755. if opts.qmake is not None:
  756. self.qmake = opts.qmake
  757. # On Windows add the directory that probably contains the Qt DLLs
  758. # to PATH.
  759. if sys.platform == 'win32':
  760. path = os.environ['PATH']
  761. path = os.path.dirname(self.qmake) + ';' + path
  762. os.environ['PATH'] = path
  763. if self.qmake is None:
  764. error(
  765. "Use the --qmake argument to explicitly specify a working "
  766. "Qt qmake.")
  767. if opts.qmakespec is not None:
  768. self.qmake_spec = opts.qmakespec
  769. if self.pyqt_package is not None:
  770. try:
  771. self.pyqt_package = opts.pyqt_package
  772. except AttributeError:
  773. # Multiple PyQt versions are not supported.
  774. pass
  775. self.module_dir = os.path.join(self.py_module_dir,
  776. self.pyqt_package)
  777. def apply_post_options(self, opts, pkg_config):
  778. """ Apply options from the command line that override the previous
  779. configuration. opts are the command line options. pkg_config is the
  780. package configuration.
  781. """
  782. if self.pyqt_package is not None:
  783. if pkg_config.user_pyqt_sip_flags_is_supported:
  784. if opts.pyqt_sip_flags is not None:
  785. self.pyqt_sip_flags = opts.pyqt_sip_flags
  786. if opts.pyqt_sip_dir is not None:
  787. self.pyqt_sip_dir = opts.pyqt_sip_dir
  788. else:
  789. self.pyqt_sip_dir = os.path.join(self.py_sip_dir,
  790. self.pyqt_package)
  791. if _has_stubs(pkg_config):
  792. if opts.stubsdir is not None:
  793. self.stubs_dir = opts.stubsdir
  794. if opts.no_stubs:
  795. self.stubs_dir = ''
  796. elif self.stubs_dir == '':
  797. self.stubs_dir = self.module_dir
  798. if pkg_config.qscintilla_api_file:
  799. if opts.apidir is not None:
  800. self.api_dir = opts.apidir
  801. if opts.no_qsci_api:
  802. self.api_dir = ''
  803. if opts.destdir is not None:
  804. self.module_dir = opts.destdir
  805. if pkg_config.protected_is_public_is_supported:
  806. if opts.prot_is_public is not None:
  807. self.prot_is_public = opts.prot_is_public
  808. else:
  809. self.prot_is_public = False
  810. if opts.sip_inc_dir is not None:
  811. self.sip_inc_dir = opts.sip_inc_dir
  812. if opts.sip is not None:
  813. self.sip = opts.sip
  814. pkg_config.apply_options(self, opts)
  815. @staticmethod
  816. def _find_exe(*exes):
  817. """ Find an executable, ie. the first on the path. """
  818. path_dirs = os.environ.get('PATH', '').split(os.pathsep)
  819. for exe in exes:
  820. # Strip any surrounding quotes.
  821. if exe.startswith('"') and exe.endswith('"'):
  822. exe = exe[1:-1]
  823. if sys.platform == 'win32':
  824. exe = exe + '.exe'
  825. for d in path_dirs:
  826. exe_path = os.path.join(d, exe)
  827. if os.access(exe_path, os.X_OK):
  828. return exe_path
  829. return None
  830. def _create_optparser(target_config, pkg_config):
  831. """ Create the parser for the command line. target_config is the target
  832. configuration containing default values. pkg_config is the package
  833. configuration.
  834. """
  835. pkg_name = pkg_config.descriptive_name
  836. p = optparse.OptionParser(usage="python %prog [options]",
  837. version=pkg_config.version)
  838. p.add_option('--spec', dest='qmakespec', default=None, action='store',
  839. metavar="SPEC",
  840. help="pass -spec SPEC to qmake")
  841. if _has_stubs(pkg_config):
  842. p.add_option('--stubsdir', dest='stubsdir', type='string',
  843. default=None, action='callback',
  844. callback=optparser_store_abspath, metavar="DIR",
  845. help="the PEP 484 stubs will be installed in DIR [default: "
  846. "with the module]")
  847. p.add_option('--no-stubs', dest='no_stubs', default=False,
  848. action='store_true',
  849. help="disable the installation of the PEP 484 stubs "
  850. "[default: enabled]")
  851. if pkg_config.qscintilla_api_file:
  852. p.add_option('--apidir', '-a', dest='apidir', type='string',
  853. default=None, action='callback',
  854. callback=optparser_store_abspath, metavar="DIR",
  855. help="the QScintilla API file will be installed in DIR "
  856. "[default: QT_INSTALL_DATA/qsci]")
  857. p.add_option('--no-qsci-api', dest='no_qsci_api', default=False,
  858. action='store_true',
  859. help="disable the installation of the QScintilla API file "
  860. "[default: enabled]")
  861. if pkg_config.user_configuration_file_is_supported:
  862. p.add_option('--configuration', dest='config_file', type='string',
  863. default=None, action='callback',
  864. callback=optparser_store_abspath, metavar="FILE",
  865. help="FILE defines the target configuration")
  866. p.add_option('--destdir', '-d', dest='destdir', type='string',
  867. default=None, action='callback', callback=optparser_store_abspath,
  868. metavar="DIR",
  869. help="install %s in DIR [default: %s]" %
  870. (pkg_name, target_config.module_dir))
  871. if pkg_config.protected_is_public_is_supported:
  872. p.add_option('--protected-is-public', dest='prot_is_public',
  873. default=None, action='store_true',
  874. help="enable building with 'protected' redefined as 'public' "
  875. "[default: %s]" % target_config.prot_is_public)
  876. p.add_option('--protected-not-public', dest='prot_is_public',
  877. action='store_false',
  878. help="disable building with 'protected' redefined as 'public'")
  879. if target_config.pyqt_package is not None:
  880. pyqt = target_config.pyqt_package
  881. if pkg_config.pyqt5_is_supported and pkg_config.pyqt4_is_supported:
  882. p.add_option('--pyqt', dest='pyqt_package', type='choice',
  883. choices=['PyQt4', 'PyQt5'], default=pyqt,
  884. action='store', metavar="PyQtn",
  885. help="configure for PyQt4 or PyQt5 [default: %s]" % pyqt)
  886. if pkg_config.user_pyqt_sip_flags_is_supported:
  887. p.add_option('--pyqt-sip-flags', dest='pyqt_sip_flags',
  888. default=None, action='store', metavar="FLAGS",
  889. help="the sip flags used to build PyQt [default: query PyQt]")
  890. p.add_option('--qmake', '-q', dest='qmake', type='string', default=None,
  891. action='callback', callback=optparser_store_abspath_exe,
  892. metavar="FILE",
  893. help="the pathname of qmake is FILE [default: %s]" % (
  894. target_config.qmake or "search PATH"))
  895. p.add_option('--sip', dest='sip', type='string', default=None,
  896. action='callback', callback=optparser_store_abspath_exe,
  897. metavar="FILE",
  898. help="the pathname of sip is FILE [default: "
  899. "%s]" % (target_config.sip or "None"))
  900. p.add_option('--sip-incdir', dest='sip_inc_dir', type='string',
  901. default=None, action='callback',
  902. callback=optparser_store_abspath_dir, metavar="DIR",
  903. help="the directory containing the sip.h header file file is DIR "
  904. "[default: %s]" % target_config.sip_inc_dir)
  905. if target_config.pyqt_package is not None:
  906. p.add_option('--pyqt-sipdir', dest='pyqt_sip_dir', type='string',
  907. default=None, action='callback',
  908. callback=optparser_store_abspath_dir, metavar="DIR",
  909. help="the directory containing the PyQt .sip files is DIR "
  910. "[default: %s]" % target_config.pyqt_sip_dir)
  911. p.add_option('--concatenate', '-c', dest='concat', default=False,
  912. action='store_true',
  913. help="concatenate the C++ source files")
  914. p.add_option('--concatenate-split', '-j', dest='split', type='int',
  915. default=1, metavar="N",
  916. help="split the concatenated C++ source files into N pieces "
  917. "[default: 1]")
  918. p.add_option('--static', '-k', dest='static', default=False,
  919. action='store_true',
  920. help="build a static %s" % pkg_name)
  921. p.add_option("--sysroot", dest='sysroot', type='string', action='callback',
  922. callback=optparser_store_abspath_dir, metavar="DIR",
  923. help="DIR is the target system root directory")
  924. p.add_option('--no-docstrings', dest='no_docstrings', default=False,
  925. action='store_true',
  926. help="disable the generation of docstrings")
  927. p.add_option('--trace', '-r', dest='tracing', default=False,
  928. action='store_true',
  929. help="build %s with tracing enabled" % pkg_name)
  930. p.add_option('--debug', '-u', default=False, action='store_true',
  931. help="build %s with debugging symbols" % pkg_name)
  932. p.add_option('--verbose', '-w', dest='verbose', default=False,
  933. action='store_true',
  934. help="enable verbose output during configuration")
  935. pkg_config.init_optparser(p, target_config)
  936. return p
  937. def _has_stubs(pkg_config):
  938. """ See if a stub file for any of the modules will be generated.
  939. pkg_config is the package configuration.
  940. """
  941. for module_config in pkg_config.modules:
  942. if module_config.pep484_stub_file:
  943. return True
  944. return False
  945. def _inform_user(target_config, pkg_config):
  946. """ Tell the user the values that are going to be used. target_config is
  947. the target configuration. pkg_config is the package configuration.
  948. """
  949. pkg_name = pkg_config.descriptive_name
  950. inform("Configuring %s %s..." % (pkg_name, pkg_config.version))
  951. pkg_config.inform_user(target_config)
  952. inform("%s will be installed in %s." %
  953. (pkg_name, target_config.module_dir))
  954. if target_config.debug:
  955. inform("A debug version of %s will be built." % pkg_name)
  956. if target_config.py_debug:
  957. inform("A debug build of Python is being used.")
  958. if target_config.pyqt_version_str != '':
  959. inform("PyQt %s is being used." % target_config.pyqt_version_str)
  960. else:
  961. inform("%s is being used." % target_config.pyqt_package)
  962. if target_config.qt_version_str != '':
  963. inform("Qt %s is being used." % target_config.qt_version_str)
  964. if target_config.sysroot != '':
  965. inform("The system root directory is %s." % target_config.sysroot)
  966. inform("sip %s is being used." % target_config.sip_version_str)
  967. inform("The sip executable is %s." % target_config.sip)
  968. if target_config.prot_is_public:
  969. inform("%s is being built with 'protected' redefined as 'public'." %
  970. pkg_name)
  971. if target_config.stubs_dir != '':
  972. inform("The PEP 484 stubs will be installed in %s." %
  973. target_config.stubs_dir)
  974. if pkg_config.qscintilla_api_file and target_config.api_dir != '':
  975. inform("The QScintilla API file will be installed in %s." %
  976. os.path.join(target_config.api_dir, 'api', 'python'))
  977. def _generate_code(target_config, opts, pkg_config, module_config):
  978. """ Generate the code for the module. target_config is the target
  979. configuration. opts are the command line options. pkg_config is the
  980. package configuration. module_config is the module configuration.
  981. """
  982. inform(
  983. "Generating the C++ source for the %s module..." %
  984. module_config.name)
  985. # Generate the code in a module-specific sub-directory.
  986. try:
  987. os.mkdir(module_config.name)
  988. except:
  989. pass
  990. # Build the SIP command line.
  991. argv = [quote(target_config.sip)]
  992. # Tell SIP if this is a debug build of Python (SIP v4.19.1 and later).
  993. if target_config.sip_version >= 0x041301 and target_config.py_debug:
  994. argv.append('-D')
  995. # Add the module-specific flags.
  996. argv.extend(pkg_config.get_sip_flags(target_config))
  997. if target_config.pyqt_package is not None:
  998. # Get the flags used for the main PyQt module.
  999. argv.extend(target_config.pyqt_sip_flags.split())
  1000. # Add the backstop version.
  1001. argv.append('-B')
  1002. argv.append('Qt_6_0_0' if target_config.pyqt_package == 'PyQt5'
  1003. else 'Qt_5_0_0')
  1004. # Add PyQt's .sip files to the search path.
  1005. argv.append('-I')
  1006. argv.append(quote(target_config.pyqt_sip_dir))
  1007. if target_config.stubs_dir != '':
  1008. # Generate the stub file.
  1009. argv.append('-y')
  1010. argv.append(quote(module_config.pep484_stub_file + '.pyi'))
  1011. if pkg_config.qscintilla_api_file and target_config.api_dir != '':
  1012. # Generate the API file.
  1013. argv.append('-a')
  1014. argv.append(quote(module_config.name + '.api'))
  1015. if target_config.prot_is_public:
  1016. argv.append('-P');
  1017. if not opts.no_docstrings:
  1018. argv.append('-o');
  1019. if opts.concat:
  1020. argv.append('-j')
  1021. argv.append(str(opts.split))
  1022. if opts.tracing:
  1023. argv.append('-r')
  1024. argv.append('-c')
  1025. argv.append(os.path.abspath(module_config.name))
  1026. # This assumes that, for multi-module packages, all modules's .sip files
  1027. # will be rooted in a common root directory.
  1028. sip_file = module_config.get_sip_file(target_config)
  1029. head, tail = os.path.split(sip_file)
  1030. while head:
  1031. head, tail = os.path.split(head)
  1032. if tail != sip_file:
  1033. argv.append('-I')
  1034. argv.append(quote(tail))
  1035. argv.append(sip_file)
  1036. check_file = os.path.join(module_config.name,
  1037. 'sipAPI%s.h' % module_config.name)
  1038. _remove_file(check_file)
  1039. _run_command(' '.join(argv), opts.verbose)
  1040. if not os.access(check_file, os.F_OK):
  1041. error("Unable to create the C++ code.")
  1042. # Generate the .pro file.
  1043. _generate_pro(target_config, opts, module_config)
  1044. def _get_qt_qmake_config(qmake_config, qt_version):
  1045. """ Return a dict of qmake configuration values for a specific Qt version.
  1046. """
  1047. qt_qmake_config = {}
  1048. for name, value in qmake_config.items():
  1049. name_parts = name.split(':')
  1050. if len(name_parts) == 2 and name_parts[0] == qt_version:
  1051. qt_qmake_config[name_parts[1]] = value
  1052. return qt_qmake_config
  1053. def _write_qt_qmake_config(qt_qmake_config, pro):
  1054. """ Write the qmake configuration values to a .pro file. """
  1055. for name in ('QT', 'CONFIG', 'DEFINES', 'INCLUDEPATH', 'LIBS'):
  1056. value = qt_qmake_config.get(name)
  1057. if value:
  1058. pro.write(' %s += %s\n' % (name, value))
  1059. def _generate_pro(target_config, opts, module_config):
  1060. """ Generate the .pro file for the module. target_config is the target
  1061. configuration. opts are the command line options. module_config is the
  1062. module configuration.
  1063. """
  1064. inform("Generating the .pro file for the %s module..." % module_config.name)
  1065. # Without the 'no_check_exist' magic the target.files must exist when qmake
  1066. # is run otherwise the install and uninstall targets are not generated.
  1067. qmake_config = module_config.get_qmake_configuration(target_config)
  1068. pro = open(os.path.join(module_config.name, module_config.name + '.pro'),
  1069. 'w')
  1070. pro.write('TEMPLATE = lib\n')
  1071. qt = qmake_config.get('QT')
  1072. if qt:
  1073. pro.write('QT += %s\n' % qt)
  1074. pro.write('CONFIG += %s\n' % ('debug' if target_config.debug else 'release'))
  1075. pro.write('CONFIG += %s\n' % ('staticlib' if opts.static else 'plugin'))
  1076. config = qmake_config.get('CONFIG')
  1077. if config:
  1078. pro.write('CONFIG += %s\n' % config)
  1079. # Work around QTBUG-39300.
  1080. pro.write('CONFIG -= android_install\n')
  1081. qt5_qmake_config = _get_qt_qmake_config(qmake_config, 'Qt5')
  1082. qt4_qmake_config = _get_qt_qmake_config(qmake_config, 'Qt4')
  1083. if qt5_qmake_config or qt4_qmake_config:
  1084. pro.write('''
  1085. greaterThan(QT_MAJOR_VERSION, 4) {
  1086. ''')
  1087. if qt5_qmake_config:
  1088. _write_qt_qmake_config(qt5_qmake_config, pro)
  1089. if qt4_qmake_config:
  1090. pro.write('} else {\n')
  1091. _write_qt_qmake_config(qt4_qmake_config, pro)
  1092. pro.write('}\n')
  1093. mname = module_config.name
  1094. if not opts.static:
  1095. pro.write('''
  1096. win32 {
  1097. PY_MODULE = %s.pyd
  1098. target.files = %s.pyd
  1099. LIBS += -L%s
  1100. } else {
  1101. PY_MODULE = %s.so
  1102. target.files = %s.so
  1103. }
  1104. target.CONFIG = no_check_exist
  1105. ''' % (mname, mname, quote(target_config.py_pylib_dir), mname, mname))
  1106. pro.write('''
  1107. target.path = %s
  1108. INSTALLS += target
  1109. ''' % quote(target_config.module_dir))
  1110. sip_installs = module_config.get_sip_installs(target_config)
  1111. if sip_installs is not None:
  1112. path, files = sip_installs
  1113. pro.write('''
  1114. sip.path = %s
  1115. sip.files =''' % quote(path))
  1116. for f in files:
  1117. pro.write(' \\\n ../%s' % f)
  1118. pro.write('''
  1119. INSTALLS += sip
  1120. ''')
  1121. pro.write('\n')
  1122. # These optimisations could apply to other platforms.
  1123. if module_config.no_exceptions:
  1124. if target_config.py_platform.startswith('linux') or target_config.py_platform == 'darwin':
  1125. pro.write('QMAKE_CXXFLAGS += -fno-exceptions\n')
  1126. if target_config.py_platform.startswith('linux') and not opts.static:
  1127. if target_config.py_version >= 0x030000:
  1128. entry_point = 'PyInit_%s' % mname
  1129. else:
  1130. entry_point = 'init%s' % mname
  1131. exp = open(os.path.join(mname, mname + '.exp'), 'wt')
  1132. exp.write('{ global: %s; local: *; };' % entry_point)
  1133. exp.close()
  1134. pro.write('QMAKE_LFLAGS += -Wl,--version-script=%s.exp\n' % mname)
  1135. if target_config.prot_is_public:
  1136. pro.write('DEFINES += SIP_PROTECTED_IS_PUBLIC protected=public\n')
  1137. defines = qmake_config.get('DEFINES')
  1138. if defines:
  1139. pro.write('DEFINES += %s\n' % defines)
  1140. includepath = qmake_config.get('INCLUDEPATH')
  1141. if includepath:
  1142. pro.write('INCLUDEPATH += %s\n' % includepath)
  1143. # Make sure the SIP include directory is searched before the Python include
  1144. # directory if they are different.
  1145. pro.write('INCLUDEPATH += %s\n' % quote(target_config.sip_inc_dir))
  1146. if target_config.py_inc_dir != target_config.sip_inc_dir:
  1147. pro.write('INCLUDEPATH += %s\n' % quote(target_config.py_inc_dir))
  1148. libs = qmake_config.get('LIBS')
  1149. if libs:
  1150. pro.write('LIBS += %s\n' % libs)
  1151. if not opts.static:
  1152. pro.write('''
  1153. win32 {
  1154. QMAKE_POST_LINK = $(COPY_FILE) $(DESTDIR_TARGET) $$PY_MODULE
  1155. } else {
  1156. QMAKE_POST_LINK = $(COPY_FILE) $(TARGET) $$PY_MODULE
  1157. }
  1158. macx {
  1159. QMAKE_LFLAGS += "-undefined dynamic_lookup"
  1160. greaterThan(QT_MAJOR_VERSION, 4) {
  1161. QMAKE_LFLAGS += "-install_name $$absolute_path($$PY_MODULE, $$target.path)"
  1162. greaterThan(QT_MINOR_VERSION, 4) {
  1163. QMAKE_RPATHDIR += $$[QT_INSTALL_LIBS]
  1164. }
  1165. }
  1166. ''')
  1167. dylib = module_config.get_mac_wrapped_library_file(target_config)
  1168. if dylib:
  1169. pro.write('''
  1170. QMAKE_POST_LINK = $$QMAKE_POST_LINK$$escape_expand(\\\\n\\\\t)$$quote(install_name_tool -change %s %s $$PY_MODULE)
  1171. ''' % (os.path.basename(dylib), dylib))
  1172. pro.write('}\n')
  1173. pro.write('\n')
  1174. pro.write('TARGET = %s\n' % mname)
  1175. pro.write('HEADERS = sipAPI%s.h\n' % mname)
  1176. pro.write('SOURCES =')
  1177. for s in os.listdir(module_config.name):
  1178. if s.endswith('.cpp'):
  1179. pro.write(' \\\n %s' % s)
  1180. pro.write('\n')
  1181. pro.close()
  1182. def _run_qmake(target_config, verbose, pro_name):
  1183. """ Run qmake against a .pro file. target_config is the target
  1184. configuration. verbose is set if the output is to be displayed. pro_name
  1185. is the name of the .pro file.
  1186. """
  1187. inform("Generating the Makefiles...")
  1188. # qmake doesn't behave consistently if it is not run from the directory
  1189. # containing the .pro file - so make sure it is.
  1190. pro_dir, pro_file = os.path.split(pro_name)
  1191. if pro_dir != '':
  1192. cwd = os.getcwd()
  1193. os.chdir(pro_dir)
  1194. else:
  1195. cwd = None
  1196. mf = 'Makefile'
  1197. _remove_file(mf)
  1198. args = [quote(target_config.qmake)]
  1199. # Make sure all Makefiles are generated now in case qmake has been
  1200. # configured with environment variables.
  1201. args.append('-recursive')
  1202. if target_config.qmake_spec != '':
  1203. args.append('-spec')
  1204. args.append(target_config.qmake_spec)
  1205. args.append(pro_file)
  1206. _run_command(' '.join(args), verbose)
  1207. if not os.access(mf, os.F_OK):
  1208. error(
  1209. "%s failed to create a Makefile from %s." %
  1210. (target_config.qmake, pro_name))
  1211. # Restore the current directory.
  1212. if cwd is not None:
  1213. os.chdir(cwd)
  1214. def _run_command(cmd, verbose):
  1215. """ Run a command and display the output if requested. cmd is the command
  1216. to run. verbose is set if the output is to be displayed.
  1217. """
  1218. if verbose:
  1219. sys.stdout.write(cmd + "\n")
  1220. fout = _get_command_output(cmd)
  1221. # Read stdout and stderr until there is no more output.
  1222. lout = fout.readline()
  1223. while lout:
  1224. if verbose:
  1225. if sys.hexversion >= 0x03000000:
  1226. sys.stdout.write(str(lout, encoding=sys.stdout.encoding))
  1227. else:
  1228. sys.stdout.write(lout)
  1229. lout = fout.readline()
  1230. fout.close()
  1231. try:
  1232. os.wait()
  1233. except:
  1234. pass
  1235. def _get_command_output(cmd):
  1236. """ Return a pipe from which a command's output can be read. cmd is the
  1237. command.
  1238. """
  1239. try:
  1240. import subprocess
  1241. except ImportError:
  1242. _, sout = os.popen4(cmd)
  1243. return sout
  1244. p = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE,
  1245. stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
  1246. return p.stdout
  1247. def _remove_file(fname):
  1248. """ Remove a file which may or may not exist. fname is the name of the
  1249. file.
  1250. """
  1251. try:
  1252. os.remove(fname)
  1253. except OSError:
  1254. pass
  1255. def _check_sip(target_config, pkg_config):
  1256. """ Check that the version of sip is good enough. target_config is the
  1257. target configuration. pkg_config is the package configuration.
  1258. """
  1259. if target_config.sip is None:
  1260. error(
  1261. "Make sure you have a working sip on your PATH or use the "
  1262. "--sip argument to explicitly specify a working sip.")
  1263. pipe = os.popen(' '.join([quote(target_config.sip), '-V']))
  1264. for l in pipe:
  1265. version_str = l.strip()
  1266. break
  1267. else:
  1268. error("'%s -V' did not generate any output." % target_config.sip)
  1269. pipe.close()
  1270. if '.dev' in version_str or 'snapshot' in version_str:
  1271. version = 0
  1272. else:
  1273. version = version_from_string(version_str)
  1274. if version is None:
  1275. error(
  1276. "'%s -V' generated unexpected output: '%s'." % (
  1277. target_config.sip, version_str))
  1278. min_sip_version = pkg_config.minimum_sip_version
  1279. if min_sip_version:
  1280. min_version = version_from_string(min_sip_version)
  1281. if version < min_version:
  1282. error(
  1283. "This version of %s requires sip %s or later." %
  1284. (pkg_config.descriptive_name, min_sip_version))
  1285. target_config.sip_version = version
  1286. target_config.sip_version_str = version_str
  1287. def _main(argv, pkg_config):
  1288. """ Create the configured package. argv is the list of command line
  1289. arguments. pkg_config is the package configuration.
  1290. """
  1291. # Create the default target configuration.
  1292. target_config = _TargetConfiguration(pkg_config)
  1293. # Parse the command line.
  1294. p = _create_optparser(target_config, pkg_config)
  1295. opts, args = p.parse_args()
  1296. if args:
  1297. p.print_help()
  1298. sys.exit(2)
  1299. target_config.apply_pre_options(opts)
  1300. # Query qmake for the basic configuration information.
  1301. target_config.get_qt_configuration(opts)
  1302. # Update the target configuration.
  1303. if pkg_config.user_configuration_file_is_supported:
  1304. config_file = opts.config_file
  1305. else:
  1306. config_file = None
  1307. if config_file is not None:
  1308. target_config.update_from_configuration_file(config_file)
  1309. else:
  1310. target_config.apply_sysroot()
  1311. target_config.apply_post_options(opts, pkg_config)
  1312. if target_config.pyqt_package is not None:
  1313. if target_config.pyqt_sip_flags is None:
  1314. target_config.introspect_pyqt(pkg_config)
  1315. # Check SIP is new enough.
  1316. _check_sip(target_config, pkg_config)
  1317. # Perform any package specific checks now that all other information has
  1318. # been captured.
  1319. pkg_config.check_package(target_config)
  1320. # Tell the user what's been found.
  1321. _inform_user(target_config, pkg_config)
  1322. # Allow for module specific hacks.
  1323. pkg_config.pre_code_generation(target_config)
  1324. # Generate the code.
  1325. for module_config in pkg_config.modules:
  1326. _generate_code(target_config, opts, pkg_config, module_config)
  1327. # Concatenate any .api files.
  1328. if pkg_config.qscintilla_api_file and target_config.api_dir != '':
  1329. inform("Generating the QScintilla API file...")
  1330. f = open(pkg_config.qscintilla_api_file + '.api', 'w')
  1331. for module_config in pkg_config.modules:
  1332. api = open(module_config.name + '.api')
  1333. for l in api:
  1334. if target_config.pyqt_package is not None:
  1335. l = target_config.pyqt_package + '.' + l
  1336. f.write(l)
  1337. api.close()
  1338. os.remove(module_config.name + '.api')
  1339. f.close()
  1340. # Generate the top-level .pro file.
  1341. inform("Generating the top-level .pro file...")
  1342. pro_name = pkg_config.descriptive_name + '.pro'
  1343. pro = open(pro_name, 'w')
  1344. pro.write('''TEMPLATE = subdirs
  1345. CONFIG += ordered nostrip
  1346. SUBDIRS = %s
  1347. ''' % ' '.join([module.name for module in pkg_config.modules]))
  1348. if target_config.stubs_dir != '':
  1349. stubs = [module.pep484_stub_file + '.pyi' for module in pkg_config.modules if module.pep484_stub_file]
  1350. if stubs:
  1351. pro.write('''
  1352. pep484_stubs.path = %s
  1353. pep484_stubs.files = %s
  1354. INSTALLS += pep484_stubs
  1355. ''' % (target_config.stubs_dir, ' '.join(stubs)))
  1356. if pkg_config.qscintilla_api_file and target_config.api_dir != '':
  1357. pro.write('''
  1358. api.path = %s/api/python
  1359. api.files = %s.api
  1360. INSTALLS += api
  1361. ''' % (target_config.api_dir, pkg_config.qscintilla_api_file))
  1362. pro.close()
  1363. # Generate the Makefile.
  1364. _run_qmake(target_config, opts.verbose, pro_name)
  1365. ###############################################################################
  1366. # The script starts here.
  1367. ###############################################################################
  1368. if __name__ == '__main__':
  1369. # Assume the product is a package containing multiple modules. If it isn't
  1370. # then create a dummy package containing the single module.
  1371. try:
  1372. pkg_config_type = PackageConfiguration
  1373. except NameError:
  1374. pkg_config_type = type('PackageConfiguration', (object, ), {})
  1375. if not hasattr(pkg_config_type, 'modules'):
  1376. mod_config_type = ModuleConfiguration
  1377. # Extract the package-specific attributes and methods.
  1378. pkg_config_type.descriptive_name = mod_config_type.descriptive_name
  1379. pkg_config_type.legacy_configuration_script = mod_config_type.legacy_configuration_script
  1380. pkg_config_type.minimum_sip_version = mod_config_type.minimum_sip_version
  1381. pkg_config_type.protected_is_public_is_supported = mod_config_type.protected_is_public_is_supported
  1382. pkg_config_type.pyqt4_is_supported = mod_config_type.pyqt4_is_supported
  1383. pkg_config_type.pyqt5_is_supported = mod_config_type.pyqt5_is_supported
  1384. pkg_config_type.pyqt5_is_default = mod_config_type.pyqt5_is_default
  1385. pkg_config_type.qscintilla_api_file = mod_config_type.qscintilla_api_file
  1386. pkg_config_type.support_email_address = mod_config_type.support_email_address
  1387. pkg_config_type.user_configuration_file_is_supported = mod_config_type.user_configuration_file_is_supported
  1388. pkg_config_type.user_pyqt_sip_flags_is_supported = mod_config_type.user_pyqt_sip_flags_is_supported
  1389. pkg_config_type.version = mod_config_type.version
  1390. pkg_config_type.init_target_configuration = staticmethod(
  1391. mod_config_type.init_target_configuration)
  1392. pkg_config_type.init_optparser = staticmethod(
  1393. mod_config_type.init_optparser)
  1394. pkg_config_type.apply_options = staticmethod(
  1395. mod_config_type.apply_options)
  1396. pkg_config_type.inform_user = staticmethod(
  1397. mod_config_type.inform_user)
  1398. pkg_config_type.pre_code_generation = staticmethod(
  1399. mod_config_type.pre_code_generation)
  1400. pkg_config_type.get_sip_flags = staticmethod(
  1401. mod_config_type.get_sip_flags)
  1402. # Note the name change.
  1403. pkg_config_type.check_package = staticmethod(
  1404. mod_config_type.check_module)
  1405. pkg_config_type.modules = [mod_config_type()]
  1406. pkg_config = pkg_config_type()
  1407. try:
  1408. _main(sys.argv, pkg_config)
  1409. except SystemExit:
  1410. raise
  1411. except:
  1412. if pkg_config.support_email_address:
  1413. sys.stderr.write(
  1414. """An internal error occured. Please report all the output from the program,
  1415. including the following traceback, to %s.
  1416. """ % pkg_config.support_email_address)
  1417. raise