mirror of
https://github.com/YosysHQ/sby.git
synced 2025-08-25 22:26:07 +00:00
Merge branch 'main' into krys/fix_status_trace
This commit is contained in:
commit
5fc7b93627
9 changed files with 156 additions and 101 deletions
|
@ -64,15 +64,15 @@ status_show = args.status
|
||||||
status_reset = args.status_reset
|
status_reset = args.status_reset
|
||||||
status_cancels = args.status_cancels
|
status_cancels = args.status_cancels
|
||||||
task_status = args.task_status
|
task_status = args.task_status
|
||||||
status_live_csv = args.livecsv
|
status_live_formats = args.live_formats
|
||||||
status_show_csv = args.statuscsv
|
status_format = args.status_format
|
||||||
status_latest = args.status_latest
|
status_latest = args.status_latest
|
||||||
|
|
||||||
if autotune and linkmode:
|
if autotune and linkmode:
|
||||||
print("ERROR: --link flag currently not available with --autotune")
|
print("ERROR: --link flag currently not available with --autotune")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
if status_show or status_reset or task_status or status_show_csv:
|
if status_show or status_reset or task_status or status_format:
|
||||||
target = workdir_prefix or workdir or sbyfile
|
target = workdir_prefix or workdir or sbyfile
|
||||||
if target is None:
|
if target is None:
|
||||||
print("ERROR: Specify a .sby config file or working directory to use --status.")
|
print("ERROR: Specify a .sby config file or working directory to use --status.")
|
||||||
|
@ -104,16 +104,16 @@ if status_show or status_reset or task_status or status_show_csv:
|
||||||
if status_show:
|
if status_show:
|
||||||
status_db.print_status_summary(status_latest)
|
status_db.print_status_summary(status_latest)
|
||||||
|
|
||||||
if status_show_csv:
|
if status_format:
|
||||||
status_db.print_status_summary_csv(tasknames, status_latest)
|
status_db.print_status_summary_fmt(tasknames, status_format, status_latest)
|
||||||
|
|
||||||
if task_status:
|
if task_status:
|
||||||
status_db.print_task_summary()
|
status_db.print_task_summary()
|
||||||
|
|
||||||
status_db.db.close()
|
status_db.db.close()
|
||||||
|
|
||||||
if status_live_csv:
|
if status_live_formats:
|
||||||
print(f"WARNING: --livecsv flag found but not used.")
|
print(f"WARNING: --live option found but not used.")
|
||||||
|
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
elif status_latest:
|
elif status_latest:
|
||||||
|
@ -496,7 +496,7 @@ def start_task(taskloop, taskname):
|
||||||
else:
|
else:
|
||||||
junit_filename = "junit"
|
junit_filename = "junit"
|
||||||
|
|
||||||
task = SbyTask(sbyconfig, my_workdir, early_logmsgs, reusedir, status_cancels, taskloop, name=taskname, live_csv=status_live_csv)
|
task = SbyTask(sbyconfig, my_workdir, early_logmsgs, reusedir, status_cancels, taskloop, name=taskname, live_formats=status_live_formats)
|
||||||
|
|
||||||
for k, v in exe_paths.items():
|
for k, v in exe_paths.items():
|
||||||
task.exe_paths[k] = v
|
task.exe_paths[k] = v
|
||||||
|
|
|
@ -29,8 +29,8 @@ def parser_func(release_version='unknown SBY version'):
|
||||||
help="maximum number of processes to run in parallel")
|
help="maximum number of processes to run in parallel")
|
||||||
parser.add_argument("--sequential", action="store_true", dest="sequential",
|
parser.add_argument("--sequential", action="store_true", dest="sequential",
|
||||||
help="run tasks in sequence, not in parallel")
|
help="run tasks in sequence, not in parallel")
|
||||||
parser.add_argument("--livecsv", action="store_true", dest="livecsv",
|
parser.add_argument("--live", action="append", choices=["csv", "jsonl"], dest="live_formats",
|
||||||
help="print live updates of property statuses during task execution in csv format")
|
help="print live updates of property statuses during task execution, may be specified multiple times")
|
||||||
|
|
||||||
parser.add_argument("--autotune", action="store_true", dest="autotune",
|
parser.add_argument("--autotune", action="store_true", dest="autotune",
|
||||||
help="automatically find a well performing engine and engine configuration for each task")
|
help="automatically find a well performing engine and engine configuration for each task")
|
||||||
|
@ -77,8 +77,8 @@ def parser_func(release_version='unknown SBY version'):
|
||||||
|
|
||||||
parser.add_argument("--status", action="store_true", dest="status",
|
parser.add_argument("--status", action="store_true", dest="status",
|
||||||
help="summarize the contents of the status database")
|
help="summarize the contents of the status database")
|
||||||
parser.add_argument("--statuscsv", action="store_true", dest="statuscsv",
|
parser.add_argument("--statusfmt", action="store", default="", choices=["csv", "jsonl"], dest="status_format",
|
||||||
help="print the most recent status for each property in csv format")
|
help="print the most recent status for each property in specified format")
|
||||||
parser.add_argument("--latest", action="store_true", dest="status_latest",
|
parser.add_argument("--latest", action="store_true", dest="status_latest",
|
||||||
help="only check statuses from the most recent run of a task")
|
help="only check statuses from the most recent run of a task")
|
||||||
parser.add_argument("--statusreset", action="store_true", dest="status_reset",
|
parser.add_argument("--statusreset", action="store_true", dest="status_reset",
|
||||||
|
|
|
@ -45,12 +45,8 @@ signal.signal(signal.SIGINT, force_shutdown)
|
||||||
signal.signal(signal.SIGTERM, force_shutdown)
|
signal.signal(signal.SIGTERM, force_shutdown)
|
||||||
|
|
||||||
def process_filename(filename):
|
def process_filename(filename):
|
||||||
if filename.startswith("~/"):
|
|
||||||
filename = os.environ['HOME'] + filename[1:]
|
|
||||||
|
|
||||||
filename = os.path.expandvars(filename)
|
filename = os.path.expandvars(filename)
|
||||||
|
return Path(filename).expanduser()
|
||||||
return filename
|
|
||||||
|
|
||||||
def dress_message(workdir, logmessage):
|
def dress_message(workdir, logmessage):
|
||||||
tm = localtime()
|
tm = localtime()
|
||||||
|
@ -579,7 +575,7 @@ class SbyConfig:
|
||||||
self.error(f"sby file syntax error: '[files]' section entry expects up to 2 arguments, {len(entries)} specified")
|
self.error(f"sby file syntax error: '[files]' section entry expects up to 2 arguments, {len(entries)} specified")
|
||||||
|
|
||||||
if len(entries) == 1:
|
if len(entries) == 1:
|
||||||
self.files[os.path.basename(entries[0])] = entries[0]
|
self.files[Path(entries[0]).name] = entries[0]
|
||||||
elif len(entries) == 2:
|
elif len(entries) == 2:
|
||||||
self.files[entries[0]] = entries[1]
|
self.files[entries[0]] = entries[1]
|
||||||
|
|
||||||
|
@ -913,7 +909,7 @@ class SbySummary:
|
||||||
|
|
||||||
|
|
||||||
class SbyTask(SbyConfig):
|
class SbyTask(SbyConfig):
|
||||||
def __init__(self, sbyconfig, workdir, early_logs, reusedir, status_cancels=False, taskloop=None, logfile=None, name=None, live_csv=False):
|
def __init__(self, sbyconfig, workdir, early_logs, reusedir, status_cancels=False, taskloop=None, logfile=None, name=None, live_formats=[]):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.used_options = set()
|
self.used_options = set()
|
||||||
self.models = dict()
|
self.models = dict()
|
||||||
|
@ -921,7 +917,7 @@ class SbyTask(SbyConfig):
|
||||||
self.reusedir = reusedir
|
self.reusedir = reusedir
|
||||||
self.status_cancels = status_cancels
|
self.status_cancels = status_cancels
|
||||||
self.name = name
|
self.name = name
|
||||||
self.live_csv = live_csv
|
self.live_formats = live_formats
|
||||||
self.status = "UNKNOWN"
|
self.status = "UNKNOWN"
|
||||||
self.total_time = 0
|
self.total_time = 0
|
||||||
self.expect = list()
|
self.expect = list()
|
||||||
|
@ -1033,42 +1029,44 @@ class SbyTask(SbyConfig):
|
||||||
raise SbyAbort(logmessage)
|
raise SbyAbort(logmessage)
|
||||||
|
|
||||||
def makedirs(self, path):
|
def makedirs(self, path):
|
||||||
if self.reusedir and os.path.isdir(path):
|
path = Path(path)
|
||||||
|
if self.reusedir and path.is_dir():
|
||||||
rmtree(path, ignore_errors=True)
|
rmtree(path, ignore_errors=True)
|
||||||
if not os.path.isdir(path):
|
path.mkdir(parents=True, exist_ok=True)
|
||||||
os.makedirs(path)
|
|
||||||
|
|
||||||
def copy_src(self, linkmode=False):
|
def copy_src(self, linkmode=False):
|
||||||
self.makedirs(self.workdir + "/src")
|
outdir = Path(self.workdir) / "src"
|
||||||
|
self.makedirs(outdir)
|
||||||
|
|
||||||
for dstfile, lines in self.verbatim_files.items():
|
for dstfile, lines in self.verbatim_files.items():
|
||||||
dstfile = self.workdir + "/src/" + dstfile
|
dstfile = outdir / dstfile
|
||||||
self.log(f"Writing '{dstfile}'.")
|
self.log(f"Writing '{dstfile.absolute()}'.")
|
||||||
|
dstfile.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
with open(dstfile, "w") as f:
|
with open(dstfile, "w") as f:
|
||||||
for line in lines:
|
for line in lines:
|
||||||
f.write(line)
|
f.write(line)
|
||||||
|
|
||||||
for dstfile, srcfile in self.files.items():
|
for dstfile, srcfile in self.files.items():
|
||||||
if dstfile.startswith("/") or dstfile.startswith("../") or ("/../" in dstfile):
|
dstfile = Path(dstfile)
|
||||||
|
if dstfile.is_absolute() or ".." in dstfile.parts:
|
||||||
self.error(f"destination filename must be a relative path without /../: {dstfile}")
|
self.error(f"destination filename must be a relative path without /../: {dstfile}")
|
||||||
dstfile = self.workdir + "/src/" + dstfile
|
dstfile = outdir / dstfile
|
||||||
|
|
||||||
srcfile = process_filename(srcfile)
|
srcfile = process_filename(srcfile)
|
||||||
|
|
||||||
basedir = os.path.dirname(dstfile)
|
basedir = dstfile.parent
|
||||||
if basedir != "" and not os.path.exists(basedir):
|
basedir.mkdir(parents=True, exist_ok=True)
|
||||||
os.makedirs(basedir)
|
|
||||||
|
|
||||||
if linkmode:
|
if linkmode:
|
||||||
verb = "Link"
|
verb = "Link"
|
||||||
else:
|
else:
|
||||||
verb = "Copy"
|
verb = "Copy"
|
||||||
self.log(f"{verb} '{os.path.abspath(srcfile)}' to '{os.path.abspath(dstfile)}'.")
|
self.log(f"{verb} '{srcfile.absolute()}' to '{dstfile.absolute()}'.")
|
||||||
|
|
||||||
if linkmode:
|
if linkmode:
|
||||||
os.symlink(os.path.relpath(srcfile, basedir), dstfile)
|
os.symlink(srcfile.resolve(), dstfile)
|
||||||
elif os.path.isdir(srcfile):
|
elif srcfile.is_dir():
|
||||||
copytree(srcfile, dstfile, dirs_exist_ok=True)
|
copytree(srcfile, dstfile, dirs_exist_ok=True)
|
||||||
else:
|
else:
|
||||||
copyfile(srcfile, dstfile)
|
copyfile(srcfile, dstfile)
|
||||||
|
@ -1097,12 +1095,12 @@ class SbyTask(SbyConfig):
|
||||||
self.__dict__["opt_" + option_name] = default_value
|
self.__dict__["opt_" + option_name] = default_value
|
||||||
|
|
||||||
def make_model(self, model_name):
|
def make_model(self, model_name):
|
||||||
if not os.path.isdir(f"{self.workdir}/model"):
|
modeldir = Path(self.workdir) / "model"
|
||||||
os.makedirs(f"{self.workdir}/model")
|
modeldir.mkdir(exist_ok=True)
|
||||||
|
|
||||||
if model_name == "prep":
|
if model_name == "prep":
|
||||||
with open(f"""{self.workdir}/model/design_prep.ys""", "w") as f:
|
with open(modeldir / "design_prep.ys", "w") as f:
|
||||||
print(f"# running in {self.workdir}/model/", file=f)
|
print(f"# running in {modeldir}/", file=f)
|
||||||
print(f"""read_rtlil design.il""", file=f)
|
print(f"""read_rtlil design.il""", file=f)
|
||||||
if not self.opt_skip_prep:
|
if not self.opt_skip_prep:
|
||||||
print("scc -select; simplemap; select -clear", file=f)
|
print("scc -select; simplemap; select -clear", file=f)
|
||||||
|
@ -1146,7 +1144,7 @@ class SbyTask(SbyConfig):
|
||||||
return [proc]
|
return [proc]
|
||||||
|
|
||||||
if model_name == "base":
|
if model_name == "base":
|
||||||
with open(f"""{self.workdir}/model/design.ys""", "w") as f:
|
with open(modeldir / "design.ys", "w") as f:
|
||||||
print(f"# running in {self.workdir}/src/", file=f)
|
print(f"# running in {self.workdir}/src/", file=f)
|
||||||
for cmd in self.script:
|
for cmd in self.script:
|
||||||
print(cmd, file=f)
|
print(cmd, file=f)
|
||||||
|
@ -1168,7 +1166,7 @@ class SbyTask(SbyConfig):
|
||||||
|
|
||||||
def instance_hierarchy_callback(retcode):
|
def instance_hierarchy_callback(retcode):
|
||||||
if self.design == None:
|
if self.design == None:
|
||||||
with open(f"{self.workdir}/model/design.json") as f:
|
with open(modeldir / "design.json") as f:
|
||||||
self.design = design_hierarchy(f)
|
self.design = design_hierarchy(f)
|
||||||
self.status_db.create_task_properties([
|
self.status_db.create_task_properties([
|
||||||
prop for prop in self.design.properties_by_path.values()
|
prop for prop in self.design.properties_by_path.values()
|
||||||
|
@ -1184,8 +1182,8 @@ class SbyTask(SbyConfig):
|
||||||
return [proc]
|
return [proc]
|
||||||
|
|
||||||
if re.match(r"^smt2(_syn)?(_nomem)?(_stbv|_stdt)?$", model_name):
|
if re.match(r"^smt2(_syn)?(_nomem)?(_stbv|_stdt)?$", model_name):
|
||||||
with open(f"{self.workdir}/model/design_{model_name}.ys", "w") as f:
|
with open(modeldir / f"design_{model_name}.ys", "w") as f:
|
||||||
print(f"# running in {self.workdir}/model/", file=f)
|
print(f"# running in {modeldir}/", file=f)
|
||||||
print(f"""read_rtlil design_prep.il""", file=f)
|
print(f"""read_rtlil design_prep.il""", file=f)
|
||||||
print("hierarchy -smtcheck", file=f)
|
print("hierarchy -smtcheck", file=f)
|
||||||
print("delete */t:$print", file=f)
|
print("delete */t:$print", file=f)
|
||||||
|
@ -1218,8 +1216,8 @@ class SbyTask(SbyConfig):
|
||||||
return [proc]
|
return [proc]
|
||||||
|
|
||||||
if re.match(r"^btor(_syn)?(_nomem)?$", model_name):
|
if re.match(r"^btor(_syn)?(_nomem)?$", model_name):
|
||||||
with open(f"{self.workdir}/model/design_{model_name}.ys", "w") as f:
|
with open(modeldir / f"design_{model_name}.ys", "w") as f:
|
||||||
print(f"# running in {self.workdir}/model/", file=f)
|
print(f"# running in {modeldir}/", file=f)
|
||||||
print(f"""read_rtlil design_prep.il""", file=f)
|
print(f"""read_rtlil design_prep.il""", file=f)
|
||||||
print("hierarchy -simcheck", file=f)
|
print("hierarchy -simcheck", file=f)
|
||||||
print("delete */t:$print", file=f)
|
print("delete */t:$print", file=f)
|
||||||
|
@ -1254,8 +1252,8 @@ class SbyTask(SbyConfig):
|
||||||
return [proc]
|
return [proc]
|
||||||
|
|
||||||
if model_name == "aig":
|
if model_name == "aig":
|
||||||
with open(f"{self.workdir}/model/design_aiger.ys", "w") as f:
|
with open(modeldir / "design_aiger.ys", "w") as f:
|
||||||
print(f"# running in {self.workdir}/model/", file=f)
|
print(f"# running in {modeldir}/", file=f)
|
||||||
print("read_rtlil design_prep.il", file=f)
|
print("read_rtlil design_prep.il", file=f)
|
||||||
print("delete */t:$print", file=f)
|
print("delete */t:$print", file=f)
|
||||||
print("hierarchy -simcheck", file=f)
|
print("hierarchy -simcheck", file=f)
|
||||||
|
@ -1281,7 +1279,7 @@ class SbyTask(SbyConfig):
|
||||||
self,
|
self,
|
||||||
"aig",
|
"aig",
|
||||||
self.model("prep"),
|
self.model("prep"),
|
||||||
f"""cd {self.workdir}/model; {self.exe_paths["yosys"]} -ql design_aiger.log design_aiger.ys"""
|
f"""cd {modeldir}; {self.exe_paths["yosys"]} -ql design_aiger.log design_aiger.ys"""
|
||||||
)
|
)
|
||||||
proc.checkretcode = True
|
proc.checkretcode = True
|
||||||
|
|
||||||
|
@ -1292,8 +1290,8 @@ class SbyTask(SbyConfig):
|
||||||
self,
|
self,
|
||||||
model_name,
|
model_name,
|
||||||
self.model("aig"),
|
self.model("aig"),
|
||||||
f"""cd {self.workdir}/model; {self.exe_paths["abc"]} -c 'read_aiger design_aiger.aig; fold{" -s" if self.opt_aigfolds else ""}; strash; write_aiger design_aiger_fold.aig'""",
|
f"""cd {modeldir}; {self.exe_paths["abc"]} -c 'read_aiger design_aiger.aig; fold{" -s" if self.opt_aigfolds else ""}; strash; write_aiger design_aiger_fold.aig'""",
|
||||||
logfile=open(f"{self.workdir}/model/design_aiger_fold.log", "w")
|
logfile=open(f"{modeldir}/design_aiger_fold.log", "w")
|
||||||
)
|
)
|
||||||
proc.checkretcode = True
|
proc.checkretcode = True
|
||||||
|
|
||||||
|
@ -1430,7 +1428,7 @@ class SbyTask(SbyConfig):
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
status_path = f"{self.workdir}/status.sqlite"
|
status_path = f"{self.workdir}/status.sqlite"
|
||||||
|
|
||||||
self.status_db = SbyStatusDb(status_path, self, live_csv=self.live_csv)
|
self.status_db = SbyStatusDb(status_path, self, live_formats=self.live_formats)
|
||||||
|
|
||||||
def setup_procs(self, setupmode, linkmode=False):
|
def setup_procs(self, setupmode, linkmode=False):
|
||||||
self.handle_non_engine_options()
|
self.handle_non_engine_options()
|
||||||
|
|
|
@ -106,10 +106,10 @@ class FileInUseError(Exception):
|
||||||
|
|
||||||
|
|
||||||
class SbyStatusDb:
|
class SbyStatusDb:
|
||||||
def __init__(self, path: Path, task, timeout: float = 5.0, live_csv = False):
|
def __init__(self, path: Path, task, timeout: float = 5.0, live_formats = []):
|
||||||
self.debug = False
|
self.debug = False
|
||||||
self.task = task
|
self.task = task
|
||||||
self.live_csv = live_csv
|
self.live_formats = live_formats
|
||||||
|
|
||||||
self.con = sqlite3.connect(path, isolation_level=None, timeout=timeout)
|
self.con = sqlite3.connect(path, isolation_level=None, timeout=timeout)
|
||||||
self.db = self.con.cursor()
|
self.db = self.con.cursor()
|
||||||
|
@ -250,10 +250,11 @@ class SbyStatusDb:
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.live_csv:
|
if self.live_formats:
|
||||||
row = self.get_status_data_joined(self.db.lastrowid)
|
row = self.get_status_data_joined(self.db.lastrowid)
|
||||||
csvline = format_status_data_csvline(row)
|
for fmt in self.live_formats:
|
||||||
self.task.log(f"{click.style('csv', fg='yellow')}: {csvline}")
|
fmtline = format_status_data_fmtline(row, fmt)
|
||||||
|
self.task.log(f"{click.style(fmt, fg='yellow')}: {fmtline}")
|
||||||
|
|
||||||
@transaction
|
@transaction
|
||||||
def add_task_trace(
|
def add_task_trace(
|
||||||
|
@ -440,14 +441,15 @@ class SbyStatusDb:
|
||||||
|
|
||||||
return {row["id"]: parse_status_data_row(row) for row in rows}
|
return {row["id"]: parse_status_data_row(row) for row in rows}
|
||||||
|
|
||||||
def print_status_summary_csv(self, tasknames: list[str], latest: bool):
|
def print_status_summary_fmt(self, tasknames: list[str], status_format: str, latest: bool):
|
||||||
# get all statuses
|
# get all statuses
|
||||||
all_properties = self.all_status_data_joined()
|
all_properties = self.all_status_data_joined()
|
||||||
latest_task_ids = filter_latest_task_ids(self.all_tasks())
|
latest_task_ids = filter_latest_task_ids(self.all_tasks())
|
||||||
|
|
||||||
# print csv header
|
# print header
|
||||||
csvheader = format_status_data_csvline(None)
|
header = format_status_data_fmtline(None, status_format)
|
||||||
print(csvheader)
|
if header:
|
||||||
|
print(header)
|
||||||
|
|
||||||
# find summary for each task/property combo
|
# find summary for each task/property combo
|
||||||
prop_map: dict[(str, str, str), dict[str, (int, int)]] = {}
|
prop_map: dict[(str, str, str), dict[str, (int, int)]] = {}
|
||||||
|
@ -497,9 +499,8 @@ class SbyStatusDb:
|
||||||
del prop["UNKNOWN"]
|
del prop["UNKNOWN"]
|
||||||
|
|
||||||
for _, row, _ in prop.values():
|
for _, row, _ in prop.values():
|
||||||
csvline = format_status_data_csvline(all_properties[row])
|
line = format_status_data_fmtline(all_properties[row], status_format)
|
||||||
print(csvline)
|
print(line)
|
||||||
|
|
||||||
|
|
||||||
def combine_statuses(statuses):
|
def combine_statuses(statuses):
|
||||||
statuses = set(statuses)
|
statuses = set(statuses)
|
||||||
|
@ -515,9 +516,7 @@ def parse_status_data_row(raw: sqlite3.Row):
|
||||||
row_dict["data"] = json.loads(row_dict.get("data") or "{}")
|
row_dict["data"] = json.loads(row_dict.get("data") or "{}")
|
||||||
return row_dict
|
return row_dict
|
||||||
|
|
||||||
def format_status_data_csvline(row: dict|None) -> str:
|
fmtline_columns = [
|
||||||
if row is None:
|
|
||||||
csv_header = [
|
|
||||||
"time",
|
"time",
|
||||||
"task_name",
|
"task_name",
|
||||||
"mode",
|
"mode",
|
||||||
|
@ -529,33 +528,52 @@ def format_status_data_csvline(row: dict|None) -> str:
|
||||||
"trace",
|
"trace",
|
||||||
"depth",
|
"depth",
|
||||||
]
|
]
|
||||||
return ','.join(csv_header)
|
|
||||||
|
def format_status_data_fmtline(row: dict|None, fmt: str = "csv") -> str:
|
||||||
|
if row is None:
|
||||||
|
data = None
|
||||||
else:
|
else:
|
||||||
engine = row['data'].get('engine', row['data'].get('source'))
|
engine = row['data'].get('engine', row['data'].get('source'))
|
||||||
try:
|
|
||||||
time = row['status_created'] - row['created']
|
|
||||||
except TypeError:
|
|
||||||
time = 0
|
|
||||||
name = row['hdlname']
|
name = row['hdlname']
|
||||||
depth = row['data'].get('step')
|
depth = row['data'].get('step')
|
||||||
try:
|
|
||||||
trace_path = Path(row['workdir']) / row['path']
|
|
||||||
except TypeError:
|
|
||||||
trace_path = None
|
|
||||||
|
|
||||||
csv_line = [
|
data = {
|
||||||
round(time, 2),
|
"task_name": row['task_name'],
|
||||||
row['task_name'],
|
"mode": row['mode'],
|
||||||
row['mode'],
|
"engine": engine,
|
||||||
engine,
|
"name": name or pretty_path(row['name']),
|
||||||
name or pretty_path(row['name']),
|
"location": row['location'],
|
||||||
row['location'],
|
"kind": row['kind'],
|
||||||
row['kind'],
|
"status": row['status'] or "UNKNOWN",
|
||||||
row['status'] or "UNKNOWN",
|
"depth": depth,
|
||||||
trace_path,
|
}
|
||||||
depth,
|
try:
|
||||||
]
|
data["trace"] = str(Path(row['workdir']) / row['path'])
|
||||||
return ','.join("" if v is None else str(v) for v in csv_line)
|
except TypeError:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
data['time'] = round(row['status_created'] - row['created'], 2)
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
|
if fmt == "csv":
|
||||||
|
if data is None:
|
||||||
|
csv_line = fmtline_columns
|
||||||
|
else:
|
||||||
|
csv_line = [data.get(column) for column in fmtline_columns]
|
||||||
|
def csv_field(value):
|
||||||
|
if value is None:
|
||||||
|
return ""
|
||||||
|
value = str(value).replace('"', '""')
|
||||||
|
if any(c in value for c in '",\n'):
|
||||||
|
value = f'"{value}"'
|
||||||
|
return value
|
||||||
|
return ','.join(map(csv_field, csv_line))
|
||||||
|
elif fmt == "jsonl":
|
||||||
|
if data is None:
|
||||||
|
return ""
|
||||||
|
# field order
|
||||||
|
data = {column: data[column] for column in fmtline_columns if data.get(column)}
|
||||||
|
return json.dumps(data)
|
||||||
|
|
||||||
def filter_latest_task_ids(all_tasks: dict[int, dict[str]]):
|
def filter_latest_task_ids(all_tasks: dict[int, dict[str]]):
|
||||||
latest: dict[str, int] = {}
|
latest: dict[str, int] = {}
|
||||||
|
|
20
tests/links/more_dirs.sby
Normal file
20
tests/links/more_dirs.sby
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
[tasks]
|
||||||
|
link
|
||||||
|
copy
|
||||||
|
|
||||||
|
[options]
|
||||||
|
mode prep
|
||||||
|
|
||||||
|
[engines]
|
||||||
|
btor btormc
|
||||||
|
|
||||||
|
[script]
|
||||||
|
read -noverific
|
||||||
|
script dir/script.ys
|
||||||
|
|
||||||
|
[files]
|
||||||
|
here/dir ${WORKDIR}/../dir
|
||||||
|
a/b/c.v prv32fmcmp.v
|
||||||
|
|
||||||
|
[file here/doc]
|
||||||
|
log foo
|
10
tests/links/more_dirs.sh
Normal file
10
tests/links/more_dirs.sh
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
if [[ $TASK == link ]]; then
|
||||||
|
flags="--setup --link"
|
||||||
|
else
|
||||||
|
flags="--setup"
|
||||||
|
fi
|
||||||
|
python3 $SBY_MAIN -f $SBY_FILE $TASK $flags
|
||||||
|
|
||||||
|
test -e ${WORKDIR}/src/here/dir -a -e ${WORKDIR}/src/a/b/c.v -a -e ${WORKDIR}/src/here/doc
|
|
@ -1,10 +1,10 @@
|
||||||
import os
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
workdir, task = sys.argv[1:]
|
workdir, task = sys.argv[1:]
|
||||||
src = Path(workdir) / "src"
|
src = Path(workdir) / "src"
|
||||||
|
count = 0
|
||||||
for srcfile in src.iterdir():
|
for srcfile in src.iterdir():
|
||||||
if srcfile.name == "heredoc":
|
if srcfile.name == "heredoc":
|
||||||
assert(not srcfile.is_symlink())
|
assert(not srcfile.is_symlink())
|
||||||
|
@ -13,6 +13,11 @@ def main():
|
||||||
assert(local_contents.strip() == 'log foo')
|
assert(local_contents.strip() == 'log foo')
|
||||||
else:
|
else:
|
||||||
assert(srcfile.is_symlink() == (task == "link"))
|
assert(srcfile.is_symlink() == (task == "link"))
|
||||||
|
assert(srcfile.name != "script.ys")
|
||||||
|
count += 1
|
||||||
|
assert(count == 4)
|
||||||
|
script_ys = src / "dir" / "script.ys"
|
||||||
|
assert(script_ys.exists())
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
[tasks]
|
[tasks]
|
||||||
link
|
link
|
||||||
copy
|
copy
|
||||||
|
dir_implicit: dir
|
||||||
|
dir_explicit: dir
|
||||||
|
|
||||||
[options]
|
[options]
|
||||||
mode prep
|
mode prep
|
||||||
|
@ -15,7 +17,9 @@ script dir/script.ys
|
||||||
[files]
|
[files]
|
||||||
../../docs/examples/demos/picorv32.v
|
../../docs/examples/demos/picorv32.v
|
||||||
prv32fmcmp.v
|
prv32fmcmp.v
|
||||||
dir
|
~dir: dir
|
||||||
|
dir_implicit: dir/
|
||||||
|
dir_explicit: dir/ dir/
|
||||||
|
|
||||||
[file heredoc]
|
[file heredoc]
|
||||||
log foo
|
log foo
|
||||||
|
|
|
@ -3,7 +3,7 @@ set -e
|
||||||
python3 $SBY_MAIN -f $SBY_FILE $TASK
|
python3 $SBY_MAIN -f $SBY_FILE $TASK
|
||||||
|
|
||||||
STATUS_CSV=${WORKDIR}/status.csv
|
STATUS_CSV=${WORKDIR}/status.csv
|
||||||
python3 $SBY_MAIN -f $SBY_FILE $TASK --statuscsv --latest | tee $STATUS_CSV
|
python3 $SBY_MAIN -f $SBY_FILE $TASK --statusfmt csv --latest | tee $STATUS_CSV
|
||||||
|
|
||||||
if [[ $TASK =~ "_cover" ]]; then
|
if [[ $TASK =~ "_cover" ]]; then
|
||||||
wc -l $STATUS_CSV | grep -q '6'
|
wc -l $STATUS_CSV | grep -q '6'
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue