Skip to content

Commit

Permalink
Merge pull request #1902 from bwhitchurch/fix-insert-includes
Browse files Browse the repository at this point in the history
Improve insert includes
  • Loading branch information
jerryz123 authored Jun 14, 2024
2 parents eca3888 + ecd7c80 commit 7f5b6a9
Showing 1 changed file with 128 additions and 74 deletions.
202 changes: 128 additions & 74 deletions scripts/insert-includes.py
Original file line number Diff line number Diff line change
@@ -1,80 +1,134 @@
#!/usr/bin/env python
"""
replaces a `include with the full include file.
# replaces a `include with the full include file.
# recursively replaces `include's until none are left
#
# args
# $1 - file to remove includes from
# $2 - file to write output to
# $3 - list of directories to search for includes in (note: NON-RECURSIVE must specify all dirs)
# includes are found relative to this path
# this is equivalent to something like +incdir+
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 sys
import re
import os
import tempfile
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))


inVlog = sys.argv[1]
outVlog = sys.argv[2]
print("[INFO] Replaces includes from: " + str(inVlog))

if inVlog == outVlog:
sys.exit("[ERROR] The input and output file cannot be the same.")

# add directories to search list
incDirs = sys.argv[3:]
print("[INFO] Searching following dirs for includes: " + str(incDirs))

def process(inF, outF):
# open file
with open(inF, 'r') as inFile:
with open(outF, 'w') as outFile:
# for each include found, search through all dirs and replace if found, error if not
for num, line in enumerate(inFile, 1):
match = re.match(r"^ *`include +\"(.*)\"", line)
if match and match.group(1) != "uvm_macros.svh":
print("[INFO] Replacing includes for {}".format(match.group(1)))
# search for include and replace
found = False
for d in incDirs:
potentialIncFileName = d + "/" + match.group(1)
if os.path.exists(potentialIncFileName):
found = True
print("[INFO] Found missing include in {}".format(potentialIncFileName))
with open(potentialIncFileName, 'r') as incFile:
for iline in incFile:
outFile.write(iline)
break

# must find something to include with
if not found:
sys.exit("[ERROR] Couldn't replace include \"" + str(match.group(1)) + "\" found on line " + str(num))
else:
outFile.write(line)

inF = inVlog

while True:
# create a copy of the input
fd, temp_path = tempfile.mkstemp()
shutil.copy2(inF, temp_path)

with open(temp_path, 'r') as inFile:
anyIncludes = False
for line in inFile:
match = re.match(r"^ *`include +\"(.*)\"", line)
if match:
anyIncludes = True
break

if anyIncludes:
process(temp_path, outVlog)
inF = outVlog
os.remove(temp_path)
else:
os.remove(temp_path)
break

print("[INFO] Success. Writing output to: " + str(outVlog))
if __name__ == "__main__":
main()

0 comments on commit 7f5b6a9

Please sign in to comment.