LaTeX.py (scons-4.2.0) | : | LaTeX.py (SCons-4.3.0) | ||
---|---|---|---|---|
skipping to change at line 29 | skipping to change at line 29 | |||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||
"""Dependency scanner for LaTeX code.""" | """Dependency scanner for LaTeX code.""" | |||
import os.path | import os.path | |||
import re | import re | |||
import SCons.Scanner | import SCons.Node.FS | |||
import SCons.Util | import SCons.Util | |||
import SCons.Warnings | ||||
from . import ScannerBase, FindPathDirs | ||||
# list of graphics file extensions for TeX and LaTeX | # list of graphics file extensions for TeX and LaTeX | |||
TexGraphics = ['.eps', '.ps'] | TexGraphics = ['.eps', '.ps'] | |||
#LatexGraphics = ['.pdf', '.png', '.jpg', '.gif', '.tif'] | #LatexGraphics = ['.pdf', '.png', '.jpg', '.gif', '.tif'] | |||
LatexGraphics = [ '.png', '.jpg', '.gif', '.tif'] | LatexGraphics = [ '.png', '.jpg', '.gif', '.tif'] | |||
# Used as a return value of modify_env_var if the variable is not set. | # Used as a return value of modify_env_var if the variable is not set. | |||
class _Null: | class _Null: | |||
pass | pass | |||
_null = _Null | _null = _Null | |||
skipping to change at line 80 | skipping to change at line 82 | |||
return save | return save | |||
class FindENVPathDirs: | class FindENVPathDirs: | |||
""" | """ | |||
A class to bind a specific E{*}PATH variable name to a function that | A class to bind a specific E{*}PATH variable name to a function that | |||
will return all of the E{*}path directories. | will return all of the E{*}path directories. | |||
""" | """ | |||
def __init__(self, variable): | def __init__(self, variable): | |||
self.variable = variable | self.variable = variable | |||
def __call__(self, env, dir=None, target=None, source=None, argument=None): | def __call__(self, env, dir=None, target=None, source=None, argument=None): | |||
import SCons.PathList | import SCons.PathList | |||
try: | try: | |||
path = env['ENV'][self.variable] | path = env['ENV'][self.variable] | |||
except KeyError: | except KeyError: | |||
return () | return () | |||
dir = dir or env.fs._cwd | dir = dir or env.fs._cwd | |||
path = SCons.PathList.PathList(path).subst_path(env, target, source) | path = SCons.PathList.PathList(path).subst_path(env, target, source) | |||
return tuple(dir.Rfindalldirs(path)) | return tuple(dir.Rfindalldirs(path)) | |||
skipping to change at line 115 | skipping to change at line 118 | |||
Return a prototype Scanner instance for scanning LaTeX source files | Return a prototype Scanner instance for scanning LaTeX source files | |||
when built with pdflatex. | when built with pdflatex. | |||
""" | """ | |||
ds = LaTeX(name = "PDFLaTeXScanner", | ds = LaTeX(name = "PDFLaTeXScanner", | |||
suffixes = '$LATEXSUFFIXES', | suffixes = '$LATEXSUFFIXES', | |||
# in the search order, see below in LaTeX class docstring | # in the search order, see below in LaTeX class docstring | |||
graphics_extensions = LatexGraphics, | graphics_extensions = LatexGraphics, | |||
recursive = 0) | recursive = 0) | |||
return ds | return ds | |||
class LaTeX(SCons.Scanner.Base): | class LaTeX(ScannerBase): | |||
"""Class for scanning LaTeX files for included files. | """Class for scanning LaTeX files for included files. | |||
Unlike most scanners, which use regular expressions that just | Unlike most scanners, which use regular expressions that just | |||
return the included file name, this returns a tuple consisting | return the included file name, this returns a tuple consisting | |||
of the keyword for the inclusion ("include", "includegraphics", | of the keyword for the inclusion ("include", "includegraphics", | |||
"input", or "bibliography"), and then the file name itself. | "input", or "bibliography"), and then the file name itself. | |||
Based on a quick look at LaTeX documentation, it seems that we | Based on a quick look at LaTeX documentation, it seems that we | |||
should append .tex suffix for the "include" keywords, append .tex if | should append .tex suffix for the "include" keywords, append .tex if | |||
there is no extension for the "input" keyword, and need to add .bib | there is no extension for the "input" keyword, and need to add .bib | |||
for the "bibliography" keyword that does not accept extensions by itself. | for the "bibliography" keyword that does not accept extensions by itself. | |||
skipping to change at line 171 | skipping to change at line 174 | |||
'addglobalbib': 'BIBINPUTS', | 'addglobalbib': 'BIBINPUTS', | |||
'addsectionbib': 'BIBINPUTS', | 'addsectionbib': 'BIBINPUTS', | |||
'makeindex': 'INDEXSTYLE', | 'makeindex': 'INDEXSTYLE', | |||
'usepackage': 'TEXINPUTS', | 'usepackage': 'TEXINPUTS', | |||
'lstinputlisting': 'TEXINPUTS'} | 'lstinputlisting': 'TEXINPUTS'} | |||
env_variables = SCons.Util.unique(list(keyword_paths.values())) | env_variables = SCons.Util.unique(list(keyword_paths.values())) | |||
two_arg_commands = ['import', 'subimport', | two_arg_commands = ['import', 'subimport', | |||
'includefrom', 'subincludefrom', | 'includefrom', 'subincludefrom', | |||
'inputfrom', 'subinputfrom'] | 'inputfrom', 'subinputfrom'] | |||
def __init__(self, name, suffixes, graphics_extensions, *args, **kw): | def __init__(self, name, suffixes, graphics_extensions, *args, **kwargs): | |||
regex = r''' | regex = r''' | |||
\\( | \\( | |||
include | include | |||
| includegraphics(?:\s*\[[^\]]+\])? | | includegraphics(?:\s*\[[^\]]+\])? | |||
| lstinputlisting(?:\[[^\]]+\])? | | lstinputlisting(?:\[[^\]]+\])? | |||
| input | | input | |||
| import | | import | |||
| subimport | | subimport | |||
| includefrom | | includefrom | |||
| subincludefrom | | subincludefrom | |||
skipping to change at line 218 | skipping to change at line 221 | |||
hides multiple instances of FindPathDirs, one per the LaTeX path | hides multiple instances of FindPathDirs, one per the LaTeX path | |||
variable in the environment. When invoked, the function calculates | variable in the environment. When invoked, the function calculates | |||
and returns all the required paths as a dictionary (converted into | and returns all the required paths as a dictionary (converted into | |||
a tuple to become hashable). Then the scan function converts it | a tuple to become hashable). Then the scan function converts it | |||
back and uses a dictionary of tuples rather than a single tuple | back and uses a dictionary of tuples rather than a single tuple | |||
of paths. | of paths. | |||
""" | """ | |||
def __init__(self, dictionary): | def __init__(self, dictionary): | |||
self.dictionary = {} | self.dictionary = {} | |||
for k,n in dictionary.items(): | for k,n in dictionary.items(): | |||
self.dictionary[k] = ( SCons.Scanner.FindPathDirs(n), | self.dictionary[k] = (FindPathDirs(n), FindENVPathDirs(n)) | |||
FindENVPathDirs(n) ) | ||||
def __call__(self, env, dir=None, target=None, source=None, | def __call__(self, env, dir=None, target=None, source=None, | |||
argument=None): | argument=None): | |||
di = {} | di = {} | |||
for k,(c,cENV) in self.dictionary.items(): | for k,(c,cENV) in self.dictionary.items(): | |||
di[k] = ( c(env, dir=None, target=None, source=None, | di[k] = ( c(env, dir=None, target=None, source=None, | |||
argument=None) , | argument=None) , | |||
cENV(env, dir=None, target=None, source=None, | cENV(env, dir=None, target=None, source=None, | |||
argument=None) ) | argument=None) ) | |||
# To prevent "dict is not hashable error" | # To prevent "dict is not hashable error" | |||
return tuple(di.items()) | return tuple(di.items()) | |||
class LaTeXScanCheck: | class LaTeXScanCheck: | |||
"""Skip all but LaTeX source files, i.e., do not scan *.eps, | """Skip all but LaTeX source files. | |||
*.pdf, *.jpg, etc. | ||||
Do not scan *.eps, *.pdf, *.jpg, etc. | ||||
""" | """ | |||
def __init__(self, suffixes): | def __init__(self, suffixes): | |||
self.suffixes = suffixes | self.suffixes = suffixes | |||
def __call__(self, node, env): | def __call__(self, node, env): | |||
current = not node.has_builder() or node.is_up_to_date() | current = not node.has_builder() or node.is_up_to_date() | |||
scannable = node.get_suffix() in env.subst_list(self.suffixes)[0 ] | scannable = node.get_suffix() in env.subst_list(self.suffixes)[0 ] | |||
# Returning false means that the file is not scanned. | # Returning false means that the file is not scanned. | |||
return scannable and current | return scannable and current | |||
kw['function'] = _scan | kwargs['function'] = _scan | |||
kw['path_function'] = FindMultiPathDirs(LaTeX.keyword_paths) | kwargs['path_function'] = FindMultiPathDirs(LaTeX.keyword_paths) | |||
kw['recursive'] = 0 | kwargs['recursive'] = 0 | |||
kw['skeys'] = suffixes | kwargs['skeys'] = suffixes | |||
kw['scan_check'] = LaTeXScanCheck(suffixes) | kwargs['scan_check'] = LaTeXScanCheck(suffixes) | |||
kw['name'] = name | kwargs['name'] = name | |||
SCons.Scanner.Base.__init__(self, *args, **kw) | super().__init__(*args, **kwargs) | |||
def _latex_names(self, include_type, filename): | def _latex_names(self, include_type, filename): | |||
if include_type == 'input': | if include_type == 'input': | |||
base, ext = os.path.splitext( filename ) | base, ext = os.path.splitext( filename ) | |||
if ext == "": | if ext == "": | |||
return [filename + '.tex'] | return [filename + '.tex'] | |||
if include_type in ('include', 'import', 'subimport', | if include_type in ('include', 'import', 'subimport', | |||
'includefrom', 'subincludefrom', | 'includefrom', 'subincludefrom', | |||
'inputfrom', 'subinputfrom'): | 'inputfrom', 'subinputfrom'): | |||
base, ext = os.path.splitext( filename ) | base, ext = os.path.splitext( filename ) | |||
skipping to change at line 392 | skipping to change at line 397 | |||
# is actually found in a Repository or locally.""" | # is actually found in a Repository or locally.""" | |||
nodes = [] | nodes = [] | |||
source_dir = node.get_dir() | source_dir = node.get_dir() | |||
#for include in includes: | #for include in includes: | |||
while queue: | while queue: | |||
include = queue.pop() | include = queue.pop() | |||
inc_type, inc_subdir, inc_filename = include | inc_type, inc_subdir, inc_filename = include | |||
try: | try: | |||
if seen[inc_filename] == 1: | if seen[inc_filename]: | |||
continue | continue | |||
except KeyError: | except KeyError: | |||
seen[inc_filename] = 1 | seen[inc_filename] = True | |||
# | # | |||
# Handle multiple filenames in include[1] | # Handle multiple filenames in include[1] | |||
# | # | |||
n, i = self.find_include(include, source_dir, path_dict) | n, i = self.find_include(include, source_dir, path_dict) | |||
if n is None: | if n is None: | |||
# Do not bother with 'usepackage' warnings, as they most | # Do not bother with 'usepackage' warnings, as they most | |||
# likely refer to system-level files | # likely refer to system-level files | |||
if inc_type != 'usepackage': | if inc_type != 'usepackage': | |||
SCons.Warnings.warn(SCons.Warnings.DependencyWarning, | SCons.Warnings.warn( | |||
"No dependency generated for file: %s (i | SCons.Warnings.DependencyWarning, | |||
ncluded from: %s) -- file not found" % (i, node)) | "No dependency generated for file: %s " | |||
"(included from: %s) -- file not found" % (i, node), | ||||
) | ||||
else: | else: | |||
sortkey = self.sort_key(n) | sortkey = self.sort_key(n) | |||
nodes.append((sortkey, n)) | nodes.append((sortkey, n)) | |||
# recurse down | # recurse down | |||
queue.extend( self.scan(n, inc_subdir) ) | queue.extend(self.scan(n, inc_subdir)) | |||
return [pair[1] for pair in sorted(nodes)] | return [pair[1] for pair in sorted(nodes)] | |||
# Local Variables: | # Local Variables: | |||
# tab-width:4 | # tab-width:4 | |||
# indent-tabs-mode:nil | # indent-tabs-mode:nil | |||
# End: | # End: | |||
# vim: set expandtab tabstop=4 shiftwidth=4: | # vim: set expandtab tabstop=4 shiftwidth=4: | |||
End of changes. 15 change blocks. | ||||
20 lines changed or deleted | 27 lines changed or added |