Skip to content

Commit 843e26f

Browse files
committed
add glossary prototype
Signed-off-by: Jack Luar <[email protected]>
1 parent cb76c3b commit 843e26f

File tree

6 files changed

+270
-0
lines changed

6 files changed

+270
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ SupportedOS.md
9292
index2.md
9393
Manpage.md
9494
mainREADME.md
95+
MessagesFinal.md
9596
build
9697
.scala-build/
9798
.bsp/

docs/conf.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,3 +209,7 @@ def setup(app):
209209
# Get Manpage file
210210
url = "https://raw.githubusercontent.com/The-OpenROAD-Project/OpenROAD/master/src/utl/README.md"
211211
get_file_from_url(url, "Manpage.md")
212+
213+
# Populate Autotuner messages.
214+
command = "python getMessages.py"
215+
_ = os.popen(command).read()

docs/getMessages.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#!/usr/bin/env python3
2+
3+
import os
4+
import re
5+
6+
command = "python ../etc/find_messages.py -d ../tools/AutoTuner/src"
7+
output = os.popen(command).read()
8+
9+
with open("user/MessagesFinal.md", "w") as f:
10+
f.write("# OpenROAD Messages Glossary\n")
11+
f.write(
12+
"Listed below are the OpenROAD warning/errors you may encounter while using the application.\n"
13+
)
14+
f.write("\n")
15+
f.write("| Tool | Code | Filename:Line Number | Type | Information |\n")
16+
f.write("| ---- | ---- | -------------------- | ---- | ----------------------- |\n")
17+
18+
lines = output.split("\n")
19+
for line in lines:
20+
columns = line.split()
21+
if not columns:
22+
continue
23+
ant = columns[0]
24+
num = columns[1]
25+
fileLineNum = f"[{columns[2]}]({columns[-1]})"
26+
msgType = columns[-2]
27+
tool = columns[0].lower()
28+
try:
29+
# aim is to match all level1 header and their corresponding text.
30+
message = open(f"./messages/{tool}/{num}.md").read()
31+
pattern = re.compile(
32+
r"#\s*(?P<header1>[^\n]+)\n*(?P<body_text>.*?)(?=\n#|$)", re.DOTALL
33+
)
34+
matches = pattern.finditer(message)
35+
m = []
36+
for match in matches:
37+
header1 = match.group("header1")
38+
body_text = match.group("body_text").strip()
39+
m.append(f"{header1}-{body_text}")
40+
message = " ".join(x for x in m)
41+
print(message)
42+
43+
except OSError as e:
44+
message = "-"
45+
if not message:
46+
message = "-"
47+
f.write(f"| {ant} | {num} | {fileLineNum} | {msgType} |{message} |\n")

docs/messages/tun/0000.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Setup repo
2+
3+
This displays the remote folder.

docs/user/InstructionsForAutoTuner.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,20 @@ python3 ./tools/AutoTuner/test/smoke_test_tune.py
232232
python3 ./tools/AutoTuner/test/smoke_test_sample_iteration.py
233233
```
234234

235+
## Message glossary
236+
237+
As tool developers, we can also choose to include useful information to the end user -
238+
be it in the form on debugging tips, or solutions to fix the errors/warnings. We compile
239+
a list of such errors in this [table](./MessagesFinal.md). The good thing about
240+
this page is the ability to encode rich formatting using Markdown, enabling you
241+
to convey more information than what can be said from the limited messages in code.
242+
243+
To format the information, refer to this [sample TUN information file](../messages/tun/0000.md).
244+
245+
```bash
246+
cd ./docs/messages/tun && touch _NUM_.md
247+
```
248+
235249
## Citation
236250

237251
Please cite the following paper.

etc/find_messages.py

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
#!/usr/bin/env python3
2+
3+
############################################################################
4+
##
5+
## Copyright (c) 2021, The Regents of the University of California
6+
## All rights reserved.
7+
##
8+
## BSD 3-Clause License
9+
##
10+
## Redistribution and use in source and binary forms, with or without
11+
## modification, are permitted provided that the following conditions are met:
12+
##
13+
## * Redistributions of source code must retain the above copyright notice, this
14+
## list of conditions and the following disclaimer.
15+
##
16+
## * Redistributions in binary form must reproduce the above copyright notice,
17+
## this list of conditions and the following disclaimer in the documentation
18+
## and/or other materials provided with the distribution.
19+
##
20+
## * Neither the name of the copyright holder nor the names of its
21+
## contributors may be used to endorse or promote products derived from
22+
## this software without specific prior written permission.
23+
##
24+
## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25+
## AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26+
## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27+
## ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
28+
## LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29+
## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30+
## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31+
## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32+
## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33+
## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34+
## POSSIBILITY OF SUCH DAMAGE.
35+
##
36+
############################################################################
37+
38+
# Usage:
39+
# cd src/<tool>
40+
# ../../etc/FindMessages.py > messages.txt
41+
42+
import argparse
43+
import glob
44+
import os
45+
import re
46+
import sys
47+
from collections import defaultdict
48+
49+
# AT module uses tab size 4.
50+
TAB_SIZE = 4
51+
52+
53+
def parse_args():
54+
parser = argparse.ArgumentParser(
55+
description="""
56+
Find logger calls and report sorted message IDs.
57+
Also checks for duplicate message IDs.
58+
""",
59+
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
60+
)
61+
parser.add_argument(
62+
"-d",
63+
"--dir",
64+
default=os.getcwd(),
65+
help="Directory to start the search for messages from",
66+
)
67+
parser.add_argument(
68+
"-l",
69+
"--local",
70+
action="store_true",
71+
help="Look only at the local files and don't recurse",
72+
)
73+
args = parser.parse_args()
74+
75+
return args
76+
77+
78+
# The three capture groups are tool, id, and message.
79+
warn_regexp_py = re.compile(
80+
r"""
81+
\[(?P<type>ERROR|INFO|WARNING) # type
82+
\s+ # white-space
83+
(?P<tool>\w+)-(?P<id>\d+)\] # tool-id
84+
\s+ # white-space
85+
(?P<message>.+?)\) # message (ended with a closing parenthesis)
86+
""",
87+
re.VERBOSE | re.MULTILINE,
88+
)
89+
90+
warn_regexp_py_lines = re.compile(
91+
r"""
92+
\[(?P<type>ERROR|INFO|WARNING) # type
93+
\s+ # white-space
94+
(?P<tool>\w+)-(?P<id>\d+)\] # tool-id
95+
\s+ # white-space
96+
(?P<message>.+) # message (end on line)
97+
""",
98+
re.VERBOSE | re.MULTILINE,
99+
)
100+
101+
102+
def scan_file(path, file_name, msgs):
103+
# Grab the file contents as a single string
104+
with open(os.path.join(path, file_name), encoding="utf-8") as file_handle:
105+
lines = file_handle.read()
106+
107+
# Preprocess
108+
original_lines = str(lines)
109+
lines = lines.replace("\n", " ")
110+
lines = re.sub(rf"\s{TAB_SIZE,}", " ", lines)
111+
match = None
112+
res, res2 = [], []
113+
114+
for match in re.finditer(warn_regexp_py, lines):
115+
tool = match.group("tool").strip('"')
116+
msg_id = int(match.group("id"))
117+
key = "{} {:04d}".format(tool, msg_id)
118+
119+
# remove quotes and join strings
120+
message = match.group("message")
121+
message = re.sub(r"\s+", " ", message).strip()
122+
message_type = match.group("type").upper()
123+
res.append([key, message, message_type])
124+
125+
for match in re.finditer(warn_regexp_py_lines, original_lines):
126+
# Count the newlines before the match starts
127+
line_num = original_lines[0 : match.start()].count("\n") + 1
128+
position = "{}:{}".format(file_name, line_num)
129+
file_link = os.path.join(path, file_name).strip("../").replace("\\", "/")
130+
file_link = "https://github.com/The-OpenROAD-Project/OpenROAD-flow-scripts/tree/master/{}#L{}".format(
131+
file_link, line_num
132+
)
133+
res2.append([position, file_link])
134+
135+
if res and res2:
136+
for i, (key, message, message_type) in enumerate(res):
137+
position, file_link = res2[i]
138+
value = "{:25} {} {} {}".format(position, message, message_type, file_link)
139+
msgs[key].add(value)
140+
141+
142+
def scan_dir(path, files, msgs):
143+
for file_name in files:
144+
if re.search(r"\.(c|cc|cpp|cxx|h|hh|yy|ll|i|tcl|py)$", file_name):
145+
scan_file(path, file_name, msgs)
146+
147+
148+
def main():
149+
args = parse_args()
150+
151+
# "tool id" -> "file:line message"
152+
msgs = defaultdict(set)
153+
154+
if args.local: # no recursion
155+
files = [
156+
os.path.basename(file) for file in glob.glob(os.path.join(args.dir, "*"))
157+
]
158+
scan_dir(args.dir, files, msgs)
159+
else:
160+
for path, _, files in os.walk(args.dir):
161+
scan_dir(path, files, msgs)
162+
163+
# Group numbers by set name
164+
set_numbers = defaultdict(set)
165+
for key in msgs:
166+
set_name, number = key.split()
167+
set_numbers[set_name].add(int(number))
168+
169+
has_error = False
170+
for key in sorted(msgs):
171+
count = len(msgs[key])
172+
if count > 1:
173+
set_name, number = key.split()
174+
next_free_integer = int(number) + 1
175+
while next_free_integer in set_numbers[set_name]:
176+
next_free_integer += 1
177+
print(
178+
"Error: {} used {} times, next free message id is {}".format(
179+
key, count, next_free_integer
180+
),
181+
file=sys.stderr,
182+
)
183+
for idloc in sorted(msgs[key]):
184+
fileloc, *_ = idloc.split()
185+
file, line = fileloc.split(":")
186+
print(
187+
" Appears in {} on line {} ".format(file, line),
188+
file=sys.stderr,
189+
)
190+
has_error = True
191+
192+
for key in sorted(msgs):
193+
for msg in sorted(msgs[key]):
194+
print(key, msg)
195+
196+
if has_error:
197+
sys.exit(1)
198+
199+
200+
if __name__ == "__main__":
201+
main()

0 commit comments

Comments
 (0)