From 316c41ea522edffd189604e360a1a9535ac92769 Mon Sep 17 00:00:00 2001 From: Brandon Whitchurch Date: Thu, 13 Jun 2024 08:19:27 -0700 Subject: [PATCH 1/4] fixes for insert includes - avoid infinite loop condition - follow pep8 style guide - encapsulate some functionality to decrease visual nesting --- scripts/insert-includes.py | 162 +++++++++++++++++++++++-------------- 1 file changed, 101 insertions(+), 61 deletions(-) diff --git a/scripts/insert-includes.py b/scripts/insert-includes.py index b939878a..6c77bb5d 100755 --- a/scripts/insert-includes.py +++ b/scripts/insert-includes.py @@ -1,80 +1,120 @@ #!/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): + +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(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 + """ + with open(in_fname, "r", encoding="utf-8") as in_file: + with open(out_fname, "w", encoding="utf-8") as out_file: + # for each include found, search through all dirs + # and replace if found, error if not + for num, line in enumerate(in_file, 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))) + print_info( + f"Replacing includes for {match.group(1)}" + f" at line {num}" + ) # 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 + inc_file_name = find_include(match.group(1), inc_dirs) + with open(inc_file_name, "r", encoding="utf-8") as inc_file: + out_file.writelines(inc_file) - # 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) + out_file.write(line) -inF = inVlog -while True: - # create a copy of the input - fd, temp_path = tempfile.mkstemp() - shutil.copy2(inF, temp_path) +def main(): + """ + Entry point for the script. - with open(temp_path, 'r') as inFile: - anyIncludes = False - for line in inFile: - match = re.match(r"^ *`include +\"(.*)\"", line) - if match: - anyIncludes = True - break + Args: + + """ + in_vlog = sys.argv[1] + out_vlog = sys.argv[2] - if anyIncludes: - process(temp_path, outVlog) - inF = outVlog - os.remove(temp_path) - else: - os.remove(temp_path) - break + if in_vlog == out_vlog: + sys.exit("[ERROR] The input and output file cannot be the same.") -print("[INFO] Success. Writing output to: " + str(outVlog)) + # 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() From feeab6b93be81ba1665910df462a4ae8cb81edda Mon Sep 17 00:00:00 2001 From: Brandon Whitchurch Date: Thu, 13 Jun 2024 08:35:37 -0700 Subject: [PATCH 2/4] add check for duplicate include directives --- scripts/insert-includes.py | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/scripts/insert-includes.py b/scripts/insert-includes.py index 6c77bb5d..bcf39a60 100755 --- a/scripts/insert-includes.py +++ b/scripts/insert-includes.py @@ -70,24 +70,29 @@ def process(in_fname, out_fname, inc_dirs=None): out_fname (str): output file name inc_dirs (list): list of directories to search for includes """ + replaced_includes = set() with open(in_fname, "r", encoding="utf-8") as in_file: with open(out_fname, "w", encoding="utf-8") as out_file: # for each include found, search through all dirs # and replace if found, error if not for num, line in enumerate(in_file, 1): match = re.match(r"^ *`include +\"(.*)\"", line) - if match and match.group(1) != "uvm_macros.svh": - 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) - with open(inc_file_name, "r", encoding="utf-8") as inc_file: - out_file.writelines(inc_file) - - else: + if not match or match.group(1) == "uvm_macros.svh": + # copy the line as is out_file.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) + with open(inc_file_name, "r", encoding="utf-8") as inc_file: + out_file.writelines(inc_file) + replaced_includes.add(match.group(1)) def main(): From e4e49ef8985a76ffe2c13e0b451ba595a1d65d4a Mon Sep 17 00:00:00 2001 From: Brandon Whitchurch Date: Thu, 13 Jun 2024 09:13:05 -0700 Subject: [PATCH 3/4] use DFS to recursively search and expand includes in a single pass --- scripts/insert-includes.py | 54 ++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/scripts/insert-includes.py b/scripts/insert-includes.py index bcf39a60..d29847c2 100755 --- a/scripts/insert-includes.py +++ b/scripts/insert-includes.py @@ -61,6 +61,36 @@ def find_include(file_name, 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. + """ + print(f"DFS state: {replaced_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. @@ -71,28 +101,8 @@ def process(in_fname, out_fname, inc_dirs=None): inc_dirs (list): list of directories to search for includes """ replaced_includes = set() - with open(in_fname, "r", encoding="utf-8") as in_file: - with open(out_fname, "w", encoding="utf-8") as out_file: - # for each include found, search through all dirs - # and replace if found, error if not - for num, line in enumerate(in_file, 1): - match = re.match(r"^ *`include +\"(.*)\"", line) - if not match or match.group(1) == "uvm_macros.svh": - # copy the line as is - out_file.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) - with open(inc_file_name, "r", encoding="utf-8") as inc_file: - out_file.writelines(inc_file) - replaced_includes.add(match.group(1)) + with open(out_fname, "w", encoding="utf-8") as out_file: + process_helper(in_fname, out_file, inc_dirs, replaced_includes) def main(): From ecd7c805a40b6aa42fcaf24b787cfbd11977b435 Mon Sep 17 00:00:00 2001 From: Brandon Whitchurch Date: Thu, 13 Jun 2024 09:18:44 -0700 Subject: [PATCH 4/4] remove debug print --- scripts/insert-includes.py | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/insert-includes.py b/scripts/insert-includes.py index d29847c2..22dd8e9d 100755 --- a/scripts/insert-includes.py +++ b/scripts/insert-includes.py @@ -65,7 +65,6 @@ def process_helper(in_fname, out_f, inc_dirs, replaced_includes): """ Helper function to DFS through include files and replace includes. """ - print(f"DFS state: {replaced_includes}") include_regex = re.compile(r"^ *`include +\"(.*)\"") # slurp the input file. # this avoids having a bunch of fds open during recursion