diff options
-rw-r--r-- | QMTest/TestCmd.py | 24 | ||||
-rw-r--r-- | doc/user/add-method.in | 2 | ||||
-rw-r--r-- | doc/user/add-method.xml | 2 | ||||
-rw-r--r-- | doc/user/builders-writing.in | 6 | ||||
-rw-r--r-- | doc/user/builders-writing.xml | 6 | ||||
-rw-r--r-- | doc/user/command-line.in | 30 | ||||
-rw-r--r-- | doc/user/command-line.xml | 30 | ||||
-rw-r--r-- | doc/user/depends.in | 6 | ||||
-rw-r--r-- | doc/user/depends.xml | 6 | ||||
-rw-r--r-- | doc/user/environments.in | 4 | ||||
-rw-r--r-- | doc/user/environments.xml | 4 | ||||
-rw-r--r-- | src/CHANGES.txt | 13 | ||||
-rw-r--r-- | src/engine/SCons/Node/FS.py | 2 | ||||
-rw-r--r-- | src/engine/SCons/Node/__init__.py | 3 | ||||
-rw-r--r-- | src/engine/SCons/Taskmaster.py | 31 | ||||
-rw-r--r-- | src/engine/SCons/TaskmasterTests.py | 91 | ||||
-rw-r--r-- | src/engine/SCons/Tool/tex.py | 38 | ||||
-rw-r--r-- | test/TEX/multibib.py | 97 | ||||
-rw-r--r-- | test/explain/basic.py | 2 |
19 files changed, 345 insertions, 52 deletions
diff --git a/QMTest/TestCmd.py b/QMTest/TestCmd.py index 1d5e4d0..a27df27 100644 --- a/QMTest/TestCmd.py +++ b/QMTest/TestCmd.py @@ -128,6 +128,7 @@ There are a bunch of methods that let you do different things: test.match_exact("actual 1\nactual 2\n", "expected 1\nexpected 2\n") test.match_exact(["actual 1\n", "actual 2\n"], ["expected 1\n", "expected 2\n"]) + test.match_caseinsensitive("Actual 1\nACTUAL 2\n", "expected 1\nEXPECTED 2\n") test.match_re("actual 1\nactual 2\n", regex_string) test.match_re(["actual 1\n", "actual 2\n"], list_of_regexes) @@ -180,6 +181,8 @@ matching in the same way as the match_*() methods described above. test = TestCmd.TestCmd(match = TestCmd.match_exact) + test = TestCmd.TestCmd(match = TestCmd.match_caseinsensitive) + test = TestCmd.TestCmd(match = TestCmd.match_re) test = TestCmd.TestCmd(match = TestCmd.match_re_dotall) @@ -190,6 +193,8 @@ These functions are also available as static methods: test = TestCmd.TestCmd(match = TestCmd.TestCmd.match_exact) + test = TestCmd.TestCmd(match = TestCmd.TestCmd.match_caseinsensitive) + test = TestCmd.TestCmd(match = TestCmd.TestCmd.match_re) test = TestCmd.TestCmd(match = TestCmd.TestCmd.match_re_dotall) @@ -200,6 +205,8 @@ These static methods can be accessed by a string naming the method: test = TestCmd.TestCmd(match = 'match_exact') + test = TestCmd.TestCmd(match = 'match_caseinsensitive') + test = TestCmd.TestCmd(match = 'match_re') test = TestCmd.TestCmd(match = 'match_re_dotall') @@ -328,6 +335,7 @@ __all__ = [ 'no_result', 'pass_test', 'match_exact', + 'match_caseinsensitive', 'match_re', 'match_re_dotall', 'python', @@ -466,6 +474,20 @@ def match_exact(lines = None, matches = None): return return 1 +def match_caseinsensitive(lines = None, matches = None): + """ + """ + if not is_List(lines): + lines = lines.split("\n") + if not is_List(matches): + matches = matches.split("\n") + if len(lines) != len(matches): + return + for i in range(len(lines)): + if lines[i].lower() != matches[i].lower(): + return + return 1 + def match_re(lines = None, res = None): """ """ @@ -1127,6 +1149,8 @@ class TestCmd(object): match_exact = staticmethod(match_exact) + match_caseinsensitive = staticmethod(match_caseinsensitive) + match_re = staticmethod(match_re) match_re_dotall = staticmethod(match_re_dotall) diff --git a/doc/user/add-method.in b/doc/user/add-method.in index 7efd923..2deb07a 100644 --- a/doc/user/add-method.in +++ b/doc/user/add-method.in @@ -65,7 +65,7 @@ <para> - As mentioned, a psuedo-builder also provides more flexibility + As mentioned, a pseudo-builder also provides more flexibility in parsing arguments than you can get with a &Builder;. The next example shows a pseudo-builder with a named argument that modifies the filename, and a separate argument diff --git a/doc/user/add-method.xml b/doc/user/add-method.xml index 51bc04b..3aac7a8 100644 --- a/doc/user/add-method.xml +++ b/doc/user/add-method.xml @@ -64,7 +64,7 @@ <para> - As mentioned, a psuedo-builder also provides more flexibility + As mentioned, a pseudo-builder also provides more flexibility in parsing arguments than you can get with a &Builder;. The next example shows a pseudo-builder with a named argument that modifies the filename, and a separate argument diff --git a/doc/user/builders-writing.in b/doc/user/builders-writing.in index 93a183c..749a8ba 100644 --- a/doc/user/builders-writing.in +++ b/doc/user/builders-writing.in @@ -98,8 +98,8 @@ This functionality could be invoked as in the following example: <para> Although &SCons; provides many useful methods - for building common software products: - programs, libraries, documents. + for building common software products + (programs, libraries, documents, etc.), you frequently want to be able to build some other type of file not supported directly by &SCons;. @@ -109,7 +109,7 @@ This functionality could be invoked as in the following example: (In fact, the &SCons; interfaces for creating &Builder; objects are flexible enough and easy enough to use that all of the the &SCons; built-in &Builder; objects - are created the mechanisms described in this section.) + are created using the mechanisms described in this section.) </para> diff --git a/doc/user/builders-writing.xml b/doc/user/builders-writing.xml index c8ff003..f42a61f 100644 --- a/doc/user/builders-writing.xml +++ b/doc/user/builders-writing.xml @@ -98,8 +98,8 @@ This functionality could be invoked as in the following example: <para> Although &SCons; provides many useful methods - for building common software products: - programs, libraries, documents. + for building common software products + (programs, libraries, documents, etc.), you frequently want to be able to build some other type of file not supported directly by &SCons;. @@ -109,7 +109,7 @@ This functionality could be invoked as in the following example: (In fact, the &SCons; interfaces for creating &Builder; objects are flexible enough and easy enough to use that all of the the &SCons; built-in &Builder; objects - are created the mechanisms described in this section.) + are created using the mechanisms described in this section.) </para> diff --git a/doc/user/command-line.in b/doc/user/command-line.in index abf8953..33f88ec 100644 --- a/doc/user/command-line.in +++ b/doc/user/command-line.in @@ -62,7 +62,7 @@ Any command-line argument containing an <literal>=</literal> (equal sign) is considered a variable setting with the form - <varname>variable</varname>=<varname>value</varname> + <varname>variable</varname>=<varname>value</varname>. &SCons; provides direct access to all of the command-line variable settings, the ability to apply command-line variable settings @@ -885,7 +885,7 @@ <scons_example name="Variables1"> <file name="SConstruct" printme="1"> - vars = Variables() + vars = Variables(None, ARGUMENTS) vars.Add('RELEASE', 'Set to 1 to build for release', 0) env = Environment(variables = vars, CPPDEFINES={'RELEASE_BUILD' : '${RELEASE}'}) @@ -902,8 +902,9 @@ <para> This &SConstruct; file first creates a &Variables; object - (the <literal>vars = Variables()</literal> call), - and then uses the object's &Add; + which uses the values from the command-line options dictionary &ARGUMENTS; + (the <literal>vars = Variables(None, ARGUMENTS)</literal> call). + It then uses the object's &Add; method to indicate that the &RELEASE; variable can be set on the command line, and that its default value will be <literal>0</literal> @@ -942,7 +943,7 @@ &PathOption;, &PackageOption; and &AddOptions;. These older names still work, and you may encounter them in older - &SConscript; fles, + &SConscript; files, but they have been officially deprecated as of &SCons; version 2.0. @@ -975,7 +976,7 @@ <scons_example name="Variables_Help"> <file name="SConstruct" printme="1"> - vars = Variables('custom.py') + vars = Variables(None, ARGUMENTS) vars.Add('RELEASE', 'Set to 1 to build for release', 0) env = Environment(variables = vars) Help(vars.GenerateHelpText(env)) @@ -1098,6 +1099,23 @@ <scons_output_command>scons -Q</scons_output_command> </scons_output> + <para> + + Finally, you can combine both methods with: + + </para> + + <screen> + vars = Variables('custom.py', ARGUMENTS) + </screen> + + <para> + + where values in the option file &custom_py; get overwritten + by the ones specified on the command line. + + </para> + </section> <section> diff --git a/doc/user/command-line.xml b/doc/user/command-line.xml index 1bb84e2..1006c6b 100644 --- a/doc/user/command-line.xml +++ b/doc/user/command-line.xml @@ -62,7 +62,7 @@ Any command-line argument containing an <literal>=</literal> (equal sign) is considered a variable setting with the form - <varname>variable</varname>=<varname>value</varname> + <varname>variable</varname>=<varname>value</varname>. &SCons; provides direct access to all of the command-line variable settings, the ability to apply command-line variable settings @@ -875,7 +875,7 @@ </para> <programlisting> - vars = Variables() + vars = Variables(None, ARGUMENTS) vars.Add('RELEASE', 'Set to 1 to build for release', 0) env = Environment(variables = vars, CPPDEFINES={'RELEASE_BUILD' : '${RELEASE}'}) @@ -885,8 +885,9 @@ <para> This &SConstruct; file first creates a &Variables; object - (the <literal>vars = Variables()</literal> call), - and then uses the object's &Add; + which uses the values from the command-line options dictionary &ARGUMENTS; + (the <literal>vars = Variables(None, ARGUMENTS)</literal> call). + It then uses the object's &Add; method to indicate that the &RELEASE; variable can be set on the command line, and that its default value will be <literal>0</literal> @@ -928,7 +929,7 @@ &PathOption;, &PackageOption; and &AddOptions;. These older names still work, and you may encounter them in older - &SConscript; fles, + &SConscript; files, but they have been officially deprecated as of &SCons; version 2.0. @@ -960,7 +961,7 @@ </para> <programlisting> - vars = Variables('custom.py') + vars = Variables(None, ARGUMENTS) vars.Add('RELEASE', 'Set to 1 to build for release', 0) env = Environment(variables = vars) Help(vars.GenerateHelpText(env)) @@ -1069,6 +1070,23 @@ cc -o foo foo.o bar.o </screen> + <para> + + Finally, you can combine both methods with: + + </para> + + <screen> + vars = Variables('custom.py', ARGUMENTS) + </screen> + + <para> + + where values in the option file &custom_py; get overwritten + by the ones specified on the command line. + + </para> + </section> <section> diff --git a/doc/user/depends.in b/doc/user/depends.in index 88828fe..f601312 100644 --- a/doc/user/depends.in +++ b/doc/user/depends.in @@ -593,11 +593,11 @@ <para> - Another thing to look out for, is the fact that the three + Another thing to look out for is the fact that the three attributes above may not be present at the time of the first run. - Without any prior build, no targets got created and no + Without any prior build, no targets have been created and no <filename>.sconsign</filename> DB file exists yet. - So, it is recommended to always check whether the + So, you should always check whether the <varname>prev_ni</varname> attribute in question is available. </para> diff --git a/doc/user/depends.xml b/doc/user/depends.xml index a5e84d6..df2a911 100644 --- a/doc/user/depends.xml +++ b/doc/user/depends.xml @@ -589,11 +589,11 @@ <para> - Another thing to look out for, is the fact that the three + Another thing to look out for is the fact that the three attributes above may not be present at the time of the first run. - Without any prior build, no targets got created and no + Without any prior build, no targets have been created and no <filename>.sconsign</filename> DB file exists yet. - So, it is recommended to always check whether the + So, you should always check whether the <varname>prev_ni</varname> attribute in question is available. </para> diff --git a/doc/user/environments.in b/doc/user/environments.in index f767676..9f39347 100644 --- a/doc/user/environments.in +++ b/doc/user/environments.in @@ -684,7 +684,7 @@ environment, of directory names, suffixes, etc. <para> Another way to get information from - a construction environment. + a construction environment is to use the &subst; method on a string containing <literal>$</literal> expansions of construction variable names. @@ -874,7 +874,7 @@ environment, of directory names, suffixes, etc. <para> You can, however, control the settings - in the default contstruction environment + in the default construction environment by using the &DefaultEnvironment; function to initialize various settings: diff --git a/doc/user/environments.xml b/doc/user/environments.xml index b2a8505..eaf4ba3 100644 --- a/doc/user/environments.xml +++ b/doc/user/environments.xml @@ -684,7 +684,7 @@ environment, of directory names, suffixes, etc. <para> Another way to get information from - a construction environment. + a construction environment is to use the &subst; method on a string containing <literal>$</literal> expansions of construction variable names. @@ -875,7 +875,7 @@ environment, of directory names, suffixes, etc. <para> You can, however, control the settings - in the default contstruction environment + in the default construction environment by using the &DefaultEnvironment; function to initialize various settings: diff --git a/src/CHANGES.txt b/src/CHANGES.txt index fbe4f25..f32c3f1 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -4,6 +4,19 @@ Change Log +RELEASE 2.X.X - + + From Alexey Klimkin: + - Fixed the Taskmaster, curing spurious build failures in + multi-threaded runs (#2720). + + From Dirk Baechle: + - Improved documentation of command-line variables (#2809). + - Fixed scons-doc.py to properly convert main XML files (#2812). + + From Rob Managan: + - Updated the TeX builder to support LaTeX's multibib package. + RELEASE 2.1.0 - Mon, 09 Sep 2011 20:54:57 -0700 diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py index f106d46..f31ca83 100644 --- a/src/engine/SCons/Node/FS.py +++ b/src/engine/SCons/Node/FS.py @@ -2715,7 +2715,7 @@ class File(Base): so only do thread safe stuff here. Do thread unsafe stuff in built(). - Returns true iff the node was successfully retrieved. + Returns true if the node was successfully retrieved. """ if self.nocache: return None diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py index 2fbe2c6..992284d 100644 --- a/src/engine/SCons/Node/__init__.py +++ b/src/engine/SCons/Node/__init__.py @@ -216,6 +216,7 @@ class Node(object): self.precious = None self.noclean = 0 self.nocache = 0 + self.cached = 0 # is this node pulled from cache? self.always_build = None self.includes = None self.attributes = self.Attrs() # Generic place to stick information about the Node. @@ -306,7 +307,7 @@ class Node(object): so only do thread safe stuff here. Do thread unsafe stuff in built(). - Returns true iff the node was successfully retrieved. + Returns true if the node was successfully retrieved. """ return 0 diff --git a/src/engine/SCons/Taskmaster.py b/src/engine/SCons/Taskmaster.py index 58a8d90..64ab84d 100644 --- a/src/engine/SCons/Taskmaster.py +++ b/src/engine/SCons/Taskmaster.py @@ -227,20 +227,26 @@ class Task(object): if T: T.write(self.trace_message(u'Task.execute()', self.node)) try: - everything_was_cached = 1 + cached_targets = [] for t in self.targets: - if t.retrieve_from_cache(): - # Call the .built() method without calling the - # .push_to_cache() method, since we just got the - # target from the cache and don't need to push - # it back there. - t.set_state(NODE_EXECUTED) - t.built() - else: - everything_was_cached = 0 + if not t.retrieve_from_cache(): break - if not everything_was_cached: + cached_targets.append(t) + if len(cached_targets) < len(self.targets): + # Remove targets before building. It's possible that we + # partially retrieved targets from the cache, leaving + # them in read-only mode. That might cause the command + # to fail. + # + for t in cached_targets: + try: + t.fs.unlink(t.path) + except (IOError, OSError): + pass self.targets[0].build() + else: + for t in cached_targets: + t.cached = 1 except SystemExit: exc_value = sys.exc_info()[1] raise SCons.Errors.ExplicitExit(self.targets[0], exc_value.code) @@ -292,7 +298,8 @@ class Task(object): for side_effect in t.side_effects: side_effect.set_state(NODE_NO_STATE) t.set_state(NODE_EXECUTED) - t.push_to_cache() + if not t.cached: + t.push_to_cache() t.built() t.visited() diff --git a/src/engine/SCons/TaskmasterTests.py b/src/engine/SCons/TaskmasterTests.py index 66c6b9c..85ade8d 100644 --- a/src/engine/SCons/TaskmasterTests.py +++ b/src/engine/SCons/TaskmasterTests.py @@ -86,16 +86,61 @@ class Node(object): def prepare(self): self.prepared = 1 + self.get_binfo() def build(self): global built_text built_text = self.name + " built" + def remove(self): + pass + + # The following four methods new_binfo(), del_binfo(), + # get_binfo(), clear() as well as its calls have been added + # to support the cached_execute() test (issue #2720). + # They are full copies (or snippets) of their actual + # counterparts in the Node class... + def new_binfo(self): + binfo = "binfo" + return binfo + + def del_binfo(self): + """Delete the build info from this node.""" + try: + delattr(self, 'binfo') + except AttributeError: + pass + + def get_binfo(self): + """Fetch a node's build information.""" + try: + return self.binfo + except AttributeError: + pass + + binfo = self.new_binfo() + self.binfo = binfo + + return binfo + + def clear(self): + # The del_binfo() call here isn't necessary for normal execution, + # but is for interactive mode, where we might rebuild the same + # target and need to start from scratch. + self.del_binfo() + def built(self): global built_text if not self.cached: built_text = built_text + " really" - + + # Clear the implicit dependency caches of any Nodes + # waiting for this Node to be built. + for parent in self.waiting_parents: + parent.implicit = None + + self.clear() + def has_builder(self): return not self.builder is None @@ -954,6 +999,50 @@ class TaskmasterTestCase(unittest.TestCase): assert built_text is None, built_text assert cache_text == ["n7 retrieved", "n8 retrieved"], cache_text + def test_cached_execute(self): + """Test executing a task with cached targets + """ + # In issue #2720 Alexei Klimkin detected that the previous + # workflow for execute() led to problems in a multithreaded build. + # We have: + # task.prepare() + # task.execute() + # task.executed() + # -> node.visited() + # for the Serial flow, but + # - Parallel - - Worker - + # task.prepare() + # requestQueue.put(task) + # task = requestQueue.get() + # task.execute() + # resultQueue.put(task) + # task = resultQueue.get() + # task.executed() + # ->node.visited() + # in parallel. Since execute() used to call built() when a target + # was cached, it could unblock dependent nodes before the binfo got + # restored again in visited(). This resulted in spurious + # "file not found" build errors, because files fetched from cache would + # be seen as not up to date and wouldn't be scanned for implicit + # dependencies. + # + # The following test ensures that execute() only marks targets as cached, + # but the actual call to built() happens in executed() only. + # Like this, the binfo should still be intact after calling execute()... + global cache_text + + n1 = Node("n1") + # Mark the node as being cached + n1.cached = 1 + tm = SCons.Taskmaster.Taskmaster([n1]) + t = tm.next_task() + t.prepare() + t.execute() + assert cache_text == ["n1 retrieved"], cache_text + # If no binfo exists anymore, something has gone wrong... + has_binfo = hasattr(n1, 'binfo') + assert has_binfo == True, has_binfo + def test_exception(self): """Test generic Taskmaster exception handling diff --git a/src/engine/SCons/Tool/tex.py b/src/engine/SCons/Tool/tex.py index b0c518e..0d871ad 100644 --- a/src/engine/SCons/Tool/tex.py +++ b/src/engine/SCons/Tool/tex.py @@ -86,6 +86,7 @@ tableofcontents_re = re.compile(r"^[^%\n]*\\tableofcontents", re.MULTILINE) makeindex_re = re.compile(r"^[^%\n]*\\makeindex", re.MULTILINE) bibliography_re = re.compile(r"^[^%\n]*\\bibliography", re.MULTILINE) bibunit_re = re.compile(r"^[^%\n]*\\begin\{bibunit\}", re.MULTILINE) +multibib_re = re.compile(r"^[^%\n]*\\newcites\{([^\}]*)\}", re.MULTILINE) listoffigures_re = re.compile(r"^[^%\n]*\\listoffigures", re.MULTILINE) listoftables_re = re.compile(r"^[^%\n]*\\listoftables", re.MULTILINE) hyperref_re = re.compile(r"^[^%\n]*\\usepackage.*\{hyperref\}", re.MULTILINE) @@ -236,6 +237,9 @@ def InternalLaTeXAuxAction(XXXLaTeXAction, target = None, source= None, env=None must_rerun_latex = True + # .aux files already processed by BibTex + already_bibtexed = [] + # # routine to update MD5 hash and compare # @@ -298,21 +302,23 @@ def InternalLaTeXAuxAction(XXXLaTeXAction, target = None, source= None, env=None # The information that bibtex reads from the .aux file is # pass-independent. If we find (below) that the .bbl file is unchanged, # then the last latex saw a correct bibliography. - # Therefore only do this on the first pass - if count == 1: - for auxfilename in auxfiles: + # Therefore only do this once + # Go through all .aux files and remember the files already done. + for auxfilename in auxfiles: + if auxfilename not in already_bibtexed: + already_bibtexed.append(auxfilename) target_aux = os.path.join(targetdir, auxfilename) if os.path.isfile(target_aux): content = open(target_aux, "rb").read() if content.find("bibdata") != -1: if Verbose: - print "Need to run bibtex" + print "Need to run bibtex on ",auxfilename bibfile = env.fs.File(SCons.Util.splitext(target_aux)[0]) result = BibTeXAction(bibfile, bibfile, env) if result != 0: check_file_error_message(env['BIBTEX'], 'blg') - must_rerun_latex = must_rerun_latex or check_MD5(suffix_nodes['.bbl'],'.bbl') - + #must_rerun_latex = must_rerun_latex or check_MD5(suffix_nodes['.bbl'],'.bbl') + must_rerun_latex = True # Now decide if latex will need to be run again due to index. if check_MD5(suffix_nodes['.idx'],'.idx') or (count == 1 and run_makeindex): # We must run makeindex @@ -553,6 +559,8 @@ def ScanFiles(theFile, target, paths, file_tests, file_tests_search, env, graphi for i in range(len(file_tests_search)): if file_tests[i][0] is None: file_tests[i][0] = file_tests_search[i].search(content) + if Verbose and file_tests[i][0]: + print " found match for ",file_tests[i][-1][-1] incResult = includeOnly_re.search(content) if incResult: @@ -621,6 +629,7 @@ def tex_emitter_core(target, source, env, graphics_extensions): makeindex_re, bibliography_re, bibunit_re, + multibib_re, tableofcontents_re, listoffigures_re, listoftables_re, @@ -636,6 +645,7 @@ def tex_emitter_core(target, source, env, graphics_extensions): ['.idx', '.ind', '.ilg','makeindex'], ['.bbl', '.blg','bibliography'], ['.bbl', '.blg','bibunit'], + ['.bbl', '.blg','multibib'], ['.toc','contents'], ['.lof','figures'], ['.lot','tables'], @@ -678,6 +688,8 @@ def tex_emitter_core(target, source, env, graphics_extensions): for (theSearch,suffix_list) in file_tests: # add side effects if feature is present.If file is to be generated,add all side effects + if Verbose and theSearch: + print "check side effects for ",suffix_list[-1] if (theSearch != None) or (not source[0].exists() ): file_list = [targetbase,] # for bibunit we need a list of files @@ -687,6 +699,18 @@ def tex_emitter_core(target, source, env, graphics_extensions): # remove the suffix '.aux' for i in range(len(file_list)): file_list[i] = SCons.Util.splitext(file_list[i])[0] + # for multibib we need a list of files + if suffix_list[-1] == 'multibib': + file_list = [] + for multibibmatch in multibib_re.finditer(content): + if Verbose: + print "multibib match ",multibibmatch.group(1) + if multibibmatch != None: + baselist = multibibmatch.group(1).split(',') + if Verbose: + print "multibib list ", baselist + for i in range(len(baselist)): + file_list.append(os.path.join(targetdir, baselist[i])) # now define the side effects for file_name in file_list: for suffix in suffix_list[:-1]: @@ -826,7 +850,7 @@ def generate_common(env): env['LATEX'] = 'latex' env['LATEXFLAGS'] = SCons.Util.CLVar('-interaction=nonstopmode -recorder') env['LATEXCOM'] = CDCOM + '${TARGET.dir} && $LATEX $LATEXFLAGS ${SOURCE.file}' - env['LATEXRETRIES'] = 3 + env['LATEXRETRIES'] = 4 env['PDFLATEX'] = 'pdflatex' env['PDFLATEXFLAGS'] = SCons.Util.CLVar('-interaction=nonstopmode -recorder') diff --git a/test/TEX/multibib.py b/test/TEX/multibib.py new file mode 100644 index 0000000..8486e15 --- /dev/null +++ b/test/TEX/multibib.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Test creation of a Tex document that uses the multibib oackage + +Test courtesy Rob Managan. +""" + +import TestSCons +import os + +test = TestSCons.TestSCons() + +latex = test.where_is('latex') +if not latex: + test.skip_test("Could not find 'latex'; skipping test.\n") + +test.subdir(['src']) + + +test.write(['SConstruct'], """\ +import os + +env = Environment() + +DVI('multibib.tex') +""") + + +test.write(['lit.bib'],r""" +@book{Knuth:1991, author = {Knuth, Donald E.}, title = {The TEX book}, publisher = {Addison-Wesley, Reading, Massachusetts}, year = {1991}} +@book{Lamport:1994, author = {Lamport, Leslie}, title = {LATEX: A Document Preparation System}, publisher = {Addison-Wesley, Reading, Massachusetts, 2 edition}, year = {1994} } +@book{Adobe:1985, author = {Adobe System Incorporated}, title = {Postscript Language Tutorial and Cookbook}, publisher = {Addison-Wesley, Reading, Massachusetts}, year = {1985}} + +""") + +test.write(['multibib.tex'],r""" +\documentclass{article} +\usepackage{multibib} +\newcites{ltex}{\TeX\ and \LaTeX\ References} +\begin{document} +References to the \TeX book \citeltex{Knuth:1991} and to Lamport's \LaTeX\ book, which appears only in the references\nociteltex{Lamport:1994}. Finally a cite to a Postscript tutorial \cite{Adobe:1985}. +\bibliographystyleltex{alpha} +\bibliographyltex{lit} +\renewcommand{\refname}{Postscript References} +\bibliographystyle{plain} +\bibliography{lit} +\end{document} +""") + + +test.run(arguments = '', stderr=None) + + +# All (?) the files we expect will get created in the docs directory +files = [ + 'ltex.aux', + 'ltex.bbl', + 'ltex.blg', + 'multibib.aux', + 'multibib.bbl', + 'multibib.blg', + 'multibib.fls', + 'multibib.log', + 'multibib.dvi', +] + +for f in files: + test.must_exist([ f]) + +#test.must_not_exist(['docs/Fig1.pdf',]) + +test.pass_test() diff --git a/test/explain/basic.py b/test/explain/basic.py index bca8436..1f0a16c 100644 --- a/test/explain/basic.py +++ b/test/explain/basic.py @@ -305,7 +305,9 @@ scons: rebuilding `file3' because `zzz' is a new dependency %(_python_)s %(cat_py)s file3 xxx yyy zzz """ % locals()) +test.set_match_function(TestSCons.match_caseinsensitive) test.run(chdir='src', arguments=args, stdout=expect) +test.set_match_function(TestSCons.match_exact) test.must_match(['src', 'file3'], "xxx 1\nyyy 2\nzzz 2\n") |