forked from SublimeText/LaTeXTools
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbasicBuilder.py
239 lines (208 loc) · 8.29 KB
/
basicBuilder.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
# ST2/ST3 compat
import sublime
if sublime.version() < '3000':
# we are on ST2 and Python 2.X
_ST3 = False
strbase = basestring
# reraise implementation from 6
exec("""def reraise(tp, value, tb=None):
raise tp, value, tb
""")
else:
_ST3 = True
strbase = str
# reraise implementation from 6
def reraise(tp, value, tb=None):
if value is None:
value = tp()
if value.__traceback__ is not tb:
raise value.with_traceback(tb)
raise value
import os
import re
import sys
# This will work because makePDF.py puts the appropriate
# builders directory in sys.path
from pdfBuilder import PdfBuilder
from latextools_utils.external_command import external_command, get_texpath
# Standard LaTeX warning
CITATIONS_REGEX = re.compile(r"Warning: Citation `.+' on page \d+ undefined")
# BibLaTeX outputs a different message from BibTeX, so we must catch that too
BIBLATEX_REGEX = re.compile(r"Package biblatex Warning: Please \(re\)run (\S*)")
# Used to indicate a subdirectory that needs to be made for a file input using
# \include
FILE_WRITE_ERROR_REGEX = re.compile(r"! I can't write on file `(.*)/([^/']*)'")
#----------------------------------------------------------------
# BasicBuilder class
#
# This is a more fully functional verion of the Simple Builder
# concept. It implements the same building features as the
# Traditional builder.
#
class BasicBuilder(PdfBuilder):
def __init__(self, *args):
super(BasicBuilder, self).__init__(*args)
self.name = "Basic Builder"
self.bibtex = self.builder_settings.get('bibtex', 'bibtex')
self.display_log = self.builder_settings.get("display_log", False)
def commands(self):
# Print greeting
self.display("\n\nBasic Builder: ")
engine = self.engine
if "la" not in engine:
# we need the command rather than the engine
engine = {
"pdftex": u"pdflatex",
"xetex": u"xelatex",
"luatex": u"lualatex"
}.get(engine, u'pdflatex')
if engine not in ['pdflatex', 'xelatex', 'lualatex']:
engine = 'pdflatex'
latex = [engine, u"-interaction=nonstopmode", u"-synctex=1"]
biber = [u"biber"]
if self.aux_directory is not None:
biber.append(u'--output-directory=' + self.aux_directory)
if self.aux_directory == self.output_directory:
latex.append(u'--output-directory=' + self.aux_directory)
else:
latex.append(u'--aux-directory=' + self.aux_directory)
elif self.output_directory is not None:
biber.append(u'--output-directory=' + self.output_directory)
if (
self.output_directory is not None and
self.output_directory != self.aux_directory
):
latex.append(u'--output-directory=' + self.output_directory)
if self.job_name != self.base_name:
latex.append(u'--jobname=' + self.job_name)
for option in self.options:
latex.append(option)
latex.append(self.base_name)
# Check if any subfolders need to be created
# this adds a number of potential runs as LaTeX treats being unable
# to open output files as fatal errors
output_directory = (
self.aux_directory_full or self.output_directory_full
)
if (
output_directory is not None and
not os.path.exists(output_directory)
):
self.make_directory(output_directory)
yield (latex, "running {0}...".format(engine))
self.display("done.\n")
self.log_output()
if output_directory is not None:
while True:
start = 0
added_directory = False
while True:
match = FILE_WRITE_ERROR_REGEX.search(self.out, start)
if match:
self.make_directory(
os.path.normpath(
os.path.join(
output_directory,
match.group(1)
)
)
)
start = match.end(1)
added_directory = True
else:
break
if added_directory:
yield (latex, "running {0}...".format(engine))
self.display("done.\n")
self.log_output()
else:
break
# Check for citations
# Use search, not match: match looks at the beginning of the string
# We need to run pdflatex twice after bibtex
if (
CITATIONS_REGEX.search(self.out) or
"Package natbib Warning: There were undefined citations."
in self.out
):
yield (self.run_bibtex(), "running bibtex...")
self.display("done.\n")
self.log_output()
for i in range(2):
yield (latex, "running {0}...".format(engine))
self.display("done.\n")
self.log_output()
else:
match = BIBLATEX_REGEX.search(self.out)
if match:
if match.group(1).lower() == 'biber':
yield (biber + [self.job_name], "running biber...")
else:
yield (
self.run_bibtex(match.group(1).lower()),
"running {0}...".format(match.group(1).lower9)
)
self.display("done.\n")
self.log_output()
for i in range(2):
yield (latex, "running {0}...".format(engine))
self.display("done.\n")
self.log_output()
# Check for changed labels
# Do this at the end, so if there are also citations to resolve,
# we may save one pdflatex run
if "Rerun to get cross-references right." in self.out:
yield (latex, "running {0}...".format(engine))
self.display("done.\n")
self.log_output()
self.display("done.\n")
def log_output(self):
if self.display_log:
self.display("\nCommand results:\n")
self.display(self.out)
self.display("\n\n")
def make_directory(self, directory):
if not os.path.exists(directory):
try:
print('making directory ' + directory)
os.makedirs(directory)
except OSError:
if not os.path.exists(directory):
reraise(*sys.exc_info())
def run_bibtex(self, command=None):
if command is None:
command = [self.bibtex]
elif isinstance(command, strbase):
command = [command]
# to get bibtex to work with the output directory, we change the
# cwd to the output directory and add the main directory to
# BIBINPUTS and BSTINPUTS
env = dict(os.environ)
cwd = self.tex_dir
output_directory = (
self.aux_directory_full or self.output_directory_full
)
if output_directory is not None:
# cwd is, at the point, the path to the main tex file
if _ST3:
env['BIBINPUTS'] = cwd + os.pathsep + env.get('BIBINPUTS', '')
env['BSTINPUTS'] = cwd + os.pathsep + env.get('BSTINPUTS', '')
else:
env['BIBINPUTS'] = \
(cwd + os.pathsep + env.get('BIBINPUTS', '')).encode(
sys.getfilesystemencoding())
env['BSTINPUTS'] = \
(cwd + os.pathsep + env.get('BSTINPUTS', '')).encode(
sys.getfilesystemencoding())
# now we modify cwd to be the output directory
# NOTE this cwd is not reused by any of the other command
cwd = output_directory
env['PATH'] = get_texpath()
command.append(self.job_name)
return external_command(
command,
env=env,
cwd=cwd,
preexec_fn=os.setsid if sublime.platform() != 'windows' else None,
use_texpath=False
)