Coverage for apio / commands / apio_api.py: 88%
273 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-25 02:31 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-25 02:31 +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 *,
135 # Options
136 timestamp: str,
137 output: str,
138 force: bool,
139):
140 """Implements the 'apio apio get-system' command."""
142 apio_ctx = ApioContext(
143 project_policy=ProjectPolicy.NO_PROJECT,
144 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
145 packages_policy=PackagesPolicy.ENSURE_PACKAGES,
146 )
148 # -- The top dict that we will emit as json.
149 top_dict = {}
151 # -- Append user timestamp if specified.
152 if timestamp: 152 ↛ 155line 152 didn't jump to line 155 because the condition on line 152 was always true
153 top_dict["timestamp"] = timestamp
155 section_dict = {}
157 # -- Add fields.
158 section_dict["apio-cli-version"] = util.get_apio_version_str()
159 section_dict["release-info"] = util.get_apio_release_info()
160 section_dict["python-version"] = util.get_python_version()
161 section_dict["python-executable"] = sys.executable
162 section_dict["platform-id"] = apio_ctx.platform_id
163 section_dict["platform-info"] = util.get_platform_info()
164 section_dict["scons-shell-id"] = apio_ctx.scons_shell_id
165 section_dict["vscode-debugger"] = str(
166 util.is_under_vscode_debugger()
167 ).lower()
168 section_dict["pyinstaller"] = str(util.is_pyinstaller_app()).lower()
169 section_dict["apio-python_package"] = str(
170 util.get_path_in_apio_package("")
171 )
172 section_dict["apio-home-dir"] = str(apio_ctx.apio_home_dir)
173 section_dict["apio-packages-dir"] = str(apio_ctx.apio_packages_dir)
174 section_dict["remote-config-url"] = apio_ctx.profile.remote_config_url
175 section_dict["verible-formatter"] = str(
176 apio_ctx.apio_packages_dir / "verible/bin/verible-verilog-format"
177 )
178 section_dict["verible-language-server"] = str(
179 apio_ctx.apio_packages_dir / "verible/bin/verible-verilog-ls"
180 )
182 # -- Add section
183 top_dict["system"] = section_dict
185 # -- Write out
186 write_as_json_doc(top_dict, output, force)
189# ------ apio api get-project
192# -- Text in the rich-text format of the python rich library.
193APIO_API_GET_PROJECT_HELP = """
194The command 'apio api get-project' exports information about an Apio
195project as a JSON foc.
197The optional flag '--timestamp' allows the caller to embed in the JSON \
198document a known timestamp that allows to verify that the JSON document \
199was indeed was generated by the same invocation.
201Examples:[code]
202 apio api get-project # Report default env
203 apio api get-project -e env1 # Report specified env
204 apio api get-project -p foo/bar # Project in another dir
205 apio api get-project -o apio.json # Write to a file[/code]
206"""
209@click.command(
210 name="get-project",
211 cls=ApioCommand,
212 short_help="Get project information.",
213 help=APIO_API_GET_PROJECT_HELP,
214)
215# @click.pass_context
216@options.env_option_gen()
217@options.project_dir_option
218@timestamp_option
219@output_option
220@options.force_option_gen(short_help="Overwrite output file.")
221def _get_project_cli(
222 *,
223 # Options
224 env: str,
225 project_dir: Optional[Path],
226 timestamp: str,
227 output: str,
228 force: bool,
229):
230 """Implements the 'apio apio get-project' command."""
232 apio_ctx = ApioContext(
233 project_policy=ProjectPolicy.PROJECT_REQUIRED,
234 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
235 packages_policy=PackagesPolicy.ENSURE_PACKAGES,
236 project_dir_arg=project_dir,
237 env_arg=env,
238 )
240 # -- Change to the project's folder.
241 os.chdir(apio_ctx.project_dir)
243 # -- The top dict that we will emit as json.
244 top_dict = {}
246 # -- Append user timestamp if specified.
247 if timestamp: 247 ↛ 250line 247 didn't jump to line 250 because the condition on line 247 was always true
248 top_dict["timestamp"] = timestamp
250 section_dict = {}
252 active_env_dict = {}
253 active_env_dict["name"] = apio_ctx.project.env_name
254 active_env_dict["options"] = apio_ctx.project.env_options
255 section_dict["active-env"] = active_env_dict
257 section_dict["envs"] = apio_ctx.project.env_names
259 synth_srcs, test_srcs = get_project_source_files()
260 section_dict["synth-files"] = synth_srcs
261 section_dict["test-benches"] = test_srcs
263 pr = apio_ctx.project_resources
265 board_dict = {"id": pr.board_id}
266 board_dict.update(pr.board_info)
267 section_dict["board"] = board_dict
269 fpga_dict = {"id": pr.fpga_id}
270 fpga_dict.update(pr.fpga_info)
271 section_dict["fpga"] = fpga_dict
273 programmer_dict = {"id": pr.programmer_id}
274 programmer_dict.update(pr.programmer_info)
275 section_dict["programmer"] = programmer_dict
277 # -- Add section
278 top_dict["project"] = section_dict
280 # -- Write out
281 write_as_json_doc(top_dict, output, force)
284# ------ apio api get-boards
287# -- Text in the rich-text format of the python rich library.
288APIO_API_GET_BOARDS_HELP = """
289The command 'apio api get-boards' exports apio boards information as a \
290JSON document.
292The optional flag '--timestamp' allows the caller to embed in the JSON \
293document a known timestamp that allows to verify that the JSON document \
294was indeed was generated by the same invocation.
296Examples:[code]
297 apio api get-boards # Write to stdout
298 apio api get-boards -o apio.json # Write to a file[/code]
299"""
302@click.command(
303 name="get-boards",
304 cls=ApioCommand,
305 short_help="Retrieve boards information.",
306 help=APIO_API_GET_BOARDS_HELP,
307)
308@timestamp_option
309@output_option
310@options.force_option_gen(short_help="Overwrite output file.")
311def _get_boards_cli(
312 *,
313 # Options
314 timestamp: str,
315 output: str,
316 force: bool,
317):
318 """Implements the 'apio apio get-boards' command."""
320 # -- For now, the information is not in a project context. That may
321 # -- change in the future.
322 apio_ctx = ApioContext(
323 project_policy=ProjectPolicy.NO_PROJECT,
324 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
325 packages_policy=PackagesPolicy.ENSURE_PACKAGES,
326 )
328 # -- The top dict that we will emit as json.
329 top_dict = {}
331 # -- Append user timestamp if specified.
332 if timestamp: 332 ↛ 336line 332 didn't jump to line 336 because the condition on line 332 was always true
333 top_dict["timestamp"] = timestamp
335 # -- Generate the boards section.
336 section = {}
337 for board_id, board_info in apio_ctx.boards.items():
338 # -- The board output dict.
339 board_dict = {}
341 # -- Add board description
342 board_dict["description"] = board_info.get("description", None)
344 # -- Add board's fpga information.
345 fpga_id = board_info.get("fpga-id", None)
346 fpga_info = apio_ctx.fpgas.get(fpga_id, {})
347 assert "id" not in fpga_info
348 fpga_dict = {"id": fpga_id}
349 fpga_dict.update(fpga_info)
350 board_dict["fpga"] = fpga_dict
352 # -- Add board's programmer information.
353 programmer_dict = {}
354 programmer_id = board_info.get("programmer", {}).get("id", None)
355 programmer_dict["id"] = programmer_id
356 board_dict["programmer"] = programmer_dict
358 # -- Add the board to the boards dict.
359 section[board_id] = board_dict
361 top_dict["boards"] = section
363 # -- Write out
364 write_as_json_doc(top_dict, output, force)
367# ------ apio api get-fpgas
370# -- Text in the rich-text format of the python rich library.
371APIO_API_GET_FPGAS_HELP = """
372The command 'apio api get-fpgas' exports apio FPGAss information as a \
373JSON document.
375The optional flag '--timestamp' allows the caller to embed in the JSON \
376document a known timestamp that allows to verify that the JSON document \
377was indeed was generated by the same invocation.
379Examples:[code]
380 apio api get-fpgas # Write to stdout
381 apio api get-fpgas -o apio.json # Write to a file[/code]
382"""
385@click.command(
386 name="get-fpgas",
387 cls=ApioCommand,
388 short_help="Retrieve FPGAs information.",
389 help=APIO_API_GET_FPGAS_HELP,
390)
391@timestamp_option
392@output_option
393@options.force_option_gen(short_help="Overwrite output file.")
394def _get_fpgas_cli(
395 *,
396 # Options
397 timestamp: str,
398 output: str,
399 force: bool,
400):
401 """Implements the 'apio apio get-fpgas' command."""
403 # -- For now, the information is not in a project context. That may
404 # -- change in the future.
405 apio_ctx = ApioContext(
406 project_policy=ProjectPolicy.NO_PROJECT,
407 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
408 packages_policy=PackagesPolicy.ENSURE_PACKAGES,
409 )
411 # -- The top dict that we will emit as json.
412 top_dict = {}
414 # -- Append user timestamp if specified.
415 if timestamp: 415 ↛ 419line 415 didn't jump to line 419 because the condition on line 415 was always true
416 top_dict["timestamp"] = timestamp
418 # -- Generate the fpgas section
419 section = {}
420 for fpga_id, fpga_info in apio_ctx.fpgas.items():
421 section[fpga_id] = fpga_info
423 top_dict["fpgas"] = section
425 # -- Write out
426 write_as_json_doc(top_dict, output, force)
429# ------ apio api get-programmers
432# -- Text in the rich-text format of the python rich library.
433APIO_API_GET_PROGRAMMERS_HELP = """
434The command 'apio api get-programmers' exports apio programmers information \
435as a JSON document.
437The optional flag '--timestamp' allows the caller to embed in the JSON \
438document a known timestamp that allows to verify that the JSON document \
439was indeed was generated by the same invocation.
441Examples:[code]
442 apio api get-programmers # Write to stdout
443 apio api get-programmers -o apio.json # Write to a file[/code]
444"""
447@click.command(
448 name="get-programmers",
449 cls=ApioCommand,
450 short_help="Retrieve programmers information.",
451 help=APIO_API_GET_PROGRAMMERS_HELP,
452)
453@timestamp_option
454@output_option
455@options.force_option_gen(short_help="Overwrite output file.")
456def _get_programmers_cli(
457 *,
458 # Options
459 timestamp: str,
460 output: str,
461 force: bool,
462):
463 """Implements the 'apio apio get-programmers' command."""
465 # -- For now, the information is not in a project context. That may
466 # -- change in the future.
467 apio_ctx = ApioContext(
468 project_policy=ProjectPolicy.NO_PROJECT,
469 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
470 packages_policy=PackagesPolicy.ENSURE_PACKAGES,
471 )
473 # -- The top dict that we will emit as json.
474 top_dict = {}
476 # -- Append user timestamp if specified.
477 if timestamp: 477 ↛ 481line 477 didn't jump to line 481 because the condition on line 477 was always true
478 top_dict["timestamp"] = timestamp
480 # -- Generate the 'programmers' section.
481 section = {}
482 for programmer_id, programmer_info in apio_ctx.programmers.items():
483 section[programmer_id] = programmer_info
485 top_dict["programmers"] = section
487 # -- Write out
488 write_as_json_doc(top_dict, output, force)
491# ------ apio api get-examples
494# -- Text in the rich-text format of the python rich library.
495APIO_API_GET_EXAMPLES_HELP = """
496The command 'apio api get-examples' exports apio examples information as a \
497JSON document.
499The optional flag '--timestamp' allows the caller to embed in the JSON \
500document a known timestamp that allows to verify that the JSON document \
501was indeed was generated by the same invocation.
503Examples:[code]
504 apio api get-examples # Write to stdout
505 apio api get-examples -o apio.json # Write to a file[/code]
506"""
509@click.command(
510 name="get-examples",
511 cls=ApioCommand,
512 short_help="Retrieve examples information.",
513 help=APIO_API_GET_EXAMPLES_HELP,
514)
515@timestamp_option
516@output_option
517@options.force_option_gen(short_help="Overwrite output file.")
518def _get_examples_cli(
519 *,
520 # Options
521 timestamp: str,
522 output: str,
523 force: bool,
524):
525 """Implements the 'apio apio get-examples' command."""
527 # -- For now, the information is not in a project context. That may
528 # -- change in the future.
529 apio_ctx = ApioContext(
530 project_policy=ProjectPolicy.NO_PROJECT,
531 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
532 packages_policy=PackagesPolicy.ENSURE_PACKAGES,
533 )
535 # -- Get examples infos.
536 examples: List[ExampleInfo] = Examples(apio_ctx).get_examples_infos()
538 # -- Group examples by boards
539 boards_examples: Dict[str, List[ExampleInfo]] = {}
540 for example in examples:
541 board_examples = boards_examples.get(example.board_id, [])
542 board_examples.append(example)
543 boards_examples[example.board_id] = board_examples
545 # -- The top dict that we will emit as json.
546 top_dict = {}
548 # -- Append user timestamp if specified.
549 if timestamp: 549 ↛ 553line 549 didn't jump to line 553 because the condition on line 549 was always true
550 top_dict["timestamp"] = timestamp
552 # -- Generate the 'examples' section.
553 section = {}
554 for board, board_examples in boards_examples.items():
555 board_dict = {}
556 # -- Generate board examples
557 for example_info in board_examples:
558 example_dict = {}
559 example_dict["description"] = example_info.description
560 board_dict[example_info.example_name] = example_dict
562 section[board] = board_dict
564 top_dict["examples"] = section
566 # -- Write out
567 write_as_json_doc(top_dict, output, force)
570# ------ apio api get-commands
573@dataclass(frozen=True)
574class CmdInfo:
575 """Represents the information of a single apio command."""
577 name: str
578 path: List[str]
579 cli: click.Command
580 children: List[Self]
583def scan_children(cmd_cli) -> Dict:
584 """Return a dict describing this command subtree."""
585 result = {}
587 # -- Sanity check
588 assert isinstance(result, dict), type(result)
590 # -- If this is a simple command, it has no sub commands.
591 if isinstance(cmd_cli, ApioCommand):
592 return result
594 # -- Here we have a group and it should have at least one sub command.
595 assert isinstance(cmd_cli, ApioGroup), type(cmd_cli)
596 subgroups: List[ApioSubgroup] = cmd_cli.subgroups
598 # -- Create the dict for the command subgroups.
599 subcommands_dict = {}
600 result["commands"] = subcommands_dict
602 # -- Iterate the subgroups and populate them. We flaten the subcommands
603 # -- group into a single list of commands.
604 for subgroup in subgroups:
605 assert isinstance(subgroup, ApioSubgroup), type(subgroup)
606 assert isinstance(subgroup.title, str), type(subgroup.title)
607 for subcommand in subgroup.commands:
608 subcommand_dict = scan_children(subcommand)
609 subcommands_dict[subcommand.name] = subcommand_dict
611 # -- All done ok.
612 return result
615# -- Text in the rich-text format of the python rich library.
616APIO_API_GET_COMMANDS_HELP = """
617The command 'apio api get-commands' exports apio command structure \
618of Apio as a JSON doc. This is used by various tools such as
619documentation generators and tests.
621The optional flag '--timestamp' allows the caller to embed in the JSON \
622document a known timestamp that allows to verify that the JSON document \
623was indeed was generated by the same invocation.
625Examples:[code]
626 apio api get-commands # Write to stdout
627 apio api get-commands -o apio.json # Write to a file[/code]
628"""
631@click.command(
632 name="get-commands",
633 cls=ApioCommand,
634 short_help="Retrieve apio commands information.",
635 help=APIO_API_GET_COMMANDS_HELP,
636)
637@click.pass_context
638@timestamp_option
639@output_option
640@options.force_option_gen(short_help="Overwrite output file.")
641def _get_commands_cli(
642 # Click context
643 cmd_ctx: ApioCmdContext,
644 *,
645 # Options
646 timestamp: str,
647 output: str,
648 force: bool,
649):
650 """Implements the 'apio apio get-commands' command."""
652 # -- Find the top cli which is the "apio" command. Would access it
653 # -- directly but it would create a circular python import.
654 ctx = cmd_ctx
655 while ctx.parent:
656 ctx = ctx.parent
657 assert isinstance(ctx, ApioCmdContext), type(ctx)
658 top_cli = ctx.command
659 assert top_cli.name == "apio", top_cli
661 # -- This initializes the console, print active env vars, etc.
662 ApioContext(
663 project_policy=ProjectPolicy.NO_PROJECT,
664 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
665 packages_policy=PackagesPolicy.ENSURE_PACKAGES,
666 )
668 # -- The top dict that we will emit as json.
669 top_dict = {}
671 # -- Append user timestamp if specified.
672 if timestamp: 672 ↛ 675line 672 didn't jump to line 675 because the condition on line 672 was always true
673 top_dict["timestamp"] = timestamp
675 section_dict = {}
676 section_dict["apio"] = scan_children(top_cli)
677 top_dict["commands"] = section_dict
679 # -- Write out
680 write_as_json_doc(top_dict, output, force)
683# ------ apio api scan-devices
686# -- Text in the rich-text format of the python rich library.
687APIO_API_SCAN_DEVICES_HELP = """
688The command 'apio api scan-devices' scans and report the available usb and \
689serial devices.
691The optional flag '--timestamp' allows the caller to embed in the JSON \
692document a known timestamp that allows to verify that the JSON document \
693was indeed was generated by the same invocation.
695Examples:[code]
696 apio api scan-devices # Write to stdout
697 apio api scan-devices -o apio.json # Write to a file[/code]
698"""
701@click.command(
702 name="scan-devices",
703 cls=ApioCommand,
704 short_help="Scan and report available devices.",
705 help=APIO_API_SCAN_DEVICES_HELP,
706)
707@timestamp_option
708@output_option
709@options.force_option_gen(short_help="Overwrite output file.")
710def _scan_devices_cli(
711 *,
712 # Options
713 timestamp: str,
714 output: str,
715 force: bool,
716):
717 """Implements the 'apio apio scan-devices' command."""
719 # -- For now, the information is not in a project context. That may
720 # -- change in the future. We need the config since we use libusb from
721 # -- the packages.
722 apio_ctx = ApioContext(
723 project_policy=ProjectPolicy.NO_PROJECT,
724 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
725 packages_policy=PackagesPolicy.ENSURE_PACKAGES,
726 )
728 # -- The top dict that we will emit as json.
729 top_dict = {}
731 # -- Append user timestamp if specified.
732 if timestamp: 732 ↛ 738line 732 didn't jump to line 738 because the condition on line 732 was always true
733 top_dict["timestamp"] = timestamp
735 # -- We need the packages for the 'libusb' backend.
736 # packages.install_missing_packages_on_the_fly(apio_ctx.packages_context)
738 usb_devices: List[UsbDevice] = usb_util.scan_usb_devices(apio_ctx)
740 # -- Scan and report usb devices.
741 section = []
742 for device in usb_devices: 742 ↛ 743line 742 didn't jump to line 743 because the loop on line 742 never started
743 dev = {}
744 dev["vid"] = device.vendor_id
745 dev["pid"] = device.product_id
746 dev["bus"] = device.bus
747 dev["device"] = device.device
748 dev["manufacturer"] = device.manufacturer
749 dev["product"] = device.product
750 dev["serial-number"] = device.serial_number
751 dev["device_type"] = device.device_type
753 section.append(dev)
755 top_dict["usb-devices"] = section
757 # -- Scan and report serial devices.
758 serial_devices: List[SerialDevice] = serial_util.scan_serial_devices()
760 section = []
761 for device in serial_devices: 761 ↛ 762line 761 didn't jump to line 762 because the loop on line 761 never started
762 dev = {}
763 dev["port"] = device.port
764 dev["port-name"] = device.port_name
765 dev["vendor-id"] = device.vendor_id
766 dev["product-id"] = device.product_id
767 dev["manufacturer"] = device.manufacturer
768 dev["product"] = device.product
769 dev["serial-number"] = device.serial_number
770 dev["device-type"] = device.device_type
772 section.append(dev)
774 top_dict["serial-devices"] = section
776 # -- Write out
777 write_as_json_doc(top_dict, output, force)
780# ------ apio api echo
783# -- Text in the rich-text format of the python rich library.
784APIO_API_ECHO_HELP = """
785The command 'apio api echo' allows external programs such as the Apio VS Code \
786extension to print a short message in a format that is consistent with \
787that Apio theme that is currently selected in the user preferences.
789The required option '--style' should have one of these values: OK, \
790INFO, WARNING, ERROR, TITLE, EMPH1, EMPH2, or EMPH3. The style colors can \
791be viewed with the command 'apio info themes'.
793Examples:[code]
794 apio api echo -t "Hello world", -s "INFO"
795 apio api echo -t "Task completed successfully", -s "OK"
796 apio api echo -t "Task failed", -s "ERROR"[/code]
797"""
799# -- Supported style names
800STYLES = {
801 "OK": SUCCESS,
802 "INFO": INFO,
803 "WARNING": WARNING,
804 "ERROR": ERROR,
805 "TITLE": TITLE,
806 "EMPH1": EMPH1,
807 "EMPH2": EMPH2,
808 "EMPH3": EMPH3,
809}
811text_option = click.option(
812 "text", # Var name.
813 "-t",
814 "--text",
815 type=str,
816 metavar="MESSAGE",
817 required=True,
818 help="Set message to echo.",
819 cls=cmd_util.ApioOption,
820)
823style_option = click.option(
824 "style", # Var name.
825 "-s",
826 "--style",
827 type=click.Choice(STYLES.keys()),
828 metavar="STYLE",
829 required=True,
830 help="Set style to use.",
831 cls=cmd_util.ApioOption,
832)
835@click.command(
836 name="echo",
837 cls=ApioCommand,
838 short_help="Print a message in given format.",
839 help=APIO_API_ECHO_HELP,
840)
841@text_option
842@style_option
843def _echo_cli(
844 *,
845 # Options
846 text: str,
847 style: str,
848):
849 """Implements the 'apio apio echo command."""
851 # -- Instanatiate Apio context, project and packages are not needed.
852 _ = ApioContext(
853 project_policy=ProjectPolicy.NO_PROJECT,
854 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
855 packages_policy=PackagesPolicy.ENSURE_PACKAGES,
856 )
858 cout(text, style=STYLES[style])
861# ------ apio apio
863# -- Text in the rich-text format of the python rich library.
864APIO_API_HELP = """
865The command group 'apio api' contains subcommands that that are intended \
866to be used by tools and programs such as icestudio, rather than being used \
867directly by users.
868"""
870# -- We have only a single group with the title 'Subcommands'.
871SUBGROUPS = [
872 ApioSubgroup(
873 "Subcommands",
874 [
875 _get_system_cli,
876 _get_project_cli,
877 _get_boards_cli,
878 _get_fpgas_cli,
879 _get_programmers_cli,
880 _get_examples_cli,
881 _get_commands_cli,
882 _scan_devices_cli,
883 _echo_cli,
884 ],
885 )
886]
889@click.command(
890 name="api",
891 cls=ApioGroup,
892 subgroups=SUBGROUPS,
893 short_help="Apio programmatic interface.",
894 help=APIO_API_HELP,
895)
896def cli():
897 """Implements the 'apio apio' command group."""
899 # pass