-
Notifications
You must be signed in to change notification settings - Fork 670
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1902 from bwhitchurch/fix-insert-includes
Improve insert includes
- Loading branch information
Showing
1 changed file
with
128 additions
and
74 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |