Coverage for apio / commands / apio_api.py: 88%

257 statements  

« prev     ^ index     » next       coverage.py v7.13.0, created at 2025-12-24 01: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""" 

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.apio_styles import INFO 

23from apio.common.common_util import get_project_source_files 

24from apio.utils import cmd_util, usb_util, serial_util, util 

25from apio.utils.usb_util import UsbDevice 

26from apio.utils.serial_util import SerialDevice 

27from apio.apio_context import ( 

28 ApioContext, 

29 PackagesPolicy, 

30 ProjectPolicy, 

31 RemoteConfigPolicy, 

32) 

33from apio.utils.cmd_util import ( 

34 ApioGroup, 

35 ApioSubgroup, 

36 ApioCommand, 

37 ApioCmdContext, 

38) 

39 

40 

41timestamp_option = click.option( 

42 "timestamp", # Var name. 

43 "-t", 

44 "--timestamp", 

45 type=str, 

46 metavar="text", 

47 help="Set a user provided timestamp.", 

48 cls=cmd_util.ApioOption, 

49) 

50 

51output_option = click.option( 

52 "output", # Var name. 

53 "-o", 

54 "--output", 

55 type=str, 

56 metavar="file-name", 

57 help="Set output file.", 

58 cls=cmd_util.ApioOption, 

59) 

60 

61 

62def write_as_json_doc(top_dict: Dict, output_flag: str, force_flag: bool): 

63 """A common function to write a dict as a JSON doc.""" 

64 # -- Format the top dict as json text. 

65 text = json.dumps(top_dict, indent=2) 

66 

67 if output_flag: 

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

69 output_path = Path(output_flag) 

70 

71 if output_path.is_dir(): 71 ↛ 72line 71 didn't jump to line 72 because the condition on line 71 was never true

72 cerror(f"The output path {output_path} is a directory.") 

73 sys.exit(1) 

74 

75 if output_path.exists() and not force_flag: 75 ↛ 76line 75 didn't jump to line 76 because the condition on line 75 was never true

76 cerror(f"The file already exists {output_path}.") 

77 cout("Use the --force option to allow overwriting.", style=INFO) 

78 sys.exit(1) 

79 

80 # -- if there file path contains a parent dir, make 

81 # -- sure it exists. If output_flag is just a file name such 

82 # -- as 'foo.json', we don nothing. 

83 dirname = os.path.dirname(output_flag) 

84 if dirname: 84 ↛ 88line 84 didn't jump to line 88 because the condition on line 84 was always true

85 os.makedirs(dirname, exist_ok=True) 

86 

87 # -- Write to file. 

88 with open(output_flag, "w", encoding="utf-8") as f: 

89 f.write(text) 

90 else: 

91 # -- Output the json text to stdout. 

92 print(text, file=sys.stdout) 

93 

94 

95# ------ apio api get-system 

96 

97 

98# -- Text in the rich-text format of the python rich library. 

99APIO_API_GET_SYSTEM_HELP = """ 

100The command 'apio api get-system' exports information about apio and \ 

101the underlying system as a JSON foc. It is similar to the command \ 

102'apio info system' which is intended for human consumption. 

103 

104The optional flag '--timestamp' allows the caller to embed in the JSON \ 

105document a known timestamp that allows to verify that the JSON document \ 

106was indeed was generated by the same invocation. 

107 

108Examples:[code] 

109 apio api get-system # Write to stdout 

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

111""" 

112 

113 

114@click.command( 

115 name="get-system", 

116 cls=ApioCommand, 

117 short_help="Retrieve apio and system information.", 

118 help=APIO_API_GET_SYSTEM_HELP, 

119) 

120# @click.pass_context 

121@timestamp_option 

122@output_option 

123@options.force_option_gen(short_help="Overwrite output file.") 

124def _get_system_cli( 

125 # Options 

126 timestamp: str, 

127 output: str, 

128 force: bool, 

129): 

130 """Implements the 'apio apio get-system' command.""" 

131 

132 apio_ctx = ApioContext( 

133 project_policy=ProjectPolicy.NO_PROJECT, 

134 remote_config_policy=RemoteConfigPolicy.CACHED_OK, 

135 packages_policy=PackagesPolicy.ENSURE_PACKAGES, 

136 ) 

137 

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

139 top_dict = {} 

140 

141 # -- Append user timestamp if specified. 

142 if timestamp: 142 ↛ 145line 142 didn't jump to line 145 because the condition on line 142 was always true

143 top_dict["timestamp"] = timestamp 

144 

145 section_dict = {} 

146 

147 # -- Add fields. 

148 section_dict["apio-version"] = util.get_apio_version_str() 

149 section_dict["python-version"] = util.get_python_version() 

150 section_dict["python-executable"] = sys.executable 

151 section_dict["platform-id"] = apio_ctx.platform_id 

152 section_dict["scons-shell-id"] = apio_ctx.scons_shell_id 

153 section_dict["vscode-debugger"] = str( 

154 util.is_under_vscode_debugger() 

155 ).lower() 

156 section_dict["pyinstaller"] = str(util.is_pyinstaller_app()).lower() 

157 section_dict["apio-python_package"] = str( 

158 util.get_path_in_apio_package("") 

159 ) 

160 section_dict["apio-home-dir"] = str(apio_ctx.apio_home_dir) 

161 section_dict["apio-packages-dir"] = str(apio_ctx.apio_packages_dir) 

162 section_dict["remote-config-url"] = apio_ctx.profile.remote_config_url 

163 section_dict["verible-formatter"] = str( 

164 apio_ctx.apio_packages_dir / "verible/bin/verible-verilog-format" 

165 ) 

166 section_dict["verible-language-server"] = str( 

167 apio_ctx.apio_packages_dir / "verible/bin/verible-verilog-ls" 

168 ) 

169 

170 # -- Add section 

171 top_dict["system"] = section_dict 

172 

173 # -- Write out 

174 write_as_json_doc(top_dict, output, force) 

175 

176 

177# ------ apio api get-project 

178 

179 

180# -- Text in the rich-text format of the python rich library. 

181APIO_API_GET_PROJECT_HELP = """ 

182The command 'apio api get-project' exports information about an Apio 

183project as a JSON foc. 

184 

185The optional flag '--timestamp' allows the caller to embed in the JSON \ 

186document a known timestamp that allows to verify that the JSON document \ 

187was indeed was generated by the same invocation. 

188 

189Examples:[code] 

190 apio api get-project # Report default env 

191 apio api get-project -e env1 # Report specified env 

192 apio api get-project -p foo/bar # Project in another dir 

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

194""" 

195 

196 

197@click.command( 

198 name="get-project", 

199 cls=ApioCommand, 

200 short_help="Get project information.", 

201 help=APIO_API_GET_PROJECT_HELP, 

202) 

203# @click.pass_context 

204@options.env_option_gen() 

205@options.project_dir_option 

206@timestamp_option 

207@output_option 

208@options.force_option_gen(short_help="Overwrite output file.") 

209def _get_project_cli( 

210 # Options 

211 env: str, 

212 project_dir: Optional[Path], 

213 timestamp: str, 

214 output: str, 

215 force: bool, 

216): 

217 """Implements the 'apio apio get-project' command.""" 

218 

219 apio_ctx = ApioContext( 

220 project_policy=ProjectPolicy.PROJECT_REQUIRED, 

221 remote_config_policy=RemoteConfigPolicy.CACHED_OK, 

222 packages_policy=PackagesPolicy.ENSURE_PACKAGES, 

223 project_dir_arg=project_dir, 

224 env_arg=env, 

225 ) 

226 

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

228 os.chdir(apio_ctx.project_dir) 

229 

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

231 top_dict = {} 

232 

233 # -- Append user timestamp if specified. 

234 if timestamp: 234 ↛ 237line 234 didn't jump to line 237 because the condition on line 234 was always true

235 top_dict["timestamp"] = timestamp 

236 

237 section_dict = {} 

238 

239 active_env_dict = {} 

240 active_env_dict["name"] = apio_ctx.project.env_name 

241 active_env_dict["options"] = apio_ctx.project.env_options 

242 section_dict["active-env"] = active_env_dict 

243 

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

245 

246 synth_srcs, test_srcs = get_project_source_files() 

247 section_dict["synth-files"] = synth_srcs 

248 section_dict["test-benches"] = test_srcs 

249 

250 # -- Add section 

251 top_dict["project"] = section_dict 

252 

253 # -- Write out 

254 write_as_json_doc(top_dict, output, force) 

255 

256 

257# ------ apio api get-boards 

258 

259 

260# -- Text in the rich-text format of the python rich library. 

261APIO_API_GET_BOARDS_HELP = """ 

262The command 'apio api get-boards' exports apio boards information as a \ 

263JSON document. 

264 

265The optional flag '--timestamp' allows the caller to embed in the JSON \ 

266document a known timestamp that allows to verify that the JSON document \ 

267was indeed was generated by the same invocation. 

268 

269Examples:[code] 

270 apio api get-boards # Write to stdout 

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

272""" 

273 

274 

275@click.command( 

276 name="get-boards", 

277 cls=ApioCommand, 

278 short_help="Retrieve boards information.", 

279 help=APIO_API_GET_BOARDS_HELP, 

280) 

281@timestamp_option 

282@output_option 

283@options.force_option_gen(short_help="Overwrite output file.") 

284def _get_boards_cli( 

285 # Options 

286 timestamp: str, 

287 output: str, 

288 force: bool, 

289): 

290 """Implements the 'apio apio get-boards' command.""" 

291 

292 # -- For now, the information is not in a project context. That may 

293 # -- change in the future. 

294 apio_ctx = ApioContext( 

295 project_policy=ProjectPolicy.NO_PROJECT, 

296 remote_config_policy=RemoteConfigPolicy.CACHED_OK, 

297 packages_policy=PackagesPolicy.ENSURE_PACKAGES, 

298 ) 

299 

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

301 top_dict = {} 

302 

303 # -- Append user timestamp if specified. 

304 if timestamp: 304 ↛ 308line 304 didn't jump to line 308 because the condition on line 304 was always true

305 top_dict["timestamp"] = timestamp 

306 

307 # -- Generate the boards section. 

308 section = {} 

309 for board_id, board_info in apio_ctx.boards.items(): 

310 # -- The board output dict. 

311 board_dict = {} 

312 

313 # -- Add board description 

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

315 

316 # -- Add board's fpga information. 

317 fpga_dict = {} 

318 fpga_id = board_info.get("fpga-id", None) 

319 fpga_info = apio_ctx.fpgas.get(fpga_id, {}) 

320 fpga_dict["id"] = fpga_id 

321 fpga_dict["part-num"] = fpga_info.get("part-num", None) 

322 fpga_dict["arch"] = fpga_info.get("arch", None) 

323 fpga_dict["size"] = fpga_info.get("size", None) 

324 board_dict["fpga"] = fpga_dict 

325 

326 # -- Add board's programmer information. 

327 programmer_dict = {} 

328 programmer_id = board_info.get("programmer", {}).get("id", None) 

329 programmer_dict["id"] = programmer_id 

330 board_dict["programmer"] = programmer_dict 

331 

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

333 section[board_id] = board_dict 

334 

335 top_dict["boards"] = section 

336 

337 # -- Write out 

338 write_as_json_doc(top_dict, output, force) 

339 

340 

341# ------ apio api get-fpgas 

342 

343 

344# -- Text in the rich-text format of the python rich library. 

345APIO_API_GET_FPGAS_HELP = """ 

346The command 'apio api get-fpgas' exports apio FPGAss information as a \ 

347JSON document. 

348 

349The optional flag '--timestamp' allows the caller to embed in the JSON \ 

350document a known timestamp that allows to verify that the JSON document \ 

351was indeed was generated by the same invocation. 

352 

353Examples:[code] 

354 apio api get-fpgas # Write to stdout 

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

356""" 

357 

358 

359@click.command( 

360 name="get-fpgas", 

361 cls=ApioCommand, 

362 short_help="Retrieve FPGAs information.", 

363 help=APIO_API_GET_FPGAS_HELP, 

364) 

365@timestamp_option 

366@output_option 

367@options.force_option_gen(short_help="Overwrite output file.") 

368def _get_fpgas_cli( 

369 # Options 

370 timestamp: str, 

371 output: str, 

372 force: bool, 

373): 

374 """Implements the 'apio apio get-fpgas' command.""" 

375 

376 # -- For now, the information is not in a project context. That may 

377 # -- change in the future. 

378 apio_ctx = ApioContext( 

379 project_policy=ProjectPolicy.NO_PROJECT, 

380 remote_config_policy=RemoteConfigPolicy.CACHED_OK, 

381 packages_policy=PackagesPolicy.ENSURE_PACKAGES, 

382 ) 

383 

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

385 top_dict = {} 

386 

387 # -- Append user timestamp if specified. 

388 if timestamp: 388 ↛ 392line 388 didn't jump to line 392 because the condition on line 388 was always true

389 top_dict["timestamp"] = timestamp 

390 

391 # -- Generate the fpgas section 

392 section = {} 

393 for fpga_id, fpga_info in apio_ctx.fpgas.items(): 

394 # -- The fpga output dict. 

395 fpga_dict = {} 

396 

397 fpga_dict["part-num"] = fpga_info.get("part-num", None) 

398 fpga_dict["arch"] = fpga_info.get("arch", None) 

399 fpga_dict["size"] = fpga_info.get("size", None) 

400 

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

402 section[fpga_id] = fpga_dict 

403 

404 top_dict["fpgas"] = section 

405 

406 # -- Write out 

407 write_as_json_doc(top_dict, output, force) 

408 

409 

410# ------ apio api get-programmers 

411 

412 

413# -- Text in the rich-text format of the python rich library. 

414APIO_API_GET_PROGRAMMERS_HELP = """ 

415The command 'apio api get-programmers' exports apio programmers information \ 

416as a JSON document. 

417 

418The optional flag '--timestamp' allows the caller to embed in the JSON \ 

419document a known timestamp that allows to verify that the JSON document \ 

420was indeed was generated by the same invocation. 

421 

422Examples:[code] 

423 apio api get-programmers # Write to stdout 

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

425""" 

426 

427 

428@click.command( 

429 name="get-programmers", 

430 cls=ApioCommand, 

431 short_help="Retrieve programmers information.", 

432 help=APIO_API_GET_PROGRAMMERS_HELP, 

433) 

434@timestamp_option 

435@output_option 

436@options.force_option_gen(short_help="Overwrite output file.") 

437def _get_programmers_cli( 

438 # Options 

439 timestamp: str, 

440 output: str, 

441 force: bool, 

442): 

443 """Implements the 'apio apio get-programmers' command.""" 

444 

445 # -- For now, the information is not in a project context. That may 

446 # -- change in the future. 

447 apio_ctx = ApioContext( 

448 project_policy=ProjectPolicy.NO_PROJECT, 

449 remote_config_policy=RemoteConfigPolicy.CACHED_OK, 

450 packages_policy=PackagesPolicy.ENSURE_PACKAGES, 

451 ) 

452 

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

454 top_dict = {} 

455 

456 # -- Append user timestamp if specified. 

457 if timestamp: 457 ↛ 461line 457 didn't jump to line 461 because the condition on line 457 was always true

458 top_dict["timestamp"] = timestamp 

459 

460 # -- Generate the 'programmers' section. 

461 section = {} 

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

463 section[programmer_id] = programmer_info 

464 

465 top_dict["programmers"] = section 

466 

467 # -- Write out 

468 write_as_json_doc(top_dict, output, force) 

469 

470 

471# ------ apio api get-examples 

472 

473 

474# -- Text in the rich-text format of the python rich library. 

475APIO_API_GET_EXAMPLES_HELP = """ 

476The command 'apio api get-examples' exports apio examples information as a \ 

477JSON document. 

478 

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. 

482 

483Examples:[code] 

484 apio api get-examples # Write to stdout 

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

486""" 

487 

488 

489@click.command( 

490 name="get-examples", 

491 cls=ApioCommand, 

492 short_help="Retrieve examples information.", 

493 help=APIO_API_GET_EXAMPLES_HELP, 

494) 

495@timestamp_option 

496@output_option 

497@options.force_option_gen(short_help="Overwrite output file.") 

498def _get_examples_cli( 

499 # Options 

500 timestamp: str, 

501 output: str, 

502 force: bool, 

503): 

504 """Implements the 'apio apio get-examples' command.""" 

505 

506 # -- For now, the information is not in a project context. That may 

507 # -- change in the future. 

508 apio_ctx = ApioContext( 

509 project_policy=ProjectPolicy.NO_PROJECT, 

510 remote_config_policy=RemoteConfigPolicy.CACHED_OK, 

511 packages_policy=PackagesPolicy.ENSURE_PACKAGES, 

512 ) 

513 

514 # -- Get examples infos. 

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

516 

517 # -- Group examples by boards 

518 boards_examples: Dict[str, List[ExampleInfo]] = {} 

519 for example in examples: 

520 board_examples = boards_examples.get(example.board_id, []) 

521 board_examples.append(example) 

522 boards_examples[example.board_id] = board_examples 

523 

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

525 top_dict = {} 

526 

527 # -- Append user timestamp if specified. 

528 if timestamp: 528 ↛ 532line 528 didn't jump to line 532 because the condition on line 528 was always true

529 top_dict["timestamp"] = timestamp 

530 

531 # -- Generate the 'examples' section. 

532 section = {} 

533 for board, board_examples in boards_examples.items(): 

534 board_dict = {} 

535 # -- Generate board examples 

536 for example_info in board_examples: 

537 example_dict = {} 

538 example_dict["description"] = example_info.description 

539 board_dict[example_info.example_name] = example_dict 

540 

541 section[board] = board_dict 

542 

543 top_dict["examples"] = section 

544 

545 # -- Write out 

546 write_as_json_doc(top_dict, output, force) 

547 

548 

549# ------ apio api get-commands 

550 

551 

552@dataclass(frozen=True) 

553class CmdInfo: 

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

555 

556 name: str 

557 path: List[str] 

558 cli: click.Command 

559 children: List[Self] 

560 

561 

562def scan_children(cmd_cli) -> Dict: 

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

564 result = {} 

565 

566 # -- Sanity check 

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

568 

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

570 if isinstance(cmd_cli, ApioCommand): 

571 return result 

572 

573 # -- Here we have a group and it should have at least one sub command. 

574 assert isinstance(cmd_cli, ApioGroup), type(cmd_cli) 

575 subgroups: List[ApioSubgroup] = cmd_cli.subgroups 

576 

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

578 subcommands_dict = {} 

579 result["commands"] = subcommands_dict 

580 

581 # -- Iterate the subgroups and populate them. We flaten the subcommands 

582 # -- group into a single list of commands. 

583 for subgroup in subgroups: 

584 assert isinstance(subgroup, ApioSubgroup), type(subgroup) 

585 assert isinstance(subgroup.title, str), type(subgroup.title) 

586 for subcommand in subgroup.commands: 

587 subcommand_dict = scan_children(subcommand) 

588 subcommands_dict[subcommand.name] = subcommand_dict 

589 

590 # -- All done ok. 

591 return result 

592 

593 

594# -- Text in the rich-text format of the python rich library. 

595APIO_API_GET_COMMANDS_HELP = """ 

596The command 'apio api get-commands' exports apio command structure \ 

597of Apio as a JSON doc. This is used by various tools such as 

598documentation generators and tests. 

599 

600The optional flag '--timestamp' allows the caller to embed in the JSON \ 

601document a known timestamp that allows to verify that the JSON document \ 

602was indeed was generated by the same invocation. 

603 

604Examples:[code] 

605 apio api get-commands # Write to stdout 

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

607""" 

608 

609 

610@click.command( 

611 name="get-commands", 

612 cls=ApioCommand, 

613 short_help="Retrieve apio commands information.", 

614 help=APIO_API_GET_COMMANDS_HELP, 

615) 

616@click.pass_context 

617@timestamp_option 

618@output_option 

619@options.force_option_gen(short_help="Overwrite output file.") 

620def _get_commands_cli( 

621 # Click context 

622 cmd_ctx: ApioCmdContext, 

623 # Options 

624 timestamp: str, 

625 output: str, 

626 force: bool, 

627): 

628 """Implements the 'apio apio get-commands' command.""" 

629 

630 # -- Find the top cli which is the "apio" command. Would access it 

631 # -- directly but it would create a circular python import. 

632 ctx = cmd_ctx 

633 while ctx.parent: 

634 ctx = ctx.parent 

635 assert isinstance(ctx, ApioCmdContext), type(ctx) 

636 top_cli = ctx.command 

637 assert top_cli.name == "apio", top_cli 

638 

639 # -- This initializes the console, print active env vars, etc. 

640 ApioContext( 

641 project_policy=ProjectPolicy.NO_PROJECT, 

642 remote_config_policy=RemoteConfigPolicy.CACHED_OK, 

643 packages_policy=PackagesPolicy.ENSURE_PACKAGES, 

644 ) 

645 

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

647 top_dict = {} 

648 

649 # -- Append user timestamp if specified. 

650 if timestamp: 650 ↛ 653line 650 didn't jump to line 653 because the condition on line 650 was always true

651 top_dict["timestamp"] = timestamp 

652 

653 section_dict = {} 

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

655 top_dict["commands"] = section_dict 

656 

657 # -- Write out 

658 write_as_json_doc(top_dict, output, force) 

659 

660 

661# ------ apio api scan-devices 

662 

663 

664# -- Text in the rich-text format of the python rich library. 

665APIO_API_SCAN_DEVICES_HELP = """ 

666The command 'apio api scan-devices' scans and report the available usb and \ 

667serial devices. 

668 

669The optional flag '--timestamp' allows the caller to embed in the JSON \ 

670document a known timestamp that allows to verify that the JSON document \ 

671was indeed was generated by the same invocation. 

672 

673Examples:[code] 

674 apio api scan-devices # Write to stdout 

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

676""" 

677 

678 

679@click.command( 

680 name="scan-devices", 

681 cls=ApioCommand, 

682 short_help="Scan and report available devices.", 

683 help=APIO_API_SCAN_DEVICES_HELP, 

684) 

685@timestamp_option 

686@output_option 

687@options.force_option_gen(short_help="Overwrite output file.") 

688def _scan_devices_cli( 

689 # Options 

690 timestamp: str, 

691 output: str, 

692 force: bool, 

693): 

694 """Implements the 'apio apio scan-devices' command.""" 

695 

696 # -- For now, the information is not in a project context. That may 

697 # -- change in the future. We need the config since we use libusb from 

698 # -- the packages. 

699 apio_ctx = ApioContext( 

700 project_policy=ProjectPolicy.NO_PROJECT, 

701 remote_config_policy=RemoteConfigPolicy.CACHED_OK, 

702 packages_policy=PackagesPolicy.ENSURE_PACKAGES, 

703 ) 

704 

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

706 top_dict = {} 

707 

708 # -- Append user timestamp if specified. 

709 if timestamp: 709 ↛ 715line 709 didn't jump to line 715 because the condition on line 709 was always true

710 top_dict["timestamp"] = timestamp 

711 

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

713 # packages.install_missing_packages_on_the_fly(apio_ctx.packages_context) 

714 

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

716 

717 # -- Scan and report usb devices. 

718 section = [] 

719 for device in usb_devices: 719 ↛ 720line 719 didn't jump to line 720 because the loop on line 719 never started

720 dev = {} 

721 dev["vid"] = device.vendor_id 

722 dev["pid"] = device.product_id 

723 dev["bus"] = device.bus 

724 dev["device"] = device.device 

725 dev["manufacturer"] = device.manufacturer 

726 dev["product"] = device.product 

727 dev["serial-number"] = device.serial_number 

728 dev["device_type"] = device.device_type 

729 

730 section.append(dev) 

731 

732 top_dict["usb-devices"] = section 

733 

734 # -- Scan and report serial devices. 

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

736 

737 section = [] 

738 for device in serial_devices: 738 ↛ 739line 738 didn't jump to line 739 because the loop on line 738 never started

739 dev = {} 

740 dev["port"] = device.port 

741 dev["port-name"] = device.port_name 

742 dev["vendor-id"] = device.vendor_id 

743 dev["product-id"] = device.product_id 

744 dev["manufacturer"] = device.manufacturer 

745 dev["product"] = device.product 

746 dev["serial-number"] = device.serial_number 

747 dev["device-type"] = device.device_type 

748 

749 section.append(dev) 

750 

751 top_dict["serial-devices"] = section 

752 

753 # -- Write out 

754 write_as_json_doc(top_dict, output, force) 

755 

756 

757# ------ apio apio 

758 

759# -- Text in the rich-text format of the python rich library. 

760APIO_API_HELP = """ 

761The command group 'apio api' contains subcommands that that are intended \ 

762to be used by tools and programs such as icestudio, rather than being used \ 

763directly by users. 

764""" 

765 

766# -- We have only a single group with the title 'Subcommands'. 

767SUBGROUPS = [ 

768 ApioSubgroup( 

769 "Subcommands", 

770 [ 

771 _get_system_cli, 

772 _get_project_cli, 

773 _get_boards_cli, 

774 _get_fpgas_cli, 

775 _get_programmers_cli, 

776 _get_examples_cli, 

777 _get_commands_cli, 

778 _scan_devices_cli, 

779 ], 

780 ) 

781] 

782 

783 

784@click.command( 

785 name="api", 

786 cls=ApioGroup, 

787 subgroups=SUBGROUPS, 

788 short_help="Apio programmatic interface.", 

789 help=APIO_API_HELP, 

790) 

791def cli(): 

792 """Implements the 'apio apio' command group.""" 

793 

794 # pass