opencos-eda 0.3.3__py3-none-any.whl → 0.3.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- opencos/commands/sim.py +16 -6
- opencos/deps/defaults.py +1 -0
- opencos/deps/deps_processor.py +60 -24
- opencos/deps_schema.py +17 -0
- opencos/eda.py +13 -19
- opencos/eda_base.py +78 -29
- opencos/eda_config.py +86 -41
- opencos/eda_config_defaults.yml +8 -1
- opencos/eda_extract_targets.py +0 -0
- opencos/eda_tool_helper.py +19 -0
- opencos/export_helper.py +89 -31
- opencos/files.py +3 -1
- opencos/hw/oc_cli.py +0 -0
- opencos/tests/helpers.py +60 -17
- opencos/tests/test_eda.py +11 -1
- opencos/tools/cocotb.py +94 -21
- opencos/tools/iverilog.py +4 -0
- opencos/tools/riviera.py +23 -12
- opencos/tools/verilator.py +91 -2
- opencos/tools/vivado.py +1 -0
- opencos/util.py +86 -57
- opencos/utils/str_helpers.py +6 -1
- opencos/utils/subprocess_helpers.py +66 -3
- {opencos_eda-0.3.3.dist-info → opencos_eda-0.3.6.dist-info}/METADATA +12 -3
- {opencos_eda-0.3.3.dist-info → opencos_eda-0.3.6.dist-info}/RECORD +28 -28
- {opencos_eda-0.3.3.dist-info → opencos_eda-0.3.6.dist-info}/WHEEL +0 -0
- {opencos_eda-0.3.3.dist-info → opencos_eda-0.3.6.dist-info}/entry_points.txt +0 -0
- {opencos_eda-0.3.3.dist-info → opencos_eda-0.3.6.dist-info}/licenses/LICENSE +0 -0
- {opencos_eda-0.3.3.dist-info → opencos_eda-0.3.6.dist-info}/licenses/LICENSE.spdx +0 -0
- {opencos_eda-0.3.3.dist-info → opencos_eda-0.3.6.dist-info}/top_level.txt +0 -0
opencos/util.py
CHANGED
|
@@ -21,6 +21,7 @@ from dotenv import load_dotenv
|
|
|
21
21
|
from supports_color import supportsColor
|
|
22
22
|
|
|
23
23
|
from opencos.utils import status_constants
|
|
24
|
+
from opencos.utils.str_helpers import strip_ansi_color
|
|
24
25
|
|
|
25
26
|
global_exit_allowed = False # pylint: disable=invalid-name
|
|
26
27
|
progname = "UNKNOWN" # pylint: disable=invalid-name
|
|
@@ -31,6 +32,7 @@ env_files_loaded = set() # pylint: disable=invalid-name
|
|
|
31
32
|
|
|
32
33
|
args = { # pylint: disable=invalid-name
|
|
33
34
|
'color' : bool(supportsColor.stdout),
|
|
35
|
+
'emoji' : bool(getattr(supportsColor.stdout, 'level', 0) >= 2),
|
|
34
36
|
'quiet' : False,
|
|
35
37
|
'verbose' : False,
|
|
36
38
|
'debug' : False,
|
|
@@ -43,16 +45,21 @@ args = { # pylint: disable=invalid-name
|
|
|
43
45
|
max_error_code = 0 # pylint: disable=invalid-name
|
|
44
46
|
|
|
45
47
|
class Colors:
|
|
46
|
-
'''Namespace class for color printing help
|
|
48
|
+
'''Namespace class for color printing help
|
|
49
|
+
|
|
50
|
+
Avoid calling these directly, other than perhapas calling info(*txt, color=Colors.red)
|
|
51
|
+
with 'color' set. It is preferred for outside callers to use one of the print_<color>(..)
|
|
52
|
+
functions, or one of info|warning|error|debug
|
|
53
|
+
'''
|
|
47
54
|
red = "\x1B[31m"
|
|
48
55
|
green = "\x1B[32m"
|
|
49
|
-
|
|
50
|
-
|
|
56
|
+
yellow = "\x1B[33m" # This looks orange, but it's techincally yellow
|
|
57
|
+
foreground = "\x1B[39m"
|
|
51
58
|
normal = "\x1B[0m"
|
|
52
59
|
|
|
53
60
|
@staticmethod
|
|
54
61
|
def color_text(text: str, color: str) -> str:
|
|
55
|
-
'''Wraps 'text' (str) with color (one of red|green|
|
|
62
|
+
'''Wraps 'text' (str) with color (one of red|green|yellow|foreground) prefix and
|
|
56
63
|
|
|
57
64
|
color (normal) suffix. Disables color prefix/suffix wrapping if args['color']=False
|
|
58
65
|
'''
|
|
@@ -60,29 +67,13 @@ class Colors:
|
|
|
60
67
|
return color + text + "\x1B[0m" # (normal)
|
|
61
68
|
return text
|
|
62
69
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if args['
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
'''Wraps text for printing as green, disabled if global args['color']=False'''
|
|
71
|
-
if args['color']:
|
|
72
|
-
return Colors.green + text + Colors.normal
|
|
73
|
-
return text
|
|
74
|
-
|
|
75
|
-
def orange_text(text: str) -> str:
|
|
76
|
-
'''Wraps text for printing as orange, disabled if global args['color']=False'''
|
|
77
|
-
if args['color']:
|
|
78
|
-
return Colors.orange + text + Colors.normal
|
|
79
|
-
return text
|
|
80
|
-
|
|
81
|
-
def yellow_text(text: str) -> str:
|
|
82
|
-
'''Wraps text for printing as yellow, disabled if global args['color']=False'''
|
|
83
|
-
if args['color']:
|
|
84
|
-
return Colors.yellow + text + Colors.normal
|
|
85
|
-
return text
|
|
70
|
+
|
|
71
|
+
def safe_emoji(emoji: str, default: str = '') -> str:
|
|
72
|
+
'''Returns emoji character if args['emoji'] is True'''
|
|
73
|
+
if args['emoji']:
|
|
74
|
+
return emoji
|
|
75
|
+
return default
|
|
76
|
+
|
|
86
77
|
|
|
87
78
|
class ArtifactTypes(Enum):
|
|
88
79
|
'''Types that are allow-listed for artifacts.add* methods. If you don't use one of
|
|
@@ -342,7 +333,7 @@ def get_argparse_bool_action_kwargs() -> dict:
|
|
|
342
333
|
def get_argparser() -> argparse.ArgumentParser:
|
|
343
334
|
'''Returns the opencos.util ArgumentParser'''
|
|
344
335
|
parser = argparse.ArgumentParser(
|
|
345
|
-
prog='opencos common options', add_help=False, allow_abbrev=False
|
|
336
|
+
prog=f'{safe_emoji("🔎 ")}opencos common options', add_help=False, allow_abbrev=False
|
|
346
337
|
)
|
|
347
338
|
# We set allow_abbrev=False so --force-logfile won't try to attempt parsing shorter similarly
|
|
348
339
|
# named args like --force, we want those to go to unparsed list.
|
|
@@ -354,6 +345,8 @@ def get_argparser() -> argparse.ArgumentParser:
|
|
|
354
345
|
parser.add_argument('--version', default=False, action='store_true')
|
|
355
346
|
parser.add_argument('--color', **bool_action_kwargs, default=bool(supportsColor.stdout),
|
|
356
347
|
help='Use shell colors for info/warning/error messaging')
|
|
348
|
+
parser.add_argument('--emoji', **bool_action_kwargs, default=args['emoji'],
|
|
349
|
+
help=f'Support emojis in terminal{safe_emoji(" 💪")}')
|
|
357
350
|
parser.add_argument('--quiet', **bool_action_kwargs, default=args['quiet'],
|
|
358
351
|
help='Do not display info messaging')
|
|
359
352
|
parser.add_argument('--verbose', **bool_action_kwargs, default=args['verbose'],
|
|
@@ -383,8 +376,10 @@ def get_argparser() -> argparse.ArgumentParser:
|
|
|
383
376
|
'Input .f file to be expanded as eda'
|
|
384
377
|
' args/defines/incdirs/files/targets'))
|
|
385
378
|
parser.add_argument('--env-file', default=[], action='append',
|
|
386
|
-
help=
|
|
387
|
-
|
|
379
|
+
help=(
|
|
380
|
+
"dotenv file(s) to pass ENV vars, (default: .env loaded first,"
|
|
381
|
+
" subsequent files' vars override .env"
|
|
382
|
+
))
|
|
388
383
|
return parser
|
|
389
384
|
|
|
390
385
|
|
|
@@ -407,12 +402,25 @@ def get_argparser_short_help(parser: object = None) -> str:
|
|
|
407
402
|
'''Returns short help for our ArgumentParser'''
|
|
408
403
|
if not parser:
|
|
409
404
|
parser = get_argparser()
|
|
410
|
-
|
|
405
|
+
|
|
406
|
+
if not args['color']:
|
|
407
|
+
# Since python3.14 doesn't care about our custom color settings,
|
|
408
|
+
# need to remove any ANSI colors from argparse help formatter:
|
|
409
|
+
full_lines = strip_ansi_color(parser.format_help()).split('\n')
|
|
410
|
+
else:
|
|
411
|
+
full_lines = parser.format_help().split('\n')
|
|
412
|
+
|
|
411
413
|
lineno = 0
|
|
412
414
|
for lineno, line in enumerate(full_lines):
|
|
413
|
-
|
|
415
|
+
# Again, strip any ANSI colors when searching for starting text:
|
|
416
|
+
# - options:
|
|
417
|
+
# - optional arguments:
|
|
418
|
+
if args['color']:
|
|
419
|
+
line = strip_ansi_color(line)
|
|
420
|
+
if any(line.startswith(x) for x in ('options:', 'optional arguments:')):
|
|
414
421
|
break
|
|
415
|
-
|
|
422
|
+
|
|
423
|
+
# skip the line that says 'options:', replace with the progname:
|
|
416
424
|
return f'{parser.prog}:\n' + '\n'.join(full_lines[lineno + 1:])
|
|
417
425
|
|
|
418
426
|
|
|
@@ -432,11 +440,12 @@ def process_token(arg: list) -> bool:
|
|
|
432
440
|
def load_env_file(env_file: str) -> None:
|
|
433
441
|
'''Handles .env file (from util CLI args --env-file)'''
|
|
434
442
|
if os.path.isfile(env_file):
|
|
435
|
-
load_dotenv(env_file)
|
|
443
|
+
load_dotenv(env_file, override=True)
|
|
436
444
|
env_files_loaded.add(os.path.abspath(env_file))
|
|
437
445
|
else:
|
|
438
446
|
warning(f'--env-file {env_file} does not exist and is not loaded.')
|
|
439
447
|
|
|
448
|
+
|
|
440
449
|
def patch_args_for_dir(tokens: list, patch_dir: str, caller_info: str) -> list:
|
|
441
450
|
'''Given list of args, attempt to correct for relative dir'''
|
|
442
451
|
|
|
@@ -536,8 +545,10 @@ def process_tokens( # pylint: disable=too-many-branches
|
|
|
536
545
|
parser.add_argument('--debug-level', type=int, default=0,
|
|
537
546
|
help='Set debug level messaging (default: 0)')
|
|
538
547
|
parser.add_argument('--env-file', default=[], action='append',
|
|
539
|
-
help=
|
|
540
|
-
|
|
548
|
+
help=(
|
|
549
|
+
"dotenv file(s) to pass ENV vars, (default: .env loaded first,"
|
|
550
|
+
" subsequent files' vars override .env"
|
|
551
|
+
))
|
|
541
552
|
parser.add_argument('-f', '--input-file', default=[], action='append',
|
|
542
553
|
help=(
|
|
543
554
|
'Input .f file to be expanded as eda args, defines, incdirs,'
|
|
@@ -553,7 +564,7 @@ def process_tokens( # pylint: disable=too-many-branches
|
|
|
553
564
|
debug(f'util.process_tokens: {parsed=} {unparsed=} from: {tokens}')
|
|
554
565
|
|
|
555
566
|
if os.path.isfile(str(Path('.env'))):
|
|
556
|
-
parsed.env_file.
|
|
567
|
+
parsed.env_file.insert(0, '.env')
|
|
557
568
|
if parsed.env_file:
|
|
558
569
|
for env_file in parsed.env_file:
|
|
559
570
|
load_env_file(env_file)
|
|
@@ -718,7 +729,7 @@ def print_post(text: str, end: str) -> None:
|
|
|
718
729
|
|
|
719
730
|
|
|
720
731
|
def print_color(text: str, color: str, end: str = '\n') -> None:
|
|
721
|
-
'''Note that color(str) must be one of Colors.[red|green|
|
|
732
|
+
'''Note that color(str) must be one of Colors.[red|green|yellow|normal]'''
|
|
722
733
|
print_pre()
|
|
723
734
|
print(Colors.color_text(text, color), end=end, flush=True)
|
|
724
735
|
print_post(text, end)
|
|
@@ -726,25 +737,25 @@ def print_color(text: str, color: str, end: str = '\n') -> None:
|
|
|
726
737
|
def print_red(text: str, end: str = '\n') -> None:
|
|
727
738
|
'''Print text as red, goes back to normal color'''
|
|
728
739
|
print_pre()
|
|
729
|
-
print(
|
|
740
|
+
print(Colors.color_text(text, color=Colors.red), end=end, flush=True)
|
|
730
741
|
print_post(text, end)
|
|
731
742
|
|
|
732
743
|
def print_green(text: str, end: str = '\n') -> None:
|
|
733
744
|
'''Print text as green, goes back to normal color'''
|
|
734
745
|
print_pre()
|
|
735
|
-
print(
|
|
746
|
+
print(Colors.color_text(text, color=Colors.green), end=end, flush=True)
|
|
736
747
|
print_post(text, end)
|
|
737
748
|
|
|
738
|
-
def
|
|
739
|
-
'''Print text as
|
|
749
|
+
def print_yellow(text: str, end: str = '\n') -> None:
|
|
750
|
+
'''Print text as yellow, goes back to normal color'''
|
|
740
751
|
print_pre()
|
|
741
|
-
print(
|
|
752
|
+
print(Colors.color_text(text, color=Colors.yellow), end=end, flush=True)
|
|
742
753
|
print_post(text, end)
|
|
743
754
|
|
|
744
|
-
def
|
|
745
|
-
'''Print text as
|
|
755
|
+
def print_foreground_color(text: str, end: str = '\n') -> None:
|
|
756
|
+
'''Print text as foreground color, goes back to normal color'''
|
|
746
757
|
print_pre()
|
|
747
|
-
print(
|
|
758
|
+
print(Colors.color_text(text, color=Colors.foreground), end=end, flush=True)
|
|
748
759
|
print_post(text, end)
|
|
749
760
|
|
|
750
761
|
|
|
@@ -757,8 +768,11 @@ def set_debug_level(level) -> None:
|
|
|
757
768
|
info(f"Set debug level to {debug_level}")
|
|
758
769
|
|
|
759
770
|
|
|
760
|
-
def debug(
|
|
761
|
-
|
|
771
|
+
def debug(
|
|
772
|
+
*text, level: int = 1, start: object = None, end: str = '\n', color=Colors.foreground
|
|
773
|
+
) -> None:
|
|
774
|
+
'''Print debug messaging (in foreground color if possible). If args['debug'] is false,
|
|
775
|
+
prints nothing.
|
|
762
776
|
|
|
763
777
|
*text: (positional str args) to be printed
|
|
764
778
|
level: (int) debug level to decide if printed or not.
|
|
@@ -771,10 +785,10 @@ def debug(*text, level: int = 1, start: object = None, end: str = '\n') -> None:
|
|
|
771
785
|
start = "DEBUG: " + (f"[{progname}] " if progname_in_message else "")
|
|
772
786
|
if args['debug'] and \
|
|
773
787
|
(((level==1) and args['verbose']) or (debug_level >= level)):
|
|
774
|
-
|
|
788
|
+
print_color(f"{start}{' '.join(list(text))}", color=color, end=end)
|
|
775
789
|
|
|
776
790
|
|
|
777
|
-
def info(*text, start: object = None, end='\n') -> None:
|
|
791
|
+
def info(*text, start: object = None, end='\n', color=Colors.green) -> None:
|
|
778
792
|
'''Print information messaging (in green if possible). If args['quiet'], prints nothing.
|
|
779
793
|
|
|
780
794
|
*text: (positional str args) to be printed
|
|
@@ -786,10 +800,10 @@ def info(*text, start: object = None, end='\n') -> None:
|
|
|
786
800
|
if start is None:
|
|
787
801
|
start = "INFO: " + (f"[{progname}] " if progname_in_message else "")
|
|
788
802
|
if not args['quiet']:
|
|
789
|
-
|
|
803
|
+
print_color(f"{start}{' '.join(list(text))}", color=color, end=end)
|
|
790
804
|
|
|
791
805
|
def warning(*text, start: object = None, end: str = '\n') -> None:
|
|
792
|
-
'''Print warning messaging (in
|
|
806
|
+
'''Print warning messaging (in yellow if possible).
|
|
793
807
|
|
|
794
808
|
*text: (positional str args) to be printed
|
|
795
809
|
start: (optional str) prefix to message; if None: chooses default start str
|
|
@@ -800,7 +814,7 @@ def warning(*text, start: object = None, end: str = '\n') -> None:
|
|
|
800
814
|
if start is None:
|
|
801
815
|
start = "WARNING: " + (f"[{progname}] " if progname_in_message else "")
|
|
802
816
|
args['warnings'] += 1
|
|
803
|
-
|
|
817
|
+
print_yellow(f"{start}{' '.join(list(text))}", end=end)
|
|
804
818
|
|
|
805
819
|
|
|
806
820
|
def error(
|
|
@@ -823,6 +837,7 @@ def error(
|
|
|
823
837
|
|
|
824
838
|
if start is None:
|
|
825
839
|
start = "ERROR: " + (f"[{progname}] " if progname_in_message else "")
|
|
840
|
+
start += safe_emoji("❌ ")
|
|
826
841
|
args['errors'] += 1
|
|
827
842
|
max_error_code = max(max_error_code, error_code)
|
|
828
843
|
print_red(f"{start}{' '.join(list(text))}", end=end)
|
|
@@ -856,7 +871,17 @@ def exit( # pylint: disable=redefined-builtin
|
|
|
856
871
|
|
|
857
872
|
if global_exit_allowed:
|
|
858
873
|
if not quiet:
|
|
859
|
-
|
|
874
|
+
info_color = Colors.green
|
|
875
|
+
start = safe_emoji('🔚 ')
|
|
876
|
+
if args['errors']:
|
|
877
|
+
info_color = Colors.red
|
|
878
|
+
start = safe_emoji('❗ ')
|
|
879
|
+
elif args['warnings']:
|
|
880
|
+
info_color = Colors.yellow
|
|
881
|
+
info(
|
|
882
|
+
f"{start}Exiting with {args['warnings']} warnings, {args['errors']} errors",
|
|
883
|
+
color=info_color
|
|
884
|
+
)
|
|
860
885
|
sys.exit(error_code)
|
|
861
886
|
|
|
862
887
|
if error_code is None:
|
|
@@ -992,14 +1017,18 @@ def import_class_from_string(full_class_name: str) -> None:
|
|
|
992
1017
|
class ShellCommandList(list):
|
|
993
1018
|
'''Wrapper around a list, of str that we'll run as a subprocess command
|
|
994
1019
|
|
|
995
|
-
included member
|
|
1020
|
+
included member vars for:
|
|
1021
|
+
- tee_path, to save a log from this subprocess commands list
|
|
1022
|
+
- work_dir - in case we want to run this from non-default location.
|
|
996
1023
|
'''
|
|
997
|
-
def __init__(self, obj: object = None, tee_fpath: str = ''):
|
|
1024
|
+
def __init__(self, obj: object = None, tee_fpath: str = '', work_dir: str = ''):
|
|
998
1025
|
super().__init__(obj)
|
|
999
|
-
for k in
|
|
1026
|
+
for k in ('tee_fpath', 'work_dir'):
|
|
1000
1027
|
setattr(self, k, getattr(obj, k, None))
|
|
1001
1028
|
if tee_fpath:
|
|
1002
1029
|
self.tee_fpath = tee_fpath
|
|
1030
|
+
if work_dir:
|
|
1031
|
+
self.work_dir = work_dir
|
|
1003
1032
|
|
|
1004
1033
|
|
|
1005
1034
|
def write_shell_command_file(
|
opencos/utils/str_helpers.py
CHANGED
|
@@ -172,7 +172,7 @@ def pretty_list_columns_manual(data: list, num_columns: int = 4, auto_columns: b
|
|
|
172
172
|
pretty_list_columns_manual(data=data, num_columns=num_columns-1, auto_columns=True)
|
|
173
173
|
)
|
|
174
174
|
return ret_lines
|
|
175
|
-
if max_line_len + max_item_len + _spacing
|
|
175
|
+
if max_line_len + max_item_len + _spacing <= window_cols:
|
|
176
176
|
# add 1 more column if we're guaranteed to have room.
|
|
177
177
|
ret_lines.extend(
|
|
178
178
|
pretty_list_columns_manual(data=data, num_columns=num_columns+1, auto_columns=True)
|
|
@@ -198,3 +198,8 @@ def print_columns_manual(data: list, num_columns: int = 4, auto_columns: bool =
|
|
|
198
198
|
data=data, num_columns=num_columns, auto_columns=auto_columns
|
|
199
199
|
)
|
|
200
200
|
print('\n'.join(lines))
|
|
201
|
+
|
|
202
|
+
def strip_ansi_color(text: str) -> str:
|
|
203
|
+
'''Strip ANSI color characters from str'''
|
|
204
|
+
ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
|
|
205
|
+
return ansi_escape.sub('', text)
|
|
@@ -5,10 +5,18 @@ import shutil
|
|
|
5
5
|
import subprocess
|
|
6
6
|
import sys
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
import psutil
|
|
9
|
+
from opencos.util import debug, error, info, warning, progname, global_log
|
|
9
10
|
|
|
10
11
|
IS_WINDOWS = sys.platform.startswith('win')
|
|
11
12
|
|
|
13
|
+
# For non-Windows, we track the background parent PIDs, because some tools (vivado XSim,
|
|
14
|
+
# most Modelsim/Questa variants) tend to spawn children PIDs that don't always respond
|
|
15
|
+
# nicely to a friendly *nix SIGTERM. So we'll remember what our parent PIDs are, and
|
|
16
|
+
# eda.py's (or other CLI opencos script) can use signal to cleanup any remaining
|
|
17
|
+
# parents + children using subprocess_helpers.cleanup_all()
|
|
18
|
+
ALL_PARENT_PIDS = set()
|
|
19
|
+
|
|
12
20
|
def subprocess_run(
|
|
13
21
|
work_dir: str, command_list: list, fake: bool = False, shell: bool = False
|
|
14
22
|
) -> int:
|
|
@@ -35,10 +43,11 @@ def subprocess_run(
|
|
|
35
43
|
|
|
36
44
|
debug(f"subprocess_run: About to call subprocess.run({c}, **{proc_kwargs}")
|
|
37
45
|
proc = subprocess.run(c, check=True, **proc_kwargs)
|
|
46
|
+
# Note - we do not get PID management for subprocess_run(...)
|
|
38
47
|
return proc.returncode
|
|
39
48
|
|
|
40
49
|
|
|
41
|
-
def subprocess_run_background(
|
|
50
|
+
def subprocess_run_background( # pylint: disable=too-many-branches
|
|
42
51
|
work_dir: str, command_list: list, background: bool = True, fake : bool = False,
|
|
43
52
|
shell: bool = False, tee_fpath: str = ''
|
|
44
53
|
) -> (str, str, int):
|
|
@@ -76,6 +85,9 @@ def subprocess_run_background(
|
|
|
76
85
|
|
|
77
86
|
debug(f"subprocess_run_background: about to call subprocess.Popen({c}, **{proc_kwargs})")
|
|
78
87
|
proc = subprocess.Popen(c, **proc_kwargs) # pylint: disable=consider-using-with
|
|
88
|
+
if not background:
|
|
89
|
+
info(f'PID {proc.pid} for {command_list[0]}')
|
|
90
|
+
add_running_parent_pid(proc.pid)
|
|
79
91
|
|
|
80
92
|
stdout = ''
|
|
81
93
|
tee_fpath_f = None
|
|
@@ -99,10 +111,61 @@ def subprocess_run_background(
|
|
|
99
111
|
stdout += line + '\n'
|
|
100
112
|
|
|
101
113
|
proc.communicate()
|
|
114
|
+
remove_completed_parent_pid(proc.pid)
|
|
115
|
+
|
|
102
116
|
rc = proc.returncode
|
|
103
117
|
if tee_fpath_f:
|
|
104
118
|
tee_fpath_f.write(f'INFO: [{progname}] subprocess_run_background: returncode={rc}\n')
|
|
105
119
|
tee_fpath_f.close()
|
|
106
|
-
|
|
120
|
+
if not background:
|
|
121
|
+
info('subprocess_run_background: wrote: ' + os.path.abspath(tee_fpath))
|
|
107
122
|
|
|
108
123
|
return stdout, '', rc
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def add_running_parent_pid(pid: int) -> None:
|
|
127
|
+
'''Adds pid (if still alive) to ALL_PARENT_PIDS'''
|
|
128
|
+
try:
|
|
129
|
+
p = psutil.Process(pid)
|
|
130
|
+
ALL_PARENT_PIDS.add(p.pid)
|
|
131
|
+
except psutil.NoSuchProcess:
|
|
132
|
+
pass
|
|
133
|
+
except Exception as e:
|
|
134
|
+
error(f'{pid=} exception {e}')
|
|
135
|
+
|
|
136
|
+
def remove_completed_parent_pid(pid: int) -> None:
|
|
137
|
+
'''Removes pid (if no longer alive) from ALL_PARENT_PIDS.'''
|
|
138
|
+
try:
|
|
139
|
+
p = psutil.Process(pid)
|
|
140
|
+
warning(f'PID {p.pid} still running')
|
|
141
|
+
except psutil.NoSuchProcess:
|
|
142
|
+
ALL_PARENT_PIDS.remove(pid)
|
|
143
|
+
except Exception as e:
|
|
144
|
+
error(f'{pid=} exception {e}')
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def cleanup_all() -> None:
|
|
148
|
+
'''Kills everything from ALL_PARENT_PIDS.'''
|
|
149
|
+
for parent in ALL_PARENT_PIDS:
|
|
150
|
+
kill_proc_tree(parent)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def kill_proc_tree(pid: int, including_parent: bool = True) -> None:
|
|
154
|
+
'''Kills a process and its entire descendant tree'''
|
|
155
|
+
try:
|
|
156
|
+
parent = psutil.Process(pid)
|
|
157
|
+
children = parent.children(recursive=True)
|
|
158
|
+
info(f'{pid=} {parent=} {children=}')
|
|
159
|
+
for child in children:
|
|
160
|
+
if psutil.Process(child.pid):
|
|
161
|
+
info(f'parent {pid=} killing {child=}')
|
|
162
|
+
child.kill()
|
|
163
|
+
_, still_alive = psutil.wait_procs(children, timeout=5)
|
|
164
|
+
if still_alive:
|
|
165
|
+
warning(f'parent {pid=} {still_alive=}')
|
|
166
|
+
if including_parent:
|
|
167
|
+
info(f'parent {pid=} killing {parent=}')
|
|
168
|
+
parent.kill()
|
|
169
|
+
parent.wait(5)
|
|
170
|
+
except psutil.NoSuchProcess:
|
|
171
|
+
pass # Process already terminated
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: opencos-eda
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.6
|
|
4
4
|
Summary: A simple Python package for wrapping RTL simuliatons and synthesis
|
|
5
5
|
Author-email: Simon Sabato <simon@cognichip.ai>, Drew Ranck <drew@cognichip.ai>
|
|
6
6
|
Project-URL: Homepage, https://github.com/cognichip/opencos
|
|
@@ -9,13 +9,22 @@ License-File: LICENSE
|
|
|
9
9
|
License-File: LICENSE.spdx
|
|
10
10
|
Requires-Dist: mergedeep>=1.3.4
|
|
11
11
|
Requires-Dist: peakrdl>=1.1.0
|
|
12
|
+
Requires-Dist: psutil>=7.0.0
|
|
12
13
|
Requires-Dist: pyyaml>=6.0.2
|
|
13
|
-
Requires-Dist: pytest>=8.3.5
|
|
14
14
|
Requires-Dist: python-dotenv>=1.0.1
|
|
15
15
|
Requires-Dist: schema>=0.7.7
|
|
16
16
|
Requires-Dist: toml>=0.10.2
|
|
17
17
|
Requires-Dist: yamllint>=1.35.1
|
|
18
18
|
Requires-Dist: PySerial>=3.5
|
|
19
|
-
Requires-Dist: cocotb>=2.0
|
|
20
19
|
Requires-Dist: supports_color>=0.2.0
|
|
20
|
+
Requires-Dist: cocotb>=2.0
|
|
21
|
+
Requires-Dist: pytest>=8.3.5
|
|
22
|
+
Requires-Dist: coverage>=7.6.1
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: pylint>=3.0.0; extra == "dev"
|
|
25
|
+
Provides-Extra: docs
|
|
26
|
+
Requires-Dist: mkdocs; extra == "docs"
|
|
27
|
+
Requires-Dist: mkdocs-material; extra == "docs"
|
|
28
|
+
Requires-Dist: mkdocs-wavedrom-plugin; extra == "docs"
|
|
29
|
+
Requires-Dist: mkdocs-plantuml; extra == "docs"
|
|
21
30
|
Dynamic: license-file
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
opencos/__init__.py,sha256=RwJA9oc1uUlvNX7v5zoqwjnSRNq2NZwRlHqtS-ICJkI,122
|
|
2
2
|
opencos/_version.py,sha256=KaWIjS0c08g-C0fgYY1kXwSPqhOFxaq5pYEeoZhOR_I,617
|
|
3
3
|
opencos/_waves_pkg.sv,sha256=TL5YT9lT-fn2FD54MbVVZROmZ7vtW3ScA_rM2eRzKmU,2068
|
|
4
|
-
opencos/deps_schema.py,sha256=
|
|
5
|
-
opencos/eda.py,sha256=
|
|
6
|
-
opencos/eda_base.py,sha256=
|
|
7
|
-
opencos/eda_config.py,sha256=
|
|
8
|
-
opencos/eda_config_defaults.yml,sha256=
|
|
4
|
+
opencos/deps_schema.py,sha256=fx1_IJhsDYkUciwwCPTXHP6ftFjTsPVjO4xg12twIjw,17384
|
|
5
|
+
opencos/eda.py,sha256=W7D44uv0rWPaQD6OOaQuotNsDnDdhwAoBi4eI-eHQ9E,23418
|
|
6
|
+
opencos/eda_base.py,sha256=jmqpirBKGKg3BLEgie-OgqYUg-JIpYS6Im491MKWF-M,111801
|
|
7
|
+
opencos/eda_config.py,sha256=cKAUKguoFEvFgvi2c36Osf6W767Y4KYjoagflqVhUSw,14168
|
|
8
|
+
opencos/eda_config_defaults.yml,sha256=tbu7hyfPM0AetW0CTMu4J3umGMpHLn5bs5bkWDyFM3w,16260
|
|
9
9
|
opencos/eda_config_max_verilator_waivers.yml,sha256=lTAU4IOEbUWVlPzuer1YYhIyxpPINeA4EJqcRIT-Ymk,840
|
|
10
10
|
opencos/eda_config_reduced.yml,sha256=cQ9jY4J7EvAbeHTiP6bvpDSVJAYiitjLZPSxxLKIEbk,1440
|
|
11
11
|
opencos/eda_deps_bash_completion.bash,sha256=jMkQKY82HBgOnQeMdA1hMrXguRFtB52SMBxUemKovL4,1958
|
|
12
12
|
opencos/eda_deps_sanitize.py,sha256=SQjvrte9Hv9JesRY0wljvbdC6pAmLCikI-Wdzzy-D04,1939
|
|
13
13
|
opencos/eda_extract_targets.py,sha256=POlxZfqf2dNH2nc1CEw5B_53vSHAicSTkpU9_-2_6Zw,2851
|
|
14
|
-
opencos/eda_tool_helper.py,sha256=
|
|
15
|
-
opencos/export_helper.py,sha256=
|
|
14
|
+
opencos/eda_tool_helper.py,sha256=Xm6nr9XweCjueWFLkrH5U3nK96JGeeh86f2GCPhwY-o,3108
|
|
15
|
+
opencos/export_helper.py,sha256=zDkvsUS6FVrpXl1UTy53QG3CuhYp5FFplI9rRzAE2g8,25395
|
|
16
16
|
opencos/export_json_convert.py,sha256=tSIMbLFtc_Fo66EhFovMii1v_qJYyFZJrPNnoPdW7L0,4182
|
|
17
|
-
opencos/files.py,sha256=
|
|
17
|
+
opencos/files.py,sha256=4fomXM5vyA5FUAImSeAjrPchQPysPSD07c_TLYg5cd8,1617
|
|
18
18
|
opencos/names.py,sha256=Y2aJ5wgpbNIJ-_P5xUXnHMv_h-zMOX2Rt6iLuduqC1Q,1213
|
|
19
19
|
opencos/peakrdl_cleanup.py,sha256=vHNGtalTrIVP335PhRjPt9RhoccgpK1HJAi-E4M8Kc8,736
|
|
20
20
|
opencos/seed.py,sha256=IL9Yg-r9SLSRseMVWaEHmuw2_DNi_eyut11EafoNTsU,942
|
|
21
|
-
opencos/util.py,sha256=
|
|
21
|
+
opencos/util.py,sha256=h0NfMe4sk5CxEO93z9k7m61kMDbrkbUUrDd9CN2aZAE,43558
|
|
22
22
|
opencos/commands/__init__.py,sha256=oOOQmn5_jHAMSOfA3swJJ7mdoyHsJA0lJwKPTudlTns,1125
|
|
23
23
|
opencos/commands/build.py,sha256=mvJYxk5J15k0Cr8R7oIdIIdsEtWV3gE-LnPweVwtSDo,1487
|
|
24
24
|
opencos/commands/deps_help.py,sha256=WDrU7H9sypzDAxe_CHqhW5B_scbQMzBEdf-v-Jcfd5Q,10682
|
|
@@ -31,27 +31,27 @@ opencos/commands/multi.py,sha256=ZkG1b_qhhxXw-Lm2S4J-i576Ai5iJA09UEIDj94FOVM,274
|
|
|
31
31
|
opencos/commands/open.py,sha256=XckvKUNwvc5KHbYGV-eQ2i0WG4X-yckroDaMC610MB4,804
|
|
32
32
|
opencos/commands/proj.py,sha256=cExW9ZZkw6nkpVyNfeQzJADzmPtbYgBgWml82tqO6jY,1158
|
|
33
33
|
opencos/commands/shell.py,sha256=upHpFs8Gdtzi-boVXwsC-QzEsnvtoZNMAu4oN10kdxw,7801
|
|
34
|
-
opencos/commands/sim.py,sha256=
|
|
34
|
+
opencos/commands/sim.py,sha256=3mASPqPiCYzoVLstftcOREtMVb0I0RaLo1wiDFl8nhE,21445
|
|
35
35
|
opencos/commands/sweep.py,sha256=ni4XFgnFF8HLXtwPhETyLWfvc2kgtm4bcxFcKzUhkf0,9343
|
|
36
36
|
opencos/commands/synth.py,sha256=m4ZwqHgOF5We0XP94F7TQli11WCPlkzhamI4fDfFR1o,4573
|
|
37
37
|
opencos/commands/targets.py,sha256=_jRNhm2Fqj0fmMvTw6Ba39DCsRHf_r_uZCy_R064kpA,1472
|
|
38
38
|
opencos/commands/upload.py,sha256=oyImgcEFGxDkdeY9EYyX2R6fTOmN-lTs-HYxAZqXUUo,871
|
|
39
39
|
opencos/commands/waves.py,sha256=nrp3ALwfJujZns44tgCgia_dEedQyKe0T3fuws8h39U,7697
|
|
40
40
|
opencos/deps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
41
|
-
opencos/deps/defaults.py,sha256=
|
|
41
|
+
opencos/deps/defaults.py,sha256=Z6mIVJEV0zQ9rC-HkQFMBFAkixjqKS1TATPSc27wOeA,1502
|
|
42
42
|
opencos/deps/deps_commands.py,sha256=q4JfSfzRO2nM2zdNT4enCy33FokEytZYQJn1HJ6osJk,16606
|
|
43
43
|
opencos/deps/deps_file.py,sha256=YQ5ftYvppRTqUto22r-XDH6-bcMO7cA-WiJ7QzPjljY,17103
|
|
44
|
-
opencos/deps/deps_processor.py,sha256=
|
|
44
|
+
opencos/deps/deps_processor.py,sha256=WvG7p4wo_gNjkS2pxySgDmkomioooctJTxMlmP42kfk,42827
|
|
45
45
|
opencos/hw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
46
46
|
opencos/hw/oc_cli.py,sha256=U1JGlshLZhtd0LgndZFBZVltAj_HemdhbjO_Zo8ZuVM,132252
|
|
47
47
|
opencos/hw/pcie.py,sha256=VUJljaZJYgScAAx5yn7F6GoA8K9eTcw24otYZbkMpYs,3035
|
|
48
48
|
opencos/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
49
49
|
opencos/tests/custom_config.yml,sha256=TRoVM9ZFKPOA_8JmlpzaMhnGO1txmaD14N_8P1oqzew,257
|
|
50
|
-
opencos/tests/helpers.py,sha256=
|
|
50
|
+
opencos/tests/helpers.py,sha256=5EyKRvpNvkJQ4bZcrm340vIZvJ6ed2G6RmWOQAZh7Lk,14113
|
|
51
51
|
opencos/tests/test_build.py,sha256=FQAxOpLVQShAHD_L5rqJctPeSAoqoOCNFI0RXflLuY0,387
|
|
52
52
|
opencos/tests/test_deps_helpers.py,sha256=uQZxleh6aKO-mZQhagHh5xLIBbpQ8dav7-5D0eemq_g,8164
|
|
53
53
|
opencos/tests/test_deps_schema.py,sha256=T3P9KjaMyKsk8b7snNVvNSsom2hIJcg6Z9apYiXoH9Y,941
|
|
54
|
-
opencos/tests/test_eda.py,sha256=
|
|
54
|
+
opencos/tests/test_eda.py,sha256=8X6kej5-uPv2l0xeWOhitCE640XZtiGLwzCktra_Ehg,37784
|
|
55
55
|
opencos/tests/test_eda_elab.py,sha256=AjU4WMYtFoHpNe1Z4yWWpxDKy4V_hAjL5rl3jqphZrk,3179
|
|
56
56
|
opencos/tests/test_eda_synth.py,sha256=BtBrNVJ9C-LJt3K0wNNS5ukEVrET16AbRXl2IzxudJ8,5744
|
|
57
57
|
opencos/tests/test_oc_cli.py,sha256=w-F-LjSSWVql3D2WG8tcV4_C52i-hL_2WT3oDpKQn9s,734
|
|
@@ -64,34 +64,34 @@ opencos/tests/deps_files/non_sv_reqs/DEPS.yml,sha256=VZkahO1AKhD9GUV5lK8VwUONEi5
|
|
|
64
64
|
opencos/tests/deps_files/tags_with_tools/DEPS.yml,sha256=-5U1qfJElgpVhmkLEu3lYuvDYva8kDlt6JOdB9jidmc,1377
|
|
65
65
|
opencos/tests/deps_files/test_err_fatal/DEPS.yml,sha256=GnXIUJvshQWR9PlYxX67T53ejf5KhDbtD8sUJB4Rwd0,95
|
|
66
66
|
opencos/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
67
|
-
opencos/tools/cocotb.py,sha256=
|
|
67
|
+
opencos/tools/cocotb.py,sha256=bR97Mb87D0HPdoV82rLT7oszPLmdIPzYU1IEBaGnuXg,20544
|
|
68
68
|
opencos/tools/invio.py,sha256=S2ChWr8xMZHSOOhX2hGKQhMmtQY2potVQjc-lsMg73o,3299
|
|
69
69
|
opencos/tools/invio_helpers.py,sha256=86WOGmSf4m_lEqBtK3DLjWqI0jnqAWzBEBRYfBUGiSY,8804
|
|
70
70
|
opencos/tools/invio_yosys.py,sha256=CszGeTdE1ilnMmWPLW77BrtobbsGb1CKXqot0hGimFU,5996
|
|
71
|
-
opencos/tools/iverilog.py,sha256=
|
|
71
|
+
opencos/tools/iverilog.py,sha256=3IQIZVDioChKEJIVVJki-q7NlvBg0k3n61oM7ltG9c8,6551
|
|
72
72
|
opencos/tools/modelsim_ase.py,sha256=Jt-6N3BZZyu25fT1ehFQLRUTVvrcCo4e2Gl7UtsQcuk,17834
|
|
73
73
|
opencos/tools/quartus.py,sha256=_TfmPSYpbhmDLw7Dur-rRP0iGwv9hhQ6E5G-XLiYPEM,30486
|
|
74
74
|
opencos/tools/questa.py,sha256=nHImM0Wydcf4YHGibHmQAwmqKHmMxKZUqY-E-vz1o8M,9827
|
|
75
75
|
opencos/tools/questa_fse.py,sha256=hytkeuGg4qImj7rStV1i2kxkz9B0KFheGtcadxmpYAo,2550
|
|
76
|
-
opencos/tools/riviera.py,sha256=
|
|
76
|
+
opencos/tools/riviera.py,sha256=_-vsN7TD6WdW4PVsSJaEhJls3RgXGRowhY_QV1hdFqE,13678
|
|
77
77
|
opencos/tools/slang.py,sha256=S_vODMT5Zl5vi9FMGHfahp5B0oMNyDIRJXtRAldVCwY,8625
|
|
78
78
|
opencos/tools/slang_yosys.py,sha256=MKh13eAmLJDkynZiezyT8E2gI4CKnXipzgFCZppaMXo,10230
|
|
79
79
|
opencos/tools/surelog.py,sha256=S2RAZJyjdISm_tRvAhXbla7_z_tJfotZih5f9Y3m7DQ,5648
|
|
80
80
|
opencos/tools/tabbycad_yosys.py,sha256=2LePPgYXBVdsy7YcffPIWN-I0B7queLQ_f_pme2SCGw,7803
|
|
81
|
-
opencos/tools/verilator.py,sha256=
|
|
82
|
-
opencos/tools/vivado.py,sha256=
|
|
81
|
+
opencos/tools/verilator.py,sha256=lEsy6VwikTUE3MmuK75NKkRV9YS4MKCPzXH1QZOgcY8,24781
|
|
82
|
+
opencos/tools/vivado.py,sha256=I9yVFK5wnFl1268l8WCk5Xh8wZBlSCODVTg4qStVF8c,41475
|
|
83
83
|
opencos/tools/yosys.py,sha256=t3Au8gdwTepIKCPCXHpRXEdtmORQK8xqNvF6baIa7DM,28260
|
|
84
84
|
opencos/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
85
85
|
opencos/utils/markup_helpers.py,sha256=A8Ev5UJ4EVKjdcF2g85SQbjdPZR4jGpNqCLaBy_4v7Q,4569
|
|
86
86
|
opencos/utils/status_constants.py,sha256=na6YsqlsCwIYzTXWE14dPadUYRNTrOS6YTXHCer2NbA,635
|
|
87
|
-
opencos/utils/str_helpers.py,sha256
|
|
88
|
-
opencos/utils/subprocess_helpers.py,sha256=
|
|
87
|
+
opencos/utils/str_helpers.py,sha256=-hR7MAQLOoY2lIfqtxNtnzb3apeJPkh8shEGFzkwQfs,6637
|
|
88
|
+
opencos/utils/subprocess_helpers.py,sha256=idWc-sy_XJaxIl06tt_QjThYWoLL_Wmy7aLCpEo9y3c,5829
|
|
89
89
|
opencos/utils/vscode_helper.py,sha256=9nHyMUIL-gzfW-qLH06sgaCnVK-YTOtu6pusitNNhL8,1363
|
|
90
90
|
opencos/utils/vsim_helper.py,sha256=1johPOGbjbMgnCDSTpgsQcSuAquiqq1Y2MBxS6WY6b4,1552
|
|
91
|
-
opencos_eda-0.3.
|
|
92
|
-
opencos_eda-0.3.
|
|
93
|
-
opencos_eda-0.3.
|
|
94
|
-
opencos_eda-0.3.
|
|
95
|
-
opencos_eda-0.3.
|
|
96
|
-
opencos_eda-0.3.
|
|
97
|
-
opencos_eda-0.3.
|
|
91
|
+
opencos_eda-0.3.6.dist-info/licenses/LICENSE,sha256=HyVuytGSiAUQ6ErWBHTqt1iSGHhLmlC8fO7jTCuR8dU,16725
|
|
92
|
+
opencos_eda-0.3.6.dist-info/licenses/LICENSE.spdx,sha256=8gn1610RMP6eFgT3Hm6q9VKXt0RvdTItL_oxMo72jII,189
|
|
93
|
+
opencos_eda-0.3.6.dist-info/METADATA,sha256=EYL2QzXAEnFV7r3iyi2DGm2IBBHNxK8c-seSSIeZIao,1039
|
|
94
|
+
opencos_eda-0.3.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
95
|
+
opencos_eda-0.3.6.dist-info/entry_points.txt,sha256=6n1T5NwVYDhN5l1h5zmyT197G4pE0SySDreB0QJzJR0,218
|
|
96
|
+
opencos_eda-0.3.6.dist-info/top_level.txt,sha256=J4JDP-LpRyJqPNeh9bSjx6yrLz2Mk0h6un6YLmtqql4,8
|
|
97
|
+
opencos_eda-0.3.6.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|