From a5e5dfb6855f07f88e16d052068b98b1e5abd89d Mon Sep 17 00:00:00 2001 From: Hannes Schulz <schulz@ais.uni-bonn.de> Date: Tue, 21 Jul 2015 17:42:48 +0200 Subject: [PATCH 1/3] add biber/biblatex support Fixes #18. --- latexrun | 64 +++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 56 insertions(+), 8 deletions(-) diff --git a/latexrun b/latexrun index 557a93c..17aed17 100755 --- a/latexrun +++ b/latexrun @@ -158,7 +158,7 @@ def main(): args.obj_dir, args.nowarns) task_commit = LaTeXCommit(db, task_latex, args.output) task_bibtex = BibTeX(db, task_latex, args.bibtex_cmd, args.bibtex_args, - args.nowarns) + args.nowarns, args.obj_dir) tasks = [task_latex, task_commit, task_bibtex] stable = run_tasks(tasks, args.max_iterations) @@ -1471,12 +1471,13 @@ class LaTeXFilter: # class BibTeX(Task): - def __init__(self, db, latex_task, cmd, cmd_args, nowarns): + def __init__(self, db, latex_task, cmd, cmd_args, nowarns, obj_dir): super().__init__(db, 'bibtex::' + normalize_input_path( latex_task.get_tex_filename())) self.__latex_task = latex_task self.__cmd = cmd self.__cmd_args = cmd_args + self.__obj_dir = obj_dir def stable(self): # If bibtex doesn't have its inputs, then it's stable because @@ -1512,6 +1513,10 @@ class BibTeX(Task): re.search(r'^\\bibdata\{', aux_data, flags=re.M): return True + if re.search(r'^\\abx@aux@cite\{', aux_data, flags=re.M): + # biber citation + return True + # Recurse into included aux files (see aux_input_command), in # case \bibliography appears in an \included file. for m in re.finditer(r'^\\@input\{([^}]*)\}', aux_data, flags=re.M): @@ -1522,10 +1527,18 @@ class BibTeX(Task): return False def _input_args(self): - aux_name = os.path.basename(self.__latex_task.get_jobname()) + '.aux' - return [self.__cmd] + self.__cmd_args + [aux_name] + if self.__is_biber(): + aux_name = os.path.basename(self.__latex_task.get_jobname()) + biber_args = ["--input-directory", self.__obj_dir, + "--output-directory", self.__obj_dir] + else: + aux_name = os.path.basename(self.__latex_task.get_jobname()) + '.aux' + biber_args = [] + return [self.__cmd] + biber_args + self.__cmd_args + [aux_name] def _input_cwd(self): + if self.__is_biber(): + return "." return os.path.dirname(self.__latex_task.get_jobname()) def _input_auxfile(self, auxname): @@ -1539,7 +1552,8 @@ class BibTeX(Task): h = hashlib.sha256() for line in aux: if line.startswith((b'\\citation{', b'\\bibdata{', - b'\\bibstyle{', b'\\@input{')): + b'\\bibstyle{', b'\\@input{', + b'\\abx@aux@cite{')): h.update(line) return h.hexdigest() except FileNotFoundError: @@ -1552,6 +1566,9 @@ class BibTeX(Task): return first + ':' return first + ':' + rest + def __is_biber(self): + return "biber" in self.__cmd + def _execute(self): # This gets complicated when \include is involved. \include # switches to a different aux file and records its path in the @@ -1583,7 +1600,7 @@ class BibTeX(Task): except OSError as e: raise TaskError('failed to execute bibtex task: ' + str(e)) from e - inputs, auxnames = self.__parse_inputs(stdout, cwd, env) + inputs, auxnames, outbase = self.__parse_inputs(stdout, cwd, env) if not inputs and not auxnames: # BibTeX failed catastrophically. print(stdout, file=sys.stderr) @@ -1599,7 +1616,8 @@ class BibTeX(Task): for path in inputs: self._input('file', path) - outbase = auxnames[0][:-4] + if self.__is_biber(): + outbase = os.path.join(cwd, outbase) outputs = [outbase + '.bbl', outbase + '.blg'] return RunResult(outputs, {'outbase': outbase, 'status': status, 'inputs': inputs}) @@ -1635,6 +1653,7 @@ class BibTeX(Task): kpathsea = Kpathsea('bibtex') inputs = [] auxnames = [] + outbase = None for line in log.splitlines(): m = re.match('(?:The top-level auxiliary file:' '|A level-[0-9]+ auxiliary file:) (.*)', line) @@ -1660,7 +1679,21 @@ class BibTeX(Task): inputs.append(filename) - return inputs, auxnames + # biber output + m = re.search("Found BibTeX data source '(.*?)'", + line) + if m: + filename = m.group(1) + inputs.append(filename) + + m = re.search("Logfile is '(.*?)'", line) + if m: + outbase = m.group(1)[:-4] + + if outbase is None: + outbase = auxnames[0][:-4] + + return inputs, auxnames, outbase def report(self): extra = self._get_result_extra() @@ -1779,6 +1812,21 @@ class BibTeXFilter: if match('Aborted at line ([0-9]+) of file (.*)'): return ('info', m.group(2), int(m.group(1)), 'aborted') + # biber type errors + if match('^.*> WARN - (.*)$'): + print ('warning', None, None, m.group(1)) + m2 = re.match("(.*) in file '(.*?)', skipping ...", m.group(1)) + if m2: + return ('warning', m2.group(2), "0", m2.group(1)) + return ('warning', None, None, m.group(1)) + + if match('^.*> ERROR - (.*)$'): + m2 = re.match("BibTeX subsystem: (.*?), line (\d+), (.*)$", m.group(1)) + if m2: + return ('error', m2.group(1), m2.group(2), m2.group(3)) + return ('error', None, None, m.group(1)) + + def __canonicalize(self, msg): if msg.startswith('Warning'): msg = re.sub('^Warning-*', '', msg) From 18a2a5c08f966e192f581c19de170b461cf4f2ba Mon Sep 17 00:00:00 2001 From: Hannes Schulz <schulz@ais.uni-bonn.de> Date: Tue, 21 Jul 2015 18:15:20 +0200 Subject: [PATCH 2/3] biber 1.8 compatible (removed biber 1.9 args) --- latexrun | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/latexrun b/latexrun index 17aed17..d5598e9 100755 --- a/latexrun +++ b/latexrun @@ -1529,16 +1529,11 @@ class BibTeX(Task): def _input_args(self): if self.__is_biber(): aux_name = os.path.basename(self.__latex_task.get_jobname()) - biber_args = ["--input-directory", self.__obj_dir, - "--output-directory", self.__obj_dir] else: aux_name = os.path.basename(self.__latex_task.get_jobname()) + '.aux' - biber_args = [] - return [self.__cmd] + biber_args + self.__cmd_args + [aux_name] + return [self.__cmd] + self.__cmd_args + [aux_name] def _input_cwd(self): - if self.__is_biber(): - return "." return os.path.dirname(self.__latex_task.get_jobname()) def _input_auxfile(self, auxname): From 5df52964fabeaa82ec773b55a8568b07c33f064b Mon Sep 17 00:00:00 2001 From: Hannes Schulz <schulz@ais.uni-bonn.de> Date: Mon, 28 Sep 2015 14:01:33 +0200 Subject: [PATCH 3/3] add tests for biblatex (biber and bibtex backends) --- test/T-biblatex-biber-fail/main.bib | 4 ++++ test/T-biblatex-biber-fail/main.tex | 19 +++++++++++++++++++ test/T-biblatex-biber/main.bib | 4 ++++ test/T-biblatex-biber/main.tex | 12 ++++++++++++ test/T-biblatex-bibtex/main.bib | 4 ++++ test/T-biblatex-bibtex/main.tex | 10 ++++++++++ test/run | 5 +++++ 7 files changed, 58 insertions(+) create mode 100644 test/T-biblatex-biber-fail/main.bib create mode 100644 test/T-biblatex-biber-fail/main.tex create mode 100644 test/T-biblatex-biber/main.bib create mode 100644 test/T-biblatex-biber/main.tex create mode 100644 test/T-biblatex-bibtex/main.bib create mode 100644 test/T-biblatex-bibtex/main.tex diff --git a/test/T-biblatex-biber-fail/main.bib b/test/T-biblatex-biber-fail/main.bib new file mode 100644 index 0000000..8d2245c --- /dev/null +++ b/test/T-biblatex-biber-fail/main.bib @@ -0,0 +1,4 @@ +@misc{ent1, + author = {Author}, + title = {Title} +} diff --git a/test/T-biblatex-biber-fail/main.tex b/test/T-biblatex-biber-fail/main.tex new file mode 100644 index 0000000..190c355 --- /dev/null +++ b/test/T-biblatex-biber-fail/main.tex @@ -0,0 +1,19 @@ +% Test biblatex + +\documentclass{article} +\usepackage[backend=biber]{biblatex} +\addbibresource{main.bib} + +\begin{document} + \cite{ent2} + \printbibliography +\end{document} + +%% bibtex-cmd: biber +%% output: +%% main.tex:8: warning: Citation 'ent2' on page 1 undefined +%% main.tex:9: warning: Empty bibliography +%% main.tex: warning: There were undefined references +%% main.tex: warning: [biblatex] Please (re)run Biber on the file: main and rerun LaTeX afterwards +%% warning None None I didn't find a database entry for 'ent2' (section 0) +%% <no file>: warning: I didn't find a database entry for 'ent2' (section 0) diff --git a/test/T-biblatex-biber/main.bib b/test/T-biblatex-biber/main.bib new file mode 100644 index 0000000..8d2245c --- /dev/null +++ b/test/T-biblatex-biber/main.bib @@ -0,0 +1,4 @@ +@misc{ent1, + author = {Author}, + title = {Title} +} diff --git a/test/T-biblatex-biber/main.tex b/test/T-biblatex-biber/main.tex new file mode 100644 index 0000000..3bdd4ff --- /dev/null +++ b/test/T-biblatex-biber/main.tex @@ -0,0 +1,12 @@ +% Test biblatex + +\documentclass{article} +\usepackage[backend=biber]{biblatex} +\addbibresource{main.bib} + +\begin{document} + \cite{ent1} + \printbibliography +\end{document} + +%% bibtex-cmd: biber diff --git a/test/T-biblatex-bibtex/main.bib b/test/T-biblatex-bibtex/main.bib new file mode 100644 index 0000000..8d2245c --- /dev/null +++ b/test/T-biblatex-bibtex/main.bib @@ -0,0 +1,4 @@ +@misc{ent1, + author = {Author}, + title = {Title} +} diff --git a/test/T-biblatex-bibtex/main.tex b/test/T-biblatex-bibtex/main.tex new file mode 100644 index 0000000..f55a24c --- /dev/null +++ b/test/T-biblatex-bibtex/main.tex @@ -0,0 +1,10 @@ +% Test biblatex + +\documentclass{article} +\usepackage[backend=bibtex]{biblatex} +\addbibresource{main.bib} + +\begin{document} + \cite{ent1} + \printbibliography +\end{document} diff --git a/test/run b/test/run index 3562da1..4b553d6 100755 --- a/test/run +++ b/test/run @@ -69,6 +69,11 @@ def test(latexrun_path, latexrun_args, input_path): else: status_expect = 0 + m = re.search(pre + 'bibtex-cmd: (.*)', input_src, re.I|re.M) + if m: + bibtex_cmd = m.group(1) + latexrun_args += ["--bibtex-cmd", bibtex_cmd] + m = re.search(pre + 'output:\n((?:' + pre + '.*\n)*)', input_src, re.I|re.M) if m: output_expect = re.sub(pre, '', m.group(1), flags=re.M)