opencos-eda 0.3.9__py3-none-any.whl → 0.3.11__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/deps_help.py +89 -120
- opencos/commands/export.py +7 -2
- opencos/commands/multi.py +3 -3
- opencos/commands/sim.py +14 -16
- opencos/commands/synth.py +1 -2
- opencos/commands/upload.py +192 -4
- opencos/commands/waves.py +8 -8
- opencos/deps/deps_commands.py +6 -6
- opencos/deps/deps_file.py +82 -79
- opencos/deps/deps_processor.py +129 -50
- opencos/docs/Architecture.md +45 -0
- opencos/docs/ConnectingApps.md +29 -0
- opencos/docs/DEPS.md +199 -0
- opencos/docs/Debug.md +77 -0
- opencos/docs/DirectoryStructure.md +22 -0
- opencos/docs/Installation.md +117 -0
- opencos/docs/OcVivadoTcl.md +63 -0
- opencos/docs/OpenQuestions.md +7 -0
- opencos/docs/README.md +13 -0
- opencos/docs/RtlCodingStyle.md +54 -0
- opencos/docs/eda.md +147 -0
- opencos/docs/oc_cli.md +135 -0
- opencos/eda.py +175 -41
- opencos/eda_base.py +180 -50
- opencos/eda_config.py +62 -16
- opencos/eda_config_defaults.yml +21 -4
- opencos/eda_deps_bash_completion.bash +37 -15
- opencos/files.py +26 -1
- opencos/tools/cocotb.py +5 -5
- opencos/tools/invio.py +2 -2
- opencos/tools/invio_yosys.py +2 -1
- opencos/tools/iverilog.py +3 -3
- opencos/tools/quartus.py +113 -115
- opencos/tools/questa_common.py +3 -4
- opencos/tools/riviera.py +3 -3
- opencos/tools/slang.py +11 -7
- opencos/tools/slang_yosys.py +1 -0
- opencos/tools/surelog.py +4 -3
- opencos/tools/verilator.py +5 -4
- opencos/tools/vivado.py +307 -176
- opencos/tools/yosys.py +4 -4
- opencos/util.py +6 -3
- opencos/utils/dict_helpers.py +31 -0
- opencos/utils/markup_helpers.py +2 -2
- opencos/utils/str_helpers.py +7 -0
- opencos/utils/subprocess_helpers.py +3 -3
- opencos/utils/vscode_helper.py +2 -2
- opencos/utils/vsim_helper.py +58 -22
- {opencos_eda-0.3.9.dist-info → opencos_eda-0.3.11.dist-info}/METADATA +1 -1
- opencos_eda-0.3.11.dist-info/RECORD +94 -0
- {opencos_eda-0.3.9.dist-info → opencos_eda-0.3.11.dist-info}/entry_points.txt +1 -0
- opencos/tests/__init__.py +0 -0
- opencos/tests/custom_config.yml +0 -13
- opencos/tests/deps_files/command_order/DEPS.yml +0 -44
- opencos/tests/deps_files/error_msgs/DEPS.yml +0 -55
- opencos/tests/deps_files/iverilog_test/DEPS.yml +0 -4
- opencos/tests/deps_files/no_deps_here/DEPS.yml +0 -0
- opencos/tests/deps_files/non_sv_reqs/DEPS.yml +0 -50
- opencos/tests/deps_files/tags_with_tools/DEPS.yml +0 -54
- opencos/tests/deps_files/test_err_fatal/DEPS.yml +0 -4
- opencos/tests/helpers.py +0 -354
- opencos/tests/test_build.py +0 -12
- opencos/tests/test_deps_helpers.py +0 -207
- opencos/tests/test_deps_schema.py +0 -30
- opencos/tests/test_eda.py +0 -921
- opencos/tests/test_eda_elab.py +0 -110
- opencos/tests/test_eda_synth.py +0 -150
- opencos/tests/test_oc_cli.py +0 -25
- opencos/tests/test_tools.py +0 -404
- opencos_eda-0.3.9.dist-info/RECORD +0 -99
- {opencos_eda-0.3.9.dist-info → opencos_eda-0.3.11.dist-info}/WHEEL +0 -0
- {opencos_eda-0.3.9.dist-info → opencos_eda-0.3.11.dist-info}/licenses/LICENSE +0 -0
- {opencos_eda-0.3.9.dist-info → opencos_eda-0.3.11.dist-info}/licenses/LICENSE.spdx +0 -0
- {opencos_eda-0.3.9.dist-info → opencos_eda-0.3.11.dist-info}/top_level.txt +0 -0
opencos/commands/waves.py
CHANGED
|
@@ -15,10 +15,10 @@ handler).
|
|
|
15
15
|
# pylint: disable=R0801
|
|
16
16
|
|
|
17
17
|
import os
|
|
18
|
-
import shutil
|
|
19
18
|
|
|
20
19
|
from opencos import util
|
|
21
20
|
from opencos.eda_base import CommandDesign
|
|
21
|
+
from opencos.files import safe_shutil_which
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
class CommandWaves(CommandDesign):
|
|
@@ -120,10 +120,10 @@ class CommandWaves(CommandDesign):
|
|
|
120
120
|
|
|
121
121
|
# TODO(drew): this feels a little customized per-tool, perhaps there's a better
|
|
122
122
|
# way to abstract this configuration for adding other waveform viewers.
|
|
123
|
-
# For example for each command we also have to check
|
|
123
|
+
# For example for each command we also have to check safe_shutil_which, because normal Tool
|
|
124
124
|
# classs should work even w/out PATH, but these don't use Tool classes.
|
|
125
125
|
if wave_file.endswith('.wdb'):
|
|
126
|
-
if 'vivado' in self.config['tools_loaded'] and
|
|
126
|
+
if 'vivado' in self.config['tools_loaded'] and safe_shutil_which('vivado'):
|
|
127
127
|
tcl_name = wave_file + '.waves.tcl'
|
|
128
128
|
with open( tcl_name, 'w', encoding='utf-8') as fo :
|
|
129
129
|
print( 'current_fileset', file=fo)
|
|
@@ -141,20 +141,20 @@ class CommandWaves(CommandDesign):
|
|
|
141
141
|
f"{self.VSIM_TOOLS} in PATH")
|
|
142
142
|
elif wave_file.endswith('.fst'):
|
|
143
143
|
if ('vaporview' in self.config['tools_loaded'] or \
|
|
144
|
-
'surfer' in self.config['tools_loaded']) and
|
|
144
|
+
'surfer' in self.config['tools_loaded']) and safe_shutil_which('code'):
|
|
145
145
|
command_list = ['code', '-n', '.', wave_file]
|
|
146
146
|
self.exec(os.path.dirname(wave_file), command_list)
|
|
147
|
-
elif 'gtkwave' in self.config['tools_loaded'] and
|
|
147
|
+
elif 'gtkwave' in self.config['tools_loaded'] and safe_shutil_which('gtkwave'):
|
|
148
148
|
command_list = ['gtkwave', wave_file]
|
|
149
149
|
self.exec(os.path.dirname(wave_file), command_list)
|
|
150
150
|
else:
|
|
151
151
|
self.error(f"Don't know how to open {wave_file} without GtkWave in PATH")
|
|
152
152
|
elif wave_file.endswith('.vcd'):
|
|
153
153
|
if ('vaporview' in self.config['tools_loaded'] or \
|
|
154
|
-
'surfer' in self.config['tools_loaded']) and
|
|
154
|
+
'surfer' in self.config['tools_loaded']) and safe_shutil_which('code'):
|
|
155
155
|
command_list = ['code', '-n', '.', wave_file]
|
|
156
156
|
self.exec(os.path.dirname(wave_file), command_list)
|
|
157
|
-
elif 'gtkwave' in self.config['tools_loaded'] and
|
|
157
|
+
elif 'gtkwave' in self.config['tools_loaded'] and safe_shutil_which('gtkwave'):
|
|
158
158
|
command_list = ['gtkwave', wave_file]
|
|
159
159
|
self.exec(os.path.dirname(wave_file), command_list)
|
|
160
160
|
elif self._vsim_available(from_tools=self.VSIM_VCD_TOOLS):
|
|
@@ -172,7 +172,7 @@ class CommandWaves(CommandDesign):
|
|
|
172
172
|
self, from_tools: list = VSIM_TOOLS
|
|
173
173
|
) -> bool:
|
|
174
174
|
'''Returns True if 'vsim' is available (Questa or Modelsim)'''
|
|
175
|
-
return bool(
|
|
175
|
+
return bool(safe_shutil_which('vsim')) and \
|
|
176
176
|
any(x in self.config['tools_loaded'] for x in from_tools)
|
|
177
177
|
|
|
178
178
|
|
opencos/deps/deps_commands.py
CHANGED
|
@@ -13,10 +13,11 @@ my_target:
|
|
|
13
13
|
import os
|
|
14
14
|
from pathlib import Path
|
|
15
15
|
import re
|
|
16
|
-
import shutil
|
|
17
16
|
|
|
18
|
-
from opencos.util import debug, error, warning, ShellCommandList
|
|
19
17
|
from opencos.deps.defaults import SUPPORTED_COMMAND_KEYS, COMMAND_ATTRIBUTES
|
|
18
|
+
from opencos.files import safe_shutil_which, PY_EXE
|
|
19
|
+
from opencos.util import debug, error, warning, ShellCommandList
|
|
20
|
+
|
|
20
21
|
|
|
21
22
|
THISPATH = os.path.dirname(__file__)
|
|
22
23
|
PEAKRDL_CLEANUP_PY = os.path.join(THISPATH, '..', 'peakrdl_cleanup.py')
|
|
@@ -221,7 +222,7 @@ def parse_deps_peakrdl( # pylint: disable=too-many-locals
|
|
|
221
222
|
+ ' however it is not enabled in edy.py - eda.config[dep_command_enables]')
|
|
222
223
|
return None
|
|
223
224
|
|
|
224
|
-
if not
|
|
225
|
+
if not safe_shutil_which('peakrdl'):
|
|
225
226
|
error('peakrdl: is not present in shell path, or the python package is not avaiable,' \
|
|
226
227
|
+ f' yet we encountered a peakrdl command in {target_path=} {target_node=}')
|
|
227
228
|
return None
|
|
@@ -255,11 +256,10 @@ def parse_deps_peakrdl( # pylint: disable=too-many-locals
|
|
|
255
256
|
|
|
256
257
|
sv_files += [ f'peakrdl/{top}_pkg.sv', f'peakrdl/{top}.sv' ]
|
|
257
258
|
|
|
258
|
-
|
|
259
259
|
shell_commands = [
|
|
260
|
-
[ 'peakrdl', 'regblock', '-o', str(Path('peakrdl/'))] + args_list,
|
|
260
|
+
[ safe_shutil_which('peakrdl'), 'regblock', '-o', str(Path('peakrdl/'))] + args_list,
|
|
261
261
|
# Edit file to apply some verilator waivers, etc, from peakrdl_cleanup.py:
|
|
262
|
-
[
|
|
262
|
+
[ PY_EXE, PEAKRDL_CLEANUP_PY, str(Path(f'peakrdl/{top}.sv')),
|
|
263
263
|
str(Path(f'peakrdl/{top}.sv')) ],
|
|
264
264
|
]
|
|
265
265
|
|
opencos/deps/deps_file.py
CHANGED
|
@@ -16,7 +16,7 @@ from opencos.util import debug, error
|
|
|
16
16
|
from opencos.utils.markup_helpers import yaml_safe_load, toml_load_only_root_line_numbers, \
|
|
17
17
|
markup_writer, markup_dumper
|
|
18
18
|
from opencos.utils.str_helpers import fnmatch_or_re, dep_str2list, pretty_list_columns_manual, \
|
|
19
|
-
is_valid_target_name, VALID_TARGET_INFO_STR
|
|
19
|
+
is_valid_target_name, VALID_TARGET_INFO_STR, get_shorter_path_str_rel_vs_abs
|
|
20
20
|
from opencos.utils.subprocess_helpers import subprocess_run_background
|
|
21
21
|
from opencos.utils.status_constants import EDA_DEPS_FILE_NOT_FOUND, EDA_DEPS_TARGET_NOT_FOUND
|
|
22
22
|
|
|
@@ -270,9 +270,12 @@ class DepsFile:
|
|
|
270
270
|
return f'line={self.line_numbers.get(target_node, "")}'
|
|
271
271
|
|
|
272
272
|
def gen_caller_info(self, target: str) -> str:
|
|
273
|
-
'''Given a full target name (path/to/my_target) return caller_info str for debug
|
|
273
|
+
'''Given a full target name (path/to/my_target) return caller_info str for debug
|
|
274
|
+
|
|
275
|
+
Use abspath if the str is shorter, for the path information part.
|
|
276
|
+
'''
|
|
274
277
|
return '::'.join([
|
|
275
|
-
self.rel_deps_file,
|
|
278
|
+
get_shorter_path_str_rel_vs_abs(rel_path=self.rel_deps_file),
|
|
276
279
|
target,
|
|
277
280
|
self.get_approx_line_number_str(target)
|
|
278
281
|
])
|
|
@@ -298,90 +301,90 @@ class DepsFile:
|
|
|
298
301
|
some caller_info(str). This is more useful for YAML or TOML markup where we have
|
|
299
302
|
caller_info.
|
|
300
303
|
'''
|
|
301
|
-
if target_node not in self.data:
|
|
302
|
-
found_target = False
|
|
303
304
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
return False
|
|
305
|
+
if target_node in self.data:
|
|
306
|
+
debug(f'Found {target_node=} in deps_file={self.rel_deps_file}')
|
|
307
|
+
return True
|
|
308
308
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
t_path = ''
|
|
314
|
-
t_node = target_node
|
|
315
|
-
t_full = os.path.join(t_path, t_node)
|
|
309
|
+
if target_node.startswith('-'):
|
|
310
|
+
# likely an unparsed arg that made it this far.
|
|
311
|
+
util.warning(f"Ignoring unparsed argument '{target_node}'")
|
|
312
|
+
return False
|
|
316
313
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
314
|
+
# For error printing, prefer relative paths, unless the abspath is shorter:
|
|
315
|
+
if self.target_path:
|
|
316
|
+
t_path = os.path.relpath(self.target_path) + os.path.sep
|
|
317
|
+
t_path = get_shorter_path_str_rel_vs_abs(rel_path=t_path)
|
|
318
|
+
else:
|
|
319
|
+
t_path = ''
|
|
320
|
+
t_node = target_node
|
|
321
|
+
t_full = os.path.join(t_path, t_node)
|
|
321
322
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
self.error_ifarg(
|
|
327
|
-
f'Trying to resolve command-line target={t_full} (file?):',
|
|
328
|
-
f'File={t_node} not found in directory={t_path}',
|
|
329
|
-
arg='error-unknown-args',
|
|
330
|
-
error_code=EDA_DEPS_FILE_NOT_FOUND
|
|
331
|
-
)
|
|
332
|
-
elif not self.rel_deps_file:
|
|
333
|
-
# target, but there's no DEPS file
|
|
334
|
-
self.error_ifarg(
|
|
335
|
-
f'Trying to resolve command-line target={t_full}:',
|
|
336
|
-
f'but path {t_path} has no DEPS markup file (DEPS.yml)',
|
|
337
|
-
arg='error-unknown-args',
|
|
338
|
-
error_code=EDA_DEPS_FILE_NOT_FOUND
|
|
339
|
-
)
|
|
340
|
-
else:
|
|
341
|
-
self.warning_show_available_targets()
|
|
342
|
-
self.error_ifarg(
|
|
343
|
-
f'Trying to resolve command-line target={t_full}:',
|
|
344
|
-
f'was not found in deps_file={self.rel_deps_file}',
|
|
345
|
-
arg='error-unknown-args',
|
|
346
|
-
error_code=EDA_DEPS_TARGET_NOT_FOUND
|
|
347
|
-
)
|
|
323
|
+
if not is_valid_target_name(target_node):
|
|
324
|
+
util.warning(
|
|
325
|
+
f"In file {self.rel_deps_file}, {target_node} {VALID_TARGET_INFO_STR}"
|
|
326
|
+
)
|
|
348
327
|
|
|
328
|
+
if not caller_info:
|
|
329
|
+
# If we don't have caller_info, likely came from command line (or DEPS JSON data):
|
|
330
|
+
if '.' in target_node:
|
|
331
|
+
# Likely a filename (target_node does not include path)
|
|
332
|
+
self.error_ifarg(
|
|
333
|
+
f'Trying to resolve command-line target={t_full} (file?):',
|
|
334
|
+
f'File={t_node} not found in directory={t_path}',
|
|
335
|
+
arg='error-unknown-args',
|
|
336
|
+
error_code=EDA_DEPS_FILE_NOT_FOUND
|
|
337
|
+
)
|
|
338
|
+
elif not self.rel_deps_file:
|
|
339
|
+
# target, but there's no DEPS file
|
|
340
|
+
self.error_ifarg(
|
|
341
|
+
f'Trying to resolve command-line target={t_full}:',
|
|
342
|
+
f'but path {t_path} has no DEPS markup file (DEPS.yml)',
|
|
343
|
+
arg='error-unknown-args',
|
|
344
|
+
error_code=EDA_DEPS_FILE_NOT_FOUND
|
|
345
|
+
)
|
|
349
346
|
else:
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
f'File={t_node} not found in directory={t_path}',
|
|
359
|
-
arg='error-unknown-args',
|
|
360
|
-
error_code=EDA_DEPS_FILE_NOT_FOUND
|
|
361
|
-
)
|
|
362
|
-
elif not self.rel_deps_file:
|
|
363
|
-
# target, but there's no DEPS file
|
|
364
|
-
self.error_ifarg(
|
|
365
|
-
f'Trying to resolve target={t_full}:',
|
|
366
|
-
f'called from {caller_info},',
|
|
367
|
-
f'but {t_path} has no DEPS markup file (DEPS.yml)',
|
|
368
|
-
arg='error-unknown-args',
|
|
369
|
-
error_code=EDA_DEPS_FILE_NOT_FOUND
|
|
370
|
-
)
|
|
371
|
-
else:
|
|
372
|
-
self.warning_show_available_targets()
|
|
373
|
-
self.error_ifarg(
|
|
374
|
-
f'Trying to resolve target={t_full}:',
|
|
375
|
-
f'called from {caller_info},',
|
|
376
|
-
f'Target not found in deps_file={self.rel_deps_file}',
|
|
377
|
-
arg='error-unknown-args',
|
|
378
|
-
error_code=EDA_DEPS_TARGET_NOT_FOUND
|
|
379
|
-
)
|
|
347
|
+
self.warning_show_available_targets()
|
|
348
|
+
self.error_ifarg(
|
|
349
|
+
f'Trying to resolve command-line target={t_full}:',
|
|
350
|
+
f'was not found in deps_file={self.rel_deps_file}',
|
|
351
|
+
arg='error-unknown-args',
|
|
352
|
+
error_code=EDA_DEPS_TARGET_NOT_FOUND
|
|
353
|
+
)
|
|
354
|
+
|
|
380
355
|
else:
|
|
381
|
-
|
|
382
|
-
|
|
356
|
+
# If we have caller_info, then this was a recursive call from another
|
|
357
|
+
# DEPS file. It should already have the useful error messaging:
|
|
358
|
+
|
|
359
|
+
if '.' in target_node:
|
|
360
|
+
# Likely a filename (target_node does not include path)
|
|
361
|
+
self.error_ifarg(
|
|
362
|
+
f'Trying to resolve target={t_full} (file?):',
|
|
363
|
+
f'called from {caller_info},',
|
|
364
|
+
f'File={t_node} not found in directory={t_path}',
|
|
365
|
+
arg='error-unknown-args',
|
|
366
|
+
error_code=EDA_DEPS_FILE_NOT_FOUND
|
|
367
|
+
)
|
|
368
|
+
elif not self.rel_deps_file:
|
|
369
|
+
# target, but there's no DEPS file
|
|
370
|
+
self.error_ifarg(
|
|
371
|
+
f'Trying to resolve target={t_full}:',
|
|
372
|
+
f'called from {caller_info},',
|
|
373
|
+
f'but {t_path} has no DEPS markup file (DEPS.yml)',
|
|
374
|
+
arg='error-unknown-args',
|
|
375
|
+
error_code=EDA_DEPS_FILE_NOT_FOUND
|
|
376
|
+
)
|
|
377
|
+
else:
|
|
378
|
+
self.warning_show_available_targets()
|
|
379
|
+
self.error_ifarg(
|
|
380
|
+
f'Trying to resolve target={t_full}:',
|
|
381
|
+
f'called from {caller_info},',
|
|
382
|
+
f'Target not found in deps_file={self.rel_deps_file}',
|
|
383
|
+
arg='error-unknown-args',
|
|
384
|
+
error_code=EDA_DEPS_TARGET_NOT_FOUND
|
|
385
|
+
)
|
|
383
386
|
|
|
384
|
-
return
|
|
387
|
+
return False
|
|
385
388
|
|
|
386
389
|
|
|
387
390
|
def get_entry(self, target_node) -> dict:
|
opencos/deps/deps_processor.py
CHANGED
|
@@ -5,15 +5,17 @@ CommandDesign ref object
|
|
|
5
5
|
'''
|
|
6
6
|
|
|
7
7
|
import argparse
|
|
8
|
+
import copy
|
|
8
9
|
import os
|
|
9
10
|
|
|
10
11
|
from opencos import files
|
|
11
12
|
from opencos import eda_config
|
|
12
|
-
from opencos.util import debug, info, warning, error, read_tokens_from_dot_f, \
|
|
13
|
+
from opencos.util import Colors, debug, info, warning, error, read_tokens_from_dot_f, \
|
|
13
14
|
patch_args_for_dir, load_env_file
|
|
14
15
|
from opencos.utils.str_helpers import dep_str2list
|
|
15
16
|
from opencos.deps.deps_file import deps_target_get_deps_list
|
|
16
17
|
from opencos.deps.deps_commands import deps_commands_handler
|
|
18
|
+
from opencos.utils.dict_helpers import dict_diff
|
|
17
19
|
|
|
18
20
|
from opencos.deps.defaults import SUPPORTED_TARGET_TABLE_KEYS, SUPPORTED_TAG_KEYS, \
|
|
19
21
|
SUPPORTED_DEP_KEYS_BY_TYPE
|
|
@@ -53,6 +55,14 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
53
55
|
self.deps_dir, _ = os.path.split(deps_file)
|
|
54
56
|
self.caller_info = caller_info
|
|
55
57
|
|
|
58
|
+
# Check if it's a Command instead of CommandDesign:
|
|
59
|
+
self.is_command_design = bool(
|
|
60
|
+
getattr(command_design_ref, 'process_plusarg', None) and \
|
|
61
|
+
getattr(command_design_ref, 'set_parameter', None) and \
|
|
62
|
+
isinstance(getattr(command_design_ref, 'incdirs', None), list)
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
56
66
|
assert isinstance(deps_entry, dict), \
|
|
57
67
|
f'{deps_entry=} for {target_node=} in {deps_file=} must be a dict'
|
|
58
68
|
assert command_design_ref is not None, \
|
|
@@ -79,6 +89,8 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
79
89
|
|
|
80
90
|
def apply_defines(self, defines_dict: dict) -> None:
|
|
81
91
|
'''Given defines_dict, applies them to our self.command_design_ref obj'''
|
|
92
|
+
if not self.is_command_design:
|
|
93
|
+
return
|
|
82
94
|
if not isinstance(defines_dict, dict):
|
|
83
95
|
self.error(f"{defines_dict=} is not type dict, can't apply defines,",
|
|
84
96
|
f"in {self.caller_info}")
|
|
@@ -97,6 +109,8 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
97
109
|
|
|
98
110
|
def apply_plusargs(self, plusargs_dict: dict) -> None:
|
|
99
111
|
'''Given plusarsg_dict, applies them to our self.command_design_ref obj'''
|
|
112
|
+
if not self.is_command_design:
|
|
113
|
+
return
|
|
100
114
|
if not isinstance(plusargs_dict, dict):
|
|
101
115
|
self.error(f"{plusargs_dict=} is not type dict, can't apply plusargs,",
|
|
102
116
|
f"in {self.caller_info}")
|
|
@@ -109,6 +123,8 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
109
123
|
|
|
110
124
|
def apply_parameters(self, parameters_dict: dict) -> None:
|
|
111
125
|
'''Given parameters_dict, applies them to our self.command_design_ref obj'''
|
|
126
|
+
if not self.is_command_design:
|
|
127
|
+
return
|
|
112
128
|
if not isinstance(parameters_dict, dict):
|
|
113
129
|
self.error(f"{parameters_dict=} is not type dict, can't apply defines,",
|
|
114
130
|
f"in {self.caller_info}")
|
|
@@ -124,6 +140,8 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
124
140
|
|
|
125
141
|
def apply_incdirs(self, incdirs_list:list) -> None:
|
|
126
142
|
'''Given incdirs_list, applies them to our self.command_design_ref obj'''
|
|
143
|
+
if not self.is_command_design:
|
|
144
|
+
return
|
|
127
145
|
if not isinstance(incdirs_list, (str, list)):
|
|
128
146
|
self.error(f"{incdirs_list=} is not type str/list, can't apply incdirs",
|
|
129
147
|
f"in {self.caller_info}")
|
|
@@ -135,38 +153,59 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
135
153
|
debug(f'Added include dir {abspath} from {self.caller_info}')
|
|
136
154
|
|
|
137
155
|
|
|
138
|
-
def _apply_args_check_tools(self, tokens: list) -> list:
|
|
139
|
-
'''Helper for apply_args(list), returns list strips --tool args under certain conditions
|
|
156
|
+
def _apply_args_check_tools(self, tokens: list, tagname: str) -> list:
|
|
157
|
+
'''Helper for apply_args(list), returns list strips --tool args under certain conditions
|
|
158
|
+
|
|
159
|
+
Basically, we want to see if a DEPS target want to set arg --tool, if so:
|
|
160
|
+
Accept it:
|
|
161
|
+
- this is not a tool class
|
|
162
|
+
- tool was automatically chosen by eda.py's auto-tool-order (--tool not set at
|
|
163
|
+
CLI)
|
|
164
|
+
- accepting means set self.args['tool'], so we can respawn with this.
|
|
165
|
+
Reject/Warn if:
|
|
166
|
+
- previous tool was not automatically applied (--tool was set at CLI)
|
|
167
|
+
and you're trying to change the --tool value.
|
|
168
|
+
- This can happen if you have complicated DEPS targets trying to set the
|
|
169
|
+
tool to different values.
|
|
170
|
+
'''
|
|
140
171
|
|
|
172
|
+
parser = argparse.ArgumentParser(
|
|
173
|
+
prog='deps_processor --tool', add_help=False, allow_abbrev=False
|
|
174
|
+
)
|
|
175
|
+
parser.add_argument('--tool', default='')
|
|
176
|
+
try:
|
|
177
|
+
parsed, unparsed = parser.parse_known_args(tokens + [''])
|
|
178
|
+
tokens2 = list(filter(None, unparsed))
|
|
179
|
+
except argparse.ArgumentError:
|
|
180
|
+
error('deps_processor --tool problem attempting to parse_known_args for:',
|
|
181
|
+
f'{tokens}, {self.caller_info} {tagname}')
|
|
182
|
+
tokens2 = tokens
|
|
183
|
+
|
|
184
|
+
_tool_class = 'tool' in self.args
|
|
141
185
|
_orig_tool = self.args.get('tool', '')
|
|
142
|
-
if not self.command_design_ref.auto_tool_applied and \
|
|
143
|
-
any(x.startswith('--tool') for x in tokens) and _orig_tool:
|
|
144
|
-
warn_tool = ''
|
|
145
|
-
for i, item in enumerate(list(tokens)):
|
|
146
|
-
if item == '--tool':
|
|
147
|
-
if tokens[i + 1] != _orig_tool:
|
|
148
|
-
warn_tool = tokens[i + 1]
|
|
149
|
-
tokens[i : i+2] = ['', ''] # remove this and next arg
|
|
150
|
-
elif item.startswith('--tool='):
|
|
151
|
-
if item[7:] != _orig_tool:
|
|
152
|
-
warn_tool = item
|
|
153
|
-
tokens[i] = '' # remove just this arg.
|
|
154
|
-
|
|
155
|
-
if warn_tool:
|
|
156
|
-
warning(
|
|
157
|
-
f'Attempting to set --tool {warn_tool} from DEPS',
|
|
158
|
-
f'(file={self.deps_file}:{self.target_node})',
|
|
159
|
-
f'however the tool was already chosen as: {_orig_tool}. The --tool arg will',
|
|
160
|
-
f'not be applied from: {tokens}'
|
|
161
|
-
)
|
|
162
186
|
|
|
163
|
-
|
|
187
|
+
if not self.command_design_ref.auto_tool_applied and \
|
|
188
|
+
_tool_class and parsed.tool and _orig_tool \
|
|
189
|
+
and parsed.tool != _orig_tool:
|
|
190
|
+
# tool arg present, --tool in this DEPS args, and tool already set.
|
|
191
|
+
warning(
|
|
192
|
+
f'Attempting to set --tool {parsed.tool} from DEPS',
|
|
193
|
+
f'(file={self.deps_file}:{self.target_node})',
|
|
194
|
+
f'however the tool was already chosen as: {_orig_tool}. The --tool arg will',
|
|
195
|
+
f'not be applied from: {tokens}'
|
|
196
|
+
)
|
|
197
|
+
elif (self.command_design_ref.auto_tool_applied or not _tool_class) and parsed.tool:
|
|
198
|
+
# tool arg wasn't present (not a Tool class), or it was auto-applied,
|
|
199
|
+
# then add the arg anyway so we can later respawn with the correct tool.
|
|
200
|
+
self.args['tool'] = parsed.tool
|
|
201
|
+
debug(f'setting arg.tool to {parsed.tool=} from {self.caller_info}')
|
|
164
202
|
|
|
165
|
-
|
|
203
|
+
# remove blanks, '--tool[=value| value]' removed.
|
|
204
|
+
return [item for item in tokens2 if item != '']
|
|
166
205
|
|
|
167
206
|
|
|
168
|
-
def apply_args( # pylint: disable=too-many-locals,too-many-branches
|
|
169
|
-
self, args_list:list
|
|
207
|
+
def apply_args( # pylint: disable=too-many-locals,too-many-branches,too-many-statements
|
|
208
|
+
self, args_list:list, tagname: str = ''
|
|
170
209
|
) -> list:
|
|
171
210
|
'''Given args_list, applies them to our self.command_design_ref obj
|
|
172
211
|
|
|
@@ -174,9 +213,15 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
174
213
|
unparsed args will show up as eda.py warnings, but will not fail. Most callers do not
|
|
175
214
|
use the unparsed args from this method.
|
|
176
215
|
'''
|
|
216
|
+
if tagname:
|
|
217
|
+
tagname = f'{tagname=}'
|
|
218
|
+
|
|
177
219
|
if not isinstance(args_list, (str, list)):
|
|
178
220
|
self.error(f"{args_list=} is not type str/list, can't apply args",
|
|
179
|
-
f"in {self.caller_info}")
|
|
221
|
+
f"in {self.caller_info} {tagname}")
|
|
222
|
+
|
|
223
|
+
prev_args = copy.deepcopy(self.args)
|
|
224
|
+
|
|
180
225
|
tokens = dep_str2list(args_list)
|
|
181
226
|
|
|
182
227
|
# patch args relative to the DEPS (if self.deps_dir exists) so things like
|
|
@@ -211,7 +256,7 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
211
256
|
tokens2 = list(filter(None, unparsed))
|
|
212
257
|
except argparse.ArgumentError:
|
|
213
258
|
error('deps_processor -f/--input-file, problem attempting to parse_known_args for:',
|
|
214
|
-
f'{tokens}')
|
|
259
|
+
f'{tokens}, {self.caller_info} {tagname}')
|
|
215
260
|
tokens2 = tokens
|
|
216
261
|
|
|
217
262
|
if parsed.input_file:
|
|
@@ -242,13 +287,12 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
242
287
|
# the user may think they were allowed to set --tool, but in our flow the Command handler
|
|
243
288
|
# (self.command_design_ref) has already been chosen, so setting the tool can have
|
|
244
289
|
# strange side-effects.
|
|
290
|
+
_tool_class = 'tool' in self.args
|
|
245
291
|
_orig_tool = self.args.get('tool', '')
|
|
246
|
-
tokens = self._apply_args_check_tools(tokens=tokens)
|
|
247
|
-
if not tokens:
|
|
248
|
-
return []
|
|
292
|
+
tokens = self._apply_args_check_tools(tokens=tokens, tagname=tagname)
|
|
249
293
|
|
|
250
294
|
debug(f'deps_processor - custom apply_args with {tokens=}',
|
|
251
|
-
f'from {self.caller_info}')
|
|
295
|
+
f'from {self.caller_info} {tagname}')
|
|
252
296
|
_, unparsed = self.command_design_ref.run_argparser_on_list(
|
|
253
297
|
tokens=tokens
|
|
254
298
|
)
|
|
@@ -256,7 +300,7 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
256
300
|
# Annoying, but check for plusargs in unparsed, and have referenced CommandDesign
|
|
257
301
|
# or CommandSim class handle it with process_plusarg.
|
|
258
302
|
for arg in list(unparsed):
|
|
259
|
-
if arg.startswith('+'):
|
|
303
|
+
if arg.startswith('+') and self.is_command_design:
|
|
260
304
|
self.command_design_ref.process_plusarg(plusarg=arg, pwd=self.target_path)
|
|
261
305
|
unparsed.remove(arg)
|
|
262
306
|
|
|
@@ -264,10 +308,7 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
264
308
|
for arg in list(unparsed):
|
|
265
309
|
# Since this isn't command line, we have to assume for files, the path is relative
|
|
266
310
|
# to this DEPS file.
|
|
267
|
-
|
|
268
|
-
target = arg
|
|
269
|
-
else:
|
|
270
|
-
target = os.path.join(self.deps_dir, arg)
|
|
311
|
+
target = self.correct_a_deps_target(target=arg, deps_dir=self.deps_dir)
|
|
271
312
|
|
|
272
313
|
file_exists, fpath, forced_extension = files.get_source_file(target)
|
|
273
314
|
if file_exists:
|
|
@@ -279,6 +320,7 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
279
320
|
|
|
280
321
|
else:
|
|
281
322
|
if not os.path.isdir(target) and \
|
|
323
|
+
self.is_command_design and \
|
|
282
324
|
self.command_design_ref.resolve_target_core(
|
|
283
325
|
target=target, no_recursion=False, caller_info=self.caller_info,
|
|
284
326
|
error_on_not_found=False
|
|
@@ -293,7 +335,10 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
293
335
|
warning(f'For {self.command_design_ref.command_name}:' \
|
|
294
336
|
+ f' in {self.caller_info} has unknown args {unparsed=}')
|
|
295
337
|
|
|
296
|
-
if self.command_design_ref.auto_tool_applied
|
|
338
|
+
if (self.command_design_ref.auto_tool_applied or not _tool_class) and \
|
|
339
|
+
_orig_tool != self.args.get('tool', ''):
|
|
340
|
+
# If there was an auto tool applied (tool class or not) then attempt to pick
|
|
341
|
+
# a new sub-command-object with that tool.
|
|
297
342
|
debug(f'deps_processor.apply_args: tool changed, {self.args["tool"]=}, will attempt',
|
|
298
343
|
f'to respawn the job using original args: {self.config["eda_original_args"]}')
|
|
299
344
|
self.command_design_ref.tool_changed_respawn = {
|
|
@@ -302,6 +347,12 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
302
347
|
'from': self.caller_info,
|
|
303
348
|
}
|
|
304
349
|
|
|
350
|
+
diff_args = dict_diff(prev_args, self.args)
|
|
351
|
+
if diff_args:
|
|
352
|
+
args_list = [item for item in args_list if item != ''] # remove blanks
|
|
353
|
+
info(f'{Colors.yellow}{self.caller_info} {tagname}{Colors.green}:',
|
|
354
|
+
f'applying args for {args_list}: {Colors.cyan}{diff_args}')
|
|
355
|
+
|
|
305
356
|
return unparsed
|
|
306
357
|
|
|
307
358
|
def apply_reqs(self, reqs_list:list) -> None:
|
|
@@ -551,9 +602,9 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
551
602
|
' skipped args due to args disabled.')
|
|
552
603
|
if args_list:
|
|
553
604
|
# This will apply knowns args to the target dep:
|
|
554
|
-
|
|
605
|
+
debug(f'{tagname=} in {self.caller_info=}:',
|
|
555
606
|
f'applying args for {args_list=}')
|
|
556
|
-
self.apply_args(args_list)
|
|
607
|
+
self.apply_args(args_list, tagname=tagname)
|
|
557
608
|
|
|
558
609
|
elif key == 'reqs':
|
|
559
610
|
reqs_list = deps_target_get_deps_list(entry=value,
|
|
@@ -578,11 +629,16 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
578
629
|
# apply replace-config-tools
|
|
579
630
|
# This will replace lists (compile-waivers).
|
|
580
631
|
tool_config = value.get('replace-config-tools', {}).get(tool, None)
|
|
581
|
-
|
|
632
|
+
ref_has_tool_config = isinstance(
|
|
633
|
+
getattr(self.command_design_ref, 'tool_config', None), dict
|
|
634
|
+
)
|
|
635
|
+
if tool_config and (not deps_tags_enables.get('replace-config-tools', None) or \
|
|
636
|
+
not ref_has_tool_config):
|
|
582
637
|
tool_config = None
|
|
583
638
|
warning(f'{tagname=} in {self.caller_info}:',
|
|
584
|
-
'
|
|
585
|
-
|
|
639
|
+
'skipped replace-config-tools b/c it is disabled or not present for',
|
|
640
|
+
'this tool and command')
|
|
641
|
+
if ref_has_tool_config and tool_config and isinstance(tool_config, dict):
|
|
586
642
|
# apply it to self.tool_config:
|
|
587
643
|
info(f'{tagname=} in {self.caller_info}:',
|
|
588
644
|
f'applying replace-config-tools for {tool=}: {tool_config}')
|
|
@@ -595,11 +651,13 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
595
651
|
# apply additive-config-tools
|
|
596
652
|
# This will append to lists (compile-waivers)
|
|
597
653
|
tool_config = value.get('additive-config-tools', {}).get(tool, None)
|
|
598
|
-
if tool_config and not deps_tags_enables.get('additive-config-tools', None)
|
|
654
|
+
if tool_config and (not deps_tags_enables.get('additive-config-tools', None) or \
|
|
655
|
+
not ref_has_tool_config):
|
|
599
656
|
tool_config = None
|
|
600
657
|
warning(f'{tagname=} in {self.caller_info}:',
|
|
601
|
-
' skipped additive-config-tools b/c it is
|
|
602
|
-
|
|
658
|
+
' skipped additive-config-tools b/c it is disable or not present for',
|
|
659
|
+
'this tool and command')
|
|
660
|
+
if ref_has_tool_config and tool_config and isinstance(tool_config, dict):
|
|
603
661
|
# apply it to self.tool_config:
|
|
604
662
|
info(f'{tagname=} in {self.caller_info}:',
|
|
605
663
|
f'applying additive-config-tools for {tool=}: {tool_config}')
|
|
@@ -774,6 +832,12 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
774
832
|
|
|
775
833
|
shell_commands_list, work_dir_add_srcs_list = self.get_commands(commands=commands, dep=dep)
|
|
776
834
|
|
|
835
|
+
if shell_commands_list and \
|
|
836
|
+
not self.is_command_design:
|
|
837
|
+
warning(f'Not applying shell commands from {self.caller_info}, not supported',
|
|
838
|
+
'for this tool and command')
|
|
839
|
+
return
|
|
840
|
+
|
|
777
841
|
# add these commands lists to self.command_design_ref:
|
|
778
842
|
# Process all shell_commands_list:
|
|
779
843
|
# This will track each shell command with its target_node and target_path
|
|
@@ -850,7 +914,8 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
850
914
|
|
|
851
915
|
|
|
852
916
|
elif isinstance(dep, str) and \
|
|
853
|
-
any(dep.startswith(x) for x in ['+define+', '+incdir+'])
|
|
917
|
+
any(dep.startswith(x) for x in ['+define+', '+incdir+']) and \
|
|
918
|
+
self.is_command_design:
|
|
854
919
|
# Note: we still support +define+ and +incdir in the deps list.
|
|
855
920
|
# check for compile-time Verilog style plusarg, which are supported under targets
|
|
856
921
|
# These are not run-time Verilog style plusargs comsumable from within the .sv:
|
|
@@ -860,10 +925,11 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
860
925
|
else:
|
|
861
926
|
# If we made it this far, dep better be a str type.
|
|
862
927
|
assert isinstance(dep, str), f'{dep=} {type(dep)=} must be str'
|
|
863
|
-
dep_path =
|
|
928
|
+
dep_path = self.correct_a_deps_target(target=dep, deps_dir=self.target_path)
|
|
864
929
|
debug(f"Got dep {dep_path=} for in {self.caller_info}")
|
|
865
930
|
|
|
866
|
-
if
|
|
931
|
+
if self.is_command_design and \
|
|
932
|
+
dep_path in self.command_design_ref.targets_dict or \
|
|
867
933
|
dep_path in deps_targets_to_resolve:
|
|
868
934
|
debug(" - already processed, skipping")
|
|
869
935
|
else:
|
|
@@ -888,3 +954,16 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
|
|
|
888
954
|
# dep3: 'command_tuple',
|
|
889
955
|
# }
|
|
890
956
|
return deps_targets_to_resolve
|
|
957
|
+
|
|
958
|
+
|
|
959
|
+
def correct_a_deps_target(self, target: str, deps_dir: str) -> str:
|
|
960
|
+
'''Give a target/file in a deps: list, return a patched version
|
|
961
|
+
|
|
962
|
+
- $VAR replacment
|
|
963
|
+
- relative to current DEPS file dir (or not if it was abspath)
|
|
964
|
+
'''
|
|
965
|
+
if self.config['deps_expandvars_enable']:
|
|
966
|
+
target = os.path.expandvars(target)
|
|
967
|
+
if not os.path.isabs(target):
|
|
968
|
+
target = os.path.join(deps_dir, target)
|
|
969
|
+
return target
|