-
Notifications
You must be signed in to change notification settings - Fork 653
/
insert-includes.py
executable file
·134 lines (106 loc) · 3.6 KB
/
insert-includes.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
#!/usr/bin/env python
"""
replaces a `include with the full include file.
args
$1 - file which has includes to be replaced
$2 - file in which output will be written
$3 - list of directories to search for includes
(note: NON-RECURSIVE must specify all dirs)
includes are found relative to this path
this is equivalent to something like +incdir+
"""
import os
import re
import shutil
import sys
import tempfile
def print_info(msg):
"""
Print an info message.
Args:
msg (str): message to print
"""
print(f"[INFO] {msg}")
def print_error(msg, critical=True):
"""
Print an error message.
Args:
msg (str): message to print
critical (bool): whether to exit after printing the message
"""
if critical:
sys.exit(f"[ERROR] {msg}")
else:
print(f"[ERROR] {msg}")
def find_include(file_name, inc_dirs):
"""
Find the include file in the list of directories.
Args:
file_name (str): include file name
inc_dirs (list): list of directories to search for includes
Returns:
str: full path to the include file
"""
for d in inc_dirs:
inc_file_name = d + "/" + file_name
if os.path.exists(inc_file_name):
return inc_file_name
print_error(f"Include file {file_name} not found in {inc_dirs}")
return None
def process_helper(in_fname, out_f, inc_dirs, replaced_includes):
"""
Helper function to DFS through include files and replace includes.
"""
include_regex = re.compile(r"^ *`include +\"(.*)\"")
# slurp the input file.
# this avoids having a bunch of fds open during recursion
with open(in_fname, "r", encoding="utf-8") as in_file:
lines = in_file.readlines()
for num, line in enumerate(lines, 1):
match = re.match(include_regex, line)
if not match or match.group(1) == "uvm_macros.svh":
# copy the line as is
out_f.write(line)
continue
if match.group(1) in replaced_includes:
print_info("Skipping duplicate include")
continue
print_info(
f"Replacing includes for {match.group(1)}" f" at line {num}"
)
# search for include and replace
inc_file_name = find_include(match.group(1), inc_dirs)
replaced_includes.add(match.group(1))
process_helper(inc_file_name, out_f, inc_dirs, replaced_includes)
def process(in_fname, out_fname, inc_dirs=None):
"""
Replace include directives in a file with the full include file.
Args:
in_fname (str): input file name
out_fname (str): output file name
inc_dirs (list): list of directories to search for includes
"""
replaced_includes = set()
with open(out_fname, "w", encoding="utf-8") as out_file:
process_helper(in_fname, out_file, inc_dirs, replaced_includes)
def main():
"""
Entry point for the script.
Args:
<input file> <output file> <list of directories to search for includes>
"""
in_vlog = sys.argv[1]
out_vlog = sys.argv[2]
if in_vlog == out_vlog:
sys.exit("[ERROR] The input and output file cannot be the same.")
# add directories to search list
inc_dirs = sys.argv[3:]
print("[INFO] Replaces includes from: " + str(in_vlog))
print("[INFO] Searching following dirs for includes: " + str(inc_dirs))
# make a copy of the input file
_, temp_path = tempfile.mkstemp()
shutil.copy2(in_vlog, temp_path)
process(temp_path, out_vlog, inc_dirs)
print("[INFO] Success. Output written to: " + str(out_vlog))
if __name__ == "__main__":
main()