s4:dsdb/acl_read: fix the calculation of the attribute array for the sub search
[Samba/gebeck_regimport.git] / buildtools / wafadmin / Tools / qt4.py
blob84d121a844f16439ed8c691c01e851efe371323c
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # Thomas Nagy, 2006 (ita)
5 """
6 Qt4 support
8 If QT4_ROOT is given (absolute path), the configuration will look in it first
10 This module also demonstrates how to add tasks dynamically (when the build has started)
11 """
13 try:
14 from xml.sax import make_parser
15 from xml.sax.handler import ContentHandler
16 except ImportError:
17 has_xml = False
18 ContentHandler = object
19 else:
20 has_xml = True
22 import os, sys
23 import ccroot, cxx
24 import TaskGen, Task, Utils, Runner, Options, Node, Configure
25 from TaskGen import taskgen, feature, after, extension
26 from Logs import error
27 from Constants import *
29 MOC_H = ['.h', '.hpp', '.hxx', '.hh']
30 EXT_RCC = ['.qrc']
31 EXT_UI = ['.ui']
32 EXT_QT4 = ['.cpp', '.cc', '.cxx', '.C']
34 class qxx_task(Task.Task):
35 "A cpp task that may create a moc task dynamically"
37 before = ['cxx_link', 'static_link']
39 def __init__(self, *k, **kw):
40 Task.Task.__init__(self, *k, **kw)
41 self.moc_done = 0
43 def scan(self):
44 (nodes, names) = ccroot.scan(self)
45 # for some reasons (variants) the moc node may end in the list of node deps
46 for x in nodes:
47 if x.name.endswith('.moc'):
48 nodes.remove(x)
49 names.append(x.relpath_gen(self.inputs[0].parent))
50 return (nodes, names)
52 def runnable_status(self):
53 if self.moc_done:
54 # if there is a moc task, delay the computation of the file signature
55 for t in self.run_after:
56 if not t.hasrun:
57 return ASK_LATER
58 # the moc file enters in the dependency calculation
59 # so we need to recompute the signature when the moc file is present
60 self.signature()
61 return Task.Task.runnable_status(self)
62 else:
63 # yes, really, there are people who generate cxx files
64 for t in self.run_after:
65 if not t.hasrun:
66 return ASK_LATER
67 self.add_moc_tasks()
68 return ASK_LATER
70 def add_moc_tasks(self):
72 node = self.inputs[0]
73 tree = node.__class__.bld
75 try:
76 # compute the signature once to know if there is a moc file to create
77 self.signature()
78 except KeyError:
79 # the moc file may be referenced somewhere else
80 pass
81 else:
82 # remove the signature, it must be recomputed with the moc task
83 delattr(self, 'cache_sig')
85 moctasks=[]
86 mocfiles=[]
87 variant = node.variant(self.env)
88 try:
89 tmp_lst = tree.raw_deps[self.unique_id()]
90 tree.raw_deps[self.unique_id()] = []
91 except KeyError:
92 tmp_lst = []
93 for d in tmp_lst:
94 if not d.endswith('.moc'): continue
95 # paranoid check
96 if d in mocfiles:
97 error("paranoia owns")
98 continue
100 # process that base.moc only once
101 mocfiles.append(d)
103 # find the extension (performed only when the .cpp has changes)
104 base2 = d[:-4]
105 for path in [node.parent] + self.generator.env['INC_PATHS']:
106 tree.rescan(path)
107 vals = getattr(Options.options, 'qt_header_ext', '') or MOC_H
108 for ex in vals:
109 h_node = path.find_resource(base2 + ex)
110 if h_node:
111 break
112 else:
113 continue
114 break
115 else:
116 raise Utils.WafError("no header found for %s which is a moc file" % str(d))
118 m_node = h_node.change_ext('.moc')
119 tree.node_deps[(self.inputs[0].parent.id, self.env.variant(), m_node.name)] = h_node
121 # create the task
122 task = Task.TaskBase.classes['moc'](self.env, normal=0)
123 task.set_inputs(h_node)
124 task.set_outputs(m_node)
126 generator = tree.generator
127 generator.outstanding.insert(0, task)
128 generator.total += 1
130 moctasks.append(task)
132 # remove raw deps except the moc files to save space (optimization)
133 tmp_lst = tree.raw_deps[self.unique_id()] = mocfiles
135 # look at the file inputs, it is set right above
136 lst = tree.node_deps.get(self.unique_id(), ())
137 for d in lst:
138 name = d.name
139 if name.endswith('.moc'):
140 task = Task.TaskBase.classes['moc'](self.env, normal=0)
141 task.set_inputs(tree.node_deps[(self.inputs[0].parent.id, self.env.variant(), name)]) # 1st element in a tuple
142 task.set_outputs(d)
144 generator = tree.generator
145 generator.outstanding.insert(0, task)
146 generator.total += 1
148 moctasks.append(task)
150 # simple scheduler dependency: run the moc task before others
151 self.run_after = moctasks
152 self.moc_done = 1
154 run = Task.TaskBase.classes['cxx'].__dict__['run']
156 def translation_update(task):
157 outs = [a.abspath(task.env) for a in task.outputs]
158 outs = " ".join(outs)
159 lupdate = task.env['QT_LUPDATE']
161 for x in task.inputs:
162 file = x.abspath(task.env)
163 cmd = "%s %s -ts %s" % (lupdate, file, outs)
164 Utils.pprint('BLUE', cmd)
165 task.generator.bld.exec_command(cmd)
167 class XMLHandler(ContentHandler):
168 def __init__(self):
169 self.buf = []
170 self.files = []
171 def startElement(self, name, attrs):
172 if name == 'file':
173 self.buf = []
174 def endElement(self, name):
175 if name == 'file':
176 self.files.append(''.join(self.buf))
177 def characters(self, cars):
178 self.buf.append(cars)
180 def scan(self):
181 "add the dependency on the files referenced in the qrc"
182 node = self.inputs[0]
183 parser = make_parser()
184 curHandler = XMLHandler()
185 parser.setContentHandler(curHandler)
186 fi = open(self.inputs[0].abspath(self.env))
187 parser.parse(fi)
188 fi.close()
190 nodes = []
191 names = []
192 root = self.inputs[0].parent
193 for x in curHandler.files:
194 nd = root.find_resource(x)
195 if nd: nodes.append(nd)
196 else: names.append(x)
198 return (nodes, names)
200 @extension(EXT_RCC)
201 def create_rcc_task(self, node):
202 "hook for rcc files"
203 rcnode = node.change_ext('_rc.cpp')
204 rcctask = self.create_task('rcc', node, rcnode)
205 cpptask = self.create_task('cxx', rcnode, rcnode.change_ext('.o'))
206 self.compiled_tasks.append(cpptask)
207 return cpptask
209 @extension(EXT_UI)
210 def create_uic_task(self, node):
211 "hook for uic tasks"
212 uictask = self.create_task('ui4', node)
213 uictask.outputs = [self.path.find_or_declare(self.env['ui_PATTERN'] % node.name[:-3])]
214 return uictask
216 class qt4_taskgen(cxx.cxx_taskgen):
217 def __init__(self, *k, **kw):
218 cxx.cxx_taskgen.__init__(self, *k, **kw)
219 self.features.append('qt4')
221 @extension('.ts')
222 def add_lang(self, node):
223 """add all the .ts file into self.lang"""
224 self.lang = self.to_list(getattr(self, 'lang', [])) + [node]
226 @feature('qt4')
227 @after('apply_link')
228 def apply_qt4(self):
229 if getattr(self, 'lang', None):
230 update = getattr(self, 'update', None)
231 lst=[]
232 trans=[]
233 for l in self.to_list(self.lang):
235 if not isinstance(l, Node.Node):
236 l = self.path.find_resource(l+'.ts')
238 t = self.create_task('ts2qm', l, l.change_ext('.qm'))
239 lst.append(t.outputs[0])
241 if update:
242 trans.append(t.inputs[0])
244 trans_qt4 = getattr(Options.options, 'trans_qt4', False)
245 if update and trans_qt4:
246 # we need the cpp files given, except the rcc task we create after
247 # FIXME may be broken
248 u = Task.TaskCmd(translation_update, self.env, 2)
249 u.inputs = [a.inputs[0] for a in self.compiled_tasks]
250 u.outputs = trans
252 if getattr(self, 'langname', None):
253 t = Task.TaskBase.classes['qm2rcc'](self.env)
254 t.set_inputs(lst)
255 t.set_outputs(self.path.find_or_declare(self.langname+'.qrc'))
256 t.path = self.path
257 k = create_rcc_task(self, t.outputs[0])
258 self.link_task.inputs.append(k.outputs[0])
260 self.env.append_value('MOC_FLAGS', self.env._CXXDEFFLAGS)
261 self.env.append_value('MOC_FLAGS', self.env._CXXINCFLAGS)
263 @extension(EXT_QT4)
264 def cxx_hook(self, node):
265 # create the compilation task: cpp or cc
266 try: obj_ext = self.obj_ext
267 except AttributeError: obj_ext = '_%d.o' % self.idx
269 task = self.create_task('qxx', node, node.change_ext(obj_ext))
270 self.compiled_tasks.append(task)
271 return task
273 def process_qm2rcc(task):
274 outfile = task.outputs[0].abspath(task.env)
275 f = open(outfile, 'w')
276 f.write('<!DOCTYPE RCC><RCC version="1.0">\n<qresource>\n')
277 for k in task.inputs:
278 f.write(' <file>')
279 #f.write(k.name)
280 f.write(k.path_to_parent(task.path))
281 f.write('</file>\n')
282 f.write('</qresource>\n</RCC>')
283 f.close()
285 b = Task.simple_task_type
286 b('moc', '${QT_MOC} ${MOC_FLAGS} ${SRC} ${MOC_ST} ${TGT}', color='BLUE', vars=['QT_MOC', 'MOC_FLAGS'], shell=False)
287 cls = b('rcc', '${QT_RCC} -name ${SRC[0].name} ${SRC[0].abspath(env)} ${RCC_ST} -o ${TGT}', color='BLUE', before='cxx moc qxx_task', after="qm2rcc", shell=False)
288 cls.scan = scan
289 b('ui4', '${QT_UIC} ${SRC} -o ${TGT}', color='BLUE', before='cxx moc qxx_task', shell=False)
290 b('ts2qm', '${QT_LRELEASE} ${QT_LRELEASE_FLAGS} ${SRC} -qm ${TGT}', color='BLUE', before='qm2rcc', shell=False)
292 Task.task_type_from_func('qm2rcc', vars=[], func=process_qm2rcc, color='BLUE', before='rcc', after='ts2qm')
294 def detect_qt4(conf):
295 env = conf.env
296 opt = Options.options
298 qtdir = getattr(opt, 'qtdir', '')
299 qtbin = getattr(opt, 'qtbin', '')
300 qtlibs = getattr(opt, 'qtlibs', '')
301 useframework = getattr(opt, 'use_qt4_osxframework', True)
303 paths = []
305 # the path to qmake has been given explicitely
306 if qtbin:
307 paths = [qtbin]
309 # the qt directory has been given - we deduce the qt binary path
310 if not qtdir:
311 qtdir = conf.environ.get('QT4_ROOT', '')
312 qtbin = os.path.join(qtdir, 'bin')
313 paths = [qtbin]
315 # no qtdir, look in the path and in /usr/local/Trolltech
316 if not qtdir:
317 paths = os.environ.get('PATH', '').split(os.pathsep)
318 paths.append('/usr/share/qt4/bin/')
319 try:
320 lst = os.listdir('/usr/local/Trolltech/')
321 except OSError:
322 pass
323 else:
324 if lst:
325 lst.sort()
326 lst.reverse()
328 # keep the highest version
329 qtdir = '/usr/local/Trolltech/%s/' % lst[0]
330 qtbin = os.path.join(qtdir, 'bin')
331 paths.append(qtbin)
333 # at the end, try to find qmake in the paths given
334 # keep the one with the highest version
335 cand = None
336 prev_ver = ['4', '0', '0']
337 for qmk in ['qmake-qt4', 'qmake4', 'qmake']:
338 qmake = conf.find_program(qmk, path_list=paths)
339 if qmake:
340 try:
341 version = Utils.cmd_output([qmake, '-query', 'QT_VERSION']).strip()
342 except ValueError:
343 pass
344 else:
345 if version:
346 new_ver = version.split('.')
347 if new_ver > prev_ver:
348 cand = qmake
349 prev_ver = new_ver
350 if cand:
351 qmake = cand
352 else:
353 conf.fatal('could not find qmake for qt4')
355 conf.env.QMAKE = qmake
356 qtincludes = Utils.cmd_output([qmake, '-query', 'QT_INSTALL_HEADERS']).strip()
357 qtdir = Utils.cmd_output([qmake, '-query', 'QT_INSTALL_PREFIX']).strip() + os.sep
358 qtbin = Utils.cmd_output([qmake, '-query', 'QT_INSTALL_BINS']).strip() + os.sep
360 if not qtlibs:
361 try:
362 qtlibs = Utils.cmd_output([qmake, '-query', 'QT_INSTALL_LIBS']).strip() + os.sep
363 except ValueError:
364 qtlibs = os.path.join(qtdir, 'lib')
366 def find_bin(lst, var):
367 for f in lst:
368 ret = conf.find_program(f, path_list=paths)
369 if ret:
370 env[var]=ret
371 break
373 vars = "QtCore QtGui QtUiTools QtNetwork QtOpenGL QtSql QtSvg QtTest QtXml QtWebKit Qt3Support".split()
375 find_bin(['uic-qt3', 'uic3'], 'QT_UIC3')
376 find_bin(['uic-qt4', 'uic'], 'QT_UIC')
377 if not env['QT_UIC']:
378 conf.fatal('cannot find the uic compiler for qt4')
380 try:
381 version = Utils.cmd_output(env['QT_UIC'] + " -version 2>&1").strip()
382 except ValueError:
383 conf.fatal('your uic compiler is for qt3, add uic for qt4 to your path')
385 version = version.replace('Qt User Interface Compiler ','')
386 version = version.replace('User Interface Compiler for Qt', '')
387 if version.find(" 3.") != -1:
388 conf.check_message('uic version', '(too old)', 0, option='(%s)'%version)
389 sys.exit(1)
390 conf.check_message('uic version', '', 1, option='(%s)'%version)
392 find_bin(['moc-qt4', 'moc'], 'QT_MOC')
393 find_bin(['rcc'], 'QT_RCC')
394 find_bin(['lrelease-qt4', 'lrelease'], 'QT_LRELEASE')
395 find_bin(['lupdate-qt4', 'lupdate'], 'QT_LUPDATE')
397 env['UIC3_ST']= '%s -o %s'
398 env['UIC_ST'] = '%s -o %s'
399 env['MOC_ST'] = '-o'
400 env['ui_PATTERN'] = 'ui_%s.h'
401 env['QT_LRELEASE_FLAGS'] = ['-silent']
403 vars_debug = [a+'_debug' for a in vars]
405 try:
406 conf.find_program('pkg-config', var='pkgconfig', path_list=paths, mandatory=True)
408 except Configure.ConfigurationError:
410 for lib in vars_debug+vars:
411 uselib = lib.upper()
413 d = (lib.find('_debug') > 0) and 'd' or ''
415 # original author seems to prefer static to shared libraries
416 for (pat, kind) in ((conf.env.staticlib_PATTERN, 'STATIC'), (conf.env.shlib_PATTERN, '')):
418 conf.check_message_1('Checking for %s %s' % (lib, kind))
420 for ext in ['', '4']:
421 path = os.path.join(qtlibs, pat % (lib + d + ext))
422 if os.path.exists(path):
423 env.append_unique(kind + 'LIB_' + uselib, lib + d + ext)
424 conf.check_message_2('ok ' + path, 'GREEN')
425 break
426 path = os.path.join(qtbin, pat % (lib + d + ext))
427 if os.path.exists(path):
428 env.append_unique(kind + 'LIB_' + uselib, lib + d + ext)
429 conf.check_message_2('ok ' + path, 'GREEN')
430 break
431 else:
432 conf.check_message_2('not found', 'YELLOW')
433 continue
434 break
436 env.append_unique('LIBPATH_' + uselib, qtlibs)
437 env.append_unique('CPPPATH_' + uselib, qtincludes)
438 env.append_unique('CPPPATH_' + uselib, qtincludes + os.sep + lib)
439 else:
440 for i in vars_debug+vars:
441 try:
442 conf.check_cfg(package=i, args='--cflags --libs --silence-errors', path=conf.env.pkgconfig)
443 except ValueError:
444 pass
446 # the libpaths are set nicely, unfortunately they make really long command-lines
447 # remove the qtcore ones from qtgui, etc
448 def process_lib(vars_, coreval):
449 for d in vars_:
450 var = d.upper()
451 if var == 'QTCORE': continue
453 value = env['LIBPATH_'+var]
454 if value:
455 core = env[coreval]
456 accu = []
457 for lib in value:
458 if lib in core: continue
459 accu.append(lib)
460 env['LIBPATH_'+var] = accu
462 process_lib(vars, 'LIBPATH_QTCORE')
463 process_lib(vars_debug, 'LIBPATH_QTCORE_DEBUG')
465 # rpath if wanted
466 want_rpath = getattr(Options.options, 'want_rpath', 1)
467 if want_rpath:
468 def process_rpath(vars_, coreval):
469 for d in vars_:
470 var = d.upper()
471 value = env['LIBPATH_'+var]
472 if value:
473 core = env[coreval]
474 accu = []
475 for lib in value:
476 if var != 'QTCORE':
477 if lib in core:
478 continue
479 accu.append('-Wl,--rpath='+lib)
480 env['RPATH_'+var] = accu
481 process_rpath(vars, 'LIBPATH_QTCORE')
482 process_rpath(vars_debug, 'LIBPATH_QTCORE_DEBUG')
484 env['QTLOCALE'] = str(env['PREFIX'])+'/share/locale'
486 def detect(conf):
487 detect_qt4(conf)
489 def set_options(opt):
490 opt.add_option('--want-rpath', type='int', default=1, dest='want_rpath', help='set rpath to 1 or 0 [Default 1]')
492 opt.add_option('--header-ext',
493 type='string',
494 default='',
495 help='header extension for moc files',
496 dest='qt_header_ext')
498 for i in 'qtdir qtbin qtlibs'.split():
499 opt.add_option('--'+i, type='string', default='', dest=i)
501 if sys.platform == "darwin":
502 opt.add_option('--no-qt4-framework', action="store_false", help='do not use the framework version of Qt4 in OS X', dest='use_qt4_osxframework',default=True)
504 opt.add_option('--translate', action="store_true", help="collect translation strings", dest="trans_qt4", default=False)