From b06265e03fdb220e5944877c29a7961143e16486 Mon Sep 17 00:00:00 2001 From: Steven Knight Date: Mon, 12 Jul 2004 20:33:44 +0000 Subject: QT fixes and enhancements: Moc() and Uic() builders, a lot of new variables. (Christoph Wiedemann) --- doc/man/scons.1 | 167 ++++++++++++------ src/CHANGES.txt | 9 +- src/engine/SCons/Builder.py | 28 ++- src/engine/SCons/BuilderTests.py | 45 +++++ src/engine/SCons/Node/FS.py | 7 +- src/engine/SCons/Script/__init__.py | 2 +- src/engine/SCons/Tool/__init__.py | 4 +- src/engine/SCons/Tool/link.py | 4 +- src/engine/SCons/Tool/qt.py | 299 ++++++++++++++++++++------------ test/Object.py | 23 ++- test/QT.py | 336 ++++++++++++++++++++++++++++++++---- test/QTFLAGS.py | 69 +++++--- test/import.py | 11 +- 13 files changed, 763 insertions(+), 241 deletions(-) diff --git a/doc/man/scons.1 b/doc/man/scons.1 index 947804f..35a1619 100644 --- a/doc/man/scons.1 +++ b/doc/man/scons.1 @@ -1220,8 +1220,8 @@ env.CFile(target = 'bar', source = 'bar.y') '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .IP CXXFile() .IP env.CXXFile() -Builds a C++ source file given a lex (.ll), yacc (.yy) -or uic (.ui) input file. +Builds a C++ source file given a lex (.ll) or yacc (.yy) +input file. The suffix specified by the $CXXFILESUFFIX construction variable (.cc by default) is automatically added to the target @@ -1405,6 +1405,19 @@ env.M4(target = 'foo.c', source = 'foo.c.m4') .EE '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.IP Moc() +.IP env.Moc() +Builds an output file from a moc input file. Moc input files are either +header files or cxx files. This builder is only available after using the +tool 'qt'. See the QTDIR variable for more information. +Example: + +.ES +env.Moc('foo.h') # generates moc_foo.cc +env.Moc('foo.cpp') # generates foo.moc +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .IP MSVSProject() .IP env.MSVSProject() Builds Microsoft Visual Studio project files. @@ -1835,6 +1848,25 @@ env.TypeLibrary(source="foo.idl") Will create foo.tlb, foo.h, foo_i.c, foo_p.c, and foo_data.c. '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.IP Uic() +.IP env.Uic() +Builds a header file, an implementation file and a moc file from an ui file. +and returns the corresponding nodes in the above order. +This builder is only available after using the tool 'qt'. Note: you can +specify .ui files directly as inputs for Program, Library and SharedLibrary +without using this builder. Using the builder lets you override the standard +naming conventions (be careful: prefixes are always prepended to names of +built files; if you don't want prefixes, you may set them to ``). +See the QTDIR variable for more information. +Example: + +.ES +env.Uic('foo.ui') # -> ['foo.h', 'uic_foo.cc', 'moc_foo.cc'] +env.Uic(target = Split('include/foo.h gen/uicfoo.cc gen/mocfoo.cc'), + source = 'foo.ui') # -> ['include/foo.h', 'gen/uicfoo.cc', 'gen/mocfoo.cc'] +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .IP Zip() .IP env.Zip() Builds a zip archive of the specified files @@ -5076,96 +5108,111 @@ are modified. Because the build-performance is affected when using this tool, you have to explicitly specify it at Environment creation: .ES -Environment(tools=['default','qt']). +Environment(tools=['default','qt']) .EE -.IP -You may want to use -.B Configure -to verify that the qt support really works. + The qt tool supports the following operations: .B Automatic moc file generation from header files. You do not have to specify moc files explicitly, the tool does it for you. However, there are a few preconditions to do so: Your header file must have the same filebase as your implementation file and must stay in the same -directory. It must have one of the suffixes .h, .hpp, .H, .hxx, .hh. +directory. It must have one of the suffixes .h, .hpp, .H, .hxx, .hh. You +can turn off automatic moc file generation by setting QT_AUTOSCAN to 0. +See also the corresponding builder method +.B Moc() .B Automatic moc file generation from cxx files. -As stated in the qt documentation, -include the moc file at the end of the cxx file. -Note that you have to include the file, which is generated by the -QT_MOCNAMEGENERATOR function. If you are using BuildDir, you may need to -specify duplicate=1. +As stated in the qt documentation, include the moc file at the end of +the cxx file. Note that you have to include the file, which is generated +by the transformation ${QT_MOCCXXPREFIX}${QT_MOCCXXSUFFIX}, by default +.moc. A warning is generated after building the moc file, if you +do not include the correct file. If you are using BuildDir, you may +need to specify duplicate=1. You can turn off automatic moc file generation +by setting QT_AUTOSCAN to 0. See also the corresponding builder method +.B Moc() .B Automatic handling of .ui files. The implementation files generated from .ui files are handled much the same -as yacc or lex files. Because there are also generated headers, you may -need to specify duplicate=1 in calls to BuildDir. +as yacc or lex files. Each .ui file given as a source of Program, Library or +SharedLibrary will generate three files, the declaration file, the +implementation file and a moc file. Because there are also generated headers, +you may need to specify duplicate=1 in calls to BuildDir. See also the corresponding builder method +.B Uic() + +.IP QT_AUTOSCAN +Turn off scanning for mocable files. Use the Moc Builder to explicitely +specify files to run moc on. -.IP QT_AUTOBUILD_MOC_SOURCES -If true, moc-generated sources are automatically compiled into the -program or library that uses them. Defaults to 1. +.IP QT_DEBUG +Prints lots of debugging information while scanning for moc files. .IP QT_LIB -Default value is 'qt'. You may want to set this to 'qt-mt' +Default value is 'qt'. You may want to set this to 'qt-mt'. .IP QT_MOC Default value is '$QTDIR/bin/moc'. -.IP QT_UIC -Default value is '$QTDIR/bin/uic'. +.IP QT_MOCCXXPREFIX +Default value is ''. Prefix for moc output files, when source is a cxx file. -.IP QT_UICIMPLFLAGS -Default value is ''. These flags are passed to uic, when creating a cxx -file from a .ui file. +.IP QT_MOCCXXSUFFIX +Default value is '.moc'. Suffix for moc output files, when source is a cxx +file. -.IP QT_UICDECLFLAGS -Default value is ''. These flags are passed to uic, when creating a a h -file from a .ui file. +.IP QT_MOCFROMCPPFLAGS +Default value is '-i'. These flags are passed to moc, when moccing a +cpp file. + +.IP QT_MOCFROMCXXCOM +Command to generate a moc file from a cpp file. + +.IP QT_MOCFROMHCOM +Command to generate a moc file from a header. .IP QT_MOCFROMHFLAGS Default value is ''. These flags are passed to moc, when moccing a header file. -.IP QT_MOCFROMCPPFLAGS -Default value is '-i'. These flags are passed to moc, when moccing a -cpp file. +.IP QT_MOCHPREFIX +Default value is 'moc_'. Prefix for moc output files, when source is a header. -.IP QT_HSUFFIX -Default value is '.h'. Suffix of headers generated with uic. +.IP QT_MOCHSUFFIX +Default value is '$CXXFILESUFFIX'. Suffix for moc output files, when source is +a header. -.IP QT_UISUFFIX -Default value is '.ui'. Suffix of designer files. - -.IP QT_UIHSUFFIX -Default value is '.ui.h'. - -.IP QT_MOCNAMEGENERATOR -Three-argument function, which generates names of moc output files. -This is the most flexible way to support the huge number of conventions -for this type of files. The arguments are the -.I filebase -, which is the file to be moc'd without path and extension, the -.I src_suffix -, which is the extension of the file to be moc'd and the environment -.I env -The default value maps 'myfile.myext' to 'moc_myfile.$CXXFILESUFFIX': +.IP QT_UIC +Default value is '$QTDIR/bin/uic'. -.ES -lambda filebase, src_suffix, env: 'moc_' + filebase + env['CXXFILESUFFIX'] -.EE +.IP QT_UICDECLCOM +Command to generate header files from .ui files. + +.IP QT_UICDECLFLAGS +Default value is ''. These flags are passed to uic, when creating a a h +file from a .ui file. + +.IP QT_UICDECLPREFIX +Default value is ''. Prefix for uic generated header files. + +.IP QT_UICDECLSUFFIX +Default value is '.h'. Suffix for uic generated header files. .IP QT_UICIMPLCOM Command to generate cxx files from .ui files. -.IP QT_UICDECLCOM -Command to generate header files from .ui files. +.IP QT_UICIMPLFLAGS +Default value is ''. These flags are passed to uic, when creating a cxx +file from a .ui file. -.IP QT_MOCFROMHCOM -Command to generate a moc file from a header. +.IP QT_UICIMPLPREFIX +Default value is 'uic_'. Prefix for uic generated implementation files. -.IP QT_MOCFROMCXXCOM -Command to generate a moc file from a cpp file. +.IP QT_UICIMPLSUFFIX +Default value is '$CXXFILESUFFIX'. Suffix for uic generated implementation +files. + +.IP QT_UISUFFIX +Default value is '.ui'. Suffix of designer input files. .IP RANLIB The archive indexer. @@ -6576,6 +6623,12 @@ Specifies a builder to use when a source file name suffix does not match any of the suffixes of the builder. Using this argument produces a multi-stage builder. +.IP single_source +Specifies that this builder expects exactly one source file per call. Giving +more than one source files without target files results in implicitely calling +the builder multiple times (once for each source given). Giving multiple +source files together with target files results in a UserError exception. + .RE .IP The diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 0ecb526..72748d9 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -16,9 +16,6 @@ RELEASE 0.96 - XXX - Allow construction variable substitutions in $LIBS specifications. - - Add a $QT_AUTOBUILD_MOC_SOURCES construction variable that controls - whether moc-generated .cpp files get compiled. - - Allow the emitter argument to a Builder() to be or expand to a list of emitter functions, which will be called in sequence. @@ -420,6 +417,12 @@ RELEASE 0.95 - Mon, 08 Mar 2004 06:43:20 -0600 the flags from the environment used to specify the target, not the environment that first has the Qt Builders attached. + - Add new Moc() and Uic() Builders for Qt, and a slew of $QT_* + construction variables to control them. + + - Add a new single_source keyword argument for Builders that enforces + a single source file on calls to the Builder. + RELEASE 0.94 - Fri, 07 Nov 2003 05:29:48 -0600 diff --git a/src/engine/SCons/Builder.py b/src/engine/SCons/Builder.py index 1dcf84c..57f50c9 100644 --- a/src/engine/SCons/Builder.py +++ b/src/engine/SCons/Builder.py @@ -311,6 +311,10 @@ def _init_nodes(builder, env, overrides, tlist, slist): elif t.sources != slist: raise UserError, "Multiple ways to build the same target were specified for: %s" % str(t) + if builder.single_source: + if len(slist) > 1: + raise UserError, "More than one source given for single-source builder: targets=%s sources=%s" % (map(str,tlist), map(str,slist)) + # The targets are fine, so find or make the appropriate Executor to # build this particular list of targets from this particular list of # sources. @@ -400,6 +404,7 @@ class BuilderBase: emitter = None, multi = 0, env = None, + single_source = 0, **overrides): if __debug__: logInstanceCreation(self, 'BuilderBase') self.action = SCons.Action.Action(action) @@ -411,6 +416,7 @@ class BuilderBase: suffix = CallableSelector(suffix) self.suffix = suffix self.env = env + self.single_source = single_source if overrides.has_key('overrides'): SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning, "The \"overrides\" keyword to Builder() creation has been deprecated;\n" +\ @@ -522,6 +528,21 @@ class BuilderBase: if source is _null: source = target target = None + + if(self.single_source and + SCons.Util.is_List(source) and + len(source) > 1 and + target is None): + result = [] + if target is None: target = [None]*len(source) + for k in range(len(source)): + t = self._execute(env, target[k], source[k], overwarn) + if SCons.Util.is_List(t): + result.extend(t) + else: + result.append(t) + return result + tlist, slist = self._create_nodes(env, overwarn, target, source) if len(tlist) == 1: @@ -605,6 +626,7 @@ class ListBuilder(SCons.Util.Proxy): self.env = env self.tlist = tlist self.multi = builder.multi + self.single_source = builder.single_source def targets(self, node): """Return the list of targets for this builder instance. @@ -639,11 +661,13 @@ class MultiStepBuilder(BuilderBase): source_factory = SCons.Node.FS.default_fs.File, target_scanner = None, source_scanner = None, - emitter=None): + emitter=None, + single_source=0): if __debug__: logInstanceCreation(self) BuilderBase.__init__(self, action, prefix, suffix, src_suffix, target_factory, source_factory, - target_scanner, source_scanner, emitter) + target_scanner, source_scanner, emitter, + single_source = single_source) if not SCons.Util.is_List(src_builder): src_builder = [ src_builder ] self.src_builder = src_builder diff --git a/src/engine/SCons/BuilderTests.py b/src/engine/SCons/BuilderTests.py index e592250..57e9f28 100644 --- a/src/engine/SCons/BuilderTests.py +++ b/src/engine/SCons/BuilderTests.py @@ -524,6 +524,51 @@ class BuilderTestCase(unittest.TestCase): tgt = builder(my_env, source = 'f6.zzz') assert tgt.path == 'f6.emit', tgt.path + def test_single_source(self): + """Test Builder with single_source flag set""" + def func(target, source, env): + open(str(target[0]), "w") + if (len(source) == 1 and len(target) == 1): + env['CNT'][0] = env['CNT'][0] + 1 + + env = Environment() + infiles = [] + outfiles = [] + for i in range(10): + infiles.append(test.workpath('%d.in' % i)) + outfiles.append(test.workpath('%d.out' % i)) + test.write(infiles[-1], "\n") + builder = SCons.Builder.Builder(action=SCons.Action.Action(func,None), + single_source = 1, suffix='.out') + env['CNT'] = [0] + tgt = builder(env, target=outfiles[0], source=infiles[0]) + tgt.prepare() + tgt.build() + assert env['CNT'][0] == 1, env['CNT'][0] + tgt = builder(env, outfiles[1], infiles[1]) + tgt.prepare() + tgt.build() + assert env['CNT'][0] == 2 + tgts = builder(env, infiles[2:4]) + for t in tgts: t.prepare() + tgts[0].build() + tgts[1].build() + assert env['CNT'][0] == 4 + try: + tgt = builder(env, outfiles[4], infiles[4:6]) + except SCons.Errors.UserError: + pass + else: + assert 0 + try: + # The builder may output more than one target per input file. + tgt = builder(env, outfiles[4:6], infiles[4:6]) + except SCons.Errors.UserError: + pass + else: + assert 0 + + def test_ListBuilder(self): """Testing ListBuilder class.""" def function2(target, source, env, tlist = [outfile, outfile2], **kw): diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py index e61878b..72ed154 100644 --- a/src/engine/SCons/Node/FS.py +++ b/src/engine/SCons/Node/FS.py @@ -169,15 +169,16 @@ LocalCopy = SCons.Action.Action(LinkFunc, LocalString) def UnlinkFunc(target, source, env): t = target[0] - t.fs.unlink(t.path) + t.fs.unlink(t.abspath) return 0 Unlink = SCons.Action.Action(UnlinkFunc, None) def MkdirFunc(target, source, env): t = target[0] - if not t.fs.exists(t.path): - t.fs.mkdir(t.path) + p = t.abspath + if not t.fs.exists(p): + t.fs.mkdir(p) return 0 Mkdir = SCons.Action.Action(MkdirFunc, None, presub=None) diff --git a/src/engine/SCons/Script/__init__.py b/src/engine/SCons/Script/__init__.py index 835409e..853a944 100644 --- a/src/engine/SCons/Script/__init__.py +++ b/src/engine/SCons/Script/__init__.py @@ -96,7 +96,7 @@ class BuildTask(SCons.Taskmaster.Task): target = self.targets[0] if target.get_state() == SCons.Node.up_to_date: if self.top and target.has_builder(): - display("scons: `%s' is up to date." % str(target)) + display("scons: `%s' is up to date." % str(self.node)) elif target.has_builder() and not hasattr(target.builder, 'status'): if print_time: start_time = time.time() diff --git a/src/engine/SCons/Tool/__init__.py b/src/engine/SCons/Tool/__init__.py index 4364cf3..5753bb9 100644 --- a/src/engine/SCons/Tool/__init__.py +++ b/src/engine/SCons/Tool/__init__.py @@ -174,7 +174,7 @@ def createObjBuilders(env): prefix = '$OBJPREFIX', suffix = '$OBJSUFFIX', src_builder = ['CFile', 'CXXFile'], - source_scanner = SCons.Defaults.ObjSourceScan) + source_scanner = SCons.Defaults.ObjSourceScan, single_source=1) env['BUILDERS']['StaticObject'] = static_obj env['BUILDERS']['Object'] = static_obj env['OBJEMITTER'] = SCons.Defaults.StaticObjectEmitter @@ -187,7 +187,7 @@ def createObjBuilders(env): prefix = '$SHOBJPREFIX', suffix = '$SHOBJSUFFIX', src_builder = ['CFile', 'CXXFile'], - source_scanner = SCons.Defaults.ObjSourceScan) + source_scanner = SCons.Defaults.ObjSourceScan, single_source=1) env['BUILDERS']['SharedObject'] = shared_obj env['SHOBJEMITTER'] = SCons.Defaults.SharedObjectEmitter diff --git a/src/engine/SCons/Tool/link.py b/src/engine/SCons/Tool/link.py index cc0d4f4..8248be9 100644 --- a/src/engine/SCons/Tool/link.py +++ b/src/engine/SCons/Tool/link.py @@ -52,7 +52,9 @@ def generate(env): env['SHLINK'] = '$LINK' env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared') env['SHLINKCOM'] = '$SHLINK $SHLINKFLAGS -o $TARGET $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' - env['SHLIBEMITTER']= None + # don't set up the emitter, cause AppendUnique will generate a list + # starting with None :-( + #env['SHLIBEMITTER']= None env['SMARTLINK'] = smart_link env['LINK'] = "$SMARTLINK" env['LINKFLAGS'] = SCons.Util.CLVar('') diff --git a/src/engine/SCons/Tool/qt.py b/src/engine/SCons/Tool/qt.py index 01a5cf6..0a45e55 100644 --- a/src/engine/SCons/Tool/qt.py +++ b/src/engine/SCons/Tool/qt.py @@ -41,10 +41,33 @@ import SCons.Defaults import SCons.Tool import SCons.Util -header_extensions = [".h", ".hxx", ".hpp", ".hh"] +class ToolQtWarning(SCons.Warnings.Warning): + pass + +class GeneratedMocFileNotIncluded(ToolQtWarning): + pass + +class QtdirNotFound(ToolQtWarning): + pass +SCons.Warnings.enableWarningClass(ToolQtWarning) + +header_extensions = [".h", ".hxx", ".hpp", ".hh"] if SCons.Util.case_sensitive_suffixes('.h', '.H'): header_extensions.append('.H') +cplusplus = __import__('c++', globals(), locals(), []) +cxx_suffixes = cplusplus.CXXSuffixes + +def checkMocIncluded(target, source, env): + moc = target[0] + cpp = source[0] + # looks like cpp.includes is cleared before the build stage :-( + includes = SCons.Defaults.CScan(cpp, env) + if not moc in includes: + SCons.Warnings.warn( + GeneratedMocFileNotIncluded, + "Generated moc file '%s' is not included by '%s'" % + (str(moc), str(cpp))) class _Automoc: """ @@ -52,87 +75,105 @@ class _Automoc: StaticLibraries. """ - def __init__(self, objBuilderName,uicDeclBuild,mocFromHBld,mocFromCppBld): + def __init__(self, objBuilderName): self.objBuilderName = objBuilderName - self.uicDeclBld = uicDeclBuild - self.mocFromHBld = mocFromHBld - self.mocFromCppBld = mocFromCppBld def __call__(self, target, source, env): """ Smart autoscan function. Gets the list of objects for the Program or Lib. Adds objects and builders for the special qt files. """ + try: + if int(env.subst('$QT_AUTOSCAN')) == 0: + return target, source + except ValueError: + pass + try: + debug = int(env.subst('$QT_DEBUG')) + except ValueError: + debug = 0 + + # some shortcuts used in the scanner FS = SCons.Node.FS.default_fs splitext = SCons.Util.splitext - + objBuilder = getattr(env, self.objBuilderName) + # To make the following work, we assume that we stay in the # root directory - old_os_cwd = os.getcwd() - old_fs_cwd = FS.getcwd() - FS.chdir(FS.Dir('#'), change_os_dir=1) - - # a regular expression for the Q_OBJECT macro - # currently fails, when Q_OBJECT is in comment (e.g. /* Q_OBJECT */) - q_object_search = re.compile(r'\sQ_OBJECT[\s;]') - # out_sources contains at least all sources for the Library or Prog + #old_os_cwd = os.getcwd() + #old_fs_cwd = FS.getcwd() + #FS.chdir(FS.Dir('#'), change_os_dir=1) + + # some regular expressions: + # Q_OBJECT detection + q_object_search = re.compile(r'[^A-Za-z0-9]Q_OBJECT[^A-Za-z0-9]') + # cxx and c comment 'eater' + comment = re.compile(r'(//.*)|(/\*(([^*])|(\*[^/]))*\*/)') + + # The following is kind of hacky to get builders working properly (FIXME) + objBuilderEnv = objBuilder.env + objBuilder.env = env + mocBuilderEnv = env.Moc.env + env.Moc.env = env + + # make a deep copy for the result; MocH objects will be appended out_sources = source[:] - for s in source: - prefix, suffix = splitext(str(s)) - # Nodes for header (h) / moc file (moc_cpp) / cpp file (cpp) - # and ui.h file (ui_h) - cpp = s.sources[0] - ui = None - if cpp.sources != None and len(cpp.sources) > 0: - src_src_suffix = splitext(str(cpp.sources[0]))[1] - if src_src_suffix == env.subst('$QT_UISUFFIX'): - ui = cpp.sources[0] - - src_prefix, src_suffix = splitext(str(cpp.srcnode())) + + for obj in source: + if not obj.has_builder(): + # binary obj file provided + if debug: + print "scons: qt: '%s' seems to be a binary. Discarded." % str(obj) + continue + cpp = obj.sources[0] + if not splitext(str(cpp))[1] in cxx_suffixes: + if debug: + print "scons: qt: '%s' is no cxx file. Discarded." % str(cpp) + # c or fortran source + continue + cpp_contents = comment.sub('', cpp.get_contents()) h=None for h_ext in header_extensions: - if os.path.exists(src_prefix + h_ext): - h = FS.File(src_prefix + h_ext) - - if ui: - # file built from .ui file -> build also header from .ui - h = self.uicDeclBld(env, prefix, ui) - env.Depends(cpp, h) - ui_h_suff = env.subst('$QT_UIHSUFFIX') - if os.path.exists(src_prefix + ui_h_suff): - # if a .ui.h file exists, we need to specify the dependecy ... - ui_h = FS.File(src_prefix + ui_h_suff) - env.Depends(cpp, ui_h) - if (h and q_object_search.search(h.get_contents())) or ui: + # try to find the header file in the corresponding source + # directory + hname = splitext(cpp.name)[0] + h_ext + h = SCons.Node.FS.find_file(hname, + (cpp.get_dir(),), + FS.File) + if h: + if debug: + print "scons: qt: Scanning '%s' (header of '%s')" % (str(h), str(cpp)) + h_contents = comment.sub('', h.get_contents()) + break + if not h and debug: + print "scons: qt: no header for '%s'." % (str(cpp)) + if h and q_object_search.search(h_contents): # h file with the Q_OBJECT macro found -> add moc_cpp - d,base = os.path.split(prefix) - src_ext = splitext(str(h))[1] - moc_cpp = FS.File(os.path.join(d, - env['QT_MOCNAMEGENERATOR'](base, src_ext, env))) - objBuilder = getattr(env, self.objBuilderName) - if env.get('QT_AUTOBUILD_MOC_SOURCES'): - moc_o = objBuilder(source=moc_cpp) - out_sources.append(moc_o) - objBuilder(moc_o, moc_cpp) - self.mocFromHBld(env, moc_cpp, h) + moc_cpp = env.Moc(h) + moc_o = objBuilder(moc_cpp) + out_sources.append(moc_o) #moc_cpp.target_scanner = SCons.Defaults.CScan - if cpp and q_object_search.search(cpp.get_contents()): + if debug: + print "scons: qt: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(h), str(moc_cpp)) + if cpp and q_object_search.search(cpp_contents): # cpp file with Q_OBJECT macro found -> add moc # (to be included in cpp) - d,base = os.path.split(prefix) - src_ext = splitext(str(cpp))[1] - moc = FS.File(os.path.join(d, - env['QT_MOCNAMEGENERATOR'](base, src_ext, env))) - self.mocFromCppBld(env, moc, cpp) + moc = env.Moc(cpp) env.Ignore(moc, moc) + if debug: + print "scons: qt: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(cpp), str(moc)) #moc.source_scanner = SCons.Defaults.CScan + # restore the original env attributes (FIXME) + objBuilder.env = objBuilderEnv + env.Moc.env = mocBuilderEnv - os.chdir(old_os_cwd) - FS.chdir(old_fs_cwd) - if env.get('QT_AUTOBUILD_MOC_SOURCES'): - return (target, out_sources) - else: - return (target, source) + #os.chdir(old_os_cwd) + #FS.chdir(old_fs_cwd) + + return (target, out_sources) + +AutomocShared = _Automoc('SharedObject') +AutomocStatic = _Automoc('StaticObject') def _detect(env): """Not really safe, but fast method to detect the QT library""" @@ -142,85 +183,121 @@ def _detect(env): if not QTDIR: QTDIR = os.environ.get('QTDIR',None) if not QTDIR: - moc = env.Detect('moc') + moc = env.WhereIs('moc') if moc: QTDIR = os.path.dirname(os.path.dirname(moc)) + SCons.Warnings.warn( + QtdirNotFound, + "Could not detect qt, using moc executable as a hint (QTDIR=%s)" % QTDIR) else: QTDIR = None + SCons.Warnings.warn( + QtdirNotFound, + "Could not detect qt, using empty QTDIR") return QTDIR + def generate(env): """Add Builders and construction variables for qt to an Environment.""" + CLVar = SCons.Util.CLVar + Action = SCons.Action.Action + Builder = SCons.Builder.Builder + splitext = SCons.Util.splitext + # the basics env['QTDIR'] = _detect(env) env['QT_MOC'] = os.path.join('$QTDIR','bin','moc') env['QT_UIC'] = os.path.join('$QTDIR','bin','uic') - env['QT_LIB'] = 'qt' + env['QT_LIB'] = 'qt' # may be set to qt-mt - # Should moc-generated sources be automatically compiled? - env['QT_AUTOBUILD_MOC_SOURCES'] = 1 + # Should the qt tool try to figure out, which sources are to be moc'ed ? + env['QT_AUTOSCAN'] = 1 # Some QT specific flags. I don't expect someone wants to # manipulate those ... - env['QT_UICIMPLFLAGS'] = SCons.Util.CLVar('') - env['QT_UICDECLFLAGS'] = SCons.Util.CLVar('') - env['QT_MOCFROMHFLAGS'] = SCons.Util.CLVar('') - env['QT_MOCFROMCXXFLAGS'] = SCons.Util.CLVar('-i') + env['QT_UICIMPLFLAGS'] = CLVar('') + env['QT_UICDECLFLAGS'] = CLVar('') + env['QT_MOCFROMHFLAGS'] = CLVar('') + env['QT_MOCFROMCXXFLAGS'] = CLVar('-i') - # Suffixes for the headers / sources to generate - env['QT_HSUFFIX'] = '.h' + # suffixes/prefixes for the headers / sources to generate + env['QT_UICDECLPREFIX'] = '' + env['QT_UICDECLSUFFIX'] = '.h' + env['QT_UICIMPLPREFIX'] = 'uic_' + env['QT_UICIMPLSUFFIX'] = '$CXXFILESUFFIX' + env['QT_MOCHPREFIX'] = 'moc_' + env['QT_MOCHSUFFIX'] = '$CXXFILESUFFIX' + env['QT_MOCCXXPREFIX'] = '' + env['QT_MOCCXXSUFFIX'] = '.moc' env['QT_UISUFFIX'] = '.ui' - env['QT_UIHSUFFIX'] = '.ui.h' - env['QT_MOCNAMEGENERATOR'] = \ - lambda x, src_suffix, env: 'moc_' + x + env.get('CXXFILESUFFIX','.cc') + + def uicEmitter(target, source, env): + adjustixes = SCons.Util.adjustixes + bs = SCons.Util.splitext(str(source[0]))[0] + # first target (header) is automatically added by builder + if len(target) < 2: + # second target is implementation + target.append(adjustixes(bs, + env.subst('$QT_UICIMPLPREFIX'), + env.subst('$QT_UICIMPLSUFFIX'))) + if len(target) < 3: + # third target is moc file + target.append(adjustixes(bs, + env.subst('$QT_MOCHPREFIX'), + env.subst('$QT_MOCHSUFFIX'))) + return target, source # Commands for the qt support ... - # command to generate implementation (cpp) file from a .ui file - env['QT_UICIMPLCOM'] = ('$QT_UIC $QT_UICIMPLFLAGS -impl ' - '${TARGETS[0].filebase}$QT_HSUFFIX ' - '-o $TARGET $SOURCES') - # command to generate declaration (h) file from a .ui file - env['QT_UICDECLCOM'] = ('$QT_UIC $QT_UICDECLFLAGS ' - '-o ${TARGETS[0].base}$QT_HSUFFIX $SOURCES') + # command to generate header, implementation and moc-file from a .ui file + env['QT_UICCOM'] = [ + CLVar('$QT_UIC $QT_UICDECLFLAGS -o ${TARGETS[0]} $SOURCE'), + CLVar('$QT_UIC $QT_UICIMPLFLAGS -impl ${TARGETS[0].file} ' + '-o ${TARGETS[1]} $SOURCE'), + CLVar('$QT_MOC $QT_MOCFROMHFLAGS -o ${TARGETS[2]} ${TARGETS[0]}')] # command to generate meta object information for a class declarated # in a header - env['QT_MOCFROMHCOM'] = '$QT_MOC $QT_MOCFROMHFLAGS -o $TARGET $SOURCE' - # command to generate meta object information for a class declatazed + env['QT_MOCFROMHCOM'] = ( + '$QT_MOC $QT_MOCFROMHFLAGS -o ${TARGETS[0]} $SOURCE') + # command to generate meta object information for a class declarated # in a cpp file - env['QT_MOCFROMCXXCOM'] = '$QT_MOC $QT_MOCFROMCXXFLAGS -o $TARGET $SOURCE' - + env['QT_MOCFROMCXXCOM'] = [ + CLVar('$QT_MOC $QT_MOCFROMCXXFLAGS -o ${TARGETS[0]} $SOURCE'), + Action(checkMocIncluded,None)] # ... and the corresponding builders - uicDeclBld = SCons.Builder.Builder(action='$QT_UICDECLCOM', - src_suffix='$QT_UISUFFIX', - suffix='$QT_HSUFFIX') - mocFromHBld = SCons.Builder.Builder(action='$QT_MOCFROMHCOM', - src_suffix='$QT_HSUFFIX', - suffix='$QT_MOCSUFFIX') - mocFromCppBld = SCons.Builder.Builder(action='$QT_MOCFROMCXXCOM', - src_suffix='$QT_CXXSUFFIX', - suffix='$QT_MOCSUFFIX') - - # we use CXXFile to generate .cpp files from .ui files - c_file, cxx_file = SCons.Tool.createCFileBuilders(env) - cxx_file.add_action('$QT_UISUFFIX', '$QT_UICIMPLCOM') + uicBld = Builder(action='$QT_UICCOM', + emitter=uicEmitter, + src_suffix='$QT_UISUFFIX', + suffix='$QT_UICDECLSUFFIX', + prefix='$QT_UICDECLPREFIX') + mocBld = Builder(action={}, prefix={}, suffix={}) + for h in header_extensions: + mocBld.add_action(h, '$QT_MOCFROMHCOM') + mocBld.prefix[h] = '$QT_MOCHPREFIX' + mocBld.suffix[h] = '$QT_MOCHSUFFIX' + for cxx in cxx_suffixes: + mocBld.add_action(cxx, '$QT_MOCFROMCXXCOM') + mocBld.prefix[cxx] = '$QT_MOCCXXPREFIX' + mocBld.suffix[cxx] = '$QT_MOCCXXSUFFIX' + + # register the builders + env['BUILDERS']['Uic'] = uicBld + env['BUILDERS']['Moc'] = mocBld + static_obj, shared_obj = SCons.Tool.createObjBuilders(env) + static_obj.src_builder.append('Uic') + shared_obj.src_builder.append('Uic') # We use the emitters of Program / StaticLibrary / SharedLibrary - # to produce almost all builders except .cpp from .ui - # First, make sure the Environment has Object builders. - SCons.Tool.createObjBuilders(env) + # to scan for moc'able files # We can't refer to the builders directly, we have to fetch them # as Environment attributes because that sets them up to be called # correctly later by our emitter. - env.Append(PROGEMITTER = [_Automoc('StaticObject', - uicDeclBld,mocFromHBld,mocFromCppBld)], - SHLIBEMITTER = [_Automoc('SharedObject', - uicDeclBld,mocFromHBld,mocFromCppBld)], - LIBEMITTER = [_Automoc('StaticObject', - uicDeclBld,mocFromHBld,mocFromCppBld)]) - # Of course, we need to link against the qt libraries - env.AppendUnique(CPPPATH=[os.path.join('$QTDIR', 'include')]) - env.AppendUnique(LIBPATH=[os.path.join('$QTDIR', 'lib')]) - env.AppendUnique(LIBS=['$QT_LIB']) + env.AppendUnique(PROGEMITTER =[AutomocStatic], + SHLIBEMITTER=[AutomocShared], + LIBEMITTER =[AutomocStatic], + # Of course, we need to link against the qt libraries + CPPPATH=[os.path.join('$QTDIR', 'include')], + LIBPATH=[os.path.join('$QTDIR', 'lib')], + LIBS=['$QT_LIB']) def exists(env): return _detect(env) diff --git a/test/Object.py b/test/Object.py index cde1391..9ed7a15 100644 --- a/test/Object.py +++ b/test/Object.py @@ -39,10 +39,11 @@ env = Environment() f1 = env.Object(target = 'f1', source = 'f1.c') f2 = Object(target = 'f2', source = 'f2.cpp') f3 = env.Object(target = 'f3', source = 'f3.c') -env.Program(target = 'prog1', source = Split('f1%s f2%s f3%s prog.cpp')) -env.Program(target = 'prog2', source = [f1, f2, f3, 'prog.cpp']) +mult_o = env.Object(['f4.c', 'f5.c']) +env.Program(target = 'prog1', source = Split('f1%s f2%s f3%s f4%s prog.cpp')) +env.Program(target = 'prog2', source = mult_o + [f1, f2, f3, 'prog.cpp']) env.Program(target = 'prog3', source = ['f1%s', f2, 'f3%s', 'prog.cpp']) -""" % (_obj, _obj, _obj, _obj, _obj)) +""" % (_obj, _obj, _obj, _obj, _obj, _obj)) test.write('f1.c', r""" void @@ -70,6 +71,22 @@ f3(void) } """) +test.write('f4.c', r""" +void +f4(void) +{ + printf("f4.c\n"); +} +""") + +test.write('f5.c', r""" +void +f5(void) +{ + printf("f5.c\n"); +} +""") + test.write('prog.cpp', r""" #include diff --git a/test/QT.py b/test/QT.py index af4c4af..6b378c4 100644 --- a/test/QT.py +++ b/test/QT.py @@ -30,6 +30,7 @@ generation of qt's moc files. """ import os.path +import re import string import TestSCons @@ -40,6 +41,7 @@ lib_ = TestSCons.lib_ _lib = TestSCons._lib dll_ = TestSCons.dll_ _dll = TestSCons._dll +_shobj = TestSCons._shobj test = TestSCons.TestSCons() @@ -72,6 +74,7 @@ sys.exit(0) test.write(['qt', 'bin', 'myuic.py'], """ import sys +import os.path import string output_arg = 0 impl_arg = 0 @@ -92,8 +95,13 @@ for arg in sys.argv[1:]: if source: sys.exit(1) source = open(arg, 'rb') + sourceFile = arg if impl: output.write( '#include "' + impl + '"\\n' ) + if string.find(source.read(), '// ui.h') != -1: + output.write( + '#include "' + + os.path.basename(os.path.splitext(sourceFile)[0]) + '.ui.h"\\n') else: output.write( '#include "my_qobject.h"\\n' + source.read() + " Q_OBJECT \\n" ) output.close() @@ -130,7 +138,9 @@ QT_UIC = '%s %s' % (python, test.workpath('qt','bin','myuic.py')) def createSConstruct(test,place): test.write(place, """ -env = Environment(QTDIR = r'%s', +if ARGUMENTS.get('noqtdir', 0): QTDIR=None +else: QTDIR=r'%s' +env = Environment(QTDIR = QTDIR, QT_LIB = r'%s', QT_MOC = r'%s', QT_UIC = r'%s', @@ -149,7 +159,11 @@ SConscript( sconscript ) """ % (QT, QT_LIB, QT_MOC, QT_UIC)) test.subdir( 'work1', 'work2', 'work3', 'work4', - 'work5', 'work6', 'work7', 'work8' ) + 'work5', 'work6', 'work7', 'work8', + 'work9', ['work9', 'local_include'], + 'work10', ['work10', 'sub'], ['work10', 'sub', 'local_include'], + 'work11', ['work11', 'include'], ['work11', 'ui'], + 'work12') ############################################################################## # 1. create a moc file from a header file. @@ -199,7 +213,8 @@ test.fail_test( not os.path.exists(test.workpath('work1', 'build', moc)) ) aaa_dll = dll_ + 'aaa' + _dll moc = 'moc_aaa.cc' -cpp = 'aaa.cc' +cpp = 'uic_aaa.cc' +obj = os.path.splitext(cpp)[0] + _shobj h = 'aaa.h' createSConstruct(test, ['work2', 'SConstruct']) @@ -209,7 +224,12 @@ env.SharedLibrary(target = 'aaa', source = ['aaa.ui', 'useit.cpp']) """) test.write(['work2', 'aaa.ui'], r""" -void aaa(void) +#if defined (_WIN32) || defined(__CYGWIN__) +#define DLLEXPORT __declspec(dllexport) +#else +#define DLLEXPORT +#endif +DLLEXPORT void aaa(void) """) test.write(['work2', 'useit.cpp'], r""" @@ -223,7 +243,12 @@ test.run(chdir='work2', arguments = aaa_dll) test.up_to_date(chdir='work2', options='-n',arguments = aaa_dll) test.write(['work2', 'aaa.ui'], r""" /* a change */ -void aaa(void) +#if defined (_WIN32) || defined(__CYGWIN__) +#define DLLEXPORT __declspec(dllexport) +#else +#define DLLEXPORT +#endif +DLLEXPORT void aaa(void) """) test.not_up_to_date(chdir='work2', options = '-n', arguments = moc) test.not_up_to_date(chdir='work2', options = '-n', arguments = cpp) @@ -232,7 +257,16 @@ test.run(chdir='work2', arguments = aaa_dll) test.write(['work2', 'aaa.ui.h'], r""" /* test dependency to .ui.h */ """) -test.not_up_to_date(chdir='work2', options = '-n', arguments = cpp) +test.write(['work2', 'aaa.ui'], r""" +void aaa(void) +// ui.h +""") +test.run(chdir='work2', arguments = aaa_dll) +test.write(['work2', 'aaa.ui.h'], r""" +/* changed */ +""") +test.not_up_to_date(chdir='work2', options = '-n', arguments = obj) +test.up_to_date(chdir='work2', options = '-n', arguments = cpp) test.up_to_date(chdir='work2', options = '-n', arguments = h) test.up_to_date(chdir='work2', options = '-n', arguments = moc) @@ -251,7 +285,7 @@ test.fail_test(not os.path.exists(test.workpath('work2','build',moc)) or # 3. create a moc file from a cpp file lib_aaa = lib_ + 'aaa' + _lib -moc = 'moc_aaa.cc' +moc = 'aaa.moc' createSConstruct(test, ['work3', 'SConstruct']) test.write(['work3', 'SConscript'], """ @@ -398,10 +432,13 @@ foo6(void) } """) -test.run(chdir='work6') +# we can receive warnings about a non detected qt (empty QTDIR) +# these are not critical, but maybe annoying +test.run(chdir='work6', stderr=None) main_exe = 'main' + _exe test.run(program = test.workpath('work6', main_exe), + stderr = None, stdout = 'qt/include/foo6.h\n') ############################################################################## @@ -465,7 +502,7 @@ class MyClass1 : public QObject { void mocFromCpp() { MyClass1 myclass; } -#include "moc_mocFromCpp.cpp" +#include "mocFromCpp.moc" """) test.write( ['work7', 'mocFromH.h'],""" @@ -522,50 +559,287 @@ int main() { QTDIR=os.environ['QTDIR'] del os.environ['QTDIR'] - test.run(chdir='work7', arguments="-c test_realqt" + _exe) - test.run(chdir='work7', arguments="PATH=%s/bin test_realqt%s"%(QTDIR,_exe)) + test.run(chdir='work7', stderr=None, arguments="-c test_realqt" + _exe) + test.fail_test(not test.match_re(test.stderr(), r""" +scons: warning: Could not detect qt, using empty QTDIR +File "SConstruct", line \d+, in .+ +""")) + + test.run(chdir='work7', stderr=None, + arguments="PATH=%s/bin test_realqt%s"%(QTDIR,_exe)) + + test.fail_test(not test.match_re(test.stderr(), r""" +scons: warning: Could not detect qt, using moc executable as a hint \(QTDIR=%s\) +File "SConstruct", line \d+, in .+ +""" % (re.escape(QTDIR)))) + else: print "Could not find QT, skipping test(s)." ############################################################################## -# 8. test the $QT_AUTOBUUILD_MOC_SOURCES variable +# 8. test the $QT_AUTOBUILD_MOC_SOURCES variable +# +# This has been removed, but I'm leaving the test here, commented out, +# in case we ever resurrect this functionality again in the future. +# +#aaa_dll = dll_ + 'aaa' + _dll +#moc = 'moc_aaa.cc' +# +#createSConstruct(test, ['work8', 'SConstruct']) +# +#test.write(['work8', 'SConscript'], """ +#Import("env") +#env = env.Copy(QT_AUTOBUILD_MOC_SOURCES = 0) +#env.SharedLibrary(target = 'aaa', source = ['aaa.ui', 'useit.cpp', 'aaa_function.cpp']) +#""") +# +#test.write(['work8', 'aaa.ui'], r""" +##if defined (_WIN32) || defined(__CYGWIN__) +##define DLLEXPORT __declspec(dllexport) +##else +##define DLLEXPORT +##endif +#DLLEXPORT void aaa(void) +#""") +# +#test.write(['work8', 'useit.cpp'], r""" +##include "aaa.h" +#void useit() { +# aaa(); +#} +#""") +# +#test.write(['work8', 'aaa_function.cpp'], r""" +##include "my_qobject.h" +##if defined (_WIN32) || defined(__CYGWIN__) +##define DLLEXPORT __declspec(dllexport) +##else +##define DLLEXPORT +##endif +#DLLEXPORT void aaa(void) +# { my_qt_symbol( "aaa_function.cpp\n" ); } +#""") +# +#test.run(chdir='work8', arguments = aaa_dll) +# +#test.must_not_exist(test.workpath('work8', moc)) +# +#test.write(['work8', 'SConscript'], """ +#Import("env") +#env = env.Copy(QT_AUTOBUILD_MOC_SOURCES = 1) +#env.SharedLibrary(target = 'aaa', source = ['aaa.ui', 'useit.cpp']) +#""") +# +#test.run(chdir='work8', arguments = aaa_dll) +# +#test.must_exist(test.workpath('work8', moc)) -aaa_dll = dll_ + 'aaa' + _dll -moc = 'moc_aaa.cc' +############################################################################## +# 9. test that an overwritten CPPPATH is working with generated files -createSConstruct(test, ['work8', 'SConstruct']) +# this is basically test 1, but with an additional include +aaa_exe = 'aaa' + _exe -test.write(['work8', 'SConscript'], """ +createSConstruct(test, ['work9', 'SConstruct']) +test.write( ['work9', 'SConscript'], """ Import("env") -env = env.Copy(QT_AUTOBUILD_MOC_SOURCES = 0) -env.SharedLibrary(target = 'aaa', source = ['aaa.ui', 'useit.cpp']) +env.Program(target = 'aaa', source = 'aaa.cpp', CPPPATH=['$CPPPATH', './local_include']) """) -test.write(['work8', 'aaa.ui'], r""" -void aaa(void) +test.write(['work9', 'aaa.cpp'], r""" +#include "aaa.h" +int main() { aaa(); return 0; } """) -test.write(['work8', 'useit.cpp'], r""" +test.write(['work9', 'aaa.h'], r""" +#include "my_qobject.h" +#include "local_include.h" +void aaa(void) Q_OBJECT; +""") + +test.write(['work9', 'local_include', 'local_include.h'], r""" +/* empty; just needs to be found */ +""") + +test.run(chdir='work9', arguments = aaa_exe) + +############################################################################## +# 10. test that an appended relative CPPPATH is working with generated files + +# this is basically test 9, but the include path is env.Append-ed and +# everything goes into sub directory "sub" +aaa_exe = os.path.join('sub', 'aaa' + _exe) + +createSConstruct(test, ['work10', 'SConstruct']) +test.write( ['work10', 'SConscript'], r""" +SConscript('sub/SConscript') +""") + +test.write( ['work10', 'sub', 'SConscript'], r""" +Import("env") +env.Append(CPPPATH=['./local_include']) +env.Program(target = 'aaa', source = 'aaa.cpp') +""") + +test.write(['work10', 'sub', 'aaa.cpp'], r""" #include "aaa.h" -void useit() { - aaa(); -} +int main() { aaa(); return 0; } +""") + +test.write(['work10', 'sub', 'aaa.h'], r""" +#include "my_qobject.h" +#include "local_include.h" +void aaa(void) Q_OBJECT; +""") + +test.write(['work10', 'sub', 'local_include', 'local_include.h'], r""" +/* empty; just needs to be found */ """) -test.run(chdir='work8', arguments = aaa_dll) +test.run(chdir='work10', arguments = aaa_exe) + +############################################################################### +# 11. test the manual QT builder calls -test.must_not_exist(test.workpath('work8', moc)) +aaa_exe = 'aaa' + _exe -test.write(['work8', 'SConscript'], """ +createSConstruct(test, ['work11', 'SConstruct']) +test.write( ['work11', 'SConscript'], r""" Import("env") -env = env.Copy(QT_AUTOBUILD_MOC_SOURCES = 1) -env.SharedLibrary(target = 'aaa', source = ['aaa.ui', 'useit.cpp']) +sources = ['aaa.cpp', 'bbb.cpp', 'ddd.cpp', 'eee.cpp', 'main.cpp'] + +# normal invocation +sources.append(env.Moc('include/aaa.h')) +env.Moc('bbb.cpp') +sources.extend(env.Uic('ui/ccc.ui')[1:]) + +# manual target specification +sources.append(env.Moc('moc-ddd.cpp', 'include/ddd.h', + QT_MOCHPREFIX='')) # Watch out ! +env.Moc('moc_eee.cpp', 'eee.cpp') +sources.extend(env.Uic(['include/uic_fff.hpp', 'fff.cpp', 'fff.moc.cpp'], + 'ui/fff.ui')[1:]) + +print map(str,sources) +env.Program(target='aaa', + source=sources, + CPPPATH=['$CPPPATH', './include'], + QT_AUTOSCAN=0) +""") + +test.write(['work11', 'aaa.cpp'], r""" +#include "aaa.h" +""") + +test.write(['work11', 'include', 'aaa.h'], r""" +#include "my_qobject.h" +void aaa(void) Q_OBJECT; +""") + +test.write(['work11', 'bbb.h'], r""" +void bbb(void); +""") + +test.write(['work11', 'bbb.cpp'], r""" +#include "my_qobject.h" +void bbb(void) Q_OBJECT +#include "bbb.moc" +""") + +test.write(['work11', 'ui', 'ccc.ui'], r""" +void ccc(void) """) -test.run(chdir='work8', arguments = aaa_dll) +test.write(['work11', 'ddd.cpp'], r""" +#include "ddd.h" +""") + +test.write(['work11', 'include', 'ddd.h'], r""" +#include "my_qobject.h" +void ddd(void) Q_OBJECT; +""") + +test.write(['work11', 'eee.h'], r""" +void eee(void); +""") + +test.write(['work11', 'eee.cpp'], r""" +#include "my_qobject.h" +void eee(void) Q_OBJECT +#include "moc_eee.cpp" +""") + +test.write(['work11', 'ui', 'fff.ui'], r""" +void fff(void) +""") -test.must_exist(test.workpath('work8', moc)) +test.write(['work11', 'main.cpp'], r""" +#include "aaa.h" +#include "bbb.h" +#include "ui/ccc.h" +#include "ddd.h" +#include "eee.h" +#include "uic_fff.hpp" +int main() { + aaa(); bbb(); ccc(); ddd(); eee(); fff(); return 0; +} +""") + +test.run(chdir='work11', arguments = aaa_exe) + +# normal invocation +test.must_exist(test.workpath('work11', 'include', 'moc_aaa.cc')) +test.must_exist(test.workpath('work11', 'bbb.moc')) +test.must_exist(test.workpath('work11', 'ui', 'ccc.h')) +test.must_exist(test.workpath('work11', 'ui', 'uic_ccc.cc')) +test.must_exist(test.workpath('work11', 'ui', 'moc_ccc.cc')) + +# manual target spec. +test.must_exist(test.workpath('work11', 'moc-ddd.cpp')) +test.must_exist(test.workpath('work11', 'moc_eee.cpp')) +test.must_exist(test.workpath('work11', 'include', 'uic_fff.hpp')) +test.must_exist(test.workpath('work11', 'fff.cpp')) +test.must_exist(test.workpath('work11', 'fff.moc.cpp')) + + +############################################################################## +# 12. test the tool warings +createSConstruct(test, ['work12', 'SConstruct']) + +test.write(['work12', 'aaa.cpp'], r""" +#include "my_qobject.h" +void aaa(void) Q_OBJECT +""") + +test.write(['work12', 'SConscript'], r""" +Import("env") +import os +env.StaticLibrary('aaa.cpp') +""") + +test.run(chdir='work12', stderr=None) +test.fail_test(not test.match_re(test.stderr(), r""" +scons: warning: Generated moc file 'aaa.moc' is not included by 'aaa.cpp' +File .+ +""")) + +os.environ['QTDIR'] = QT +test.run(chdir='work12', arguments='-n noqtdir=1') + +# We'd like to eliminate $QTDIR from the environment as follows: +# del os.environ['QTDIR'] +# But unfortunately, in at least some versions of Python, the Environment +# class doesn't implement a __delitem__() method to make the library +# call to actually remove the deleted variable from the *external* +# environment, so it only gets removed from the Python dictionary. +# Consequently, we need to just wipe out its value as follows> +os.environ['QTDIR'] = '' +test.run(chdir='work12', stderr=None, arguments='-n noqtdir=1') +test.fail_test(not test.match_re(test.stderr(), r""" +scons: warning: Could not detect qt, using empty QTDIR +File "SConstruct", line \d+, in .+ +""")) test.pass_test() diff --git a/test/QTFLAGS.py b/test/QTFLAGS.py index 7964d44..867dbfb 100644 --- a/test/QTFLAGS.py +++ b/test/QTFLAGS.py @@ -152,17 +152,21 @@ createSConstruct(test, ['SConstruct'], QT_UICDECLFLAGS='-y', QT_MOCFROMHFLAGS='-z', QT_MOCFROMCXXFLAGS='-i -w', - QT_HSUFFIX='.hpp', - QT_MOCNAMEGENERATOR=lambda x,src_suffix,env: x + '.moc.cpp', - QT_UISUFFIX='.des', - QT_UIHSUFFUX='.des.hpp', - CXXFILESUFFIX='.cpp',""") + QT_UICDECLPREFIX='uic-', + QT_UICDECLSUFFIX='.hpp', + QT_UICIMPLPREFIX='', + QT_UICIMPLSUFFIX='.cxx', + QT_MOCHPREFIX='mmm', + QT_MOCHSUFFIX='.cxx', + QT_MOCCXXPREFIX='moc', + QT_MOCCXXSUFFIX='.inl', + QT_UISUFFIX='.myui',""") test.write('SConscript',""" Import("env") env.Program('mytest', ['mocFromH.cpp', 'mocFromCpp.cpp', - 'an_ui_file.des', - 'another_ui_file.des', + 'an_ui_file.myui', + 'another_ui_file.myui', 'main.cpp']) """) @@ -178,14 +182,14 @@ test.write('mocFromH.cpp', """ test.write('mocFromCpp.cpp', """ #include "my_qobject.h" void mocFromCpp() Q_OBJECT -#include "mocFromCpp.moc.cpp" +#include "mocmocFromCpp.inl" """) -test.write('an_ui_file.des', """ +test.write('an_ui_file.myui', """ void an_ui_file() """) -test.write('another_ui_file.des', """ +test.write('another_ui_file.myui', """ void another_ui_file() """) @@ -195,8 +199,8 @@ test.write('another_ui_file.desc.hpp', """ test.write('main.cpp', """ #include "mocFromH.hpp" -#include "an_ui_file.hpp" -#include "another_ui_file.hpp" +#include "uic-an_ui_file.hpp" +#include "uic-another_ui_file.hpp" void mocFromCpp(); int main() { @@ -213,14 +217,29 @@ def _existAll( test, files ): return reduce(lambda x,y: x and y, map(os.path.exists,map(test.workpath, files))) -test.fail_test(not _existAll(test, ['mocFromH.moc.cpp', - 'mocFromCpp.moc.cpp', - 'an_ui_file.cpp', - 'an_ui_file.hpp', - 'an_ui_file.moc.cpp', - 'another_ui_file.cpp', - 'another_ui_file.hpp', - 'another_ui_file.moc.cpp'])) +createSConstruct(test, ['SConstruct'], + """QT_UICIMPLFLAGS='-x', + QT_UICDECLFLAGS='-y', + QT_MOCFROMHFLAGS='-z', + QT_MOCFROMCXXFLAGS='-i -w', + QT_UICDECLPREFIX='uic-', + QT_UICDECLSUFFIX='.hpp', + QT_UICIMPLPREFIX='', + QT_UICIMPLSUFFIX='.cxx', + QT_MOCHPREFIX='mmm', + QT_MOCHSUFFIX='.cxx', + QT_MOCCXXPREFIX='moc', + QT_MOCCXXSUFFIX=`.inl', + QT_UISUFFIX='.myui',""") + +test.fail_test(not _existAll(test, ['mmmmocFromH.cxx', + 'mocmocFromCpp.inl', + 'an_ui_file.cxx', + 'uic-an_ui_file.hpp', + 'mmman_ui_file.cxx', + 'another_ui_file.cxx', + 'uic-another_ui_file.hpp', + 'mmmanother_ui_file.cxx'])) def _flagTest(test,fileToContentsStart): import string @@ -229,10 +248,10 @@ def _flagTest(test,fileToContentsStart): return 1 return 0 -test.fail_test(_flagTest(test, {'mocFromH.moc.cpp':'/* mymoc.py -z */', - 'mocFromCpp.moc.cpp':'/* mymoc.py -w */', - 'an_ui_file.cpp':'/* myuic.py -x */', - 'an_ui_file.hpp':'/* myuic.py -y */', - 'an_ui_file.moc.cpp':'/* mymoc.py -z */'})) +test.fail_test(_flagTest(test, {'mmmmocFromH.cxx':'/* mymoc.py -z */', + 'mocmocFromCpp.inl':'/* mymoc.py -w */', + 'an_ui_file.cxx':'/* myuic.py -x */', + 'uic-an_ui_file.hpp':'/* myuic.py -y */', + 'mmman_ui_file.cxx':'/* mymoc.py -z */'})) test.pass_test() diff --git a/test/import.py b/test/import.py index 48ae546..e7e21f1 100644 --- a/test/import.py +++ b/test/import.py @@ -119,7 +119,11 @@ error_output = { 'icl' : """ scons: warning: Intel license dir was not found. Tried using the INTEL_LICENSE_FILE environment variable (), the registry () and the default path (C:\Program Files\Common Files\Intel\Licenses). Using the default path as a last resort. File "SConstruct", line 1, in ? -""" +""", + 'qt' : """ +scons: warning: Could not detect qt, using empty QTDIR +File "SConstruct", line 1, in ? +""", } # An SConstruct for importing Tool names that have illegal characters @@ -138,6 +142,7 @@ import SCons.Tool.%s x = SCons.Tool.%s.generate """ +failures = [] for tool in tools: if tool[0] in '0123456789' or '+' in tool: test.write('SConstruct', indirect_import % (tool, tool, tool)) @@ -148,6 +153,8 @@ for tool in tools: if stderr != '' and stderr != error_output.get(tool, ''): print "Failed importing '%s', stderr:" % tool print stderr - test.fail_test(1) + failures.append[tool] + +test.fail_test(len(failures)) test.pass_test() -- cgit v0.12