opencos-eda 0.2.52__py3-none-any.whl → 0.2.53__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.
Files changed (41) hide show
  1. opencos/commands/__init__.py +2 -0
  2. opencos/commands/build.py +1 -1
  3. opencos/commands/deps_help.py +259 -0
  4. opencos/commands/export.py +1 -1
  5. opencos/commands/flist.py +3 -0
  6. opencos/commands/lec.py +1 -1
  7. opencos/commands/open.py +2 -0
  8. opencos/commands/proj.py +1 -1
  9. opencos/commands/shell.py +1 -1
  10. opencos/commands/sim.py +32 -8
  11. opencos/commands/synth.py +1 -1
  12. opencos/commands/upload.py +3 -0
  13. opencos/commands/waves.py +1 -0
  14. opencos/deps/defaults.py +1 -0
  15. opencos/deps/deps_file.py +30 -4
  16. opencos/deps/deps_processor.py +72 -2
  17. opencos/deps_schema.py +3 -0
  18. opencos/eda.py +50 -26
  19. opencos/eda_base.py +159 -20
  20. opencos/eda_config.py +1 -1
  21. opencos/eda_config_defaults.yml +49 -3
  22. opencos/eda_extract_targets.py +1 -58
  23. opencos/tests/helpers.py +16 -0
  24. opencos/tests/test_eda.py +13 -2
  25. opencos/tests/test_tools.py +159 -132
  26. opencos/tools/cocotb.py +9 -0
  27. opencos/tools/modelsim_ase.py +67 -51
  28. opencos/tools/quartus.py +638 -0
  29. opencos/tools/questa.py +167 -88
  30. opencos/tools/questa_fse.py +10 -0
  31. opencos/tools/riviera.py +1 -0
  32. opencos/tools/vivado.py +3 -3
  33. opencos/util.py +20 -3
  34. opencos/utils/str_helpers.py +85 -0
  35. {opencos_eda-0.2.52.dist-info → opencos_eda-0.2.53.dist-info}/METADATA +1 -1
  36. {opencos_eda-0.2.52.dist-info → opencos_eda-0.2.53.dist-info}/RECORD +41 -39
  37. {opencos_eda-0.2.52.dist-info → opencos_eda-0.2.53.dist-info}/WHEEL +0 -0
  38. {opencos_eda-0.2.52.dist-info → opencos_eda-0.2.53.dist-info}/entry_points.txt +0 -0
  39. {opencos_eda-0.2.52.dist-info → opencos_eda-0.2.53.dist-info}/licenses/LICENSE +0 -0
  40. {opencos_eda-0.2.52.dist-info → opencos_eda-0.2.53.dist-info}/licenses/LICENSE.spdx +0 -0
  41. {opencos_eda-0.2.52.dist-info → opencos_eda-0.2.53.dist-info}/top_level.txt +0 -0
@@ -20,6 +20,7 @@ from .waves import CommandWaves
20
20
  from .shell import CommandShell
21
21
  from .targets import CommandTargets
22
22
  from .lec import CommandLec
23
+ from .deps_help import CommandDepsHelp
23
24
 
24
25
  __all__ = [
25
26
  'CommandBuild',
@@ -38,4 +39,5 @@ __all__ = [
38
39
  'CommandShell',
39
40
  'CommandTargets',
40
41
  'CommandLec',
42
+ 'CommandDepsHelp',
41
43
  ]
opencos/commands/build.py CHANGED
@@ -31,7 +31,7 @@ class CommandBuild(CommandDesign):
31
31
  self, tokens=tokens, process_all=process_all, pwd=pwd
32
32
  )
33
33
 
34
- if self.status_any_error():
34
+ if self.stop_process_tokens_before_do_it():
35
35
  return unparsed
36
36
 
37
37
  # add defines for this job type
@@ -0,0 +1,259 @@
1
+ '''opencos.commands.deps_help - command handler for: eda deps_help [args]
2
+
3
+ Note this command is handled differently than others (such as CommandSim),
4
+ it is generally run as simply
5
+
6
+ > eda deps_help
7
+ > eda deps_help --verbose
8
+ > eda deps_help --help
9
+
10
+ uses no tools and will print a help text regarding DEPS markup files to stdout.
11
+ '''
12
+
13
+
14
+ import os
15
+
16
+ from opencos.eda_base import Command
17
+ from opencos import util
18
+
19
+
20
+ BASIC_DEPS_HELP = '''
21
+
22
+ --------------------------------------------------------------------
23
+
24
+ What is a DEPS.yml file and why does `eda` use this?
25
+ - DEPS.yml is a fancy filelist.
26
+ - Used to organize a project into "targets", a tool can run on a "target".
27
+ - Allows for more than just source files attached to a "target".
28
+ -- incdirs, defines, and args can be applied to a "target".
29
+
30
+ --------------------------------------------------------------------
31
+
32
+ Hello World example:
33
+
34
+ The following example is a DEPS.yml file example for a SystemVerilog simulation of
35
+ hello_world_tb.sv. DEPS.yml is, in short, a fancy filelist. We use them in the `eda`
36
+ app to organize projects.
37
+
38
+ --- DEPS.yml: ---
39
+
40
+ hello-world: # <-- this is a named target that will be run
41
+
42
+ deps: # <-- 'deps' is a list of SV, Verilog, VHDL files in compile order
43
+ - hello_world_tb.sv
44
+
45
+ top: hello_world_tb # <-- For testbenches, it is good practice to specifiy the topmost
46
+ # module using using 'top'. This is not necessary for design
47
+ # files.
48
+
49
+
50
+ --- hello_world_tb.sv: ---
51
+
52
+ module hello_world_tb;
53
+
54
+ initial begin
55
+ #10ns;
56
+ $display("%t %m: Hello World!", $realtime);
57
+ $display("%t %m: Test finished", $realtime);
58
+ $finish;
59
+ end
60
+
61
+ endmodule : hello_world_tb
62
+
63
+ ---
64
+
65
+
66
+ hello-world:
67
+ The target name in the DEPS.yml we named is hello-world. That is a valid target
68
+ that `eda` can use. Such as:
69
+
70
+ eda sim --tool=verilator hello-world
71
+
72
+
73
+ --------------------------------------------------------------------
74
+
75
+ Beyond Hello World example:
76
+
77
+ The following example is a DEPS.yml file for a more complex module simulation.
78
+ It has two files in ./DEPS.yml and ./lib/DEPS.yml.
79
+
80
+ --- ./DEPS.yml: ---
81
+
82
+ my_fifo: # <-- this is a design
83
+ incdirs: . lib # <-- 'incdirs' define the paths searched to find `include files
84
+ defines:
85
+ FIFO_DEBUG # add a basic define
86
+ FIFO_IMPLEMENTATION=uram # add a define with a value
87
+ deps: # <-- 'deps' is a list of SV, Verilog, VHDL files in compile order
88
+ - my_fifo.sv # an SV file pulled in directly
89
+ - lib/bin_to_gray # a target, in a subdirectory that has it's own DEPS
90
+
91
+ my_fifo_test: # <-- this is a TEST
92
+ top: my_fifo_test # the top will default to whatever target is provided
93
+ # by the user, so this could be optional
94
+ deps:
95
+ - my_fifo # the target that is defined above
96
+ - my_fifo_tb.sv # an SV file pulled in directly
97
+
98
+ my_fifo_stress_test: # <-- this is another TEST
99
+ top: my_fifo_test # not optional because top is not "my_fifo_stress_test"
100
+ defines:
101
+ STRESS_TEST # configures my_fifo_test to be more stressful
102
+ deps:
103
+ - my_fifo_test # aside from the define, this is same as "my_fifo_test"
104
+
105
+ --- lib/DEPS.yml: ---
106
+
107
+ lib_pkg: # <-- this is a package required by bin_to_gray below
108
+ deps:
109
+ - assert_pkg.sv # an SV package pulled in directly, before it's needed below
110
+ - lib_pkg.sv # an SV package pulled in directly
111
+
112
+ bin_to_gray: # <-- this is the target that was required by ../my_fifo
113
+ deps:
114
+ - lib_pkg # a target package, listed first as SV requires packages
115
+ # to be read before the code that uses them
116
+ - bin_to_gray.sv # an SV module pulled in directly
117
+
118
+ --------------------------------------------------------------------
119
+ '''
120
+
121
+
122
+ FULL_DEPS_HELP = '''
123
+
124
+ --------------------------------------------------------------------
125
+
126
+ Full DEPS.yml schema:
127
+
128
+ ```
129
+ DEFAULTS: # <table> defaults applied to ALL targets in this file, local targets ** override ** the defaults.
130
+
131
+ METADATA: # <table> unstructured data, any UPPERCASE first level key is not considered a target.
132
+
133
+ target-spec:
134
+
135
+ args: # <array or | separated str>
136
+ - --waves
137
+ - --sim_plusargs="+info=500"
138
+
139
+ defines: # <table>
140
+ SOME_DEFINE: value
141
+ SOME_DEFINE_NO_VALUE: # we just leave this blank, or use nil (yaml's None)
142
+
143
+ incdirs: # <array>
144
+ - some/relative/path
145
+
146
+ top: # <string>
147
+
148
+ deps: # <array or | space separated string>
149
+ - some_relative_target # <string> aka, a target
150
+ - some_file.sv # <string> aka, a file
151
+ - sv@some_file.txt # <string> aka, ext@file where we'd like a file not ending in .sv to be
152
+ # treated as a .sv file for tools.
153
+ # Supported for sv@, v@, vhdl@, cpp@
154
+ - commands: # <table> with key 'commands' for a <array>: support for built-in commands
155
+ # Note this cannot be confused for other targets or files.
156
+ - shell: # <string>
157
+ var-subst-args: # <bool> default false. If true, substitute vars in commands, such as {fpga}
158
+ # substituted from eda arg --fpga=SomeFpga, such that {fpga} becomes SomeFpga
159
+ var-subst-os-env: #<bool> default false. If true, substitute vars in commands using os.environ vars,
160
+ # such as {FPGA} could get substituted by env value for $FPGA
161
+ tee: # <string> optional filename, otherwise shell commands write to {target-spec}__shell_0.log
162
+ run-from-work-dir: #<bool> default true. If false, runs from the directory of this DEPS file.
163
+ filepath-subst-target-dir: #<bool> default true. If false, disables shell file path
164
+ substituion on this target's directory (this DEPS file dir).
165
+ dirpath-subst-target-dir: #<bool> default false. If true, enables shell directory path
166
+ substituion on this target's directory (this DEPS file dir).
167
+ - shell: echo "Hello World!"
168
+ - work-dir-add-sources: # <array or | space separated string>, this is how to add generated files
169
+ # to compile order list.
170
+ - peakrdl: # <string> ## peakrdl command to generate CSRs
171
+
172
+ reqs: # <array or | space separated string>
173
+ - some_file.mem # <string> aka, a non-source file required for this target.
174
+ # This file is checked for existence prior to invoking the tool involved, for example,
175
+ # in a simulation this would be done prior to a compile step.
176
+
177
+ multi:
178
+ ignore-this-target: # <array of tables> eda commands to be ignored in `eda multi <command>` for this target only
179
+ # this is checked in the matching multi targets list, and is not inherited through dependencies.
180
+ - commands: synth # space separated strings
181
+ tools: vivado # space separated strings
182
+
183
+ - commands: sim # omit tools, ignores 'sim' commands for all tools, for this target only, when this target
184
+ # is in the target list called by `eda multi`.
185
+
186
+ - tools: vivado # omit commands, ignores all commands if tool is vivado, for this target only, when this target
187
+ # is in the target list called by `eda multi`.
188
+
189
+ args: # <array> additional args added to all multi commands of this target.
190
+ # Note that all args are POSIX with dashes, --sim-plusargs=value, etc.
191
+
192
+ <eda-command>: # key is one of sim, flist, build, synth, etc.
193
+ # can be used instead of 'tags' to support different args or deps.
194
+ disable-tools: # Note: not implemented yet.
195
+ only-tools: # Note: not implemented yet.
196
+ args: # <array or | space separated string>
197
+ deps: # <array or | space separated string> # Note: not implemented yet
198
+ defines: ## <table>
199
+ incdirs: ## <array>
200
+
201
+ tags: # <table> this is the currently support tags features in a target.
202
+ <tag-name>: # <string> key for table, can be anything, name is not used.
203
+ with-tools: <array or | space separated string>
204
+ # If using one of these tools, apply these values.
205
+ # entries can be in the form: vivado, or vivado:2024.1
206
+ with-commands: <array or | space separated string>
207
+ # apply if this was the `eda` command, such as: sim
208
+ with-args: # <table> (optional) arg key/value pairs to match for this tag.
209
+ # this would be an alternative to running eda with --tags=value
210
+ # The existence of an argument with correct value would enable a tag.
211
+ # And example would be:
212
+ # with-args:
213
+ # waves: true
214
+ args: <array or | space separated string> # args to be applied if this target is used, with a matching
215
+ # tool in 'with-tools'.
216
+ deps: <array or | space separated string, applied with tag>
217
+ defines: <table, applied with tag>
218
+ incdirs: <array, applied with tag>
219
+ replace-config-tools: <table> # spec matching eda_config_defaults.yml::tools.<tool> (replace merge strategy)
220
+ additive-config-tools: <table> # spec matching eda_config_defaults.yml::tools.<tool> (additive merge strategy)
221
+
222
+
223
+ ```
224
+ '''
225
+
226
+
227
+ class CommandDepsHelp:
228
+ '''command handler for: eda deps-help'''
229
+
230
+ command_name = 'deps-help'
231
+
232
+ def __init__(self, config: dict):
233
+ # We don't inherit opencos.eda_base.Command, so we have to set a few
234
+ # member vars for Command.help to work.
235
+ self.args = {}
236
+ self.args_help = {}
237
+ self.config = config
238
+ self.status = 0
239
+
240
+ def process_tokens( # pylint: disable=unused-argument
241
+ self, tokens: list, process_all: bool = True,
242
+ pwd: str = os.getcwd()
243
+ ) -> list:
244
+ '''This is effectively our 'run' method, entrypoint from opencos.eda.main'''
245
+
246
+ print(BASIC_DEPS_HELP)
247
+ if util.args['verbose'] or util.args['debug']:
248
+ print()
249
+ print(FULL_DEPS_HELP)
250
+
251
+ return []
252
+
253
+ def help(self, tokens: list) -> None:
254
+ '''Since we don't inherit from opencos.eda_base.Command, need our own help
255
+ method
256
+ '''
257
+ Command.help(self, tokens=tokens, no_targets=True)
258
+ print()
259
+ print(FULL_DEPS_HELP)
@@ -40,7 +40,7 @@ class CommandExport(CommandDesign):
40
40
  unparsed = CommandDesign.process_tokens(
41
41
  self, tokens=tokens, process_all=process_all, pwd=pwd
42
42
  )
43
- if self.status_any_error():
43
+ if self.stop_process_tokens_before_do_it():
44
44
  return unparsed
45
45
  if self.args['top']:
46
46
  # create our work dir, b/c top is set. We do this so any shell or peakrdl style
opencos/commands/flist.py CHANGED
@@ -63,6 +63,9 @@ class CommandFList(CommandDesign):
63
63
  unparsed = CommandDesign.process_tokens(
64
64
  self, tokens=tokens, process_all=process_all, pwd=pwd
65
65
  )
66
+ if self.stop_process_tokens_before_do_it():
67
+ return unparsed
68
+
66
69
  self.do_it()
67
70
  return unparsed
68
71
 
opencos/commands/lec.py CHANGED
@@ -66,7 +66,7 @@ class CommandLec(CommandDesign):
66
66
  self, tokens=tokens, process_all=process_all, pwd=pwd
67
67
  )
68
68
 
69
- if self.status_any_error():
69
+ if self.stop_process_tokens_before_do_it():
70
70
  return unparsed
71
71
 
72
72
  # we require there to be two --designs set.
opencos/commands/open.py CHANGED
@@ -23,5 +23,7 @@ class CommandOpen(CommandDesign):
23
23
  unparsed = CommandDesign.process_tokens(
24
24
  self, tokens=tokens, process_all=process_all, pwd=pwd
25
25
  )
26
+ if self.stop_process_tokens_before_do_it():
27
+ return unparsed
26
28
  self.do_it()
27
29
  return unparsed
opencos/commands/proj.py CHANGED
@@ -28,7 +28,7 @@ class CommandProj(CommandDesign):
28
28
  self, tokens=tokens, process_all=process_all, pwd=pwd
29
29
  )
30
30
 
31
- if self.status_any_error():
31
+ if self.stop_process_tokens_before_do_it():
32
32
  return unparsed
33
33
 
34
34
  # add defines for this job type
opencos/commands/shell.py CHANGED
@@ -50,7 +50,7 @@ class CommandShell(CommandDesign):
50
50
  self, tokens=tokens, process_all=process_all, pwd=pwd
51
51
  )
52
52
 
53
- if self.status_any_error():
53
+ if self.stop_process_tokens_before_do_it():
54
54
  return unparsed
55
55
 
56
56
  if self.args['top']:
opencos/commands/sim.py CHANGED
@@ -72,6 +72,8 @@ class CommandSim(CommandDesign):
72
72
  'waves': 'Include waveforms, if possible for tool',
73
73
  'waves-start': 'Starting time of waveform capture, if possible for tool',
74
74
  'work-dir': 'Optional override for working directory, defaults to ./eda.work/<top>.sim',
75
+ 'test-mode': ('stops the command early without executing, if --gui is present will'
76
+ ' instead test without spawning gui')
75
77
 
76
78
  })
77
79
 
@@ -97,7 +99,7 @@ class CommandSim(CommandDesign):
97
99
  self, tokens=tokens, process_all=process_all, pwd=pwd
98
100
  )
99
101
 
100
- if self.status_any_error():
102
+ if self.stop_process_tokens_before_do_it():
101
103
  return unparsed
102
104
 
103
105
  # add defines for this job type
@@ -176,8 +178,13 @@ class CommandSim(CommandDesign):
176
178
  )
177
179
 
178
180
  if check_logs and log_fname:
181
+ # Note this call will check on stdout if not GUI, not opening the log_fname,
182
+ # but if this is GUI we normally lose stdout and have to open the log.
183
+ gui_mode = self.args.get('gui', False)
184
+ file_contents_str = '' if gui_mode else stdout
179
185
  self.check_logs_for_errors(
180
- filename='', file_contents_str=stdout,
186
+ filename=os.path.join(self.args['work-dir'], log_fname),
187
+ file_contents_str=file_contents_str,
181
188
  bad_strings=bad_strings, must_strings=must_strings,
182
189
  use_bad_strings=use_bad_strings, use_must_strings=use_must_strings
183
190
  )
@@ -285,6 +292,9 @@ class CommandSim(CommandDesign):
285
292
  '''Returns None, checks logs using args bad_strings, must_strings,
286
293
 
287
294
  and internals self.args["log-[bad|must]-strings"] (lists).
295
+
296
+ file_contents_str will take precedence over opening filename, but filename is still
297
+ used in messaging.
288
298
  '''
289
299
 
290
300
  _bad_strings = bad_strings
@@ -304,13 +314,27 @@ class CommandSim(CommandDesign):
304
314
  hit_must_string_dict = dict.fromkeys(_must_strings)
305
315
 
306
316
  lines = []
307
- fname = ''
317
+
318
+ log_fpath = ''
319
+ if os.path.exists(filename):
320
+ log_fpath = filename
321
+
308
322
  if file_contents_str:
309
323
  lines = file_contents_str.split('\n')
324
+ log_fname = log_fpath + '(STDOUT)'
325
+ util.debug(f'Checking log for errors: {log_fpath=} but checking from STDOUT string...')
310
326
  elif filename:
311
- fname = os.path.join(self.args['work-dir'], filename)
312
- with open(fname, 'r', encoding='utf-8') as f:
313
- lines = f.readl.splitlines()
327
+ log_fname = log_fpath
328
+ util.debug(f'Checking log for errors: {log_fpath=} opening file...')
329
+ if not os.path.exists(log_fname):
330
+ self.error(f'sim.check_logs_for_errors: log {log_fpath} does not exist, cannot',
331
+ 'check it for errors or passing strings')
332
+ return
333
+ with open(log_fpath, 'r', encoding='utf-8') as f:
334
+ lines = f.read().splitlines()
335
+ else:
336
+ self.error(f'sim.check_logs_for_errors: {log_fpath=} does not exist, and no',
337
+ 'file_contents_str exists to check')
314
338
 
315
339
  if lines:
316
340
  for lineno, line in enumerate(lines):
@@ -320,13 +344,13 @@ class CommandSim(CommandDesign):
320
344
  hit_must_string_dict[k] = True
321
345
  if any(bad_str in line for bad_str in _bad_strings):
322
346
  self.error(
323
- f"log {fname}:{lineno} contains one of {_bad_strings=}",
347
+ f"log {log_fname}:{lineno} contains one of {_bad_strings=}",
324
348
  error_code=status_constants.EDA_SIM_LOG_HAS_BAD_STRING
325
349
  )
326
350
 
327
351
  if any(x is None for x in hit_must_string_dict.values()):
328
352
  self.error(
329
- f"Didn't get all passing patterns in log {fname}: {_must_strings=}",
353
+ f"Didn't get all passing patterns in log {log_fname}: {_must_strings=}",
330
354
  f" {hit_must_string_dict=}",
331
355
  error_code=status_constants.EDA_SIM_LOG_MISSING_MUST_STRING
332
356
  )
opencos/commands/synth.py CHANGED
@@ -64,7 +64,7 @@ class CommandSynth(CommandDesign):
64
64
  self, tokens=tokens, process_all=process_all, pwd=pwd
65
65
  )
66
66
 
67
- if self.status_any_error():
67
+ if self.stop_process_tokens_before_do_it():
68
68
  return unparsed
69
69
 
70
70
  # add defines for this job type
@@ -25,6 +25,9 @@ class CommandUpload(Command):
25
25
  self.unparsed_args = Command.process_tokens(
26
26
  self, tokens=tokens, process_all=False, pwd=pwd
27
27
  )
28
+ if self.stop_process_tokens_before_do_it():
29
+ return []
30
+
28
31
  self.create_work_dir()
29
32
  self.do_it()
30
33
  return []
opencos/commands/waves.py CHANGED
@@ -95,6 +95,7 @@ class CommandWaves(CommandDesign):
95
95
  self.error(f"Was already given {wave_file=}, not sure what",
96
96
  f"to do with {tokens[0]}")
97
97
  wave_dirs.append(tokens[0])
98
+ self.warning_show_known_args()
98
99
  self.error(f"Didn't understand command arg/token: '{tokens[0]}'",
99
100
  "in CommandWaves")
100
101
 
opencos/deps/defaults.py CHANGED
@@ -46,6 +46,7 @@ SUPPORTED_DEP_KEYS_BY_TYPE = {
46
46
 
47
47
  SUPPORTED_TAG_KEYS = set([
48
48
  'with-tools',
49
+ 'with-commands',
49
50
  'with-args',
50
51
  'args',
51
52
  'deps',
opencos/deps/deps_file.py CHANGED
@@ -15,7 +15,8 @@ from opencos.deps.defaults import DEPS_FILE_EXTS, ROOT_TABLE_KEYS_NOT_TARGETS
15
15
  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
- from opencos.utils.str_helpers import fnmatch_or_re, dep_str2list
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
20
  from opencos.utils.subprocess_helpers import subprocess_run_background
20
21
  from opencos.utils.status_constants import EDA_DEPS_FILE_NOT_FOUND, EDA_DEPS_TARGET_NOT_FOUND
21
22
 
@@ -24,7 +25,8 @@ def deps_data_get_all_targets(data: dict) -> list:
24
25
  '''Given extracted DEPS data (dict) get all the root level keys that aren't defaults'''
25
26
  if data is None:
26
27
  return []
27
- return [x for x in data.keys() if x not in ROOT_TABLE_KEYS_NOT_TARGETS]
28
+ return [x for x in data.keys() if (x not in ROOT_TABLE_KEYS_NOT_TARGETS and
29
+ is_valid_target_name(x))]
28
30
 
29
31
 
30
32
  def get_deps_markup_file(base_path: str) -> str:
@@ -272,6 +274,19 @@ class DepsFile:
272
274
  self.get_approx_line_number_str(target)
273
275
  ])
274
276
 
277
+
278
+ def warning_show_available_targets(self) -> None:
279
+ '''call warning showing available targets (pretty column printed)'''
280
+ if not self.data or not self.rel_deps_file:
281
+ return
282
+ pretty_possible_targets = pretty_list_columns_manual(
283
+ data=deps_data_get_all_targets(self.data)
284
+ )
285
+ targets_str = " " + "\n ".join(pretty_possible_targets).strip()
286
+ util.warning((f'Targets available in deps_file={self.rel_deps_file}:\n'
287
+ f'{targets_str}'))
288
+
289
+
275
290
  def lookup(self, target_node: str, caller_info: str) -> bool:
276
291
  '''Returns True if the target_node is in the DEPS markup file. If not, error with
277
292
 
@@ -284,6 +299,15 @@ class DepsFile:
284
299
  t_path = os.path.relpath(self.target_path) + os.path.sep
285
300
  t_node = target_node
286
301
  t_full = os.path.join(t_path, t_node)
302
+
303
+ if target_node.startswith('-'):
304
+ # likely an unparsed arg that made it this far.
305
+ util.warning(f"Ignoring unparsed argument '{target_node}'")
306
+ return False
307
+
308
+ if not is_valid_target_name(target_node):
309
+ util.warning(f"In file {self.rel_deps_file}, {target_node} {VALID_TARGET_INFO_STR}")
310
+
287
311
  if not caller_info:
288
312
  # If we don't have caller_info, likely came from command line (or DEPS JSON data):
289
313
  if '.' in target_node:
@@ -297,10 +321,11 @@ class DepsFile:
297
321
  f'but path {t_path} has no DEPS markup file (DEPS.yml)',
298
322
  error_code=EDA_DEPS_FILE_NOT_FOUND)
299
323
  else:
324
+ self.warning_show_available_targets()
300
325
  self.error(f'Trying to resolve command-line target={t_full}:',
301
- f'was not found in deps_file={self.rel_deps_file},',
302
- f'possible targets in deps file = {list(self.data.keys())}',
326
+ f'was not found in deps_file={self.rel_deps_file}',
303
327
  error_code=EDA_DEPS_TARGET_NOT_FOUND)
328
+
304
329
  else:
305
330
  # If we have caller_info, then this was a recursive call from another
306
331
  # DEPS file. It should already have the useful error messaging:
@@ -318,6 +343,7 @@ class DepsFile:
318
343
  f'but {t_path} has no DEPS markup file (DEPS.yml)',
319
344
  error_code=EDA_DEPS_FILE_NOT_FOUND)
320
345
  else:
346
+ self.warning_show_available_targets()
321
347
  self.error(f'Trying to resolve target={t_full}:',
322
348
  f'called from {caller_info},',
323
349
  f'Target not found in deps_file={self.rel_deps_file}',