third_party:waf: update to upstream 2.0.4 release
[amitay/samba.git] / third_party / waf / waflib / extras / pyqt5.py
1 #! /usr/bin/env python
2 # encoding: utf-8
3 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file
4
5 #!/usr/bin/env python
6 # encoding: utf-8
7 # Federico Pellegrin, 2016-2018 (fedepell) adapted for Python
8
9 """
10 This tool helps with finding Python Qt5 tools and libraries,
11 and provides translation from QT5 files to Python code.
12
13 The following snippet illustrates the tool usage::
14
15         def options(opt):
16                 opt.load('py pyqt5')
17
18         def configure(conf):
19                 conf.load('py pyqt5')
20
21         def build(bld):
22                 bld(
23                         features = 'py pyqt5',
24                         source   = 'main.py textures.qrc aboutDialog.ui',
25                 )
26
27 Here, the UI description and resource files will be processed
28 to generate code.
29
30 Usage
31 =====
32
33 Load the "pyqt5" tool.
34
35 Add into the sources list also the qrc resources files or ui5
36 definition files and they will be translated into python code
37 with the system tools (PyQt5, pyside2, PyQt4 are searched in this
38 order) and then compiled
39 """
40
41 try:
42         from xml.sax import make_parser
43         from xml.sax.handler import ContentHandler
44 except ImportError:
45         has_xml = False
46         ContentHandler = object
47 else:
48         has_xml = True
49
50 import os
51 from waflib.Tools import python
52 from waflib import Task, Options
53 from waflib.TaskGen import feature, extension
54 from waflib.Configure import conf
55 from waflib import Logs
56
57 EXT_RCC = ['.qrc']
58 """
59 File extension for the resource (.qrc) files
60 """
61
62 EXT_UI  = ['.ui']
63 """
64 File extension for the user interface (.ui) files
65 """
66
67
68 class XMLHandler(ContentHandler):
69         """
70         Parses ``.qrc`` files
71         """
72         def __init__(self):
73                 self.buf = []
74                 self.files = []
75         def startElement(self, name, attrs):
76                 if name == 'file':
77                         self.buf = []
78         def endElement(self, name):
79                 if name == 'file':
80                         self.files.append(str(''.join(self.buf)))
81         def characters(self, cars):
82                 self.buf.append(cars)
83
84 @extension(*EXT_RCC)
85 def create_pyrcc_task(self, node):
86         "Creates rcc and py task for ``.qrc`` files"
87         rcnode = node.change_ext('.py')
88         self.create_task('pyrcc', node, rcnode)
89         if getattr(self, 'install_from', None):
90                 self.install_from = self.install_from.get_bld()
91         else:
92                 self.install_from = self.path.get_bld()
93         self.install_path = getattr(self, 'install_path', '${PYTHONDIR}')
94         self.process_py(rcnode)
95
96 @extension(*EXT_UI)
97 def create_pyuic_task(self, node):
98         "Create uic tasks and py for user interface ``.ui`` definition files"
99         uinode = node.change_ext('.py')
100         self.create_task('ui5py', node, uinode)
101         if getattr(self, 'install_from', None):
102                 self.install_from = self.install_from.get_bld()
103         else:
104                 self.install_from = self.path.get_bld()
105         self.install_path = getattr(self, 'install_path', '${PYTHONDIR}')
106         self.process_py(uinode)
107
108 @extension('.ts')
109 def add_pylang(self, node):
110         """Adds all the .ts file into ``self.lang``"""
111         self.lang = self.to_list(getattr(self, 'lang', [])) + [node]
112
113 @feature('pyqt5')
114 def apply_pyqt5(self):
115         """
116         The additional parameters are:
117
118         :param lang: list of translation files (\*.ts) to process
119         :type lang: list of :py:class:`waflib.Node.Node` or string without the .ts extension
120         :param langname: if given, transform the \*.ts files into a .qrc files to include in the binary file
121         :type langname: :py:class:`waflib.Node.Node` or string without the .qrc extension
122         """
123         if getattr(self, 'lang', None):
124                 qmtasks = []
125                 for x in self.to_list(self.lang):
126                         if isinstance(x, str):
127                                 x = self.path.find_resource(x + '.ts')
128                         qmtasks.append(self.create_task('ts2qm', x, x.change_ext('.qm')))
129
130
131                 if getattr(self, 'langname', None):
132                         qmnodes = [k.outputs[0] for k in qmtasks]
133                         rcnode = self.langname
134                         if isinstance(rcnode, str):
135                                 rcnode = self.path.find_or_declare(rcnode + '.qrc')
136                         t = self.create_task('qm2rcc', qmnodes, rcnode)
137                         create_pyrcc_task(self, t.outputs[0])
138
139 class pyrcc(Task.Task):
140         """
141         Processes ``.qrc`` files
142         """
143         color   = 'BLUE'
144         run_str = '${QT_PYRCC} ${SRC} -o ${TGT}'
145         ext_out = ['.py']
146
147         def rcname(self):
148                 return os.path.splitext(self.inputs[0].name)[0]
149
150         def scan(self):
151                 """Parse the *.qrc* files"""
152                 if not has_xml:
153                         Logs.error('No xml.sax support was found, rcc dependencies will be incomplete!')
154                         return ([], [])
155
156                 parser = make_parser()
157                 curHandler = XMLHandler()
158                 parser.setContentHandler(curHandler)
159                 fi = open(self.inputs[0].abspath(), 'r')
160                 try:
161                         parser.parse(fi)
162                 finally:
163                         fi.close()
164
165                 nodes = []
166                 names = []
167                 root = self.inputs[0].parent
168                 for x in curHandler.files:
169                         nd = root.find_resource(x)
170                         if nd:
171                                 nodes.append(nd)
172                         else:
173                                 names.append(x)
174                 return (nodes, names)
175
176
177 class ui5py(Task.Task):
178         """
179         Processes ``.ui`` files for python
180         """
181         color   = 'BLUE'
182         run_str = '${QT_PYUIC} ${SRC} -o ${TGT}'
183         ext_out = ['.py']
184
185 class ts2qm(Task.Task):
186         """
187         Generates ``.qm`` files from ``.ts`` files
188         """
189         color   = 'BLUE'
190         run_str = '${QT_LRELEASE} ${QT_LRELEASE_FLAGS} ${SRC} -qm ${TGT}'
191
192 class qm2rcc(Task.Task):
193         """
194         Generates ``.qrc`` files from ``.qm`` files
195         """
196         color = 'BLUE'
197         after = 'ts2qm'
198         def run(self):
199                 """Create a qrc file including the inputs"""
200                 txt = '\n'.join(['<file>%s</file>' % k.path_from(self.outputs[0].parent) for k in self.inputs])
201                 code = '<!DOCTYPE RCC><RCC version="1.0">\n<qresource>\n%s\n</qresource>\n</RCC>' % txt
202                 self.outputs[0].write(code)
203
204 def configure(self):
205         self.find_pyqt5_binaries()
206
207         # warn about this during the configuration too
208         if not has_xml:
209                 Logs.error('No xml.sax support was found, rcc dependencies will be incomplete!')
210
211 @conf
212 def find_pyqt5_binaries(self):
213         """
214         Detects PyQt5 or pyside2 programs such as pyuic5/pyside2-uic, pyrcc5/pyside2-rcc
215         """
216         env = self.env
217
218         if getattr(Options.options, 'want_pyside2', True):
219                 self.find_program(['pyside2-uic'], var='QT_PYUIC')
220                 self.find_program(['pyside2-rcc'], var='QT_PYRCC')
221                 self.find_program(['pyside2-lupdate'], var='QT_PYLUPDATE')
222         elif getattr(Options.options, 'want_pyqt4', True):
223                 self.find_program(['pyuic4'], var='QT_PYUIC')
224                 self.find_program(['pyrcc4'], var='QT_PYRCC')
225                 self.find_program(['pylupdate4'], var='QT_PYLUPDATE')
226         else:
227                 self.find_program(['pyuic5','pyside2-uic','pyuic4'], var='QT_PYUIC')
228                 self.find_program(['pyrcc5','pyside2-rcc','pyrcc4'], var='QT_PYRCC')
229                 self.find_program(['pylupdate5', 'pyside2-lupdate','pylupdate4'], var='QT_PYLUPDATE')
230
231         if not env.QT_PYUIC:
232                 self.fatal('cannot find the uic compiler for python for qt5')
233
234         if not env.QT_PYUIC:
235                 self.fatal('cannot find the rcc compiler for python for qt5')
236
237         self.find_program(['lrelease-qt5', 'lrelease'], var='QT_LRELEASE')
238
239 def options(opt):
240         """
241         Command-line options
242         """
243         pyqt5opt=opt.add_option_group("Python QT5 Options")
244         pyqt5opt.add_option('--pyqt5-pyside2', action='store_true', default=False, dest='want_pyside2', help='use pyside2 bindings as python QT5 bindings (default PyQt5 is searched first, PySide2 after)')
245         pyqt5opt.add_option('--pyqt5-pyqt4', action='store_true', default=False, dest='want_pyqt4', help='use PyQt4 bindings as python QT5 bindings (default PyQt5 is searched first, PySide2 after, PyQt4 last)')