Coverage for apio/commands/apio_api.py: 89%
305 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-09 01:55 +0000
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-09 01:55 +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"""
10# pylint: disable=too-many-lines
12import sys
13import os
14from typing import Dict, List, Self, Optional, cast
15from dataclasses import dataclass
16import json
17from pathlib import Path
18import click
19from apio.commands import options
21# from apio.managers import packages
22from apio.managers.examples import Examples, ExampleInfo
23from apio.common.apio_console import cout, cerror
24from apio.common.common_util import get_project_source_files
25from apio.utils import cmd_util, usb_util, serial_util, util, apio_platforms
26from apio.utils.usb_util import UsbDevice
27from apio.utils.serial_util import SerialDevice
28from apio.common.apio_styles import (
29 INFO,
30 ERROR,
31 SUCCESS,
32 WARNING,
33 EMPH1,
34 EMPH2,
35 EMPH3,
36 TITLE,
37)
38from apio.apio_context import (
39 ApioContext,
40 PackagesPolicy,
41 ProjectPolicy,
42 RemoteConfigPolicy,
43)
44from apio.utils.cmd_util import (
45 ApioGroup,
46 ApioSubgroup,
47 ApioCommand,
48 ApioCmdContext,
49)
50from apio.common import build_report
52timestamp_option = click.option(
53 "timestamp", # Var name.
54 "-t",
55 "--timestamp",
56 type=str,
57 metavar="text",
58 help="Set a user provided timestamp.",
59 cls=cmd_util.ApioOption,
60)
62output_option = click.option(
63 "output", # Var name.
64 "-o",
65 "--output",
66 type=str,
67 metavar="file-name",
68 help="Set output file.",
69 cls=cmd_util.ApioOption,
70)
73def write_as_json_doc(top_dict: Dict, output_flag: str, force_flag: bool):
74 """A common function to write a dict as a JSON doc."""
75 # -- Format the top dict as json text.
76 text = json.dumps(top_dict, indent=2)
78 if output_flag:
79 # -- Output the json text to a user specified file.
80 output_path = Path(output_flag)
82 if output_path.is_dir(): 82 ↛ 83line 82 didn't jump to line 83 because the condition on line 82 was never true
83 cerror(f"The output path {output_path} is a directory.")
84 sys.exit(1)
86 if output_path.exists() and not force_flag: 86 ↛ 87line 86 didn't jump to line 87 because the condition on line 86 was never true
87 cerror(f"The file already exists {output_path}.")
88 cout("Use the --force option to allow overwriting.", style=INFO)
89 sys.exit(1)
91 # -- if there file path contains a parent dir, make
92 # -- sure it exists. If output_flag is just a file name such
93 # -- as 'foo.json', we don nothing.
94 dirname = os.path.dirname(output_flag)
95 if dirname: 95 ↛ 99line 95 didn't jump to line 99 because the condition on line 95 was always true
96 os.makedirs(dirname, exist_ok=True)
98 # -- Write to file.
99 with open(output_flag, "w", encoding="utf-8") as f:
100 f.write(text)
101 else:
102 # -- Output the json text to stdout.
103 print(text, file=sys.stdout)
106# ------ apio api get-system
109# -- Text in the rich-text format of the python rich library.
110APIO_API_GET_SYSTEM_HELP = """
111The command 'apio api get-system' exports information about apio and \
112the underlying system as a JSON foc. It is similar to the command \
113'apio info system' which is intended for human consumption.
115The optional flag '--timestamp' allows the caller to embed in the JSON \
116document a known timestamp that allows to verify that the JSON document \
117was indeed was generated by the same invocation.
119Examples:[code]
120 apio api get-system # Write to stdout
121 apio api get-system -o apio.json # Write to a file[/code]
122"""
125@click.command(
126 name="get-system",
127 cls=ApioCommand,
128 short_help="Retrieve apio and system information.",
129 help=APIO_API_GET_SYSTEM_HELP,
130)
131# @click.pass_context
132@timestamp_option
133@output_option
134@options.force_option_gen(short_help="Overwrite output file.")
135def _get_system_cli(
136 *,
137 # Options
138 timestamp: str,
139 output: str,
140 force: bool,
141):
142 """Implements the 'apio apio get-system' command."""
144 apio_ctx = ApioContext(
145 project_policy=ProjectPolicy.NO_PROJECT,
146 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
147 packages_policy=PackagesPolicy.ENSURE_PACKAGES,
148 )
150 platform = apio_ctx.platform
152 # -- The top dict that we will emit as json.
153 top_dict = {}
155 # -- Append user timestamp if specified.
156 if timestamp: 156 ↛ 159line 156 didn't jump to line 159 because the condition on line 156 was always true
157 top_dict["timestamp"] = timestamp
159 section_dict = {}
161 # -- Add fields.
162 section_dict["apio-cli-version"] = util.get_apio_version_str()
163 section_dict["release-info"] = util.get_apio_release_info()
164 section_dict["python-version"] = util.get_python_version()
165 section_dict["python-executable"] = sys.executable
166 section_dict["platform-info"] = apio_platforms.get_system_info()
167 section_dict["platform-id"] = platform.id
168 section_dict["is-darwin"] = platform.is_darwin
169 section_dict["is-linux"] = platform.is_linux
170 section_dict["is-windows"] = platform.is_windows
171 section_dict["scons-shell-id"] = apio_ctx.scons_shell_id
172 section_dict["vscode-debugger"] = str(
173 util.is_under_vscode_debugger()
174 ).lower()
175 section_dict["pyinstaller"] = str(util.is_pyinstaller_app()).lower()
176 section_dict["apio-python_package"] = str(
177 util.get_path_in_apio_package("")
178 )
179 section_dict["apio-home-dir"] = str(apio_ctx.apio_home_dir)
180 section_dict["apio-packages-dir"] = str(apio_ctx.apio_packages_dir)
181 section_dict["remote-config-url"] = apio_ctx.profile.remote_config_url
182 section_dict["verible-formatter"] = str(
183 apio_ctx.apio_packages_dir / "verible/bin/verible-verilog-format"
184 )
185 section_dict["verible-language-server"] = str(
186 apio_ctx.apio_packages_dir / "verible/bin/verible-verilog-ls"
187 )
189 # -- Add section
190 top_dict["system"] = section_dict
192 # -- Write out
193 write_as_json_doc(top_dict, output, force)
196# ------ apio api get-build-report
199# -- Text in the rich-text format of the python rich library.
200APIO_API_GET_BUILD_REPORT_HELP = """
201The command 'apio api get-build-report' provides utilization and max \
202clock information from a built project. The information is extracted \
203from the file 'hardware.pnr' that is generated by Apio when building \
204the project.
206The optional flag '--timestamp' allows the caller to embed in the JSON \
207document a known timestamp that allows to verify that the JSON document \
208was indeed was generated by the same invocation.
210Examples:[code]
211 apio api get-build-report # Report for default env
212 apio api get-build-report -e env1 # Report for specified env
213 apio api get-build-report -p foo/bar # Project in another dir
214 apio api get-build-report -o apio.json # Write to a file[/code]
215"""
218@click.command(
219 name="get-build-report",
220 cls=ApioCommand,
221 short_help="Get project build information.",
222 help=APIO_API_GET_BUILD_REPORT_HELP,
223)
224# @click.pass_context
225@options.env_option_gen()
226@options.project_dir_option
227@timestamp_option
228@output_option
229@options.force_option_gen(short_help="Overwrite output file.")
230def _get_build_report_cli(
231 *,
232 # Options
233 env: str,
234 project_dir: Optional[Path],
235 timestamp: str,
236 output: str,
237 force: bool,
238):
239 """Implements the 'apio apio get-build-report' command."""
241 apio_ctx = ApioContext(
242 project_policy=ProjectPolicy.PROJECT_REQUIRED,
243 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
244 packages_policy=PackagesPolicy.ENSURE_PACKAGES,
245 project_dir_arg=project_dir,
246 env_arg=env,
247 report_env=False,
248 )
250 # -- Change to the project's folder.
251 os.chdir(apio_ctx.project_dir)
253 # -- The build process generates this report file.
254 pnr_json_file = apio_ctx.env_build_path / "hardware.pnr"
256 # -- Read the report
257 report = build_report.read_build_report(pnr_json_file)
259 # -- The top dict that we will emit as json.
260 top_dict = {}
262 # -- Append user timestamp if specified.
263 if timestamp: 263 ↛ 266line 263 didn't jump to line 266 because the condition on line 263 was always true
264 top_dict["timestamp"] = timestamp
266 section_dict = {}
267 section_dict["env"] = apio_ctx.project.env_name
269 resources_dict = {}
270 for res in report.resources:
271 resources_dict[res.name] = {
272 "used": res.used,
273 "available": res.available,
274 "percentage": res.percentage,
275 }
277 section_dict["resources"] = resources_dict
279 clocks_dict = {}
280 for clk in report.clocks:
281 clocks_dict[clk.name] = {"fmax_mhz": clk.fmax_mhz}
283 section_dict["clocks"] = clocks_dict
285 # -- Add section
286 top_dict["build-report"] = section_dict
288 # -- Write out
289 write_as_json_doc(top_dict, output, force)
292# ------ apio api get-project
295# -- Text in the rich-text format of the python rich library.
296APIO_API_GET_PROJECT_HELP = """
297The command 'apio api get-project' exports information about an Apio
298project as a JSON foc.
300The optional flag '--timestamp' allows the caller to embed in the JSON \
301document a known timestamp that allows to verify that the JSON document \
302was indeed was generated by the same invocation.
304Examples:[code]
305 apio api get-project # Report default env
306 apio api get-project -e env1 # Report specified env
307 apio api get-project -p foo/bar # Project in another dir
308 apio api get-project -o apio.json # Write to a file[/code]
309"""
312@click.command(
313 name="get-project",
314 cls=ApioCommand,
315 short_help="Get project information.",
316 help=APIO_API_GET_PROJECT_HELP,
317)
318# @click.pass_context
319@options.env_option_gen()
320@options.project_dir_option
321@timestamp_option
322@output_option
323@options.force_option_gen(short_help="Overwrite output file.")
324def _get_project_cli(
325 *,
326 # Options
327 env: str,
328 project_dir: Optional[Path],
329 timestamp: str,
330 output: str,
331 force: bool,
332):
333 """Implements the 'apio apio get-project' command."""
335 apio_ctx = ApioContext(
336 project_policy=ProjectPolicy.PROJECT_REQUIRED,
337 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
338 packages_policy=PackagesPolicy.ENSURE_PACKAGES,
339 project_dir_arg=project_dir,
340 env_arg=env,
341 report_env=False,
342 )
344 # -- Change to the project's folder.
345 os.chdir(apio_ctx.project_dir)
347 # -- The top dict that we will emit as json.
348 top_dict = {}
350 # -- Append user timestamp if specified.
351 if timestamp: 351 ↛ 354line 351 didn't jump to line 354 because the condition on line 351 was always true
352 top_dict["timestamp"] = timestamp
354 section_dict = {}
356 active_env_dict = {}
357 active_env_dict["name"] = apio_ctx.project.env_name
358 active_env_dict["options"] = apio_ctx.project.env_options
359 section_dict["active-env"] = active_env_dict
361 section_dict["envs"] = apio_ctx.project.env_names
363 synth_srcs, test_srcs = get_project_source_files()
364 section_dict["synth-files"] = synth_srcs
365 section_dict["test-benches"] = test_srcs
367 pr = apio_ctx.project_resources
369 board_dict = {"id": pr.board_id}
370 board_dict.update(pr.board_info)
371 section_dict["board"] = board_dict
373 fpga_dict = {"id": pr.fpga_id}
374 fpga_dict.update(pr.fpga_info)
375 section_dict["fpga"] = fpga_dict
377 programmer_dict = {"id": pr.programmer_id}
378 programmer_dict.update(pr.programmer_info)
379 section_dict["programmer"] = programmer_dict
381 # -- Add section
382 top_dict["project"] = section_dict
384 # -- Write out
385 write_as_json_doc(top_dict, output, force)
388# ------ apio api get-boards
391# -- Text in the rich-text format of the python rich library.
392APIO_API_GET_BOARDS_HELP = """
393The command 'apio api get-boards' exports apio boards information as a \
394JSON document.
396The optional flag '--timestamp' allows the caller to embed in the JSON \
397document a known timestamp that allows to verify that the JSON document \
398was indeed was generated by the same invocation.
400Examples:[code]
401 apio api get-boards # Write to stdout
402 apio api get-boards -o apio.json # Write to a file[/code]
403"""
406@click.command(
407 name="get-boards",
408 cls=ApioCommand,
409 short_help="Retrieve boards information.",
410 help=APIO_API_GET_BOARDS_HELP,
411)
412@timestamp_option
413@output_option
414@options.force_option_gen(short_help="Overwrite output file.")
415def _get_boards_cli(
416 *,
417 # Options
418 timestamp: str,
419 output: str,
420 force: bool,
421):
422 """Implements the 'apio apio get-boards' command."""
424 # -- For now, the information is not in a project context. That may
425 # -- change in the future.
426 apio_ctx = ApioContext(
427 project_policy=ProjectPolicy.NO_PROJECT,
428 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
429 packages_policy=PackagesPolicy.ENSURE_PACKAGES,
430 )
432 # -- The top dict that we will emit as json.
433 top_dict = {}
435 # -- Append user timestamp if specified.
436 if timestamp: 436 ↛ 440line 436 didn't jump to line 440 because the condition on line 436 was always true
437 top_dict["timestamp"] = timestamp
439 # -- Generate the boards section.
440 section = {}
441 for board_id, board_info in apio_ctx.boards.items():
442 # -- The board output dict.
443 board_dict = {}
445 # -- Add board description
446 board_dict["description"] = board_info.get("description", None)
448 # -- Add board's fpga information.
449 fpga_id = board_info.get("fpga-id", None)
450 fpga_info = apio_ctx.fpgas.get(fpga_id, {})
451 assert "id" not in fpga_info
452 fpga_dict = {"id": fpga_id}
453 fpga_dict.update(fpga_info)
454 board_dict["fpga"] = fpga_dict
456 # -- Add board's programmer information.
457 programmer_dict = {}
458 programmer_id = board_info.get("programmer", {}).get("id", None)
459 programmer_dict["id"] = programmer_id
460 board_dict["programmer"] = programmer_dict
462 # -- Add the board to the boards dict.
463 section[board_id] = board_dict
465 top_dict["boards"] = section
467 # -- Write out
468 write_as_json_doc(top_dict, output, force)
471# ------ apio api get-fpgas
474# -- Text in the rich-text format of the python rich library.
475APIO_API_GET_FPGAS_HELP = """
476The command 'apio api get-fpgas' exports apio FPGAss information as a \
477JSON document.
479The optional flag '--timestamp' allows the caller to embed in the JSON \
480document a known timestamp that allows to verify that the JSON document \
481was indeed was generated by the same invocation.
483Examples:[code]
484 apio api get-fpgas # Write to stdout
485 apio api get-fpgas -o apio.json # Write to a file[/code]
486"""
489@click.command(
490 name="get-fpgas",
491 cls=ApioCommand,
492 short_help="Retrieve FPGAs information.",
493 help=APIO_API_GET_FPGAS_HELP,
494)
495@timestamp_option
496@output_option
497@options.force_option_gen(short_help="Overwrite output file.")
498def _get_fpgas_cli(
499 *,
500 # Options
501 timestamp: str,
502 output: str,
503 force: bool,
504):
505 """Implements the 'apio apio get-fpgas' command."""
507 # -- For now, the information is not in a project context. That may
508 # -- change in the future.
509 apio_ctx = ApioContext(
510 project_policy=ProjectPolicy.NO_PROJECT,
511 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
512 packages_policy=PackagesPolicy.ENSURE_PACKAGES,
513 )
515 # -- The top dict that we will emit as json.
516 top_dict = {}
518 # -- Append user timestamp if specified.
519 if timestamp: 519 ↛ 523line 519 didn't jump to line 523 because the condition on line 519 was always true
520 top_dict["timestamp"] = timestamp
522 # -- Generate the fpgas section
523 section = {}
524 for fpga_id, fpga_info in apio_ctx.fpgas.items():
525 section[fpga_id] = fpga_info
527 top_dict["fpgas"] = section
529 # -- Write out
530 write_as_json_doc(top_dict, output, force)
533# ------ apio api get-programmers
536# -- Text in the rich-text format of the python rich library.
537APIO_API_GET_PROGRAMMERS_HELP = """
538The command 'apio api get-programmers' exports apio programmers information \
539as a JSON document.
541The optional flag '--timestamp' allows the caller to embed in the JSON \
542document a known timestamp that allows to verify that the JSON document \
543was indeed was generated by the same invocation.
545Examples:[code]
546 apio api get-programmers # Write to stdout
547 apio api get-programmers -o apio.json # Write to a file[/code]
548"""
551@click.command(
552 name="get-programmers",
553 cls=ApioCommand,
554 short_help="Retrieve programmers information.",
555 help=APIO_API_GET_PROGRAMMERS_HELP,
556)
557@timestamp_option
558@output_option
559@options.force_option_gen(short_help="Overwrite output file.")
560def _get_programmers_cli(
561 *,
562 # Options
563 timestamp: str,
564 output: str,
565 force: bool,
566):
567 """Implements the 'apio apio get-programmers' command."""
569 # -- For now, the information is not in a project context. That may
570 # -- change in the future.
571 apio_ctx = ApioContext(
572 project_policy=ProjectPolicy.NO_PROJECT,
573 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
574 packages_policy=PackagesPolicy.ENSURE_PACKAGES,
575 )
577 # -- The top dict that we will emit as json.
578 top_dict = {}
580 # -- Append user timestamp if specified.
581 if timestamp: 581 ↛ 585line 581 didn't jump to line 585 because the condition on line 581 was always true
582 top_dict["timestamp"] = timestamp
584 # -- Generate the 'programmers' section.
585 section = {}
586 for programmer_id, programmer_info in apio_ctx.programmers.items():
587 section[programmer_id] = programmer_info
589 top_dict["programmers"] = section
591 # -- Write out
592 write_as_json_doc(top_dict, output, force)
595# ------ apio api get-examples
598# -- Text in the rich-text format of the python rich library.
599APIO_API_GET_EXAMPLES_HELP = """
600The command 'apio api get-examples' exports apio examples information as a \
601JSON document.
603The optional flag '--timestamp' allows the caller to embed in the JSON \
604document a known timestamp that allows to verify that the JSON document \
605was indeed was generated by the same invocation.
607Examples:[code]
608 apio api get-examples # Write to stdout
609 apio api get-examples -o apio.json # Write to a file[/code]
610"""
613@click.command(
614 name="get-examples",
615 cls=ApioCommand,
616 short_help="Retrieve examples information.",
617 help=APIO_API_GET_EXAMPLES_HELP,
618)
619@timestamp_option
620@output_option
621@options.force_option_gen(short_help="Overwrite output file.")
622def _get_examples_cli(
623 *,
624 # Options
625 timestamp: str,
626 output: str,
627 force: bool,
628):
629 """Implements the 'apio apio get-examples' command."""
631 # -- For now, the information is not in a project context. That may
632 # -- change in the future.
633 apio_ctx = ApioContext(
634 project_policy=ProjectPolicy.NO_PROJECT,
635 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
636 packages_policy=PackagesPolicy.ENSURE_PACKAGES,
637 )
639 # -- Get examples infos.
640 examples: List[ExampleInfo] = Examples(apio_ctx).get_examples_infos()
642 # -- Group examples by boards
643 boards_examples: Dict[str, List[ExampleInfo]] = {}
644 for example in examples:
645 board_examples = boards_examples.get(example.board_id, [])
646 board_examples.append(example)
647 boards_examples[example.board_id] = board_examples
649 # -- The top dict that we will emit as json.
650 top_dict = {}
652 # -- Append user timestamp if specified.
653 if timestamp: 653 ↛ 657line 653 didn't jump to line 657 because the condition on line 653 was always true
654 top_dict["timestamp"] = timestamp
656 # -- Generate the 'examples' section.
657 section = {}
658 for board, board_examples in boards_examples.items():
659 board_dict = {}
660 # -- Generate board examples
661 for example_info in board_examples:
662 example_dict = {}
663 example_dict["description"] = example_info.description
664 board_dict[example_info.example_name] = example_dict
666 section[board] = board_dict
668 top_dict["examples"] = section
670 # -- Write out
671 write_as_json_doc(top_dict, output, force)
674# ------ apio api get-commands
677@dataclass(frozen=True)
678class CmdInfo:
679 """Represents the information of a single apio command."""
681 name: str
682 path: List[str]
683 cli: click.Command
684 children: List[Self]
687def scan_children(cmd_cli) -> Dict:
688 """Return a dict describing this command subtree."""
689 result = {}
691 # -- Sanity check
692 assert isinstance(result, dict), type(result)
694 # -- If this is a simple command, it has no sub commands.
695 if isinstance(cmd_cli, ApioCommand):
696 return result
698 # -- Here we have a group and it should have at least one sub command.
699 assert isinstance(cmd_cli, ApioGroup), type(cmd_cli)
700 subgroups: List[ApioSubgroup] = cmd_cli.subgroups
702 # -- Create the dict for the command subgroups.
703 subcommands_dict = {}
704 result["commands"] = subcommands_dict
706 # -- Iterate the subgroups and populate them. We flaten the subcommands
707 # -- group into a single list of commands.
708 for subgroup in subgroups:
709 assert isinstance(subgroup, ApioSubgroup), type(subgroup)
710 assert isinstance(subgroup.title, str), type(subgroup.title)
711 for subcommand in subgroup.commands:
712 subcommand_dict = scan_children(subcommand)
713 subcommands_dict[subcommand.name] = subcommand_dict
715 # -- All done ok.
716 return result
719# -- Text in the rich-text format of the python rich library.
720APIO_API_GET_COMMANDS_HELP = """
721The command 'apio api get-commands' exports apio command structure \
722of Apio as a JSON doc. This is used by various tools such as
723documentation generators and tests.
725The optional flag '--timestamp' allows the caller to embed in the JSON \
726document a known timestamp that allows to verify that the JSON document \
727was indeed was generated by the same invocation.
729Examples:[code]
730 apio api get-commands # Write to stdout
731 apio api get-commands -o apio.json # Write to a file[/code]
732"""
735@click.command(
736 name="get-commands",
737 cls=ApioCommand,
738 short_help="Retrieve apio commands information.",
739 help=APIO_API_GET_COMMANDS_HELP,
740)
741@click.pass_context
742@timestamp_option
743@output_option
744@options.force_option_gen(short_help="Overwrite output file.")
745def _get_commands_cli(
746 # Click context
747 cmd_ctx: click.Context,
748 *,
749 # Options
750 timestamp: str,
751 output: str,
752 force: bool,
753):
754 """Implements the 'apio apio get-commands' command."""
756 # -- Find the top cli which is the "apio" command. Would access it
757 # -- directly but it would create a circular python import.
758 ctx = cast(ApioCmdContext, cmd_ctx)
759 while ctx.parent:
760 ctx = ctx.parent
761 assert isinstance(ctx, ApioCmdContext), type(ctx)
762 top_cli = ctx.command
763 assert top_cli.name == "apio", top_cli
765 # -- This initializes the console, print active env vars, etc.
766 ApioContext(
767 project_policy=ProjectPolicy.NO_PROJECT,
768 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
769 packages_policy=PackagesPolicy.ENSURE_PACKAGES,
770 )
772 # -- The top dict that we will emit as json.
773 top_dict = {}
775 # -- Append user timestamp if specified.
776 if timestamp: 776 ↛ 779line 776 didn't jump to line 779 because the condition on line 776 was always true
777 top_dict["timestamp"] = timestamp
779 section_dict = {}
780 section_dict["apio"] = scan_children(top_cli)
781 top_dict["commands"] = section_dict
783 # -- Write out
784 write_as_json_doc(top_dict, output, force)
787# ------ apio api scan-devices
790# -- Text in the rich-text format of the python rich library.
791APIO_API_SCAN_DEVICES_HELP = """
792The command 'apio api scan-devices' scans and report the available usb and \
793serial devices.
795The optional flag '--timestamp' allows the caller to embed in the JSON \
796document a known timestamp that allows to verify that the JSON document \
797was indeed was generated by the same invocation.
799Examples:[code]
800 apio api scan-devices # Write to stdout
801 apio api scan-devices -o apio.json # Write to a file[/code]
802"""
805@click.command(
806 name="scan-devices",
807 cls=ApioCommand,
808 short_help="Scan and report available devices.",
809 help=APIO_API_SCAN_DEVICES_HELP,
810)
811@timestamp_option
812@output_option
813@options.force_option_gen(short_help="Overwrite output file.")
814def _scan_devices_cli(
815 *,
816 # Options
817 timestamp: str,
818 output: str,
819 force: bool,
820):
821 """Implements the 'apio apio scan-devices' command."""
823 # -- For now, the information is not in a project context. That may
824 # -- change in the future. We need the config since we use libusb from
825 # -- the packages.
826 apio_ctx = ApioContext(
827 project_policy=ProjectPolicy.NO_PROJECT,
828 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
829 packages_policy=PackagesPolicy.ENSURE_PACKAGES,
830 )
832 # -- The top dict that we will emit as json.
833 top_dict = {}
835 # -- Append user timestamp if specified.
836 if timestamp: 836 ↛ 842line 836 didn't jump to line 842 because the condition on line 836 was always true
837 top_dict["timestamp"] = timestamp
839 # -- We need the packages for the 'libusb' backend.
840 # packages.install_missing_packages_on_the_fly(apio_ctx.packages_context)
842 usb_devices: List[UsbDevice] = usb_util.scan_usb_devices(apio_ctx)
844 # -- Scan and report usb devices.
845 section = []
846 for device in usb_devices: 846 ↛ 847line 846 didn't jump to line 847 because the loop on line 846 never started
847 dev = {}
848 dev["vid"] = device.vendor_id
849 dev["pid"] = device.product_id
850 dev["bus"] = device.bus
851 dev["device"] = device.device
852 dev["manufacturer"] = device.manufacturer
853 dev["product"] = device.product
854 dev["serial-number"] = device.serial_number
855 dev["device_type"] = device.device_type
857 section.append(dev)
859 top_dict["usb-devices"] = section
861 # -- Scan and report serial devices.
862 serial_devices: List[SerialDevice] = serial_util.scan_serial_devices(
863 apio_ctx
864 )
866 section = []
867 for device in serial_devices: 867 ↛ 868line 867 didn't jump to line 868 because the loop on line 867 never started
868 dev = {}
869 dev["port"] = device.port
870 dev["port-name"] = device.port_name
871 dev["vendor-id"] = device.vendor_id
872 dev["product-id"] = device.product_id
873 dev["manufacturer"] = device.manufacturer
874 dev["product"] = device.product
875 dev["serial-number"] = device.serial_number
876 dev["device-type"] = device.device_type
878 section.append(dev)
880 top_dict["serial-devices"] = section
882 # -- Write out
883 write_as_json_doc(top_dict, output, force)
886# ------ apio api echo
889# -- Text in the rich-text format of the python rich library.
890APIO_API_ECHO_HELP = """
891The command 'apio api echo' allows external programs such as the Apio VS Code \
892extension to print a short message in a format that is consistent with \
893that Apio theme that is currently selected in the user preferences.
895The required option '--style' should have one of these values: OK, \
896INFO, WARNING, ERROR, TITLE, EMPH1, EMPH2, or EMPH3. The style colors can \
897be viewed with the command 'apio info themes'.
899Examples:[code]
900 apio api echo -t "Hello world", -s "INFO"
901 apio api echo -t "Task completed successfully", -s "OK"
902 apio api echo -t "Task failed", -s "ERROR"[/code]
903"""
905# -- Supported style names
906STYLES = {
907 "OK": SUCCESS,
908 "INFO": INFO,
909 "WARNING": WARNING,
910 "ERROR": ERROR,
911 "TITLE": TITLE,
912 "EMPH1": EMPH1,
913 "EMPH2": EMPH2,
914 "EMPH3": EMPH3,
915}
917text_option = click.option(
918 "text", # Var name.
919 "-t",
920 "--text",
921 type=str,
922 metavar="MESSAGE",
923 required=True,
924 help="Set message to echo.",
925 cls=cmd_util.ApioOption,
926)
929style_option = click.option(
930 "style", # Var name.
931 "-s",
932 "--style",
933 type=click.Choice(STYLES.keys()),
934 metavar="STYLE",
935 required=True,
936 help="Set style to use.",
937 cls=cmd_util.ApioOption,
938)
941@click.command(
942 name="echo",
943 cls=ApioCommand,
944 short_help="Print a message in given format.",
945 help=APIO_API_ECHO_HELP,
946)
947@text_option
948@style_option
949def _echo_cli(
950 *,
951 # Options
952 text: str,
953 style: str,
954):
955 """Implements the 'apio apio echo command."""
957 # -- Instanatiate Apio context, project and packages are not needed.
958 _ = ApioContext(
959 project_policy=ProjectPolicy.NO_PROJECT,
960 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
961 packages_policy=PackagesPolicy.ENSURE_PACKAGES,
962 )
964 cout(text, style=STYLES[style])
967# ------ apio apio
969# -- Text in the rich-text format of the python rich library.
970APIO_API_HELP = """
971The command group 'apio api' contains subcommands that that are intended \
972to be used by tools and programs such as icestudio, rather than being used \
973directly by users.
974"""
976# -- We have only a single group with the title 'Subcommands'.
977SUBGROUPS = [
978 ApioSubgroup(
979 "Subcommands",
980 [
981 _get_system_cli,
982 _get_project_cli,
983 _get_build_report_cli,
984 _get_boards_cli,
985 _get_fpgas_cli,
986 _get_programmers_cli,
987 _get_examples_cli,
988 _get_commands_cli,
989 _scan_devices_cli,
990 _echo_cli,
991 ],
992 )
993]
996@click.command(
997 name="api",
998 cls=ApioGroup,
999 subgroups=SUBGROUPS,
1000 short_help="Apio programmatic interface.",
1001 help=APIO_API_HELP,
1002)
1003def cli():
1004 """Implements the 'apio apio' command group."""
1006 # pass