configure-old.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  1. # This script configures QScintilla for PyQt v3 and/or v4.
  2. #
  3. # Copyright (c) 2017 Riverbank Computing Limited <info@riverbankcomputing.com>
  4. #
  5. # This file is part of QScintilla.
  6. #
  7. # This file may be used under the terms of the GNU General Public License
  8. # version 3.0 as published by the Free Software Foundation and appearing in
  9. # the file LICENSE included in the packaging of this file. Please review the
  10. # following information to ensure the GNU General Public License version 3.0
  11. # requirements will be met: http://www.gnu.org/copyleft/gpl.html.
  12. #
  13. # If you do not wish to use this file under the terms of the GPL version 3.0
  14. # then you may purchase a commercial license. For more information contact
  15. # info@riverbankcomputing.com.
  16. #
  17. # This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
  18. # WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  19. import sys
  20. import os
  21. import glob
  22. import optparse
  23. # Import SIP's configuration module so that we have access to the error
  24. # reporting. Then try and import the configuration modules for both PyQt3 and
  25. # PyQt4.
  26. try:
  27. import sipconfig
  28. except ImportError:
  29. sys.stderr.write("Unable to import sipconfig. Please make sure SIP is installed.\n")
  30. sys.exit(1)
  31. try:
  32. import PyQt4.pyqtconfig as pyqt4
  33. except:
  34. pyqt4 = None
  35. try:
  36. import pyqtconfig as pyqt3
  37. except:
  38. pyqt3 = None
  39. if pyqt4 is not None:
  40. pyqt = pyqt4.Configuration()
  41. qt_data_dir = pyqt.qt_data_dir
  42. elif pyqt3 is not None:
  43. pyqt = pyqt3.Configuration()
  44. qt_data_dir = pyqt.qt_dir
  45. else:
  46. sipconfig.error("Unable to find either PyQt v3 or v4.")
  47. # This must be kept in sync with Python/configure.py, qscintilla.pro,
  48. # example-Qt4Qt5/application.pro and designer-Qt4Qt5/designer.pro.
  49. QSCI_API_MAJOR = 13
  50. # Initialise the globals.
  51. sip_min_version = 0x040c00
  52. qsci_define = "QSCINTILLA_DLL"
  53. def create_optparser():
  54. """Create the parser for the command line.
  55. """
  56. def store_abspath(option, opt_str, value, parser):
  57. setattr(parser.values, option.dest, os.path.abspath(value))
  58. def store_abspath_dir(option, opt_str, value, parser):
  59. if not os.path.isdir(value):
  60. raise optparse.OptionValueError("'%s' is not a directory" % value)
  61. setattr(parser.values, option.dest, os.path.abspath(value))
  62. p = optparse.OptionParser(usage="python %prog [options]",
  63. version="2.10.1")
  64. p.add_option("-a", "--apidir", action="callback", default=None,
  65. type="string", metavar="DIR", dest="qscidir",
  66. callback=store_abspath, help="where QScintilla's API file will be "
  67. "installed [default: QTDIR/qsci]")
  68. p.add_option("-c", "--concatenate", action="store_true", default=False,
  69. dest="concat", help="concatenate the C++ source files")
  70. p.add_option("-d", "--destdir", action="callback",
  71. default=pyqt.pyqt_mod_dir, type="string", metavar="DIR",
  72. dest="qscimoddir", callback=store_abspath, help="where the "
  73. "QScintilla module will be installed [default: %s]" %
  74. pyqt.pyqt_mod_dir)
  75. p.add_option("-j", "--concatenate-split", type="int", default=1,
  76. metavar="N", dest="split", help="split the concatenated C++ "
  77. "source files into N pieces [default: 1]")
  78. p.add_option("-k", "--static", action="store_true", default=False,
  79. dest="static", help="build the QScintilla module as a static "
  80. "library")
  81. p.add_option("-n", action="callback", default=None, type="string",
  82. metavar="DIR", dest="qsciincdir", callback=store_abspath_dir,
  83. help="the directory containing the QScintilla Qsci header file "
  84. "directory [default: %s]" % pyqt.qt_inc_dir)
  85. p.add_option("--no-docstrings", action="store_true", default=False,
  86. dest="no_docstrings", help="disable the generation of docstrings")
  87. p.add_option("-o", action="callback", default=None, type="string",
  88. metavar="DIR", dest="qscilibdir", callback=store_abspath_dir,
  89. help="the directory containing the QScintilla library [default: "
  90. "%s]" % pyqt.qt_lib_dir)
  91. p.add_option("-p", type="int", default=-1, metavar="3|4", dest="pyqt_major",
  92. help="specifically configure for PyQt v3 or v4 [default v4, if "
  93. "found]")
  94. p.add_option("-r", "--trace", action="store_true", default=False,
  95. dest="tracing", help="build the QScintilla module with tracing "
  96. "enabled")
  97. p.add_option("-s", action="store_true", default=False, dest="not_dll",
  98. help="QScintilla is a static library and not a DLL (Windows only)")
  99. p.add_option("-u", "--debug", action="store_true", default=False,
  100. help="build the QScintilla module with debugging symbols")
  101. p.add_option("-v", "--sipdir", action="callback", default=None,
  102. metavar="DIR", dest="qscisipdir", callback=store_abspath,
  103. type="string", help="where the QScintilla .sip files will be "
  104. "installed [default: %s]" % pyqt.pyqt_sip_dir)
  105. p.add_option("-T", "--no-timestamp", action="store_true", default=False,
  106. dest="no_timestamp", help="suppress timestamps in the header "
  107. "comments of generated code [default: include timestamps]")
  108. if sys.platform != 'win32':
  109. if sys.platform.startswith('linux') or sys.platform == 'darwin':
  110. pip_default = True
  111. pip_default_str = "enabled"
  112. else:
  113. pip_default = False
  114. pip_default_str = "disabled"
  115. p.add_option("--protected-is-public", action="store_true",
  116. default=pip_default, dest="prot_is_public",
  117. help="enable building with 'protected' redefined as 'public' "
  118. "[default: %s]" % pip_default_str)
  119. p.add_option("--protected-not-public", action="store_false",
  120. dest="prot_is_public",
  121. help="disable building with 'protected' redefined as 'public'")
  122. return p
  123. def inform_user():
  124. """Tell the user the option values that are going to be used.
  125. """
  126. sipconfig.inform("PyQt %s is being used." % pyqt.pyqt_version_str)
  127. sipconfig.inform("Qt v%s %s edition is being used." % (sipconfig.version_to_string(pyqt.qt_version), pyqt.qt_edition))
  128. sipconfig.inform("SIP %s is being used." % pyqt.sip_version_str)
  129. sipconfig.inform("The QScintilla module will be installed in %s." % opts.qscimoddir)
  130. sipconfig.inform("The QScintilla API file will be installed in %s." % os.path.join(opts.qscidir, "api", "python"))
  131. sipconfig.inform("The QScintilla .sip files will be installed in %s." % opts.qscisipdir)
  132. if opts.no_docstrings:
  133. sipconfig.inform("The QScintilla module is being built without generated docstrings.")
  134. else:
  135. sipconfig.inform("The QScintilla module is being built with generated docstrings.")
  136. if opts.prot_is_public:
  137. sipconfig.inform("The QScintilla module is being built with 'protected' redefined as 'public'.")
  138. def check_qscintilla():
  139. """See if QScintilla can be found and what its version is.
  140. """
  141. # Find the QScintilla header files.
  142. sciglobal = os.path.join(opts.qsciincdir, "Qsci", "qsciglobal.h")
  143. if os.access(sciglobal, os.F_OK):
  144. # Get the QScintilla version string.
  145. _, sciversstr = sipconfig.read_version(sciglobal, "QScintilla", "QSCINTILLA_VERSION", "QSCINTILLA_VERSION_STR")
  146. if glob.glob(os.path.join(opts.qscilibdir, "*qscintilla2*")):
  147. # Because we include the Python bindings with the C++ code we can
  148. # reasonably force the same version to be used and not bother about
  149. # versioning.
  150. if sciversstr != "2.10.1":
  151. sipconfig.error("QScintilla %s is being used but the Python bindings 2.10.1 are being built. Please use matching versions." % sciversstr)
  152. sipconfig.inform("QScintilla %s is being used." % sciversstr)
  153. else:
  154. sipconfig.error("The QScintilla library could not be found in %s. If QScintilla is installed then use the -o argument to explicitly specify the correct directory." % opts.qscilibdir)
  155. else:
  156. sipconfig.error("Qsci/qsciglobal.h could not be found in %s. If QScintilla is installed then use the -n argument to explicitly specify the correct directory." % opts.qsciincdir)
  157. def sip_flags():
  158. """Return the SIP flags.
  159. """
  160. # Get the flags used for the main PyQt module.
  161. if pyqt.pyqt_version >= 0x040000:
  162. flags = pyqt.pyqt_sip_flags.split()
  163. else:
  164. flags = pyqt.pyqt_qt_sip_flags.split()
  165. flags.append("-x")
  166. flags.append("Qsci_Qt4")
  167. # Generate the API file.
  168. flags.append("-a")
  169. flags.append("QScintilla2.api")
  170. # Add PyQt's .sip files to the search path.
  171. flags.append("-I")
  172. flags.append(pyqt.pyqt_sip_dir)
  173. return flags
  174. def generate_code():
  175. """Generate the code for the QScintilla module.
  176. """
  177. if pyqt.pyqt_version >= 0x040000:
  178. mname = "Qsci"
  179. else:
  180. mname = "qsci"
  181. sipconfig.inform("Generating the C++ source for the %s module..." % mname)
  182. # Build the SIP command line.
  183. argv = ['"' + pyqt.sip_bin + '"']
  184. argv.extend(sip_flags())
  185. if opts.no_timestamp:
  186. argv.append("-T")
  187. if not opts.no_docstrings:
  188. argv.append("-o");
  189. if opts.prot_is_public:
  190. argv.append("-P");
  191. if opts.concat:
  192. argv.append("-j")
  193. argv.append(str(opts.split))
  194. if opts.tracing:
  195. argv.append("-r")
  196. argv.append("-c")
  197. argv.append(".")
  198. buildfile = os.path.join("qsci.sbf")
  199. argv.append("-b")
  200. argv.append(buildfile)
  201. if pyqt.pyqt_version >= 0x040000:
  202. argv.append("sip/qscimod4.sip")
  203. else:
  204. argv.append("sip/qscimod3.sip")
  205. os.system(" ".join(argv))
  206. # Check the result.
  207. if not os.access(buildfile, os.F_OK):
  208. sipconfig.error("Unable to create the C++ code.")
  209. # Generate the Makefile.
  210. sipconfig.inform("Creating the Makefile for the %s module..." % mname)
  211. def fix_install(mfile):
  212. if sys.platform != "darwin" or opts.static:
  213. return
  214. mfile.write("\tinstall_name_tool -change libqscintilla2.%u.dylib %s/libqscintilla2.%u.dylib $(DESTDIR)%s/$(TARGET)\n" % (QSCI_API_MAJOR, opts.qscilibdir, QSCI_API_MAJOR, opts.qscimoddir))
  215. if pyqt.pyqt_version >= 0x040000:
  216. class Makefile(pyqt4.QtGuiModuleMakefile):
  217. def generate_target_install(self, mfile):
  218. pyqt4.QtGuiModuleMakefile.generate_target_install(self, mfile)
  219. fix_install(mfile)
  220. else:
  221. class Makefile(pyqt3.QtModuleMakefile):
  222. def generate_target_install(self, mfile):
  223. pyqt3.QtModuleMakefile.generate_target_install(self, mfile)
  224. fix_install(mfile)
  225. installs = []
  226. sipfiles = []
  227. for s in glob.glob("sip/*.sip"):
  228. sipfiles.append(os.path.join("sip", os.path.basename(s)))
  229. installs.append([sipfiles, os.path.join(opts.qscisipdir, mname)])
  230. installs.append(("QScintilla2.api", os.path.join(opts.qscidir, "api", "python")))
  231. # PyQt v4.2 and later can handle MacOS/X universal binaries.
  232. if pyqt.pyqt_version >= 0x040200:
  233. makefile = Makefile(
  234. configuration=pyqt,
  235. build_file="qsci.sbf",
  236. install_dir=opts.qscimoddir,
  237. installs=installs,
  238. static=opts.static,
  239. debug=opts.debug,
  240. universal=pyqt.universal,
  241. arch=pyqt.arch,
  242. prot_is_public=opts.prot_is_public,
  243. deployment_target=pyqt.deployment_target
  244. )
  245. else:
  246. makefile = Makefile(
  247. configuration=pyqt,
  248. build_file="qsci.sbf",
  249. install_dir=opts.qscimoddir,
  250. installs=installs,
  251. static=opts.static,
  252. debug=opts.debug
  253. )
  254. if qsci_define:
  255. makefile.extra_defines.append(qsci_define)
  256. makefile.extra_include_dirs.append(opts.qsciincdir)
  257. makefile.extra_lib_dirs.append(opts.qscilibdir)
  258. makefile.extra_libs.append("qscintilla2")
  259. makefile.generate()
  260. def main(argv):
  261. """Create the configuration module module.
  262. argv is the list of command line arguments.
  263. """
  264. global pyqt
  265. # Check SIP is new enough.
  266. if ".dev" not in pyqt.sip_version_str and "snapshot" not in pyqt.sip_version_str:
  267. if pyqt.sip_version < sip_min_version:
  268. sipconfig.error("This version of QScintilla requires SIP v%s or later" % sipconfig.version_to_string(sip_min_version))
  269. # Parse the command line.
  270. global opts
  271. p = create_optparser()
  272. opts, args = p.parse_args()
  273. if args:
  274. p.print_help()
  275. sys.exit(2)
  276. # Provide defaults for platform-specific options.
  277. if sys.platform == 'win32':
  278. opts.prot_is_public = False
  279. if opts.not_dll:
  280. global qsci_define
  281. qsci_define = ""
  282. # Set the version of PyQt explicitly.
  283. global qt_data_dir
  284. if opts.pyqt_major == 4:
  285. if pyqt4 is None:
  286. sipconfig.error("PyQt v4 was specified with the -p argument but doesn't seem to be installed.")
  287. else:
  288. pyqt = pyqt4.Configuration()
  289. qt_data_dir = pyqt.qt_data_dir
  290. elif opts.pyqt_major == 3:
  291. if pyqt3 is None:
  292. sipconfig.error("PyQt v3 was specified with the -p argument but doesn't seem to be installed.")
  293. else:
  294. pyqt = pyqt3.Configuration()
  295. qt_data_dir = pyqt.qt_dir
  296. elif opts.pyqt_major >= 0:
  297. sipconfig.error("Specify either 3 or 4 with the -p argument.")
  298. # Now we know which version of PyQt to use we can set defaults for those
  299. # arguments that weren't specified.
  300. if opts.qscimoddir is None:
  301. opts.qscimoddir = pyqt.pyqt_mod_dir
  302. if opts.qsciincdir is None:
  303. opts.qsciincdir = pyqt.qt_inc_dir
  304. if opts.qscilibdir is None:
  305. opts.qscilibdir = pyqt.qt_lib_dir
  306. if opts.qscisipdir is None:
  307. opts.qscisipdir = pyqt.pyqt_sip_dir
  308. if opts.qscidir is None:
  309. opts.qscidir = os.path.join(qt_data_dir, "qsci")
  310. # Check for QScintilla.
  311. check_qscintilla()
  312. # Tell the user what's been found.
  313. inform_user()
  314. # Generate the code.
  315. generate_code()
  316. ###############################################################################
  317. # The script starts here.
  318. ###############################################################################
  319. if __name__ == "__main__":
  320. try:
  321. main(sys.argv)
  322. except SystemExit:
  323. raise
  324. except:
  325. sys.stderr.write(
  326. """An internal error occured. Please report all the output from the program,
  327. including the following traceback, to support@riverbankcomputing.com.
  328. """)
  329. raise