-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathmake_dependency.py
134 lines (107 loc) · 4.11 KB
/
make_dependency.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
"""
make_dependency.py
Generates Makefile dependencies for a given file, based on the
LaTeX include directives it uses. By using auto-dependencies,
the main Makefile can better detect when a target needs to be rebuilt.
This script should not have be called manually by users. It is intended
for use as a utility in Makefiles for auto-dependency generation
(similar to auto-dependencies that are generated by gcc).
Once all includes have been scanned, a dependency file
is created. This dependency file can be included by Makefiles.
"""
import os
import re
USAGE = """
usage: python3 {0} TARGET SRC DESTINATION
Generates Makefile dependency files for source files by searching for
LaTeX include directives.
"""
EXPORT_DIR = "made"
SOLUTIONS_SUFFIX = "_sol"
META_SUFFIX = "_meta"
def make_dep_file(target, dependencies):
"""Returns the contents of a dependency file.
PARAMETERS:
target -- str; Makefile target that requires dependencies.
dependencies -- list of str; list of dependencies.
assets -- dict; keys are target names for assets, values
are actual asset locations on the filesystem.
phony -- str or None; if str, this denotes a convenient
phony target that depends on assets. If None,
the phony target is omitted from the dependency
file.
RETURNS:
str; the contents to write to a dependency file.
"""
dep_format = '{0}: {1}'
deps = []
dependencies = set(dependencies)
targets = [
os.path.join(EXPORT_DIR, target + ".pdf").replace("\\","/"),
os.path.join(EXPORT_DIR, target + SOLUTIONS_SUFFIX + ".pdf").replace("\\","/"),
os.path.join(EXPORT_DIR, target + META_SUFFIX + ".pdf").replace("\\","/")
]
# Add source file dependencies.
for full_target in targets:
deps.append(dep_format.format(full_target, ' '.join(dependencies)))
return '\n\n'.join(deps)
re_subimport = re.compile(r"\\subimport\{(.+?)\}\{(.+?)\}")
re_includegraphics = re.compile(r"""
(?:
\\includegraphics |
\\lstinputlisting
)
(?:\[.*\])?\{(.*)\}
""", re.X)
def get_dependencies(filepath):
"""Recursively retrieve dependencies from the specified filepath.
PARAMETERS:
filepath -- str; path to source file. Expected to be expressed
relative to this script.
This function follows included filepaths recursively to
fetch more dependencies.
RETURNS:
list of str; list of dependencies.
"""
dirname = os.path.dirname(filepath)
file_contents = read_file(filepath)
# Convert paths to be relative to this script.
dependencies = re_subimport.findall(file_contents)
for i in range(len(dependencies)):
dep = os.path.normpath(os.path.join(dirname, os.path.join(*dependencies[i])))
dependencies[i] = dep.replace("\\","/")
# Recursively fetch more dependencies.
for dependency in dependencies[:]:
recursive_deps = get_dependencies(dependency)
dependencies.extend(recursive_deps)
for graphic in re_includegraphics.findall(file_contents):
dep = os.path.normpath(os.path.join(dirname, graphic))
dependencies.append(dep.replace("\\","/"))
return dependencies
#############
# Utilities #
#############
def read_file(filepath):
try:
with open(filepath, 'r', encoding="utf-8") as f:
return f.read()
except Exception as error:
trace = sys.exc_info()[2]
raise Exception(f"Exception encountered while reading from {filepath}:\n\n{type(error).__name__}: {str(error)}") from error
def write_file(filepath, text):
dirname = os.path.dirname(filepath)
if not os.path.exists(dirname):
os.makedirs(dirname)
with open(filepath, 'w', encoding="utf-8") as f:
f.write(text)
def main(args):
if len(args) != 4:
print(USAGE.format(args[0]))
exit(1)
_, target, src, destination = args
destination = os.path.join(destination, src) + '.d'
dependencies = get_dependencies(src)
write_file(destination, make_dep_file(target, dependencies))
if __name__ == '__main__':
import sys
main(sys.argv)