Coverage for apio/commands/apio_api.py: 90%
331 statements
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-23 03:53 +0000
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-23 03:53 +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 Self, cast, Any
15from dataclasses import dataclass
16import json
17from pathlib import Path
18import click
19from apio.commands import options
20from apio.managers.examples import Examples, ExampleInfo
21from apio.common.apio_console import cout, fatal_error
22from apio.common import proto_util
23from apio.common.debug_util import is_under_vscode_debugger
24from apio.common.common_util import get_project_source_files
25from apio.utils import (
26 cmd_util,
27 usb_util,
28 serial_util,
29 util,
30 apio_platforms,
31 env_options,
32)
33from apio.utils.usb_util import UsbDevice
34from apio.utils.serial_util import SerialDevice
35from apio.common.apio_styles import (
36 INFO,
37 ERROR,
38 SUCCESS,
39 WARNING,
40 EMPH1,
41 EMPH2,
42 EMPH3,
43 TITLE,
44)
45from apio.apio_context import (
46 ApioContext,
47 PackagesPolicy,
48 ProjectPolicy,
49 RemoteConfigPolicy,
50)
51from apio.utils.cmd_util import (
52 ApioGroup,
53 ApioSubgroup,
54 ApioCommand,
55 ApioCmdContext,
56)
57from apio.common import build_report
59timestamp_option = click.option(
60 "timestamp", # Var name.
61 "-t",
62 "--timestamp",
63 type=str,
64 metavar="text",
65 help="Set a user provided timestamp.",
66 cls=cmd_util.ApioOption,
67)
69output_option = click.option(
70 "output", # Var name.
71 "-o",
72 "--output",
73 type=str,
74 metavar="file-name",
75 help="Set output file.",
76 cls=cmd_util.ApioOption,
77)
80def write_as_json_doc(top_dict: dict, output_flag: str, force_flag: bool):
81 """A common function to write a dict as a JSON doc."""
82 # -- Format the top dict as json text.
83 text = json.dumps(top_dict, indent=2)
85 if output_flag:
86 # -- Output the json text to a user specified file.
87 output_path = Path(output_flag)
89 if output_path.is_dir(): 89 ↛ 90line 89 didn't jump to line 90 because the condition on line 89 was never true
90 fatal_error(f"The output path '{output_path}' is a directory.")
92 if output_path.exists() and not force_flag: 92 ↛ 93line 92 didn't jump to line 93 because the condition on line 92 was never true
93 fatal_error(
94 f"The file already exists '{output_path}'.",
95 info="Use the --force option to allow overwriting.",
96 )
98 # -- if there file path contains a parent dir, make
99 # -- sure it exists. If output_flag is just a file name such
100 # -- as 'foo.json', we don nothing.
101 dirname = os.path.dirname(output_flag)
102 if dirname: 102 ↛ 106line 102 didn't jump to line 106 because the condition on line 102 was always true
103 os.makedirs(dirname, exist_ok=True)
105 # -- Write to file.
106 with open(output_flag, "w", encoding="utf-8") as f:
107 f.write(text)
108 else:
109 # -- Output the json text to stdout.
110 print(text, file=sys.stdout)
113# ------ apio api get-system
116# -- Text in the rich-text format of the python rich library.
117APIO_API_GET_SYSTEM_HELP = """
118The command 'apio api get-system' exports information about apio and \
119the underlying system as a JSON foc. It is similar to the command \
120'apio info system' which is intended for human consumption.
122The optional flag '--timestamp' allows the caller to embed in the JSON \
123document a known timestamp that allows to verify that the JSON document \
124was indeed was generated by the same invocation.
126Examples:[code]
127 apio api get-system # Write to stdout
128 apio api get-system -o apio.json # Write to a file[/code]
129"""
132@click.command(
133 name="get-system",
134 cls=ApioCommand,
135 short_help="Retrieve apio and system information.",
136 help=APIO_API_GET_SYSTEM_HELP,
137)
138# @click.pass_context
139@timestamp_option
140@output_option
141@options.force_option_gen(short_help="Overwrite output file.")
142def _get_system_cli(
143 *,
144 # Options
145 timestamp: str,
146 output: str,
147 force: bool,
148):
149 """Implements the 'apio apio get-system' command."""
151 apio_ctx = ApioContext(
152 project_policy=ProjectPolicy.NO_PROJECT,
153 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
154 packages_policy=PackagesPolicy.ENSURE_PACKAGES,
155 )
157 platform = apio_ctx.platform
159 # -- The top dict that we will emit as json.
160 top_dict: dict[str, Any] = {}
162 # -- Append user timestamp if specified.
163 if timestamp: 163 ↛ 166line 163 didn't jump to line 166 because the condition on line 163 was always true
164 top_dict["timestamp"] = timestamp
166 section_dict: dict[str, Any] = {}
168 # -- Add fields.
169 section_dict["apio-cli-version"] = util.get_apio_version_str()
170 section_dict["release-info"] = util.get_apio_release_info()
171 section_dict["yosys-release-tag"] = (
172 apio_ctx.package_manager.get_yosys_release_tag()
173 )
174 section_dict["python-version"] = util.get_python_version()
175 section_dict["python-executable"] = sys.executable
176 section_dict["platform-info"] = apio_platforms.get_system_info()
177 section_dict["platform-id"] = platform.id
178 section_dict["is-darwin"] = platform.is_darwin
179 section_dict["is-linux"] = platform.is_linux
180 section_dict["is-windows"] = platform.is_windows
181 section_dict["scons-shell-id"] = apio_ctx.scons_shell_id
182 section_dict["vscode-debugger"] = str(is_under_vscode_debugger()).lower()
183 section_dict["pyinstaller"] = str(util.is_pyinstaller_app()).lower()
184 section_dict["apio-python_package"] = str(
185 util.get_path_in_apio_package("")
186 )
187 section_dict["apio-home-dir"] = str(apio_ctx.apio_home_dir)
188 section_dict["apio-packages-dir"] = str(apio_ctx.apio_packages_dir)
189 section_dict["remote-config-url"] = (
190 apio_ctx.remote_config.remote_config_url
191 )
192 section_dict["verible-formatter"] = str(
193 apio_ctx.apio_packages_dir / "verible/bin/verible-verilog-format"
194 )
195 section_dict["verible-language-server"] = str(
196 apio_ctx.apio_packages_dir / "verible/bin/verible-verilog-ls"
197 )
199 # -- Include all supported apio env vars, defined and undefined.
200 apio_vars_dict = {
201 var: env_options.get(var, None) for var in env_options.get_all()
202 }
203 section_dict["apio-env-vars"] = apio_vars_dict
205 # -- Add section
206 top_dict["system"] = section_dict
208 # -- Write out
209 write_as_json_doc(top_dict, output, force)
212# ------ apio api get-build-report
215# -- Text in the rich-text format of the python rich library.
216APIO_API_GET_BUILD_REPORT_HELP = """
217The command 'apio api get-build-report' provides utilization and max \
218clock information from a built project. The information is extracted \
219from the file 'hardware.pnr' that is generated by Apio when building \
220the project.
222The optional flag '--timestamp' allows the caller to embed in the JSON \
223document a known timestamp that allows to verify that the JSON document \
224was indeed was generated by the same invocation.
226Examples:[code]
227 apio api get-build-report # Report for default env
228 apio api get-build-report -e env1 # Report for specified env
229 apio api get-build-report -p foo/bar # Project in another dir
230 apio api get-build-report -o apio.json # Write to a file[/code]
231"""
234@click.command(
235 name="get-build-report",
236 cls=ApioCommand,
237 short_help="Get project build information.",
238 help=APIO_API_GET_BUILD_REPORT_HELP,
239)
240# @click.pass_context
241@options.env_option_gen()
242@options.project_dir_option
243@timestamp_option
244@output_option
245@options.force_option_gen(short_help="Overwrite output file.")
246def _get_build_report_cli(
247 *,
248 # Options
249 env: str,
250 project_dir: Path | None,
251 timestamp: str,
252 output: str,
253 force: bool,
254):
255 """Implements the 'apio apio get-build-report' command."""
257 apio_ctx = ApioContext(
258 project_policy=ProjectPolicy.PROJECT_REQUIRED,
259 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
260 packages_policy=PackagesPolicy.ENSURE_PACKAGES,
261 project_dir_arg=project_dir,
262 env_arg=env,
263 report_env=False,
264 )
266 # -- Change to the project's folder.
267 os.chdir(apio_ctx.project_dir)
269 # -- The build process generates this report file.
270 pnr_json_file = apio_ctx.env_build_path / "hardware.pnr"
272 # -- Read the report
273 report = build_report.read_build_report(pnr_json_file)
275 # -- The top dict that we will emit as json.
276 top_dict: dict[str, Any] = {}
278 # -- Append user timestamp if specified.
279 if timestamp: 279 ↛ 282line 279 didn't jump to line 282 because the condition on line 279 was always true
280 top_dict["timestamp"] = timestamp
282 section_dict: dict[str, Any] = {}
283 section_dict["env"] = apio_ctx.project.env_name
285 resources_dict: dict[str, Any] = {}
286 for res in report.resources:
287 resources_dict[res.name] = {
288 "used": res.used,
289 "available": res.available,
290 "percentage": res.percentage,
291 }
293 section_dict["resources"] = resources_dict
295 clocks_dict: dict[str, Any] = {}
296 for clk in report.clocks:
297 clocks_dict[clk.name] = {"fmax_mhz": clk.fmax_mhz}
299 section_dict["clocks"] = clocks_dict
301 # -- Add section
302 top_dict["build-report"] = section_dict
304 # -- Write out
305 write_as_json_doc(top_dict, output, force)
308# ------ apio api get-project
311# -- Text in the rich-text format of the python rich library.
312APIO_API_GET_PROJECT_HELP = """
313The command 'apio api get-project' exports information about an Apio
314project as a JSON foc.
316The optional flag '--timestamp' allows the caller to embed in the JSON \
317document a known timestamp that allows to verify that the JSON document \
318was indeed was generated by the same invocation.
320Examples:[code]
321 apio api get-project # Report default env
322 apio api get-project -e env1 # Report specified env
323 apio api get-project -p foo/bar # Project in another dir
324 apio api get-project -o apio.json # Write to a file[/code]
325"""
328@click.command(
329 name="get-project",
330 cls=ApioCommand,
331 short_help="Get project information.",
332 help=APIO_API_GET_PROJECT_HELP,
333)
334# @click.pass_context
335@options.env_option_gen()
336@options.project_dir_option
337@timestamp_option
338@output_option
339@options.force_option_gen(short_help="Overwrite output file.")
340def _get_project_cli(
341 *,
342 # Options
343 env: str,
344 project_dir: Path | None,
345 timestamp: str,
346 output: str,
347 force: bool,
348):
349 """Implements the 'apio apio get-project' command."""
351 apio_ctx = ApioContext(
352 project_policy=ProjectPolicy.PROJECT_REQUIRED,
353 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
354 packages_policy=PackagesPolicy.ENSURE_PACKAGES,
355 project_dir_arg=project_dir,
356 env_arg=env,
357 report_env=False,
358 )
359 assert apio_ctx.definitions is not None
361 # -- Change to the project's folder for function such as
362 # -- get_project_source_files() which expects to run at the project root.
363 os.chdir(apio_ctx.project_dir)
365 # -- The top dict that we will emit as json.
366 top_dict: dict[str, Any] = {}
368 # -- Append user timestamp if specified.
369 if timestamp: 369 ↛ 372line 369 didn't jump to line 372 because the condition on line 369 was always true
370 top_dict["timestamp"] = timestamp
372 section_dict: dict[str, Any] = {}
374 active_env_dict: dict[str, Any] = {}
375 active_env_dict["name"] = apio_ctx.project.env_name
376 active_env_dict["options"] = apio_ctx.project.env_options
377 section_dict["active-env"] = active_env_dict
379 section_dict["envs"] = apio_ctx.project.env_names
381 synth_srcs, test_srcs = get_project_source_files()
382 section_dict["synth-files"] = synth_srcs
383 section_dict["test-benches"] = test_srcs
385 pr = apio_ctx.project_resources
387 board_dict = {
388 "id": pr.board_id,
389 "is-custom": apio_ctx.definitions.is_custom_board(pr.board_id),
390 }
391 board_dict["definition"] = proto_util.proto_to_json_dict(
392 pr.board_definition
393 )
394 section_dict["board"] = board_dict
396 fpga_dict = {
397 "id": pr.fpga_id,
398 "is-custom": apio_ctx.definitions.is_custom_fpga(pr.fpga_id),
399 }
400 fpga_dict["definition"] = proto_util.proto_to_json_dict(pr.fpga_definition)
401 section_dict["fpga"] = fpga_dict
403 programmer_dict = {
404 "id": pr.programmer_id,
405 "is-custom": apio_ctx.definitions.is_custom_programmer(
406 pr.programmer_id
407 ),
408 }
409 programmer_dict["definition"] = proto_util.proto_to_json_dict(
410 pr.programmer_definition
411 )
412 section_dict["programmer"] = programmer_dict
414 # -- Add section
415 top_dict["project"] = section_dict
417 # -- Write out
418 write_as_json_doc(top_dict, output, force)
421# ------ apio api get-boards
424# -- Text in the rich-text format of the python rich library.
425APIO_API_GET_BOARDS_HELP = """
426The command 'apio api get-boards' exports apio boards information as a \
427JSON document.
429The optional flag '--timestamp' allows the caller to embed in the JSON \
430document a known timestamp that allows to verify that the JSON document \
431was indeed was generated by the same invocation.
433Examples:[code]
434 apio api get-boards # Write to stdout
435 apio api get-boards -o apio.json # Write to a file[/code]
436"""
439@click.command(
440 name="get-boards",
441 cls=ApioCommand,
442 short_help="Retrieve boards information.",
443 help=APIO_API_GET_BOARDS_HELP,
444)
445@timestamp_option
446@output_option
447@options.force_option_gen(short_help="Overwrite output file.")
448def _get_boards_cli(
449 *,
450 # Options
451 timestamp: str,
452 output: str,
453 force: bool,
454):
455 """Implements the 'apio apio get-boards' command."""
457 # -- For now, the information is not in a project context. That may
458 # -- change in the future.
459 apio_ctx = ApioContext(
460 project_policy=ProjectPolicy.NO_PROJECT,
461 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
462 packages_policy=PackagesPolicy.ENSURE_PACKAGES,
463 )
464 assert apio_ctx.definitions is not None
466 # -- The top dict that we will emit as json.
467 top_dict: dict[str, Any] = {}
469 # -- Append user timestamp if specified.
470 if timestamp: 470 ↛ 474line 470 didn't jump to line 474 because the condition on line 470 was always true
471 top_dict["timestamp"] = timestamp
473 # -- Generate the boards section.
474 section: dict[str, Any] = {}
475 for board_id, board_definition in apio_ctx.definitions.boards.items():
476 # -- The board output dict.
477 board_dict: dict[str, Any] = {}
479 # -- We assume that these proto fields are requires and therefore
480 # -- must exist.
481 proto_util.check_is_required(
482 board_definition,
483 "description",
484 "fpga_id",
485 "programmer",
486 "programmer.id",
487 )
489 # -- Add board description
490 board_dict["description"] = board_definition.description
492 # -- Add board's fpga information.
493 fpga_id = board_definition.fpga_id
494 fpga_definition = apio_ctx.definitions.fpgas[fpga_id]
495 fpga_dict = {"id": fpga_id}
496 fpga_dict.update(proto_util.proto_to_json_dict(fpga_definition))
497 board_dict["fpga"] = fpga_dict
499 # -- Add board's programmer information.
500 programmer_dict = {}
501 programmer_id = board_definition.programmer.id
502 programmer_dict["id"] = programmer_id
503 board_dict["programmer"] = programmer_dict
505 # -- Add the board to the boards dict.
506 section[board_id] = board_dict
508 top_dict["boards"] = section
510 # -- Write out
511 write_as_json_doc(top_dict, output, force)
514# ------ apio api get-fpgas
517# -- Text in the rich-text format of the python rich library.
518APIO_API_GET_FPGAS_HELP = """
519The command 'apio api get-fpgas' exports apio FPGAss information as a \
520JSON document.
522The optional flag '--timestamp' allows the caller to embed in the JSON \
523document a known timestamp that allows to verify that the JSON document \
524was indeed was generated by the same invocation.
526Examples:[code]
527 apio api get-fpgas # Write to stdout
528 apio api get-fpgas -o apio.json # Write to a file[/code]
529"""
532@click.command(
533 name="get-fpgas",
534 cls=ApioCommand,
535 short_help="Retrieve FPGAs information.",
536 help=APIO_API_GET_FPGAS_HELP,
537)
538@timestamp_option
539@output_option
540@options.force_option_gen(short_help="Overwrite output file.")
541def _get_fpgas_cli(
542 *,
543 # Options
544 timestamp: str,
545 output: str,
546 force: bool,
547):
548 """Implements the 'apio apio get-fpgas' command."""
550 # -- For now, the information is not in a project context. That may
551 # -- change in the future.
552 apio_ctx = ApioContext(
553 project_policy=ProjectPolicy.NO_PROJECT,
554 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
555 packages_policy=PackagesPolicy.ENSURE_PACKAGES,
556 )
557 assert apio_ctx.definitions is not None
559 # -- The top dict that we will emit as json.
560 top_dict: dict[str, Any] = {}
562 # -- Append user timestamp if specified.
563 if timestamp: 563 ↛ 567line 563 didn't jump to line 567 because the condition on line 563 was always true
564 top_dict["timestamp"] = timestamp
566 # -- Generate the fpgas section
567 section: dict[str, Any] = {}
568 for fpga_id, fpga_definition in apio_ctx.definitions.fpgas.items():
569 section[fpga_id] = proto_util.proto_to_json_dict(fpga_definition)
571 top_dict["fpgas"] = section
573 # -- Write out
574 write_as_json_doc(top_dict, output, force)
577# ------ apio api get-programmers
580# -- Text in the rich-text format of the python rich library.
581APIO_API_GET_PROGRAMMERS_HELP = """
582The command 'apio api get-programmers' exports apio programmers information \
583as a JSON document.
585The optional flag '--timestamp' allows the caller to embed in the JSON \
586document a known timestamp that allows to verify that the JSON document \
587was indeed was generated by the same invocation.
589Examples:[code]
590 apio api get-programmers # Write to stdout
591 apio api get-programmers -o apio.json # Write to a file[/code]
592"""
595@click.command(
596 name="get-programmers",
597 cls=ApioCommand,
598 short_help="Retrieve programmers information.",
599 help=APIO_API_GET_PROGRAMMERS_HELP,
600)
601@timestamp_option
602@output_option
603@options.force_option_gen(short_help="Overwrite output file.")
604def _get_programmers_cli(
605 *,
606 # Options
607 timestamp: str,
608 output: str,
609 force: bool,
610):
611 """Implements the 'apio apio get-programmers' command."""
613 # -- For now, the information is not in a project context. That may
614 # -- change in the future.
615 apio_ctx = ApioContext(
616 project_policy=ProjectPolicy.NO_PROJECT,
617 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
618 packages_policy=PackagesPolicy.ENSURE_PACKAGES,
619 )
620 assert apio_ctx.definitions is not None
622 # -- The top dict that we will emit as json.
623 top_dict: dict[str, Any] = {}
625 # -- Append user timestamp if specified.
626 if timestamp: 626 ↛ 630line 626 didn't jump to line 630 because the condition on line 626 was always true
627 top_dict["timestamp"] = timestamp
629 # -- Generate the 'programmers' section.
630 section: dict[str, Any] = {}
631 for (
632 programmer_id,
633 programmer_definition,
634 ) in apio_ctx.definitions.programmers.items():
635 section[programmer_id] = proto_util.proto_to_json_dict(
636 programmer_definition
637 )
639 top_dict["programmers"] = section
641 # -- Write out
642 write_as_json_doc(top_dict, output, force)
645# ------ apio api get-examples
648# -- Text in the rich-text format of the python rich library.
649APIO_API_GET_EXAMPLES_HELP = """
650The command 'apio api get-examples' exports apio examples information as a \
651JSON document.
653The optional flag '--timestamp' allows the caller to embed in the JSON \
654document a known timestamp that allows to verify that the JSON document \
655was indeed was generated by the same invocation.
657Examples:[code]
658 apio api get-examples # Write to stdout
659 apio api get-examples -o apio.json # Write to a file[/code]
660"""
663@click.command(
664 name="get-examples",
665 cls=ApioCommand,
666 short_help="Retrieve examples information.",
667 help=APIO_API_GET_EXAMPLES_HELP,
668)
669@timestamp_option
670@output_option
671@options.force_option_gen(short_help="Overwrite output file.")
672def _get_examples_cli(
673 *,
674 # Options
675 timestamp: str,
676 output: str,
677 force: bool,
678):
679 """Implements the 'apio apio get-examples' command."""
681 # -- For now, the information is not in a project context. That may
682 # -- change in the future.
683 apio_ctx = ApioContext(
684 project_policy=ProjectPolicy.NO_PROJECT,
685 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
686 packages_policy=PackagesPolicy.ENSURE_PACKAGES,
687 )
689 # -- Get examples infos.
690 examples: list[ExampleInfo] = Examples(apio_ctx).get_examples_infos()
692 # -- Group examples by boards
693 boards_examples: dict[str, list[ExampleInfo]] = {}
694 for example in examples:
695 board_examples = boards_examples.get(example.board_id, [])
696 board_examples.append(example)
697 boards_examples[example.board_id] = board_examples
699 # -- The top dict that we will emit as json.
700 top_dict: dict[str, Any] = {}
702 # -- Append user timestamp if specified.
703 if timestamp: 703 ↛ 707line 703 didn't jump to line 707 because the condition on line 703 was always true
704 top_dict["timestamp"] = timestamp
706 # -- Generate the 'examples' section.
707 section: dict[str, Any] = {}
708 for board, board_examples in boards_examples.items():
709 board_dict = {}
710 # -- Generate board examples
711 for example_info in board_examples:
712 example_dict = {}
713 example_dict["description"] = example_info.description
714 board_dict[example_info.example_name] = example_dict
716 section[board] = board_dict
718 top_dict["examples"] = section
720 # -- Write out
721 write_as_json_doc(top_dict, output, force)
724# ------ apio api get-commands
727@dataclass(frozen=True)
728class CmdInfo:
729 """Represents the information of a single apio command."""
731 name: str
732 path: list[str]
733 cli: click.Command
734 children: list[Self]
737def scan_children(cmd_cli) -> dict:
738 """Return a dict describing this command subtree."""
739 result: dict[str, Any] = {}
741 # -- Sanity check
742 assert isinstance(result, dict), type(result)
744 # -- If this is a simple command, it has no sub commands.
745 if isinstance(cmd_cli, ApioCommand):
746 return result
748 # -- Here we have a group and it should have at least one sub command.
749 assert isinstance(cmd_cli, ApioGroup), type(cmd_cli)
750 subgroups: list[ApioSubgroup] = cmd_cli.subgroups
752 # -- Create the dict for the command subgroups.
753 subcommands_dict: dict[str, Any] = {}
754 result["commands"] = subcommands_dict
756 # -- Iterate the subgroups and populate them. We flaten the subcommands
757 # -- group into a single list of commands.
758 for subgroup in subgroups:
759 assert isinstance(subgroup, ApioSubgroup), type(subgroup)
760 assert isinstance(subgroup.title, str), type(subgroup.title)
761 for subcommand in subgroup.commands:
762 subcommand_dict = scan_children(subcommand)
763 assert subcommand.name is not None
764 subcommands_dict[subcommand.name] = subcommand_dict
766 # -- All done ok.
767 return result
770# -- Text in the rich-text format of the python rich library.
771APIO_API_GET_COMMANDS_HELP = """
772The command 'apio api get-commands' exports apio command structure \
773of Apio as a JSON doc. This is used by various tools such as
774documentation generators and tests.
776The optional flag '--timestamp' allows the caller to embed in the JSON \
777document a known timestamp that allows to verify that the JSON document \
778was indeed was generated by the same invocation.
780Examples:[code]
781 apio api get-commands # Write to stdout
782 apio api get-commands -o apio.json # Write to a file[/code]
783"""
786@click.command(
787 name="get-commands",
788 cls=ApioCommand,
789 short_help="Retrieve apio commands information.",
790 help=APIO_API_GET_COMMANDS_HELP,
791)
792@click.pass_context
793@timestamp_option
794@output_option
795@options.force_option_gen(short_help="Overwrite output file.")
796def _get_commands_cli(
797 # Click context
798 cmd_ctx: click.Context,
799 *,
800 # Options
801 timestamp: str,
802 output: str,
803 force: bool,
804):
805 """Implements the 'apio apio get-commands' command."""
807 # -- Find the top cli which is the "apio" command. Would access it
808 # -- directly but it would create a circular python import.
809 ctx = cast(ApioCmdContext, cmd_ctx)
810 while ctx.parent:
811 assert isinstance(ctx.parent, ApioCmdContext), type(ctx.parent)
812 ctx = ctx.parent
813 top_cli = ctx.command
814 assert top_cli.name == "apio", top_cli
816 # -- This initializes the console, print active env vars, etc.
817 ApioContext(
818 project_policy=ProjectPolicy.NO_PROJECT,
819 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
820 packages_policy=PackagesPolicy.ENSURE_PACKAGES,
821 )
823 # -- The top dict that we will emit as json.
824 top_dict: dict[str, Any] = {}
826 # -- Append user timestamp if specified.
827 if timestamp: 827 ↛ 830line 827 didn't jump to line 830 because the condition on line 827 was always true
828 top_dict["timestamp"] = timestamp
830 section_dict: dict[str, Any] = {}
831 section_dict["apio"] = scan_children(top_cli)
832 top_dict["commands"] = section_dict
834 # -- Write out
835 write_as_json_doc(top_dict, output, force)
838# ------ apio api get-packages
840# -- Text in the rich-text format of the python rich library.
841APIO_API_GET_PACKAGES_HELP = """
842The command 'apio api get-packages' exports information about \
843the apio packages on the current platform as a JSON foc. Note that \
844the set of apio packages used depends on the platform used as some \
845packages may be platform specific.
847The optional flag '--timestamp' allows the caller to embed in the JSON \
848document a known timestamp that allows to verify that the JSON document \
849was indeed was generated by the same invocation.
851Examples:[code]
852 apio api get-packages # Write to stdout
853 apio api get-packages -o apio.json # Write to a file[/code]
854"""
857@click.command(
858 name="get-packages",
859 cls=ApioCommand,
860 short_help="Retrieve apio packages information.",
861 help=APIO_API_GET_PACKAGES_HELP,
862)
863# @click.pass_context
864@timestamp_option
865@output_option
866@options.force_option_gen(short_help="Overwrite output file.")
867def _get_packages_cli(
868 *,
869 # Options
870 timestamp: str,
871 output: str,
872 force: bool,
873):
874 """Implements the 'apio apio get-packages' command."""
876 apio_ctx = ApioContext(
877 project_policy=ProjectPolicy.NO_PROJECT,
878 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
879 packages_policy=PackagesPolicy.ENSURE_PACKAGES,
880 )
882 package_manager = apio_ctx.package_manager
884 # -- The top dict that we will emit as json.
885 top_dict: dict[str, Any] = {}
887 # -- Append user timestamp if specified.
888 if timestamp: 888 ↛ 892line 888 didn't jump to line 892 because the condition on line 888 was always true
889 top_dict["timestamp"] = timestamp
891 # -- Packages section
892 section_dict: dict[str, Any] = {}
893 top_dict["packages"] = section_dict
895 for package_name in package_manager.required_packages:
896 metadata = package_manager.installed_packages[package_name]
897 config = apio_ctx.all_packages[package_name]
898 build_info = package_manager.read_package_build_info(package_name)
899 section_dict[package_name] = {
900 "description": config["description"],
901 "installation": metadata,
902 "build-info": build_info,
903 "env": config["env"],
904 }
906 # -- Write out
907 write_as_json_doc(top_dict, output, force)
910# ------ apio api scan-devices
913# -- Text in the rich-text format of the python rich library.
914APIO_API_SCAN_DEVICES_HELP = """
915The command 'apio api scan-devices' scans and report the available usb and \
916serial devices.
918The optional flag '--timestamp' allows the caller to embed in the JSON \
919document a known timestamp that allows to verify that the JSON document \
920was indeed was generated by the same invocation.
922Examples:[code]
923 apio api scan-devices # Write to stdout
924 apio api scan-devices -o apio.json # Write to a file[/code]
925"""
928@click.command(
929 name="scan-devices",
930 cls=ApioCommand,
931 short_help="Scan and report available devices.",
932 help=APIO_API_SCAN_DEVICES_HELP,
933)
934@timestamp_option
935@output_option
936@options.force_option_gen(short_help="Overwrite output file.")
937def _scan_devices_cli(
938 *,
939 # Options
940 timestamp: str,
941 output: str,
942 force: bool,
943):
944 """Implements the 'apio apio scan-devices' command."""
946 # -- For now, the information is not in a project context. That may
947 # -- change in the future. We need the packages for the libusb operation.
948 apio_ctx = ApioContext(
949 project_policy=ProjectPolicy.NO_PROJECT,
950 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
951 packages_policy=PackagesPolicy.ENSURE_PACKAGES,
952 )
954 # -- The top dict that we will emit as json.
955 top_dict: dict[str, Any] = {}
957 # -- Append user timestamp if specified.
958 if timestamp: 958 ↛ 961line 958 didn't jump to line 961 because the condition on line 958 was always true
959 top_dict["timestamp"] = timestamp
961 usb_devices: list[UsbDevice] = usb_util.scan_usb_devices(apio_ctx)
963 # -- Scan and report usb devices.
964 section = []
965 for usb_device in usb_devices: 965 ↛ 966line 965 didn't jump to line 966 because the loop on line 965 never started
966 dev = {}
967 dev["vid"] = usb_device.vid
968 dev["pid"] = usb_device.pid
969 dev["bus"] = str(usb_device.bus)
970 dev["device"] = str(usb_device.device)
971 dev["manufacturer"] = usb_device.manufacturer
972 dev["product"] = usb_device.product
973 dev["serial-number"] = usb_device.serial_number
974 dev["device_type"] = usb_device.device_type
976 section.append(dev)
978 top_dict["usb-devices"] = section
980 # -- Scan and report serial devices.
981 serial_devices: list[SerialDevice] = serial_util.scan_serial_devices(
982 apio_ctx
983 )
985 section = []
986 for serial_device in serial_devices: 986 ↛ 987line 986 didn't jump to line 987 because the loop on line 986 never started
987 dev = {}
988 dev["port"] = serial_device.port
989 dev["port-name"] = serial_device.port_name
990 dev["vendor-id"] = serial_device.vid
991 dev["product-id"] = serial_device.pid
992 dev["manufacturer"] = serial_device.manufacturer
993 dev["product"] = serial_device.product
994 dev["serial-number"] = serial_device.serial_number
995 dev["device-type"] = serial_device.device_type
997 section.append(dev)
999 top_dict["serial-devices"] = section
1001 # -- Write out
1002 write_as_json_doc(top_dict, output, force)
1005# ------ apio api echo
1008# -- Text in the rich-text format of the python rich library.
1009APIO_API_ECHO_HELP = """
1010The command 'apio api echo' allows external programs such as the Apio VS Code \
1011extension to print a short message in a format that is consistent with \
1012that Apio theme that is currently selected in the user preferences.
1014The required option '--style' should have one of these values: OK, \
1015INFO, WARNING, ERROR, TITLE, EMPH1, EMPH2, or EMPH3. The style colors can \
1016be viewed with the command 'apio info themes'.
1018Examples:[code]
1019 apio api echo -t "Hello world", -s "INFO"
1020 apio api echo -t "Task completed successfully", -s "OK"
1021 apio api echo -t "Task failed", -s "ERROR"[/code]
1022"""
1024# -- Supported style names
1025STYLES = {
1026 "OK": SUCCESS,
1027 "INFO": INFO,
1028 "WARNING": WARNING,
1029 "ERROR": ERROR,
1030 "TITLE": TITLE,
1031 "EMPH1": EMPH1,
1032 "EMPH2": EMPH2,
1033 "EMPH3": EMPH3,
1034}
1036text_option = click.option(
1037 "text", # Var name.
1038 "-t",
1039 "--text",
1040 type=str,
1041 metavar="MESSAGE",
1042 required=True,
1043 help="Set message to echo.",
1044 cls=cmd_util.ApioOption,
1045)
1048style_option = click.option(
1049 "style", # Var name.
1050 "-s",
1051 "--style",
1052 type=click.Choice(STYLES.keys()),
1053 metavar="STYLE",
1054 required=True,
1055 help="Set style to use.",
1056 cls=cmd_util.ApioOption,
1057)
1060@click.command(
1061 name="echo",
1062 cls=ApioCommand,
1063 short_help="Print a message in given format.",
1064 help=APIO_API_ECHO_HELP,
1065)
1066@text_option
1067@style_option
1068def _echo_cli(
1069 *,
1070 # Options
1071 text: str,
1072 style: str,
1073):
1074 """Implements the 'apio apio echo command."""
1076 # -- Instanatiate Apio context, project and packages are not needed.
1077 _ = ApioContext(
1078 project_policy=ProjectPolicy.NO_PROJECT,
1079 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
1080 packages_policy=PackagesPolicy.ENSURE_PACKAGES,
1081 )
1083 cout(text, style=STYLES[style])
1086# ------ apio apio
1088# -- Text in the rich-text format of the python rich library.
1089APIO_API_HELP = """
1090The command group 'apio api' contains subcommands that that are intended \
1091to be used by tools and programs such as icestudio, rather than being used \
1092directly by users.
1093"""
1095# -- We have only a single group with the title 'Subcommands'.
1096SUBGROUPS = [
1097 ApioSubgroup(
1098 "Subcommands",
1099 [
1100 _get_system_cli,
1101 _get_project_cli,
1102 _get_build_report_cli,
1103 _get_boards_cli,
1104 _get_fpgas_cli,
1105 _get_programmers_cli,
1106 _get_examples_cli,
1107 _get_commands_cli,
1108 _get_packages_cli,
1109 _scan_devices_cli,
1110 _echo_cli,
1111 ],
1112 )
1113]
1116@click.command(
1117 name="api",
1118 cls=ApioGroup,
1119 subgroups=SUBGROUPS,
1120 short_help="Apio programmatic interface.",
1121 help=APIO_API_HELP,
1122)
1123def cli():
1124 """Implements the 'apio apio' command group."""
1126 # pass