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

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""" 

9 

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 

18 

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) 

48 

49 

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) 

59 

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) 

69 

70 

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) 

75 

76 if output_flag: 

77 # -- Output the json text to a user specified file. 

78 output_path = Path(output_flag) 

79 

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) 

83 

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) 

88 

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) 

95 

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) 

102 

103 

104# ------ apio api get-system 

105 

106 

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. 

112 

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. 

116 

117Examples:[code] 

118 apio api get-system # Write to stdout 

119 apio api get-system -o apio.json # Write to a file[/code] 

120""" 

121 

122 

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.""" 

140 

141 apio_ctx = ApioContext( 

142 project_policy=ProjectPolicy.NO_PROJECT, 

143 remote_config_policy=RemoteConfigPolicy.CACHED_OK, 

144 packages_policy=PackagesPolicy.ENSURE_PACKAGES, 

145 ) 

146 

147 # -- The top dict that we will emit as json. 

148 top_dict = {} 

149 

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 

153 

154 section_dict = {} 

155 

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 ) 

180 

181 # -- Add section 

182 top_dict["system"] = section_dict 

183 

184 # -- Write out 

185 write_as_json_doc(top_dict, output, force) 

186 

187 

188# ------ apio api get-project 

189 

190 

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. 

195 

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. 

199 

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""" 

206 

207 

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.""" 

229 

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 ) 

237 

238 # -- Change to the project's folder. 

239 os.chdir(apio_ctx.project_dir) 

240 

241 # -- The top dict that we will emit as json. 

242 top_dict = {} 

243 

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 

247 

248 section_dict = {} 

249 

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 

254 

255 section_dict["envs"] = apio_ctx.project.env_names 

256 

257 synth_srcs, test_srcs = get_project_source_files() 

258 section_dict["synth-files"] = synth_srcs 

259 section_dict["test-benches"] = test_srcs 

260 

261 pr = apio_ctx.project_resources 

262 

263 board_dict = {"id": pr.board_id} 

264 board_dict.update(pr.board_info) 

265 section_dict["board"] = board_dict 

266 

267 fpga_dict = {"id": pr.fpga_id} 

268 fpga_dict.update(pr.fpga_info) 

269 section_dict["fpga"] = fpga_dict 

270 

271 programmer_dict = {"id": pr.programmer_id} 

272 programmer_dict.update(pr.programmer_info) 

273 section_dict["programmer"] = programmer_dict 

274 

275 # -- Add section 

276 top_dict["project"] = section_dict 

277 

278 # -- Write out 

279 write_as_json_doc(top_dict, output, force) 

280 

281 

282# ------ apio api get-boards 

283 

284 

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. 

289 

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. 

293 

294Examples:[code] 

295 apio api get-boards # Write to stdout 

296 apio api get-boards -o apio.json # Write to a file[/code] 

297""" 

298 

299 

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.""" 

316 

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 ) 

324 

325 # -- The top dict that we will emit as json. 

326 top_dict = {} 

327 

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 

331 

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 = {} 

337 

338 # -- Add board description 

339 board_dict["description"] = board_info.get("description", None) 

340 

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 

350 

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 

356 

357 # -- Add the board to the boards dict. 

358 section[board_id] = board_dict 

359 

360 top_dict["boards"] = section 

361 

362 # -- Write out 

363 write_as_json_doc(top_dict, output, force) 

364 

365 

366# ------ apio api get-fpgas 

367 

368 

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. 

373 

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. 

377 

378Examples:[code] 

379 apio api get-fpgas # Write to stdout 

380 apio api get-fpgas -o apio.json # Write to a file[/code] 

381""" 

382 

383 

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.""" 

400 

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 ) 

408 

409 # -- The top dict that we will emit as json. 

410 top_dict = {} 

411 

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 

415 

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 = {} 

421 

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) 

425 

426 # -- Add the fpga to the fpgas dict. 

427 section[fpga_id] = fpga_dict 

428 

429 top_dict["fpgas"] = section 

430 

431 # -- Write out 

432 write_as_json_doc(top_dict, output, force) 

433 

434 

435# ------ apio api get-programmers 

436 

437 

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. 

442 

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. 

446 

447Examples:[code] 

448 apio api get-programmers # Write to stdout 

449 apio api get-programmers -o apio.json # Write to a file[/code] 

450""" 

451 

452 

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.""" 

469 

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 ) 

477 

478 # -- The top dict that we will emit as json. 

479 top_dict = {} 

480 

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 

484 

485 # -- Generate the 'programmers' section. 

486 section = {} 

487 for programmer_id, programmer_info in apio_ctx.programmers.items(): 

488 section[programmer_id] = programmer_info 

489 

490 top_dict["programmers"] = section 

491 

492 # -- Write out 

493 write_as_json_doc(top_dict, output, force) 

494 

495 

496# ------ apio api get-examples 

497 

498 

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. 

503 

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. 

507 

508Examples:[code] 

509 apio api get-examples # Write to stdout 

510 apio api get-examples -o apio.json # Write to a file[/code] 

511""" 

512 

513 

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.""" 

530 

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 ) 

538 

539 # -- Get examples infos. 

540 examples: List[ExampleInfo] = Examples(apio_ctx).get_examples_infos() 

541 

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 

548 

549 # -- The top dict that we will emit as json. 

550 top_dict = {} 

551 

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 

555 

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 

565 

566 section[board] = board_dict 

567 

568 top_dict["examples"] = section 

569 

570 # -- Write out 

571 write_as_json_doc(top_dict, output, force) 

572 

573 

574# ------ apio api get-commands 

575 

576 

577@dataclass(frozen=True) 

578class CmdInfo: 

579 """Represents the information of a single apio command.""" 

580 

581 name: str 

582 path: List[str] 

583 cli: click.Command 

584 children: List[Self] 

585 

586 

587def scan_children(cmd_cli) -> Dict: 

588 """Return a dict describing this command subtree.""" 

589 result = {} 

590 

591 # -- Sanity check 

592 assert isinstance(result, dict), type(result) 

593 

594 # -- If this is a simple command, it has no sub commands. 

595 if isinstance(cmd_cli, ApioCommand): 

596 return result 

597 

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 

601 

602 # -- Create the dict for the command subgroups. 

603 subcommands_dict = {} 

604 result["commands"] = subcommands_dict 

605 

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 

614 

615 # -- All done ok. 

616 return result 

617 

618 

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. 

624 

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. 

628 

629Examples:[code] 

630 apio api get-commands # Write to stdout 

631 apio api get-commands -o apio.json # Write to a file[/code] 

632""" 

633 

634 

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.""" 

654 

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 

663 

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 ) 

670 

671 # -- The top dict that we will emit as json. 

672 top_dict = {} 

673 

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 

677 

678 section_dict = {} 

679 section_dict["apio"] = scan_children(top_cli) 

680 top_dict["commands"] = section_dict 

681 

682 # -- Write out 

683 write_as_json_doc(top_dict, output, force) 

684 

685 

686# ------ apio api scan-devices 

687 

688 

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. 

693 

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. 

697 

698Examples:[code] 

699 apio api scan-devices # Write to stdout 

700 apio api scan-devices -o apio.json # Write to a file[/code] 

701""" 

702 

703 

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.""" 

720 

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 ) 

729 

730 # -- The top dict that we will emit as json. 

731 top_dict = {} 

732 

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 

736 

737 # -- We need the packages for the 'libusb' backend. 

738 # packages.install_missing_packages_on_the_fly(apio_ctx.packages_context) 

739 

740 usb_devices: List[UsbDevice] = usb_util.scan_usb_devices(apio_ctx) 

741 

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 

754 

755 section.append(dev) 

756 

757 top_dict["usb-devices"] = section 

758 

759 # -- Scan and report serial devices. 

760 serial_devices: List[SerialDevice] = serial_util.scan_serial_devices() 

761 

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 

773 

774 section.append(dev) 

775 

776 top_dict["serial-devices"] = section 

777 

778 # -- Write out 

779 write_as_json_doc(top_dict, output, force) 

780 

781 

782# ------ apio api echo 

783 

784 

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. 

790 

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'. 

794 

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""" 

800 

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} 

812 

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) 

823 

824 

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) 

835 

836 

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.""" 

851 

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 ) 

858 

859 cout(text, style=STYLES[style]) 

860 

861 

862# ------ apio apio 

863 

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""" 

870 

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] 

888 

889 

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.""" 

899 

900 # pass