Coverage for apio / commands / apio_api.py: 88%
279 statements
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-08 02:47 +0000
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-08 02:47 +0000
1# -*- coding: utf-8 -*-
2# -- This file is part of the Apio project
3# -- (C) 2016-2024 FPGAwars
4# -- Authors
5# -- * Jesús Arroyo (2016-2019)
6# -- * Juan Gonzalez (obijuan) (2019-2024)
7# -- License GPLv2
8"""Implementation of 'apio api' command"""
10import sys
11import os
12from typing import Dict, List, Self, Optional
13from dataclasses import dataclass
14import json
15from pathlib import Path
16import click
17from apio.commands import options
19# from apio.managers import packages
20from apio.managers.examples import Examples, ExampleInfo
21from apio.common.apio_console import cout, cerror
22from apio.common.common_util import get_project_source_files
23from apio.utils import cmd_util, usb_util, serial_util, util
24from apio.utils.usb_util import UsbDevice
25from apio.utils.serial_util import SerialDevice
26from apio.common.apio_styles import (
27 INFO,
28 ERROR,
29 SUCCESS,
30 WARNING,
31 EMPH1,
32 EMPH2,
33 EMPH3,
34 TITLE,
35)
36from apio.apio_context import (
37 ApioContext,
38 PackagesPolicy,
39 ProjectPolicy,
40 RemoteConfigPolicy,
41)
42from apio.utils.cmd_util import (
43 ApioGroup,
44 ApioSubgroup,
45 ApioCommand,
46 ApioCmdContext,
47)
50timestamp_option = click.option(
51 "timestamp", # Var name.
52 "-t",
53 "--timestamp",
54 type=str,
55 metavar="text",
56 help="Set a user provided timestamp.",
57 cls=cmd_util.ApioOption,
58)
60output_option = click.option(
61 "output", # Var name.
62 "-o",
63 "--output",
64 type=str,
65 metavar="file-name",
66 help="Set output file.",
67 cls=cmd_util.ApioOption,
68)
71def write_as_json_doc(top_dict: Dict, output_flag: str, force_flag: bool):
72 """A common function to write a dict as a JSON doc."""
73 # -- Format the top dict as json text.
74 text = json.dumps(top_dict, indent=2)
76 if output_flag:
77 # -- Output the json text to a user specified file.
78 output_path = Path(output_flag)
80 if output_path.is_dir(): 80 ↛ 81line 80 didn't jump to line 81 because the condition on line 80 was never true
81 cerror(f"The output path {output_path} is a directory.")
82 sys.exit(1)
84 if output_path.exists() and not force_flag: 84 ↛ 85line 84 didn't jump to line 85 because the condition on line 84 was never true
85 cerror(f"The file already exists {output_path}.")
86 cout("Use the --force option to allow overwriting.", style=INFO)
87 sys.exit(1)
89 # -- if there file path contains a parent dir, make
90 # -- sure it exists. If output_flag is just a file name such
91 # -- as 'foo.json', we don nothing.
92 dirname = os.path.dirname(output_flag)
93 if dirname: 93 ↛ 97line 93 didn't jump to line 97 because the condition on line 93 was always true
94 os.makedirs(dirname, exist_ok=True)
96 # -- Write to file.
97 with open(output_flag, "w", encoding="utf-8") as f:
98 f.write(text)
99 else:
100 # -- Output the json text to stdout.
101 print(text, file=sys.stdout)
104# ------ apio api get-system
107# -- Text in the rich-text format of the python rich library.
108APIO_API_GET_SYSTEM_HELP = """
109The command 'apio api get-system' exports information about apio and \
110the underlying system as a JSON foc. It is similar to the command \
111'apio info system' which is intended for human consumption.
113The optional flag '--timestamp' allows the caller to embed in the JSON \
114document a known timestamp that allows to verify that the JSON document \
115was indeed was generated by the same invocation.
117Examples:[code]
118 apio api get-system # Write to stdout
119 apio api get-system -o apio.json # Write to a file[/code]
120"""
123@click.command(
124 name="get-system",
125 cls=ApioCommand,
126 short_help="Retrieve apio and system information.",
127 help=APIO_API_GET_SYSTEM_HELP,
128)
129# @click.pass_context
130@timestamp_option
131@output_option
132@options.force_option_gen(short_help="Overwrite output file.")
133def _get_system_cli(
134 # Options
135 timestamp: str,
136 output: str,
137 force: bool,
138):
139 """Implements the 'apio apio get-system' command."""
141 apio_ctx = ApioContext(
142 project_policy=ProjectPolicy.NO_PROJECT,
143 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
144 packages_policy=PackagesPolicy.ENSURE_PACKAGES,
145 )
147 # -- The top dict that we will emit as json.
148 top_dict = {}
150 # -- Append user timestamp if specified.
151 if timestamp: 151 ↛ 154line 151 didn't jump to line 154 because the condition on line 151 was always true
152 top_dict["timestamp"] = timestamp
154 section_dict = {}
156 # -- Add fields.
157 section_dict["apio-cli-version"] = util.get_apio_version_str()
158 section_dict["release-info"] = util.get_apio_release_info()
159 section_dict["python-version"] = util.get_python_version()
160 section_dict["python-executable"] = sys.executable
161 section_dict["platform-id"] = apio_ctx.platform_id
162 section_dict["platform-info"] = util.get_platform_info()
163 section_dict["scons-shell-id"] = apio_ctx.scons_shell_id
164 section_dict["vscode-debugger"] = str(
165 util.is_under_vscode_debugger()
166 ).lower()
167 section_dict["pyinstaller"] = str(util.is_pyinstaller_app()).lower()
168 section_dict["apio-python_package"] = str(
169 util.get_path_in_apio_package("")
170 )
171 section_dict["apio-home-dir"] = str(apio_ctx.apio_home_dir)
172 section_dict["apio-packages-dir"] = str(apio_ctx.apio_packages_dir)
173 section_dict["remote-config-url"] = apio_ctx.profile.remote_config_url
174 section_dict["verible-formatter"] = str(
175 apio_ctx.apio_packages_dir / "verible/bin/verible-verilog-format"
176 )
177 section_dict["verible-language-server"] = str(
178 apio_ctx.apio_packages_dir / "verible/bin/verible-verilog-ls"
179 )
181 # -- Add section
182 top_dict["system"] = section_dict
184 # -- Write out
185 write_as_json_doc(top_dict, output, force)
188# ------ apio api get-project
191# -- Text in the rich-text format of the python rich library.
192APIO_API_GET_PROJECT_HELP = """
193The command 'apio api get-project' exports information about an Apio
194project as a JSON foc.
196The optional flag '--timestamp' allows the caller to embed in the JSON \
197document a known timestamp that allows to verify that the JSON document \
198was indeed was generated by the same invocation.
200Examples:[code]
201 apio api get-project # Report default env
202 apio api get-project -e env1 # Report specified env
203 apio api get-project -p foo/bar # Project in another dir
204 apio api get-project -o apio.json # Write to a file[/code]
205"""
208@click.command(
209 name="get-project",
210 cls=ApioCommand,
211 short_help="Get project information.",
212 help=APIO_API_GET_PROJECT_HELP,
213)
214# @click.pass_context
215@options.env_option_gen()
216@options.project_dir_option
217@timestamp_option
218@output_option
219@options.force_option_gen(short_help="Overwrite output file.")
220def _get_project_cli(
221 # Options
222 env: str,
223 project_dir: Optional[Path],
224 timestamp: str,
225 output: str,
226 force: bool,
227):
228 """Implements the 'apio apio get-project' command."""
230 apio_ctx = ApioContext(
231 project_policy=ProjectPolicy.PROJECT_REQUIRED,
232 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
233 packages_policy=PackagesPolicy.ENSURE_PACKAGES,
234 project_dir_arg=project_dir,
235 env_arg=env,
236 )
238 # -- Change to the project's folder.
239 os.chdir(apio_ctx.project_dir)
241 # -- The top dict that we will emit as json.
242 top_dict = {}
244 # -- Append user timestamp if specified.
245 if timestamp: 245 ↛ 248line 245 didn't jump to line 248 because the condition on line 245 was always true
246 top_dict["timestamp"] = timestamp
248 section_dict = {}
250 active_env_dict = {}
251 active_env_dict["name"] = apio_ctx.project.env_name
252 active_env_dict["options"] = apio_ctx.project.env_options
253 section_dict["active-env"] = active_env_dict
255 section_dict["envs"] = apio_ctx.project.env_names
257 synth_srcs, test_srcs = get_project_source_files()
258 section_dict["synth-files"] = synth_srcs
259 section_dict["test-benches"] = test_srcs
261 pr = apio_ctx.project_resources
263 board_dict = {"id": pr.board_id}
264 board_dict.update(pr.board_info)
265 section_dict["board"] = board_dict
267 fpga_dict = {"id": pr.fpga_id}
268 fpga_dict.update(pr.fpga_info)
269 section_dict["fpga"] = fpga_dict
271 programmer_dict = {"id": pr.programmer_id}
272 programmer_dict.update(pr.programmer_info)
273 section_dict["programmer"] = programmer_dict
275 # -- Add section
276 top_dict["project"] = section_dict
278 # -- Write out
279 write_as_json_doc(top_dict, output, force)
282# ------ apio api get-boards
285# -- Text in the rich-text format of the python rich library.
286APIO_API_GET_BOARDS_HELP = """
287The command 'apio api get-boards' exports apio boards information as a \
288JSON document.
290The optional flag '--timestamp' allows the caller to embed in the JSON \
291document a known timestamp that allows to verify that the JSON document \
292was indeed was generated by the same invocation.
294Examples:[code]
295 apio api get-boards # Write to stdout
296 apio api get-boards -o apio.json # Write to a file[/code]
297"""
300@click.command(
301 name="get-boards",
302 cls=ApioCommand,
303 short_help="Retrieve boards information.",
304 help=APIO_API_GET_BOARDS_HELP,
305)
306@timestamp_option
307@output_option
308@options.force_option_gen(short_help="Overwrite output file.")
309def _get_boards_cli(
310 # Options
311 timestamp: str,
312 output: str,
313 force: bool,
314):
315 """Implements the 'apio apio get-boards' command."""
317 # -- For now, the information is not in a project context. That may
318 # -- change in the future.
319 apio_ctx = ApioContext(
320 project_policy=ProjectPolicy.NO_PROJECT,
321 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
322 packages_policy=PackagesPolicy.ENSURE_PACKAGES,
323 )
325 # -- The top dict that we will emit as json.
326 top_dict = {}
328 # -- Append user timestamp if specified.
329 if timestamp: 329 ↛ 333line 329 didn't jump to line 333 because the condition on line 329 was always true
330 top_dict["timestamp"] = timestamp
332 # -- Generate the boards section.
333 section = {}
334 for board_id, board_info in apio_ctx.boards.items():
335 # -- The board output dict.
336 board_dict = {}
338 # -- Add board description
339 board_dict["description"] = board_info.get("description", None)
341 # -- Add board's fpga information.
342 fpga_dict = {}
343 fpga_id = board_info.get("fpga-id", None)
344 fpga_info = apio_ctx.fpgas.get(fpga_id, {})
345 fpga_dict["id"] = fpga_id
346 fpga_dict["part-num"] = fpga_info.get("part-num", None)
347 fpga_dict["arch"] = fpga_info.get("arch", None)
348 fpga_dict["size"] = fpga_info.get("size", None)
349 board_dict["fpga"] = fpga_dict
351 # -- Add board's programmer information.
352 programmer_dict = {}
353 programmer_id = board_info.get("programmer", {}).get("id", None)
354 programmer_dict["id"] = programmer_id
355 board_dict["programmer"] = programmer_dict
357 # -- Add the board to the boards dict.
358 section[board_id] = board_dict
360 top_dict["boards"] = section
362 # -- Write out
363 write_as_json_doc(top_dict, output, force)
366# ------ apio api get-fpgas
369# -- Text in the rich-text format of the python rich library.
370APIO_API_GET_FPGAS_HELP = """
371The command 'apio api get-fpgas' exports apio FPGAss information as a \
372JSON document.
374The optional flag '--timestamp' allows the caller to embed in the JSON \
375document a known timestamp that allows to verify that the JSON document \
376was indeed was generated by the same invocation.
378Examples:[code]
379 apio api get-fpgas # Write to stdout
380 apio api get-fpgas -o apio.json # Write to a file[/code]
381"""
384@click.command(
385 name="get-fpgas",
386 cls=ApioCommand,
387 short_help="Retrieve FPGAs information.",
388 help=APIO_API_GET_FPGAS_HELP,
389)
390@timestamp_option
391@output_option
392@options.force_option_gen(short_help="Overwrite output file.")
393def _get_fpgas_cli(
394 # Options
395 timestamp: str,
396 output: str,
397 force: bool,
398):
399 """Implements the 'apio apio get-fpgas' command."""
401 # -- For now, the information is not in a project context. That may
402 # -- change in the future.
403 apio_ctx = ApioContext(
404 project_policy=ProjectPolicy.NO_PROJECT,
405 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
406 packages_policy=PackagesPolicy.ENSURE_PACKAGES,
407 )
409 # -- The top dict that we will emit as json.
410 top_dict = {}
412 # -- Append user timestamp if specified.
413 if timestamp: 413 ↛ 417line 413 didn't jump to line 417 because the condition on line 413 was always true
414 top_dict["timestamp"] = timestamp
416 # -- Generate the fpgas section
417 section = {}
418 for fpga_id, fpga_info in apio_ctx.fpgas.items():
419 # -- The fpga output dict.
420 fpga_dict = {}
422 fpga_dict["part-num"] = fpga_info.get("part-num", None)
423 fpga_dict["arch"] = fpga_info.get("arch", None)
424 fpga_dict["size"] = fpga_info.get("size", None)
426 # -- Add the fpga to the fpgas dict.
427 section[fpga_id] = fpga_dict
429 top_dict["fpgas"] = section
431 # -- Write out
432 write_as_json_doc(top_dict, output, force)
435# ------ apio api get-programmers
438# -- Text in the rich-text format of the python rich library.
439APIO_API_GET_PROGRAMMERS_HELP = """
440The command 'apio api get-programmers' exports apio programmers information \
441as a JSON document.
443The optional flag '--timestamp' allows the caller to embed in the JSON \
444document a known timestamp that allows to verify that the JSON document \
445was indeed was generated by the same invocation.
447Examples:[code]
448 apio api get-programmers # Write to stdout
449 apio api get-programmers -o apio.json # Write to a file[/code]
450"""
453@click.command(
454 name="get-programmers",
455 cls=ApioCommand,
456 short_help="Retrieve programmers information.",
457 help=APIO_API_GET_PROGRAMMERS_HELP,
458)
459@timestamp_option
460@output_option
461@options.force_option_gen(short_help="Overwrite output file.")
462def _get_programmers_cli(
463 # Options
464 timestamp: str,
465 output: str,
466 force: bool,
467):
468 """Implements the 'apio apio get-programmers' command."""
470 # -- For now, the information is not in a project context. That may
471 # -- change in the future.
472 apio_ctx = ApioContext(
473 project_policy=ProjectPolicy.NO_PROJECT,
474 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
475 packages_policy=PackagesPolicy.ENSURE_PACKAGES,
476 )
478 # -- The top dict that we will emit as json.
479 top_dict = {}
481 # -- Append user timestamp if specified.
482 if timestamp: 482 ↛ 486line 482 didn't jump to line 486 because the condition on line 482 was always true
483 top_dict["timestamp"] = timestamp
485 # -- Generate the 'programmers' section.
486 section = {}
487 for programmer_id, programmer_info in apio_ctx.programmers.items():
488 section[programmer_id] = programmer_info
490 top_dict["programmers"] = section
492 # -- Write out
493 write_as_json_doc(top_dict, output, force)
496# ------ apio api get-examples
499# -- Text in the rich-text format of the python rich library.
500APIO_API_GET_EXAMPLES_HELP = """
501The command 'apio api get-examples' exports apio examples information as a \
502JSON document.
504The optional flag '--timestamp' allows the caller to embed in the JSON \
505document a known timestamp that allows to verify that the JSON document \
506was indeed was generated by the same invocation.
508Examples:[code]
509 apio api get-examples # Write to stdout
510 apio api get-examples -o apio.json # Write to a file[/code]
511"""
514@click.command(
515 name="get-examples",
516 cls=ApioCommand,
517 short_help="Retrieve examples information.",
518 help=APIO_API_GET_EXAMPLES_HELP,
519)
520@timestamp_option
521@output_option
522@options.force_option_gen(short_help="Overwrite output file.")
523def _get_examples_cli(
524 # Options
525 timestamp: str,
526 output: str,
527 force: bool,
528):
529 """Implements the 'apio apio get-examples' command."""
531 # -- For now, the information is not in a project context. That may
532 # -- change in the future.
533 apio_ctx = ApioContext(
534 project_policy=ProjectPolicy.NO_PROJECT,
535 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
536 packages_policy=PackagesPolicy.ENSURE_PACKAGES,
537 )
539 # -- Get examples infos.
540 examples: List[ExampleInfo] = Examples(apio_ctx).get_examples_infos()
542 # -- Group examples by boards
543 boards_examples: Dict[str, List[ExampleInfo]] = {}
544 for example in examples:
545 board_examples = boards_examples.get(example.board_id, [])
546 board_examples.append(example)
547 boards_examples[example.board_id] = board_examples
549 # -- The top dict that we will emit as json.
550 top_dict = {}
552 # -- Append user timestamp if specified.
553 if timestamp: 553 ↛ 557line 553 didn't jump to line 557 because the condition on line 553 was always true
554 top_dict["timestamp"] = timestamp
556 # -- Generate the 'examples' section.
557 section = {}
558 for board, board_examples in boards_examples.items():
559 board_dict = {}
560 # -- Generate board examples
561 for example_info in board_examples:
562 example_dict = {}
563 example_dict["description"] = example_info.description
564 board_dict[example_info.example_name] = example_dict
566 section[board] = board_dict
568 top_dict["examples"] = section
570 # -- Write out
571 write_as_json_doc(top_dict, output, force)
574# ------ apio api get-commands
577@dataclass(frozen=True)
578class CmdInfo:
579 """Represents the information of a single apio command."""
581 name: str
582 path: List[str]
583 cli: click.Command
584 children: List[Self]
587def scan_children(cmd_cli) -> Dict:
588 """Return a dict describing this command subtree."""
589 result = {}
591 # -- Sanity check
592 assert isinstance(result, dict), type(result)
594 # -- If this is a simple command, it has no sub commands.
595 if isinstance(cmd_cli, ApioCommand):
596 return result
598 # -- Here we have a group and it should have at least one sub command.
599 assert isinstance(cmd_cli, ApioGroup), type(cmd_cli)
600 subgroups: List[ApioSubgroup] = cmd_cli.subgroups
602 # -- Create the dict for the command subgroups.
603 subcommands_dict = {}
604 result["commands"] = subcommands_dict
606 # -- Iterate the subgroups and populate them. We flaten the subcommands
607 # -- group into a single list of commands.
608 for subgroup in subgroups:
609 assert isinstance(subgroup, ApioSubgroup), type(subgroup)
610 assert isinstance(subgroup.title, str), type(subgroup.title)
611 for subcommand in subgroup.commands:
612 subcommand_dict = scan_children(subcommand)
613 subcommands_dict[subcommand.name] = subcommand_dict
615 # -- All done ok.
616 return result
619# -- Text in the rich-text format of the python rich library.
620APIO_API_GET_COMMANDS_HELP = """
621The command 'apio api get-commands' exports apio command structure \
622of Apio as a JSON doc. This is used by various tools such as
623documentation generators and tests.
625The optional flag '--timestamp' allows the caller to embed in the JSON \
626document a known timestamp that allows to verify that the JSON document \
627was indeed was generated by the same invocation.
629Examples:[code]
630 apio api get-commands # Write to stdout
631 apio api get-commands -o apio.json # Write to a file[/code]
632"""
635@click.command(
636 name="get-commands",
637 cls=ApioCommand,
638 short_help="Retrieve apio commands information.",
639 help=APIO_API_GET_COMMANDS_HELP,
640)
641@click.pass_context
642@timestamp_option
643@output_option
644@options.force_option_gen(short_help="Overwrite output file.")
645def _get_commands_cli(
646 # Click context
647 cmd_ctx: ApioCmdContext,
648 # Options
649 timestamp: str,
650 output: str,
651 force: bool,
652):
653 """Implements the 'apio apio get-commands' command."""
655 # -- Find the top cli which is the "apio" command. Would access it
656 # -- directly but it would create a circular python import.
657 ctx = cmd_ctx
658 while ctx.parent:
659 ctx = ctx.parent
660 assert isinstance(ctx, ApioCmdContext), type(ctx)
661 top_cli = ctx.command
662 assert top_cli.name == "apio", top_cli
664 # -- This initializes the console, print active env vars, etc.
665 ApioContext(
666 project_policy=ProjectPolicy.NO_PROJECT,
667 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
668 packages_policy=PackagesPolicy.ENSURE_PACKAGES,
669 )
671 # -- The top dict that we will emit as json.
672 top_dict = {}
674 # -- Append user timestamp if specified.
675 if timestamp: 675 ↛ 678line 675 didn't jump to line 678 because the condition on line 675 was always true
676 top_dict["timestamp"] = timestamp
678 section_dict = {}
679 section_dict["apio"] = scan_children(top_cli)
680 top_dict["commands"] = section_dict
682 # -- Write out
683 write_as_json_doc(top_dict, output, force)
686# ------ apio api scan-devices
689# -- Text in the rich-text format of the python rich library.
690APIO_API_SCAN_DEVICES_HELP = """
691The command 'apio api scan-devices' scans and report the available usb and \
692serial devices.
694The optional flag '--timestamp' allows the caller to embed in the JSON \
695document a known timestamp that allows to verify that the JSON document \
696was indeed was generated by the same invocation.
698Examples:[code]
699 apio api scan-devices # Write to stdout
700 apio api scan-devices -o apio.json # Write to a file[/code]
701"""
704@click.command(
705 name="scan-devices",
706 cls=ApioCommand,
707 short_help="Scan and report available devices.",
708 help=APIO_API_SCAN_DEVICES_HELP,
709)
710@timestamp_option
711@output_option
712@options.force_option_gen(short_help="Overwrite output file.")
713def _scan_devices_cli(
714 # Options
715 timestamp: str,
716 output: str,
717 force: bool,
718):
719 """Implements the 'apio apio scan-devices' command."""
721 # -- For now, the information is not in a project context. That may
722 # -- change in the future. We need the config since we use libusb from
723 # -- the packages.
724 apio_ctx = ApioContext(
725 project_policy=ProjectPolicy.NO_PROJECT,
726 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
727 packages_policy=PackagesPolicy.ENSURE_PACKAGES,
728 )
730 # -- The top dict that we will emit as json.
731 top_dict = {}
733 # -- Append user timestamp if specified.
734 if timestamp: 734 ↛ 740line 734 didn't jump to line 740 because the condition on line 734 was always true
735 top_dict["timestamp"] = timestamp
737 # -- We need the packages for the 'libusb' backend.
738 # packages.install_missing_packages_on_the_fly(apio_ctx.packages_context)
740 usb_devices: List[UsbDevice] = usb_util.scan_usb_devices(apio_ctx)
742 # -- Scan and report usb devices.
743 section = []
744 for device in usb_devices: 744 ↛ 745line 744 didn't jump to line 745 because the loop on line 744 never started
745 dev = {}
746 dev["vid"] = device.vendor_id
747 dev["pid"] = device.product_id
748 dev["bus"] = device.bus
749 dev["device"] = device.device
750 dev["manufacturer"] = device.manufacturer
751 dev["product"] = device.product
752 dev["serial-number"] = device.serial_number
753 dev["device_type"] = device.device_type
755 section.append(dev)
757 top_dict["usb-devices"] = section
759 # -- Scan and report serial devices.
760 serial_devices: List[SerialDevice] = serial_util.scan_serial_devices()
762 section = []
763 for device in serial_devices: 763 ↛ 764line 763 didn't jump to line 764 because the loop on line 763 never started
764 dev = {}
765 dev["port"] = device.port
766 dev["port-name"] = device.port_name
767 dev["vendor-id"] = device.vendor_id
768 dev["product-id"] = device.product_id
769 dev["manufacturer"] = device.manufacturer
770 dev["product"] = device.product
771 dev["serial-number"] = device.serial_number
772 dev["device-type"] = device.device_type
774 section.append(dev)
776 top_dict["serial-devices"] = section
778 # -- Write out
779 write_as_json_doc(top_dict, output, force)
782# ------ apio api echo
785# -- Text in the rich-text format of the python rich library.
786APIO_API_ECHO_HELP = """
787The command 'apio api echo' allows external programs such as the Apio VS Code \
788extension to print a short message in a format that is consistent with \
789that Apio theme that is currently selected in the user preferences.
791The required option '--style' should have one of these values: OK, \
792INFO, WARNING, ERROR, TITLE, EMPH1, EMPH2, or EMPH3. The style colors can \
793be viewed with the command 'apio info themes'.
795Examples:[code]
796 apio api echo -t "Hello world", -s "INFO"
797 apio api echo -t "Task completed successfully", -s "OK"
798 apio api echo -t "Task failed", -s "ERROR"[/code]
799"""
801# -- Supported style names
802STYLES = {
803 "OK": SUCCESS,
804 "INFO": INFO,
805 "WARNING": WARNING,
806 "ERROR": ERROR,
807 "TITLE": TITLE,
808 "EMPH1": EMPH1,
809 "EMPH2": EMPH2,
810 "EMPH3": EMPH3,
811}
813text_option = click.option(
814 "text", # Var name.
815 "-t",
816 "--text",
817 type=str,
818 metavar="MESSAGE",
819 required=True,
820 help="Set message to echo.",
821 cls=cmd_util.ApioOption,
822)
825style_option = click.option(
826 "style", # Var name.
827 "-s",
828 "--style",
829 type=click.Choice(STYLES.keys()),
830 metavar="STYLE",
831 required=True,
832 help="Set style to use.",
833 cls=cmd_util.ApioOption,
834)
837@click.command(
838 name="echo",
839 cls=ApioCommand,
840 short_help="Print a message in given format.",
841 help=APIO_API_ECHO_HELP,
842)
843@text_option
844@style_option
845def _echo_cli(
846 # Options
847 text: str,
848 style: str,
849):
850 """Implements the 'apio apio echo command."""
852 # -- Instanatiate Apio context, project and packages are not needed.
853 _ = ApioContext(
854 project_policy=ProjectPolicy.NO_PROJECT,
855 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
856 packages_policy=PackagesPolicy.ENSURE_PACKAGES,
857 )
859 cout(text, style=STYLES[style])
862# ------ apio apio
864# -- Text in the rich-text format of the python rich library.
865APIO_API_HELP = """
866The command group 'apio api' contains subcommands that that are intended \
867to be used by tools and programs such as icestudio, rather than being used \
868directly by users.
869"""
871# -- We have only a single group with the title 'Subcommands'.
872SUBGROUPS = [
873 ApioSubgroup(
874 "Subcommands",
875 [
876 _get_system_cli,
877 _get_project_cli,
878 _get_boards_cli,
879 _get_fpgas_cli,
880 _get_programmers_cli,
881 _get_examples_cli,
882 _get_commands_cli,
883 _scan_devices_cli,
884 _echo_cli,
885 ],
886 )
887]
890@click.command(
891 name="api",
892 cls=ApioGroup,
893 subgroups=SUBGROUPS,
894 short_help="Apio programmatic interface.",
895 help=APIO_API_HELP,
896)
897def cli():
898 """Implements the 'apio apio' command group."""
900 # pass