opencos-eda 0.2.49__tar.gz → 0.2.51__tar.gz
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_eda-0.2.49/opencos_eda.egg-info → opencos_eda-0.2.51}/PKG-INFO +1 -1
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/commands/lec.py +7 -4
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/commands/multi.py +1 -1
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/commands/shell.py +11 -8
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/commands/sim.py +43 -22
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/deps/deps_file.py +67 -14
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/eda.py +15 -3
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/eda_base.py +54 -20
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/eda_config.py +6 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/eda_config_defaults.yml +36 -14
- opencos_eda-0.2.51/opencos/eda_deps_sanitize.py +73 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/tests/test_eda_elab.py +2 -1
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/tests/test_tools.py +1 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/tools/modelsim_ase.py +22 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/tools/questa.py +5 -3
- opencos_eda-0.2.51/opencos/tools/questa_fse.py +59 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/tools/slang.py +8 -2
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/tools/verilator.py +25 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/tools/vivado.py +33 -26
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/tools/yosys.py +21 -5
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/util.py +183 -8
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/utils/markup_helpers.py +31 -2
- opencos_eda-0.2.51/opencos/utils/status_constants.py +27 -0
- opencos_eda-0.2.51/opencos/utils/vsim_helper.py +55 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51/opencos_eda.egg-info}/PKG-INFO +1 -1
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos_eda.egg-info/SOURCES.txt +4 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos_eda.egg-info/entry_points.txt +1 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/pyproject.toml +3 -1
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/LICENSE +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/LICENSE.spdx +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/README.md +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/__init__.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/_version.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/_waves_pkg.sv +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/commands/__init__.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/commands/build.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/commands/elab.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/commands/export.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/commands/flist.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/commands/open.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/commands/proj.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/commands/sweep.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/commands/synth.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/commands/targets.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/commands/upload.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/commands/waves.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/deps/__init__.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/deps/defaults.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/deps/deps_commands.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/deps/deps_processor.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/deps_schema.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/eda_config_max_verilator_waivers.yml +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/eda_config_reduced.yml +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/eda_deps_bash_completion.bash +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/eda_extract_targets.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/eda_tool_helper.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/export_helper.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/export_json_convert.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/files.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/hw/__init__.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/hw/oc_cli.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/hw/pcie.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/names.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/peakrdl_cleanup.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/seed.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/tests/__init__.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/tests/custom_config.yml +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/tests/deps_files/command_order/DEPS.yml +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/tests/deps_files/error_msgs/DEPS.yml +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/tests/deps_files/iverilog_test/DEPS.yml +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/tests/deps_files/no_deps_here/DEPS.yml +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/tests/deps_files/non_sv_reqs/DEPS.yml +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/tests/deps_files/tags_with_tools/DEPS.yml +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/tests/deps_files/test_err_fatal/DEPS.yml +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/tests/helpers.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/tests/test_build.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/tests/test_deps_helpers.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/tests/test_deps_schema.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/tests/test_eda.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/tests/test_eda_synth.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/tests/test_oc_cli.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/tools/__init__.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/tools/invio.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/tools/invio_helpers.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/tools/invio_yosys.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/tools/iverilog.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/tools/riviera.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/tools/slang_yosys.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/tools/surelog.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/tools/tabbycad_yosys.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/utils/__init__.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/utils/str_helpers.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos/utils/subprocess_helpers.py +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos_eda.egg-info/dependency_links.txt +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos_eda.egg-info/requires.txt +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/opencos_eda.egg-info/top_level.txt +0 -0
- {opencos_eda-0.2.49 → opencos_eda-0.2.51}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: opencos-eda
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.51
|
|
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
|
|
@@ -69,16 +69,13 @@ class CommandLec(CommandDesign):
|
|
|
69
69
|
if self.status_any_error():
|
|
70
70
|
return unparsed
|
|
71
71
|
|
|
72
|
-
# add defines for this job type
|
|
73
|
-
if not self.args['top']:
|
|
74
|
-
self.args['top'] = 'eda.lec'
|
|
75
|
-
|
|
76
72
|
# we require there to be two --designs set.
|
|
77
73
|
if not self.args['designs'] and len(self.args['designs']) != 2:
|
|
78
74
|
self.error('Requires two designs via --designs=<target1> --designs=<target2>',
|
|
79
75
|
f'designs={self.args["designs"]}')
|
|
80
76
|
return []
|
|
81
77
|
|
|
78
|
+
|
|
82
79
|
# Before we do anything else, make sure the two designs actually exist.
|
|
83
80
|
for design in self.args['designs']:
|
|
84
81
|
# maybe it's a file?
|
|
@@ -89,6 +86,12 @@ class CommandLec(CommandDesign):
|
|
|
89
86
|
else:
|
|
90
87
|
self.error(f'--designs={design}, value is not a file or target')
|
|
91
88
|
|
|
89
|
+
if not self.args['top']:
|
|
90
|
+
# Correct the 'top' name so it's eda.<target1>.<target2>.lec (shortnames)
|
|
91
|
+
_, short_target1 = os.path.split(self.args['designs'][0])
|
|
92
|
+
_, short_target2 = os.path.split(self.args['designs'][1])
|
|
93
|
+
self.args['top'] = f'eda.{short_target1}.{short_target2}'
|
|
94
|
+
|
|
92
95
|
# create our work dir
|
|
93
96
|
self.create_work_dir()
|
|
94
97
|
self.run_dep_commands()
|
|
@@ -590,7 +590,7 @@ class CommandToolsMulti(CommandMulti):
|
|
|
590
590
|
def multi_which_tools(self, command):
|
|
591
591
|
'''Overrides CommandMulti.multi_which_tool(command), return a list of all
|
|
592
592
|
possible tools that can run this command'''
|
|
593
|
-
if self.tools is None or
|
|
593
|
+
if self.tools is None or not self.tools:
|
|
594
594
|
# wasn't set via arg --tools, so use all if possible for this command.
|
|
595
595
|
which_tools = self.all_handler_commands.get(command, [])
|
|
596
596
|
else:
|
|
@@ -16,6 +16,8 @@ import os
|
|
|
16
16
|
from opencos import util, export_helper
|
|
17
17
|
from opencos.eda_base import CommandDesign
|
|
18
18
|
|
|
19
|
+
from opencos.utils import status_constants
|
|
20
|
+
|
|
19
21
|
|
|
20
22
|
class CommandShell(CommandDesign):
|
|
21
23
|
'''Base class command handler for: eda sim ...'''
|
|
@@ -181,7 +183,6 @@ class CommandShell(CommandDesign):
|
|
|
181
183
|
_must_strings.append(self.args['pass-pattern'])
|
|
182
184
|
|
|
183
185
|
if len(_bad_strings) > 0 or len(_must_strings) > 0:
|
|
184
|
-
hit_bad_string = False
|
|
185
186
|
hit_must_string_dict = dict.fromkeys(_must_strings)
|
|
186
187
|
fname = os.path.join(self.args['work-dir'], filename)
|
|
187
188
|
with open(fname, 'r', encoding='utf-8') as f:
|
|
@@ -191,12 +192,14 @@ class CommandShell(CommandDesign):
|
|
|
191
192
|
if k in line:
|
|
192
193
|
hit_must_string_dict[k] = True
|
|
193
194
|
if any(bad_str in line for bad_str in _bad_strings):
|
|
194
|
-
|
|
195
|
-
|
|
195
|
+
self.error(
|
|
196
|
+
f"log {fname}:{lineno} contains one of {_bad_strings=}",
|
|
197
|
+
error_code=status_constants.EDA_SHELL_LOG_HAS_BAD_STRING
|
|
198
|
+
)
|
|
196
199
|
|
|
197
|
-
if hit_bad_string:
|
|
198
|
-
self.status += 1
|
|
199
200
|
if any(x is None for x in hit_must_string_dict.values()):
|
|
200
|
-
self.error(
|
|
201
|
-
|
|
202
|
-
|
|
201
|
+
self.error(
|
|
202
|
+
f"Didn't get all passing patterns in log {fname}: {_must_strings=}",
|
|
203
|
+
f" {hit_must_string_dict=}",
|
|
204
|
+
error_code=status_constants.EDA_SHELL_LOG_MISSING_MUST_STRING
|
|
205
|
+
)
|
|
@@ -15,6 +15,7 @@ import os
|
|
|
15
15
|
|
|
16
16
|
from opencos import util, export_helper
|
|
17
17
|
from opencos.eda_base import CommandDesign, Tool
|
|
18
|
+
from opencos.utils import status_constants
|
|
18
19
|
|
|
19
20
|
class CommandSim(CommandDesign):
|
|
20
21
|
'''Base class command handler for: eda sim ...'''
|
|
@@ -170,13 +171,21 @@ class CommandSim(CommandDesign):
|
|
|
170
171
|
if log_filename:
|
|
171
172
|
log_fname = log_filename
|
|
172
173
|
|
|
173
|
-
|
|
174
|
+
_, stdout, _ = self.exec(
|
|
175
|
+
work_dir=self.args['work-dir'], command_list=clist, tee_fpath=tee_fpath
|
|
176
|
+
)
|
|
174
177
|
|
|
175
178
|
if check_logs and log_fname:
|
|
176
179
|
self.check_logs_for_errors(
|
|
177
|
-
filename=
|
|
180
|
+
filename='', file_contents_str=stdout,
|
|
181
|
+
bad_strings=bad_strings, must_strings=must_strings,
|
|
178
182
|
use_bad_strings=use_bad_strings, use_must_strings=use_must_strings
|
|
179
183
|
)
|
|
184
|
+
if log_fname:
|
|
185
|
+
self.artifacts_add(
|
|
186
|
+
name=os.path.join(self.args['work-dir'], log_fname),
|
|
187
|
+
typ='text', description='Simulator stdout/stderr log file'
|
|
188
|
+
)
|
|
180
189
|
|
|
181
190
|
def do_export(self) -> None:
|
|
182
191
|
'''CommandSim helper for handling args --export*
|
|
@@ -268,8 +277,8 @@ class CommandSim(CommandDesign):
|
|
|
268
277
|
'''
|
|
269
278
|
return
|
|
270
279
|
|
|
271
|
-
def check_logs_for_errors( # pylint: disable=dangerous-default-value
|
|
272
|
-
self, filename: str,
|
|
280
|
+
def check_logs_for_errors( # pylint: disable=dangerous-default-value,too-many-locals,too-many-branches
|
|
281
|
+
self, filename: str = '', file_contents_str: str = '',
|
|
273
282
|
bad_strings: list = [], must_strings: list = [],
|
|
274
283
|
use_bad_strings: bool = True, use_must_strings: bool = True
|
|
275
284
|
) -> None:
|
|
@@ -289,26 +298,38 @@ class CommandSim(CommandDesign):
|
|
|
289
298
|
if self.args['pass-pattern'] != "":
|
|
290
299
|
_must_strings.append(self.args['pass-pattern'])
|
|
291
300
|
|
|
292
|
-
if len(_bad_strings)
|
|
293
|
-
|
|
294
|
-
|
|
301
|
+
if len(_bad_strings) == 0 and len(_must_strings) == 0:
|
|
302
|
+
return
|
|
303
|
+
|
|
304
|
+
hit_must_string_dict = dict.fromkeys(_must_strings)
|
|
305
|
+
|
|
306
|
+
lines = []
|
|
307
|
+
fname = ''
|
|
308
|
+
if file_contents_str:
|
|
309
|
+
lines = file_contents_str.split('\n')
|
|
310
|
+
elif filename:
|
|
295
311
|
fname = os.path.join(self.args['work-dir'], filename)
|
|
296
312
|
with open(fname, 'r', encoding='utf-8') as f:
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
313
|
+
lines = f.readl.splitlines()
|
|
314
|
+
|
|
315
|
+
if lines:
|
|
316
|
+
for lineno, line in enumerate(lines):
|
|
317
|
+
if any(must_str in line for must_str in _must_strings):
|
|
318
|
+
for k, _ in hit_must_string_dict.items():
|
|
319
|
+
if k in line:
|
|
320
|
+
hit_must_string_dict[k] = True
|
|
321
|
+
if any(bad_str in line for bad_str in _bad_strings):
|
|
322
|
+
self.error(
|
|
323
|
+
f"log {fname}:{lineno} contains one of {_bad_strings=}",
|
|
324
|
+
error_code=status_constants.EDA_SIM_LOG_HAS_BAD_STRING
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
if any(x is None for x in hit_must_string_dict.values()):
|
|
328
|
+
self.error(
|
|
329
|
+
f"Didn't get all passing patterns in log {fname}: {_must_strings=}",
|
|
330
|
+
f" {hit_must_string_dict=}",
|
|
331
|
+
error_code=status_constants.EDA_SIM_LOG_MISSING_MUST_STRING
|
|
332
|
+
)
|
|
312
333
|
|
|
313
334
|
# Methods that derived classes must override:
|
|
314
335
|
|
|
@@ -10,15 +10,20 @@ from pathlib import Path
|
|
|
10
10
|
|
|
11
11
|
import toml
|
|
12
12
|
|
|
13
|
+
from opencos import util
|
|
13
14
|
from opencos.deps.defaults import DEPS_FILE_EXTS, ROOT_TABLE_KEYS_NOT_TARGETS
|
|
14
15
|
from opencos.util import debug, error
|
|
15
|
-
from opencos.utils.markup_helpers import yaml_safe_load, toml_load_only_root_line_numbers
|
|
16
|
+
from opencos.utils.markup_helpers import yaml_safe_load, toml_load_only_root_line_numbers, \
|
|
17
|
+
markup_writer, markup_dumper
|
|
16
18
|
from opencos.utils.str_helpers import fnmatch_or_re, dep_str2list
|
|
17
19
|
from opencos.utils.subprocess_helpers import subprocess_run_background
|
|
20
|
+
from opencos.utils.status_constants import EDA_DEPS_FILE_NOT_FOUND, EDA_DEPS_TARGET_NOT_FOUND
|
|
18
21
|
|
|
19
22
|
|
|
20
23
|
def deps_data_get_all_targets(data: dict) -> list:
|
|
21
24
|
'''Given extracted DEPS data (dict) get all the root level keys that aren't defaults'''
|
|
25
|
+
if data is None:
|
|
26
|
+
return []
|
|
22
27
|
return [x for x in data.keys() if x not in ROOT_TABLE_KEYS_NOT_TARGETS]
|
|
23
28
|
|
|
24
29
|
|
|
@@ -166,32 +171,43 @@ def deps_target_get_deps_list(
|
|
|
166
171
|
ret = []
|
|
167
172
|
for dep in deps:
|
|
168
173
|
if isinstance(dep, str):
|
|
169
|
-
if dep.startswith('#')
|
|
174
|
+
if not dep or dep.startswith('#'):
|
|
170
175
|
continue
|
|
171
176
|
ret.append(dep)
|
|
172
177
|
return ret
|
|
173
178
|
|
|
174
179
|
|
|
175
180
|
def deps_list_target_sanitize(
|
|
176
|
-
entry, default_key: str = 'deps', target_node: str = '', deps_file: str = ''
|
|
181
|
+
entry, default_key: str = 'deps', target_node: str = '', deps_file: str = '',
|
|
182
|
+
entry_fix_deps_key: bool = True
|
|
177
183
|
) -> dict:
|
|
178
184
|
'''Returns a sanitized DEPS markup table entry (dict --> dict)
|
|
179
185
|
|
|
180
186
|
Since we support target entries that can be dict, list, or str(), sanitize
|
|
181
187
|
them so they are a dict, with a key named 'deps' that has a list of deps.
|
|
182
188
|
'''
|
|
189
|
+
ret = None
|
|
183
190
|
if isinstance(entry, dict):
|
|
184
|
-
|
|
191
|
+
ret = entry
|
|
185
192
|
|
|
186
193
|
if isinstance(entry, str):
|
|
187
194
|
mylist = dep_str2list(entry) # convert str to list
|
|
188
|
-
|
|
195
|
+
ret = {default_key: mylist}
|
|
189
196
|
|
|
190
197
|
if isinstance(entry, list):
|
|
191
198
|
# it's already a list
|
|
192
|
-
|
|
199
|
+
ret = {default_key: entry}
|
|
200
|
+
|
|
201
|
+
if ret is not None:
|
|
202
|
+
if entry_fix_deps_key and 'deps' in ret:
|
|
203
|
+
ret['deps'] = deps_target_get_deps_list(
|
|
204
|
+
entry=ret, default_key='deps', deps_file=deps_file,
|
|
205
|
+
entry_must_have_default_key=True
|
|
206
|
+
)
|
|
207
|
+
else:
|
|
208
|
+
assert False, f"Can't convert to list {entry=} {default_key=} {target_node=} {deps_file=}"
|
|
193
209
|
|
|
194
|
-
|
|
210
|
+
return ret
|
|
195
211
|
|
|
196
212
|
|
|
197
213
|
class DepsFile:
|
|
@@ -230,7 +246,10 @@ class DepsFile:
|
|
|
230
246
|
if deps_path and os.path.exists(deps_path):
|
|
231
247
|
self.rel_deps_file = os.path.join(os.path.relpath(deps_path), deps_leaf)
|
|
232
248
|
|
|
233
|
-
self.error = command_design_ref
|
|
249
|
+
self.error = getattr(command_design_ref, 'error', None)
|
|
250
|
+
if not self.error:
|
|
251
|
+
self.error = util.error
|
|
252
|
+
|
|
234
253
|
|
|
235
254
|
def found(self) -> bool:
|
|
236
255
|
'''Returns true if this DEPS file exists and extracted non-empty data'''
|
|
@@ -270,15 +289,18 @@ class DepsFile:
|
|
|
270
289
|
if '.' in target_node:
|
|
271
290
|
# Likely a filename (target_node does not include path)
|
|
272
291
|
self.error(f'Trying to resolve command-line target={t_full} (file?):',
|
|
273
|
-
f'File={t_node} not found in directory={t_path}'
|
|
292
|
+
f'File={t_node} not found in directory={t_path}',
|
|
293
|
+
error_code=EDA_DEPS_FILE_NOT_FOUND)
|
|
274
294
|
elif not self.rel_deps_file:
|
|
275
295
|
# target, but there's no DEPS file
|
|
276
296
|
self.error(f'Trying to resolve command-line target={t_full}:',
|
|
277
|
-
f'but path {t_path} has no DEPS markup file (DEPS.yml)'
|
|
297
|
+
f'but path {t_path} has no DEPS markup file (DEPS.yml)',
|
|
298
|
+
error_code=EDA_DEPS_FILE_NOT_FOUND)
|
|
278
299
|
else:
|
|
279
300
|
self.error(f'Trying to resolve command-line target={t_full}:',
|
|
280
301
|
f'was not found in deps_file={self.rel_deps_file},',
|
|
281
|
-
f'possible targets in deps file = {list(self.data.keys())}'
|
|
302
|
+
f'possible targets in deps file = {list(self.data.keys())}',
|
|
303
|
+
error_code=EDA_DEPS_TARGET_NOT_FOUND)
|
|
282
304
|
else:
|
|
283
305
|
# If we have caller_info, then this was a recursive call from another
|
|
284
306
|
# DEPS file. It should already have the useful error messaging:
|
|
@@ -287,16 +309,19 @@ class DepsFile:
|
|
|
287
309
|
# Likely a filename (target_node does not include path)
|
|
288
310
|
self.error(f'Trying to resolve target={t_full} (file?):',
|
|
289
311
|
f'called from {caller_info},',
|
|
290
|
-
f'File={t_node} not found in directory={t_path}'
|
|
312
|
+
f'File={t_node} not found in directory={t_path}',
|
|
313
|
+
error_code=EDA_DEPS_FILE_NOT_FOUND)
|
|
291
314
|
elif not self.rel_deps_file:
|
|
292
315
|
# target, but there's no DEPS file
|
|
293
316
|
self.error(f'Trying to resolve target={t_full}:',
|
|
294
317
|
f'called from {caller_info},',
|
|
295
|
-
f'but {t_path} has no DEPS markup file (DEPS.yml)'
|
|
318
|
+
f'but {t_path} has no DEPS markup file (DEPS.yml)',
|
|
319
|
+
error_code=EDA_DEPS_FILE_NOT_FOUND)
|
|
296
320
|
else:
|
|
297
321
|
self.error(f'Trying to resolve target={t_full}:',
|
|
298
322
|
f'called from {caller_info},',
|
|
299
|
-
f'Target not found in deps_file={self.rel_deps_file}'
|
|
323
|
+
f'Target not found in deps_file={self.rel_deps_file}',
|
|
324
|
+
error_code=EDA_DEPS_TARGET_NOT_FOUND)
|
|
300
325
|
else:
|
|
301
326
|
debug(f'Found {target_node=} in deps_file={self.rel_deps_file}')
|
|
302
327
|
found_target = True
|
|
@@ -324,3 +349,31 @@ class DepsFile:
|
|
|
324
349
|
entry.update(entry_sanitized)
|
|
325
350
|
|
|
326
351
|
return entry
|
|
352
|
+
|
|
353
|
+
def get_all_targets(self) -> list:
|
|
354
|
+
'''Returns list of all targets in this obj, skipping keys like DEFAULTS and METADATA'''
|
|
355
|
+
return deps_data_get_all_targets(self.data)
|
|
356
|
+
|
|
357
|
+
def get_sanitized_data(self) -> dict:
|
|
358
|
+
'''Returns a sanitized dict of self.data, resolves DEFAULTS, METADATA, and implicit
|
|
359
|
+
|
|
360
|
+
space-separated-str, lists for 'deps' in a target entry
|
|
361
|
+
'''
|
|
362
|
+
new_data = {}
|
|
363
|
+
targets = self.get_all_targets()
|
|
364
|
+
for target in targets:
|
|
365
|
+
new_data[target] = self.get_entry(target)
|
|
366
|
+
return new_data
|
|
367
|
+
|
|
368
|
+
def write_sanitized_markup(self, filepath: str) -> None:
|
|
369
|
+
'''Writes the sanitized dict of self.data as JSON/YAML (filepath extension)'''
|
|
370
|
+
markup_writer(data=self.get_sanitized_data(), filepath=filepath)
|
|
371
|
+
|
|
372
|
+
def str_sanitized_markup(self, as_yaml: bool = False) -> str:
|
|
373
|
+
'''Returns str sanitized YAML or JSON str of self.data'''
|
|
374
|
+
return markup_dumper(self.get_sanitized_data(), as_yaml=as_yaml)
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
def print_sanitized_markup(self, as_yaml: bool = False) -> None:
|
|
378
|
+
'''Prints sanitized JSON str of self.data to STDOUT'''
|
|
379
|
+
print(self.str_sanitized_markup(as_yaml=as_yaml))
|
|
@@ -16,9 +16,12 @@ import argparse
|
|
|
16
16
|
import shlex
|
|
17
17
|
import importlib.util
|
|
18
18
|
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
19
21
|
import opencos
|
|
20
22
|
from opencos import util, eda_config, eda_base
|
|
21
23
|
from opencos.eda_base import Tool, which_tool
|
|
24
|
+
from opencos.utils import vsim_helper
|
|
22
25
|
|
|
23
26
|
# Configure util:
|
|
24
27
|
util.progname = "EDA"
|
|
@@ -151,6 +154,7 @@ def auto_tool_setup( # pylint: disable=too-many-locals,too-many-branches,too-man
|
|
|
151
154
|
If so, updates config['auto_tools_order'][tool]['exe']
|
|
152
155
|
'''
|
|
153
156
|
|
|
157
|
+
|
|
154
158
|
tool = eda_config.update_config_auto_tool_order_for_tool(
|
|
155
159
|
tool=tool, config=config
|
|
156
160
|
)
|
|
@@ -192,16 +196,23 @@ def auto_tool_setup( # pylint: disable=too-many-locals,too-many-branches,too-man
|
|
|
192
196
|
has_all_exe = True
|
|
193
197
|
has_all_in_exe_path = True
|
|
194
198
|
for exe in exe_list:
|
|
195
|
-
assert exe != '', f'{
|
|
199
|
+
assert exe != '', f'{name=} {value=} value missing "exe" {exe=}'
|
|
196
200
|
p = shutil.which(exe)
|
|
197
201
|
if not p:
|
|
198
202
|
has_all_exe = False
|
|
199
203
|
util.debug(f"... No, missing exe {exe}")
|
|
200
204
|
for req in value.get('requires_in_exe_path', []):
|
|
201
|
-
if p and req and str(req)
|
|
205
|
+
if p and req and str(Path(req)) not in str(Path(p)):
|
|
202
206
|
has_all_in_exe_path = False
|
|
203
207
|
util.debug(f"... No, missing path requirement {req}")
|
|
204
208
|
|
|
209
|
+
has_vsim_helper = True
|
|
210
|
+
if value.get('requires_vsim_helper', False):
|
|
211
|
+
# This tool name must be in opencos.utils.vsim_helper.TOOL_IS[name].
|
|
212
|
+
# Special case for vsim being used by a lot of tools.
|
|
213
|
+
vsim_helper.init() # only runs checks once internally
|
|
214
|
+
has_vsim_helper = vsim_helper.TOOL_IS.get(name, False)
|
|
215
|
+
|
|
205
216
|
if has_all_exe:
|
|
206
217
|
requires_cmd_list = value.get('requires_cmd', [])
|
|
207
218
|
for cmd in requires_cmd_list:
|
|
@@ -219,7 +230,8 @@ def auto_tool_setup( # pylint: disable=too-many-locals,too-many-branches,too-man
|
|
|
219
230
|
util.debug(f"... No, exception {e} running {cmd_list}")
|
|
220
231
|
|
|
221
232
|
|
|
222
|
-
if all([has_all_py, has_all_env, has_all_exe, has_all_in_exe_path
|
|
233
|
+
if all([has_all_py, has_all_env, has_all_exe, has_all_in_exe_path,
|
|
234
|
+
has_vsim_helper]):
|
|
223
235
|
exe = exe_list[0]
|
|
224
236
|
p = shutil.which(exe)
|
|
225
237
|
config['auto_tools_found'][name] = exe # populate key-value pairs w/ first exe in list
|
|
@@ -28,6 +28,7 @@ from opencos.util import Colors
|
|
|
28
28
|
from opencos.utils.str_helpers import sprint_time, strip_outer_quotes, string_or_space, \
|
|
29
29
|
indent_wrap_long_text
|
|
30
30
|
from opencos.utils.subprocess_helpers import subprocess_run_background
|
|
31
|
+
from opencos.utils import status_constants
|
|
31
32
|
|
|
32
33
|
from opencos.deps.deps_file import DepsFile, deps_data_get_all_targets
|
|
33
34
|
from opencos.deps.deps_processor import DepsProcessor
|
|
@@ -257,10 +258,15 @@ class Command:
|
|
|
257
258
|
if self.args['work-dir']:
|
|
258
259
|
if not self.errors_log_f:
|
|
259
260
|
try:
|
|
261
|
+
fullpath = os.path.join(self.args['work-dir'], 'eda.errors.log')
|
|
260
262
|
self.errors_log_f = open( # pylint: disable=consider-using-with
|
|
261
|
-
|
|
262
|
-
encoding='utf-8'
|
|
263
|
+
fullpath, 'w', encoding='utf-8'
|
|
263
264
|
)
|
|
265
|
+
util.artifacts.add(
|
|
266
|
+
name=fullpath,
|
|
267
|
+
typ='text', description='EDA reported errors'
|
|
268
|
+
)
|
|
269
|
+
|
|
264
270
|
except FileNotFoundError:
|
|
265
271
|
pass
|
|
266
272
|
if self.errors_log_f:
|
|
@@ -269,7 +275,7 @@ class Command:
|
|
|
269
275
|
file=self.errors_log_f
|
|
270
276
|
)
|
|
271
277
|
|
|
272
|
-
self.status = util.error(*args, **kwargs)
|
|
278
|
+
self.status = util.error(*args, **kwargs) # error_code passed and returned via kwargs
|
|
273
279
|
|
|
274
280
|
def status_any_error(self, report=True) -> bool:
|
|
275
281
|
'''Used by derived classes process_tokens() to know an error was reached
|
|
@@ -374,8 +380,16 @@ class Command:
|
|
|
374
380
|
if self.args['keep']:
|
|
375
381
|
open(keep_file, 'w', encoding='utf-8').close() # pylint: disable=consider-using-with
|
|
376
382
|
util.debug(f'create_work_dir: created {keep_file=}')
|
|
383
|
+
|
|
384
|
+
# Set the util.artifacts path with our work-dir:
|
|
385
|
+
util.artifacts.set_artifacts_json_dir(self.args['work-dir'])
|
|
386
|
+
|
|
377
387
|
return self.args['work-dir']
|
|
378
388
|
|
|
389
|
+
def artifacts_add(self, name: str, typ: str, description: str) -> None:
|
|
390
|
+
'''Adds a file to util.artifacts, derived classes may override'''
|
|
391
|
+
util.artifacts.add(name=name, typ=typ, description=description)
|
|
392
|
+
|
|
379
393
|
|
|
380
394
|
def exec(self, work_dir: str, command_list: list, background: bool = False,
|
|
381
395
|
stop_on_error: bool = True, quiet: bool = False, tee_fpath: str = '',
|
|
@@ -403,10 +417,16 @@ class Command:
|
|
|
403
417
|
shell=shell
|
|
404
418
|
)
|
|
405
419
|
|
|
406
|
-
if return_code:
|
|
407
|
-
|
|
420
|
+
if return_code > 0:
|
|
421
|
+
if return_code == 1:
|
|
422
|
+
self.status = status_constants.EDA_EXEC_NONZERO_RETURN_CODE1
|
|
423
|
+
if return_code == 255:
|
|
424
|
+
self.status = status_constants.EDA_EXEC_NONZERO_RETURN_CODE255
|
|
425
|
+
else:
|
|
426
|
+
self.status = status_constants.EDA_EXEC_NONZERO_RETURN_CODE2
|
|
408
427
|
if stop_on_error:
|
|
409
|
-
self.error(f"exec: returned with error (return code: {return_code})"
|
|
428
|
+
self.error(f"exec: returned with error (return code: {return_code})",
|
|
429
|
+
error_code=self.status)
|
|
410
430
|
else:
|
|
411
431
|
util.debug(f"exec: returned with error (return code: {return_code})")
|
|
412
432
|
else:
|
|
@@ -424,7 +444,8 @@ class Command:
|
|
|
424
444
|
# Do some minimal type handling, preserving the type(self.args[key])
|
|
425
445
|
if key not in self.args:
|
|
426
446
|
self.error(f'set_arg, {key=} not in self.args {value=}',
|
|
427
|
-
f'({self.command_name=}, {self.__class__.__name__=})'
|
|
447
|
+
f'({self.command_name=}, {self.__class__.__name__=})',
|
|
448
|
+
error_code=status_constants.EDA_COMMAND_OR_ARGS_ERROR)
|
|
428
449
|
|
|
429
450
|
cur_value = self.args[key]
|
|
430
451
|
|
|
@@ -555,7 +576,8 @@ class Command:
|
|
|
555
576
|
parsed, unparsed = parser.parse_known_args(tokens + [''])
|
|
556
577
|
unparsed = list(filter(None, unparsed))
|
|
557
578
|
except argparse.ArgumentError:
|
|
558
|
-
self.error(f'problem {self.command_name=} attempting to parse_known_args for {tokens=}'
|
|
579
|
+
self.error(f'problem {self.command_name=} attempting to parse_known_args for {tokens=}',
|
|
580
|
+
error_code=status_constants.EDA_COMMAND_OR_ARGS_ERROR)
|
|
559
581
|
|
|
560
582
|
parsed_as_dict = vars(parsed)
|
|
561
583
|
|
|
@@ -617,7 +639,8 @@ class Command:
|
|
|
617
639
|
_, unparsed = self.run_argparser_on_list(tokens)
|
|
618
640
|
if process_all and len(unparsed) > 0:
|
|
619
641
|
self.error(f"Didn't understand argument: '{unparsed=}' in",
|
|
620
|
-
f" {self.command_name=} context, {pwd=}"
|
|
642
|
+
f" {self.command_name=} context, {pwd=}",
|
|
643
|
+
error_code=status_constants.EDA_COMMAND_OR_ARGS_ERROR)
|
|
621
644
|
|
|
622
645
|
return unparsed
|
|
623
646
|
|
|
@@ -637,7 +660,8 @@ class Command:
|
|
|
637
660
|
|
|
638
661
|
if not ret and error_if_no_command:
|
|
639
662
|
self.error(f"Looking for a valid eda {self.command_name} <command>",
|
|
640
|
-
f"but didn't find one in {tokens=}"
|
|
663
|
+
f"but didn't find one in {tokens=}",
|
|
664
|
+
error_code=status_constants.EDA_COMMAND_OR_ARGS_ERROR)
|
|
641
665
|
return ret
|
|
642
666
|
|
|
643
667
|
|
|
@@ -679,7 +703,8 @@ class Command:
|
|
|
679
703
|
process_tokens(..) is the starting entrypoint from eda.py'''
|
|
680
704
|
self.write_eda_config_and_args()
|
|
681
705
|
self.error(f"No tool bound to command '{self.command_name}', you",
|
|
682
|
-
" probably need to setup tool, or use '--tool <name>'"
|
|
706
|
+
" probably need to setup tool, or use '--tool <name>'",
|
|
707
|
+
error_code=status_constants.EDA_CONFIG_ERROR)
|
|
683
708
|
|
|
684
709
|
def command_safe_set_tool_defines(self) -> None:
|
|
685
710
|
'''Safe wrapper for calling self.set_tool_defines() in case a Tool parent class is
|
|
@@ -993,7 +1018,8 @@ class CommandDesign(Command): # pylint: disable=too-many-instance-attributes
|
|
|
993
1018
|
self.defines[k] = v
|
|
994
1019
|
util.debug(f"Defined {k}={v}")
|
|
995
1020
|
return None
|
|
996
|
-
self.error(f"Didn't understand +define+: '{plusarg}'"
|
|
1021
|
+
self.error(f"Didn't understand +define+: '{plusarg}'",
|
|
1022
|
+
error_code=status_constants.EDA_COMMAND_OR_ARGS_ERROR)
|
|
997
1023
|
return None
|
|
998
1024
|
|
|
999
1025
|
if plusarg.startswith('+incdir+'):
|
|
@@ -1005,7 +1031,8 @@ class CommandDesign(Command): # pylint: disable=too-many-instance-attributes
|
|
|
1005
1031
|
self.incdirs.append(os.path.abspath(incdir))
|
|
1006
1032
|
util.debug(f"Added include dir '{os.path.abspath(incdir)}'")
|
|
1007
1033
|
return None
|
|
1008
|
-
self.error(f"Didn't understand +incdir+: '{plusarg}'"
|
|
1034
|
+
self.error(f"Didn't understand +incdir+: '{plusarg}'",
|
|
1035
|
+
error_code=status_constants.EDA_COMMAND_OR_ARGS_ERROR)
|
|
1009
1036
|
return None
|
|
1010
1037
|
|
|
1011
1038
|
# remaining plusargs as stored in self.args['unprocessed-plusargs'] (list)
|
|
@@ -1019,7 +1046,8 @@ class CommandDesign(Command): # pylint: disable=too-many-instance-attributes
|
|
|
1019
1046
|
# derived classes have the option to handle it
|
|
1020
1047
|
return plusarg
|
|
1021
1048
|
|
|
1022
|
-
self.error(f"Didn't understand +plusarg: '{plusarg}'"
|
|
1049
|
+
self.error(f"Didn't understand +plusarg: '{plusarg}'",
|
|
1050
|
+
error_code=status_constants.EDA_COMMAND_OR_ARGS_ERROR)
|
|
1023
1051
|
return None
|
|
1024
1052
|
|
|
1025
1053
|
|
|
@@ -1211,7 +1239,8 @@ class CommandDesign(Command): # pylint: disable=too-many-instance-attributes
|
|
|
1211
1239
|
found_target = True
|
|
1212
1240
|
break # move on to the next target
|
|
1213
1241
|
if not found_target: # if STILL not found_this_target...
|
|
1214
|
-
self.error(f"Unable to resolve {target=}"
|
|
1242
|
+
self.error(f"Unable to resolve {target=}",
|
|
1243
|
+
error_code=status_constants.EDA_DEPS_TARGET_NOT_FOUND)
|
|
1215
1244
|
|
|
1216
1245
|
# if we've found any target since being called, it means we found the one we were called for
|
|
1217
1246
|
return found_target
|
|
@@ -1341,7 +1370,8 @@ class CommandDesign(Command): # pylint: disable=too-many-instance-attributes
|
|
|
1341
1370
|
if len(unparsed) == 0:
|
|
1342
1371
|
# If unparsed is still empty, then error.
|
|
1343
1372
|
self.error(f"For command '{self.command_name}' no files or targets were",
|
|
1344
|
-
f"presented at the command line: {orig_tokens}"
|
|
1373
|
+
f"presented at the command line: {orig_tokens}",
|
|
1374
|
+
error_code=status_constants.EDA_COMMAND_OR_ARGS_ERROR)
|
|
1345
1375
|
|
|
1346
1376
|
# by this point hopefully this is a target ... is it a simple filename?
|
|
1347
1377
|
remove_list = []
|
|
@@ -1395,7 +1425,8 @@ class CommandDesign(Command): # pylint: disable=too-many-instance-attributes
|
|
|
1395
1425
|
|
|
1396
1426
|
# we were unable to figure out what this command line token is for...
|
|
1397
1427
|
if process_all and len(unparsed) > 0:
|
|
1398
|
-
self.error(f"Didn't understand command remaining tokens {unparsed=} in CommandDesign"
|
|
1428
|
+
self.error(f"Didn't understand command remaining tokens {unparsed=} in CommandDesign",
|
|
1429
|
+
error_code=status_constants.EDA_COMMAND_OR_ARGS_ERROR)
|
|
1399
1430
|
|
|
1400
1431
|
# handle a missing self.args['top'] with last filepath or last target:
|
|
1401
1432
|
if not self.args.get('top', ''):
|
|
@@ -1428,7 +1459,8 @@ class CommandDesign(Command): # pylint: disable=too-many-instance-attributes
|
|
|
1428
1459
|
|
|
1429
1460
|
if self.error_on_missing_top and not self.args.get('top', ''):
|
|
1430
1461
|
self.error("Did not get a --top or DEPS top, required to run command",
|
|
1431
|
-
f"'{self.command_name}' for tool={self.args.get('tool', None)}"
|
|
1462
|
+
f"'{self.command_name}' for tool={self.args.get('tool', None)}",
|
|
1463
|
+
error_code=status_constants.EDA_COMMAND_MISSING_TOP)
|
|
1432
1464
|
|
|
1433
1465
|
return unparsed
|
|
1434
1466
|
|
|
@@ -1930,7 +1962,8 @@ class CommandParallel(Command):
|
|
|
1930
1962
|
bad_remaining_args = [x for x in single_cmd_unparsed if x.startswith('-')]
|
|
1931
1963
|
if bad_remaining_args:
|
|
1932
1964
|
self.error(f'for {self.command_name} {command=} the following args are unknown',
|
|
1933
|
-
f'{bad_remaining_args}'
|
|
1965
|
+
f'{bad_remaining_args}',
|
|
1966
|
+
error_code=status_constants.EDA_COMMAND_OR_ARGS_ERROR)
|
|
1934
1967
|
|
|
1935
1968
|
# Remove unparsed args starting with '+', since those are commonly sent downstream to
|
|
1936
1969
|
# single job (example, CommandSim plusargs).
|
|
@@ -1972,7 +2005,8 @@ class CommandParallel(Command):
|
|
|
1972
2005
|
|
|
1973
2006
|
key = get_job_arg(job_dict, arg_name='job-name')
|
|
1974
2007
|
if not key:
|
|
1975
|
-
self.error(f'{job_dict=} needs to have a --job-name= arg attached'
|
|
2008
|
+
self.error(f'{job_dict=} needs to have a --job-name= arg attached',
|
|
2009
|
+
error_code=status_constants.EDA_COMMAND_OR_ARGS_ERROR)
|
|
1976
2010
|
if key not in job_names_count_dict:
|
|
1977
2011
|
job_names_count_dict[key] = 1
|
|
1978
2012
|
else:
|
|
@@ -46,6 +46,7 @@ class Defaults:
|
|
|
46
46
|
supported_config_auto_tools_order_keys = set([
|
|
47
47
|
'exe', 'handlers',
|
|
48
48
|
'requires_env', 'requires_py', 'requires_cmd', 'requires_in_exe_path',
|
|
49
|
+
'requires_vsim_helper',
|
|
49
50
|
'disable-tools-multi',
|
|
50
51
|
])
|
|
51
52
|
supported_config_tool_keys = set([
|
|
@@ -308,6 +309,11 @@ def write_eda_config_and_args(
|
|
|
308
309
|
# Use deep copy b/c otherwise these are references to opencos.eda.
|
|
309
310
|
data[x] = copy.deepcopy(getattr(command_obj_ref, x, ''))
|
|
310
311
|
|
|
312
|
+
# copy util.args
|
|
313
|
+
data['util'] = {
|
|
314
|
+
'args': util.args
|
|
315
|
+
}
|
|
316
|
+
|
|
311
317
|
# fix some burried class references in command_obj_ref.config,
|
|
312
318
|
# otherwise we won't be able to safe load this yaml, so cast as str repr.
|
|
313
319
|
for k, v in getattr(command_obj_ref, 'config', {}).items():
|