From 4f4bce5301ffd8c12aed1b108affa1a75feefb67 Mon Sep 17 00:00:00 2001 From: Jelmer Vernooij Date: Wed, 4 Jan 2012 00:31:27 +0100 Subject: Include waf as an extracted source directory, rather than as a one-in-a-file script. --- buildtools/wafadmin/Tools/qt4.py | 505 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 505 insertions(+) create mode 100644 buildtools/wafadmin/Tools/qt4.py (limited to 'buildtools/wafadmin/Tools/qt4.py') diff --git a/buildtools/wafadmin/Tools/qt4.py b/buildtools/wafadmin/Tools/qt4.py new file mode 100644 index 0000000000..84d121a844 --- /dev/null +++ b/buildtools/wafadmin/Tools/qt4.py @@ -0,0 +1,505 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2006 (ita) + +""" +Qt4 support + +If QT4_ROOT is given (absolute path), the configuration will look in it first + +This module also demonstrates how to add tasks dynamically (when the build has started) +""" + +try: + from xml.sax import make_parser + from xml.sax.handler import ContentHandler +except ImportError: + has_xml = False + ContentHandler = object +else: + has_xml = True + +import os, sys +import ccroot, cxx +import TaskGen, Task, Utils, Runner, Options, Node, Configure +from TaskGen import taskgen, feature, after, extension +from Logs import error +from Constants import * + +MOC_H = ['.h', '.hpp', '.hxx', '.hh'] +EXT_RCC = ['.qrc'] +EXT_UI = ['.ui'] +EXT_QT4 = ['.cpp', '.cc', '.cxx', '.C'] + +class qxx_task(Task.Task): + "A cpp task that may create a moc task dynamically" + + before = ['cxx_link', 'static_link'] + + def __init__(self, *k, **kw): + Task.Task.__init__(self, *k, **kw) + self.moc_done = 0 + + def scan(self): + (nodes, names) = ccroot.scan(self) + # for some reasons (variants) the moc node may end in the list of node deps + for x in nodes: + if x.name.endswith('.moc'): + nodes.remove(x) + names.append(x.relpath_gen(self.inputs[0].parent)) + return (nodes, names) + + def runnable_status(self): + if self.moc_done: + # if there is a moc task, delay the computation of the file signature + for t in self.run_after: + if not t.hasrun: + return ASK_LATER + # the moc file enters in the dependency calculation + # so we need to recompute the signature when the moc file is present + self.signature() + return Task.Task.runnable_status(self) + else: + # yes, really, there are people who generate cxx files + for t in self.run_after: + if not t.hasrun: + return ASK_LATER + self.add_moc_tasks() + return ASK_LATER + + def add_moc_tasks(self): + + node = self.inputs[0] + tree = node.__class__.bld + + try: + # compute the signature once to know if there is a moc file to create + self.signature() + except KeyError: + # the moc file may be referenced somewhere else + pass + else: + # remove the signature, it must be recomputed with the moc task + delattr(self, 'cache_sig') + + moctasks=[] + mocfiles=[] + variant = node.variant(self.env) + try: + tmp_lst = tree.raw_deps[self.unique_id()] + tree.raw_deps[self.unique_id()] = [] + except KeyError: + tmp_lst = [] + for d in tmp_lst: + if not d.endswith('.moc'): continue + # paranoid check + if d in mocfiles: + error("paranoia owns") + continue + + # process that base.moc only once + mocfiles.append(d) + + # find the extension (performed only when the .cpp has changes) + base2 = d[:-4] + for path in [node.parent] + self.generator.env['INC_PATHS']: + tree.rescan(path) + vals = getattr(Options.options, 'qt_header_ext', '') or MOC_H + for ex in vals: + h_node = path.find_resource(base2 + ex) + if h_node: + break + else: + continue + break + else: + raise Utils.WafError("no header found for %s which is a moc file" % str(d)) + + m_node = h_node.change_ext('.moc') + tree.node_deps[(self.inputs[0].parent.id, self.env.variant(), m_node.name)] = h_node + + # create the task + task = Task.TaskBase.classes['moc'](self.env, normal=0) + task.set_inputs(h_node) + task.set_outputs(m_node) + + generator = tree.generator + generator.outstanding.insert(0, task) + generator.total += 1 + + moctasks.append(task) + + # remove raw deps except the moc files to save space (optimization) + tmp_lst = tree.raw_deps[self.unique_id()] = mocfiles + + # look at the file inputs, it is set right above + lst = tree.node_deps.get(self.unique_id(), ()) + for d in lst: + name = d.name + if name.endswith('.moc'): + task = Task.TaskBase.classes['moc'](self.env, normal=0) + task.set_inputs(tree.node_deps[(self.inputs[0].parent.id, self.env.variant(), name)]) # 1st element in a tuple + task.set_outputs(d) + + generator = tree.generator + generator.outstanding.insert(0, task) + generator.total += 1 + + moctasks.append(task) + + # simple scheduler dependency: run the moc task before others + self.run_after = moctasks + self.moc_done = 1 + + run = Task.TaskBase.classes['cxx'].__dict__['run'] + +def translation_update(task): + outs = [a.abspath(task.env) for a in task.outputs] + outs = " ".join(outs) + lupdate = task.env['QT_LUPDATE'] + + for x in task.inputs: + file = x.abspath(task.env) + cmd = "%s %s -ts %s" % (lupdate, file, outs) + Utils.pprint('BLUE', cmd) + task.generator.bld.exec_command(cmd) + +class XMLHandler(ContentHandler): + def __init__(self): + self.buf = [] + self.files = [] + def startElement(self, name, attrs): + if name == 'file': + self.buf = [] + def endElement(self, name): + if name == 'file': + self.files.append(''.join(self.buf)) + def characters(self, cars): + self.buf.append(cars) + +def scan(self): + "add the dependency on the files referenced in the qrc" + node = self.inputs[0] + parser = make_parser() + curHandler = XMLHandler() + parser.setContentHandler(curHandler) + fi = open(self.inputs[0].abspath(self.env)) + parser.parse(fi) + fi.close() + + nodes = [] + names = [] + root = self.inputs[0].parent + for x in curHandler.files: + nd = root.find_resource(x) + if nd: nodes.append(nd) + else: names.append(x) + + return (nodes, names) + +@extension(EXT_RCC) +def create_rcc_task(self, node): + "hook for rcc files" + rcnode = node.change_ext('_rc.cpp') + rcctask = self.create_task('rcc', node, rcnode) + cpptask = self.create_task('cxx', rcnode, rcnode.change_ext('.o')) + self.compiled_tasks.append(cpptask) + return cpptask + +@extension(EXT_UI) +def create_uic_task(self, node): + "hook for uic tasks" + uictask = self.create_task('ui4', node) + uictask.outputs = [self.path.find_or_declare(self.env['ui_PATTERN'] % node.name[:-3])] + return uictask + +class qt4_taskgen(cxx.cxx_taskgen): + def __init__(self, *k, **kw): + cxx.cxx_taskgen.__init__(self, *k, **kw) + self.features.append('qt4') + +@extension('.ts') +def add_lang(self, node): + """add all the .ts file into self.lang""" + self.lang = self.to_list(getattr(self, 'lang', [])) + [node] + +@feature('qt4') +@after('apply_link') +def apply_qt4(self): + if getattr(self, 'lang', None): + update = getattr(self, 'update', None) + lst=[] + trans=[] + for l in self.to_list(self.lang): + + if not isinstance(l, Node.Node): + l = self.path.find_resource(l+'.ts') + + t = self.create_task('ts2qm', l, l.change_ext('.qm')) + lst.append(t.outputs[0]) + + if update: + trans.append(t.inputs[0]) + + trans_qt4 = getattr(Options.options, 'trans_qt4', False) + if update and trans_qt4: + # we need the cpp files given, except the rcc task we create after + # FIXME may be broken + u = Task.TaskCmd(translation_update, self.env, 2) + u.inputs = [a.inputs[0] for a in self.compiled_tasks] + u.outputs = trans + + if getattr(self, 'langname', None): + t = Task.TaskBase.classes['qm2rcc'](self.env) + t.set_inputs(lst) + t.set_outputs(self.path.find_or_declare(self.langname+'.qrc')) + t.path = self.path + k = create_rcc_task(self, t.outputs[0]) + self.link_task.inputs.append(k.outputs[0]) + + self.env.append_value('MOC_FLAGS', self.env._CXXDEFFLAGS) + self.env.append_value('MOC_FLAGS', self.env._CXXINCFLAGS) + +@extension(EXT_QT4) +def cxx_hook(self, node): + # create the compilation task: cpp or cc + try: obj_ext = self.obj_ext + except AttributeError: obj_ext = '_%d.o' % self.idx + + task = self.create_task('qxx', node, node.change_ext(obj_ext)) + self.compiled_tasks.append(task) + return task + +def process_qm2rcc(task): + outfile = task.outputs[0].abspath(task.env) + f = open(outfile, 'w') + f.write('\n\n') + for k in task.inputs: + f.write(' ') + #f.write(k.name) + f.write(k.path_to_parent(task.path)) + f.write('\n') + f.write('\n') + f.close() + +b = Task.simple_task_type +b('moc', '${QT_MOC} ${MOC_FLAGS} ${SRC} ${MOC_ST} ${TGT}', color='BLUE', vars=['QT_MOC', 'MOC_FLAGS'], shell=False) +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) +cls.scan = scan +b('ui4', '${QT_UIC} ${SRC} -o ${TGT}', color='BLUE', before='cxx moc qxx_task', shell=False) +b('ts2qm', '${QT_LRELEASE} ${QT_LRELEASE_FLAGS} ${SRC} -qm ${TGT}', color='BLUE', before='qm2rcc', shell=False) + +Task.task_type_from_func('qm2rcc', vars=[], func=process_qm2rcc, color='BLUE', before='rcc', after='ts2qm') + +def detect_qt4(conf): + env = conf.env + opt = Options.options + + qtdir = getattr(opt, 'qtdir', '') + qtbin = getattr(opt, 'qtbin', '') + qtlibs = getattr(opt, 'qtlibs', '') + useframework = getattr(opt, 'use_qt4_osxframework', True) + + paths = [] + + # the path to qmake has been given explicitely + if qtbin: + paths = [qtbin] + + # the qt directory has been given - we deduce the qt binary path + if not qtdir: + qtdir = conf.environ.get('QT4_ROOT', '') + qtbin = os.path.join(qtdir, 'bin') + paths = [qtbin] + + # no qtdir, look in the path and in /usr/local/Trolltech + if not qtdir: + paths = os.environ.get('PATH', '').split(os.pathsep) + paths.append('/usr/share/qt4/bin/') + try: + lst = os.listdir('/usr/local/Trolltech/') + except OSError: + pass + else: + if lst: + lst.sort() + lst.reverse() + + # keep the highest version + qtdir = '/usr/local/Trolltech/%s/' % lst[0] + qtbin = os.path.join(qtdir, 'bin') + paths.append(qtbin) + + # at the end, try to find qmake in the paths given + # keep the one with the highest version + cand = None + prev_ver = ['4', '0', '0'] + for qmk in ['qmake-qt4', 'qmake4', 'qmake']: + qmake = conf.find_program(qmk, path_list=paths) + if qmake: + try: + version = Utils.cmd_output([qmake, '-query', 'QT_VERSION']).strip() + except ValueError: + pass + else: + if version: + new_ver = version.split('.') + if new_ver > prev_ver: + cand = qmake + prev_ver = new_ver + if cand: + qmake = cand + else: + conf.fatal('could not find qmake for qt4') + + conf.env.QMAKE = qmake + qtincludes = Utils.cmd_output([qmake, '-query', 'QT_INSTALL_HEADERS']).strip() + qtdir = Utils.cmd_output([qmake, '-query', 'QT_INSTALL_PREFIX']).strip() + os.sep + qtbin = Utils.cmd_output([qmake, '-query', 'QT_INSTALL_BINS']).strip() + os.sep + + if not qtlibs: + try: + qtlibs = Utils.cmd_output([qmake, '-query', 'QT_INSTALL_LIBS']).strip() + os.sep + except ValueError: + qtlibs = os.path.join(qtdir, 'lib') + + def find_bin(lst, var): + for f in lst: + ret = conf.find_program(f, path_list=paths) + if ret: + env[var]=ret + break + + vars = "QtCore QtGui QtUiTools QtNetwork QtOpenGL QtSql QtSvg QtTest QtXml QtWebKit Qt3Support".split() + + find_bin(['uic-qt3', 'uic3'], 'QT_UIC3') + find_bin(['uic-qt4', 'uic'], 'QT_UIC') + if not env['QT_UIC']: + conf.fatal('cannot find the uic compiler for qt4') + + try: + version = Utils.cmd_output(env['QT_UIC'] + " -version 2>&1").strip() + except ValueError: + conf.fatal('your uic compiler is for qt3, add uic for qt4 to your path') + + version = version.replace('Qt User Interface Compiler ','') + version = version.replace('User Interface Compiler for Qt', '') + if version.find(" 3.") != -1: + conf.check_message('uic version', '(too old)', 0, option='(%s)'%version) + sys.exit(1) + conf.check_message('uic version', '', 1, option='(%s)'%version) + + find_bin(['moc-qt4', 'moc'], 'QT_MOC') + find_bin(['rcc'], 'QT_RCC') + find_bin(['lrelease-qt4', 'lrelease'], 'QT_LRELEASE') + find_bin(['lupdate-qt4', 'lupdate'], 'QT_LUPDATE') + + env['UIC3_ST']= '%s -o %s' + env['UIC_ST'] = '%s -o %s' + env['MOC_ST'] = '-o' + env['ui_PATTERN'] = 'ui_%s.h' + env['QT_LRELEASE_FLAGS'] = ['-silent'] + + vars_debug = [a+'_debug' for a in vars] + + try: + conf.find_program('pkg-config', var='pkgconfig', path_list=paths, mandatory=True) + + except Configure.ConfigurationError: + + for lib in vars_debug+vars: + uselib = lib.upper() + + d = (lib.find('_debug') > 0) and 'd' or '' + + # original author seems to prefer static to shared libraries + for (pat, kind) in ((conf.env.staticlib_PATTERN, 'STATIC'), (conf.env.shlib_PATTERN, '')): + + conf.check_message_1('Checking for %s %s' % (lib, kind)) + + for ext in ['', '4']: + path = os.path.join(qtlibs, pat % (lib + d + ext)) + if os.path.exists(path): + env.append_unique(kind + 'LIB_' + uselib, lib + d + ext) + conf.check_message_2('ok ' + path, 'GREEN') + break + path = os.path.join(qtbin, pat % (lib + d + ext)) + if os.path.exists(path): + env.append_unique(kind + 'LIB_' + uselib, lib + d + ext) + conf.check_message_2('ok ' + path, 'GREEN') + break + else: + conf.check_message_2('not found', 'YELLOW') + continue + break + + env.append_unique('LIBPATH_' + uselib, qtlibs) + env.append_unique('CPPPATH_' + uselib, qtincludes) + env.append_unique('CPPPATH_' + uselib, qtincludes + os.sep + lib) + else: + for i in vars_debug+vars: + try: + conf.check_cfg(package=i, args='--cflags --libs --silence-errors', path=conf.env.pkgconfig) + except ValueError: + pass + + # the libpaths are set nicely, unfortunately they make really long command-lines + # remove the qtcore ones from qtgui, etc + def process_lib(vars_, coreval): + for d in vars_: + var = d.upper() + if var == 'QTCORE': continue + + value = env['LIBPATH_'+var] + if value: + core = env[coreval] + accu = [] + for lib in value: + if lib in core: continue + accu.append(lib) + env['LIBPATH_'+var] = accu + + process_lib(vars, 'LIBPATH_QTCORE') + process_lib(vars_debug, 'LIBPATH_QTCORE_DEBUG') + + # rpath if wanted + want_rpath = getattr(Options.options, 'want_rpath', 1) + if want_rpath: + def process_rpath(vars_, coreval): + for d in vars_: + var = d.upper() + value = env['LIBPATH_'+var] + if value: + core = env[coreval] + accu = [] + for lib in value: + if var != 'QTCORE': + if lib in core: + continue + accu.append('-Wl,--rpath='+lib) + env['RPATH_'+var] = accu + process_rpath(vars, 'LIBPATH_QTCORE') + process_rpath(vars_debug, 'LIBPATH_QTCORE_DEBUG') + + env['QTLOCALE'] = str(env['PREFIX'])+'/share/locale' + +def detect(conf): + detect_qt4(conf) + +def set_options(opt): + opt.add_option('--want-rpath', type='int', default=1, dest='want_rpath', help='set rpath to 1 or 0 [Default 1]') + + opt.add_option('--header-ext', + type='string', + default='', + help='header extension for moc files', + dest='qt_header_ext') + + for i in 'qtdir qtbin qtlibs'.split(): + opt.add_option('--'+i, type='string', default='', dest=i) + + if sys.platform == "darwin": + 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) + + opt.add_option('--translate', action="store_true", help="collect translation strings", dest="trans_qt4", default=False) + -- cgit