diff --git a/scripts/insert-includes.py b/scripts/insert-includes.py
index b939878a..22dd8e9d 100755
--- a/scripts/insert-includes.py
+++ b/scripts/insert-includes.py
@@ -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
-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.")
+def print_info(msg):
+ """
+ Print an info message.
-# add directories to search list
-incDirs = sys.argv[3:]
-print("[INFO] Searching following dirs for includes: " + str(incDirs))
+ Args:
+ msg (str): message to print
+ """
+ print(f"[INFO] {msg}")
-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)
+def print_error(msg, critical=True):
+ """
+ Print an error message.
-inF = inVlog
+ 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}")
-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
+def find_include(file_name, inc_dirs):
+ """
+ Find the include file in the list of directories.
- if anyIncludes:
- process(temp_path, outVlog)
- inF = outVlog
- os.remove(temp_path)
- else:
- os.remove(temp_path)
- break
+ Args:
+ file_name (str): include file name
+ inc_dirs (list): list of directories to search for includes
-print("[INFO] Success. Writing output to: " + str(outVlog))
+ 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:
+