Skip to content

Commit a5e5dfb

Browse files
committed
add biber/biblatex support
Fixes #18.
1 parent 37dce88 commit a5e5dfb

File tree

1 file changed

+56
-8
lines changed

1 file changed

+56
-8
lines changed

latexrun

Lines changed: 56 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ def main():
158158
args.obj_dir, args.nowarns)
159159
task_commit = LaTeXCommit(db, task_latex, args.output)
160160
task_bibtex = BibTeX(db, task_latex, args.bibtex_cmd, args.bibtex_args,
161-
args.nowarns)
161+
args.nowarns, args.obj_dir)
162162
tasks = [task_latex, task_commit, task_bibtex]
163163
stable = run_tasks(tasks, args.max_iterations)
164164

@@ -1471,12 +1471,13 @@ class LaTeXFilter:
14711471
#
14721472

14731473
class BibTeX(Task):
1474-
def __init__(self, db, latex_task, cmd, cmd_args, nowarns):
1474+
def __init__(self, db, latex_task, cmd, cmd_args, nowarns, obj_dir):
14751475
super().__init__(db, 'bibtex::' + normalize_input_path(
14761476
latex_task.get_tex_filename()))
14771477
self.__latex_task = latex_task
14781478
self.__cmd = cmd
14791479
self.__cmd_args = cmd_args
1480+
self.__obj_dir = obj_dir
14801481

14811482
def stable(self):
14821483
# If bibtex doesn't have its inputs, then it's stable because
@@ -1512,6 +1513,10 @@ class BibTeX(Task):
15121513
re.search(r'^\\bibdata\{', aux_data, flags=re.M):
15131514
return True
15141515

1516+
if re.search(r'^\\abx@aux@cite\{', aux_data, flags=re.M):
1517+
# biber citation
1518+
return True
1519+
15151520
# Recurse into included aux files (see aux_input_command), in
15161521
# case \bibliography appears in an \included file.
15171522
for m in re.finditer(r'^\\@input\{([^}]*)\}', aux_data, flags=re.M):
@@ -1522,10 +1527,18 @@ class BibTeX(Task):
15221527
return False
15231528

15241529
def _input_args(self):
1525-
aux_name = os.path.basename(self.__latex_task.get_jobname()) + '.aux'
1526-
return [self.__cmd] + self.__cmd_args + [aux_name]
1530+
if self.__is_biber():
1531+
aux_name = os.path.basename(self.__latex_task.get_jobname())
1532+
biber_args = ["--input-directory", self.__obj_dir,
1533+
"--output-directory", self.__obj_dir]
1534+
else:
1535+
aux_name = os.path.basename(self.__latex_task.get_jobname()) + '.aux'
1536+
biber_args = []
1537+
return [self.__cmd] + biber_args + self.__cmd_args + [aux_name]
15271538

15281539
def _input_cwd(self):
1540+
if self.__is_biber():
1541+
return "."
15291542
return os.path.dirname(self.__latex_task.get_jobname())
15301543

15311544
def _input_auxfile(self, auxname):
@@ -1539,7 +1552,8 @@ class BibTeX(Task):
15391552
h = hashlib.sha256()
15401553
for line in aux:
15411554
if line.startswith((b'\\citation{', b'\\bibdata{',
1542-
b'\\bibstyle{', b'\\@input{')):
1555+
b'\\bibstyle{', b'\\@input{',
1556+
b'\\abx@aux@cite{')):
15431557
h.update(line)
15441558
return h.hexdigest()
15451559
except FileNotFoundError:
@@ -1552,6 +1566,9 @@ class BibTeX(Task):
15521566
return first + ':'
15531567
return first + ':' + rest
15541568

1569+
def __is_biber(self):
1570+
return "biber" in self.__cmd
1571+
15551572
def _execute(self):
15561573
# This gets complicated when \include is involved. \include
15571574
# switches to a different aux file and records its path in the
@@ -1583,7 +1600,7 @@ class BibTeX(Task):
15831600
except OSError as e:
15841601
raise TaskError('failed to execute bibtex task: ' + str(e)) from e
15851602

1586-
inputs, auxnames = self.__parse_inputs(stdout, cwd, env)
1603+
inputs, auxnames, outbase = self.__parse_inputs(stdout, cwd, env)
15871604
if not inputs and not auxnames:
15881605
# BibTeX failed catastrophically.
15891606
print(stdout, file=sys.stderr)
@@ -1599,7 +1616,8 @@ class BibTeX(Task):
15991616
for path in inputs:
16001617
self._input('file', path)
16011618

1602-
outbase = auxnames[0][:-4]
1619+
if self.__is_biber():
1620+
outbase = os.path.join(cwd, outbase)
16031621
outputs = [outbase + '.bbl', outbase + '.blg']
16041622
return RunResult(outputs, {'outbase': outbase, 'status': status,
16051623
'inputs': inputs})
@@ -1635,6 +1653,7 @@ class BibTeX(Task):
16351653
kpathsea = Kpathsea('bibtex')
16361654
inputs = []
16371655
auxnames = []
1656+
outbase = None
16381657
for line in log.splitlines():
16391658
m = re.match('(?:The top-level auxiliary file:'
16401659
'|A level-[0-9]+ auxiliary file:) (.*)', line)
@@ -1660,7 +1679,21 @@ class BibTeX(Task):
16601679

16611680
inputs.append(filename)
16621681

1663-
return inputs, auxnames
1682+
# biber output
1683+
m = re.search("Found BibTeX data source '(.*?)'",
1684+
line)
1685+
if m:
1686+
filename = m.group(1)
1687+
inputs.append(filename)
1688+
1689+
m = re.search("Logfile is '(.*?)'", line)
1690+
if m:
1691+
outbase = m.group(1)[:-4]
1692+
1693+
if outbase is None:
1694+
outbase = auxnames[0][:-4]
1695+
1696+
return inputs, auxnames, outbase
16641697

16651698
def report(self):
16661699
extra = self._get_result_extra()
@@ -1779,6 +1812,21 @@ class BibTeXFilter:
17791812
if match('Aborted at line ([0-9]+) of file (.*)'):
17801813
return ('info', m.group(2), int(m.group(1)), 'aborted')
17811814

1815+
# biber type errors
1816+
if match('^.*> WARN - (.*)$'):
1817+
print ('warning', None, None, m.group(1))
1818+
m2 = re.match("(.*) in file '(.*?)', skipping ...", m.group(1))
1819+
if m2:
1820+
return ('warning', m2.group(2), "0", m2.group(1))
1821+
return ('warning', None, None, m.group(1))
1822+
1823+
if match('^.*> ERROR - (.*)$'):
1824+
m2 = re.match("BibTeX subsystem: (.*?), line (\d+), (.*)$", m.group(1))
1825+
if m2:
1826+
return ('error', m2.group(1), m2.group(2), m2.group(3))
1827+
return ('error', None, None, m.group(1))
1828+
1829+
17821830
def __canonicalize(self, msg):
17831831
if msg.startswith('Warning'):
17841832
msg = re.sub('^Warning-*', '', msg)

0 commit comments

Comments
 (0)