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

273 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-03-25 02:31 +0000

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 *, 

135 # Options 

136 timestamp: str, 

137 output: str, 

138 force: bool, 

139): 

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

141 

142 apio_ctx = ApioContext( 

143 project_policy=ProjectPolicy.NO_PROJECT, 

144 remote_config_policy=RemoteConfigPolicy.CACHED_OK, 

145 packages_policy=PackagesPolicy.ENSURE_PACKAGES, 

146 ) 

147 

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

149 top_dict = {} 

150 

151 # -- Append user timestamp if specified. 

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

153 top_dict["timestamp"] = timestamp 

154 

155 section_dict = {} 

156 

157 # -- Add fields. 

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

159 section_dict["release-info"] = util.get_apio_release_info() 

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

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

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

163 section_dict["platform-info"] = util.get_platform_info() 

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

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

166 util.is_under_vscode_debugger() 

167 ).lower() 

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

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

170 util.get_path_in_apio_package("") 

171 ) 

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

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

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

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

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

177 ) 

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

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

180 ) 

181 

182 # -- Add section 

183 top_dict["system"] = section_dict 

184 

185 # -- Write out 

186 write_as_json_doc(top_dict, output, force) 

187 

188 

189# ------ apio api get-project 

190 

191 

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

193APIO_API_GET_PROJECT_HELP = """ 

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

195project as a JSON foc. 

196 

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

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

199was indeed was generated by the same invocation. 

200 

201Examples:[code] 

202 apio api get-project # Report default env 

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

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

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

206""" 

207 

208 

209@click.command( 

210 name="get-project", 

211 cls=ApioCommand, 

212 short_help="Get project information.", 

213 help=APIO_API_GET_PROJECT_HELP, 

214) 

215# @click.pass_context 

216@options.env_option_gen() 

217@options.project_dir_option 

218@timestamp_option 

219@output_option 

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

221def _get_project_cli( 

222 *, 

223 # Options 

224 env: str, 

225 project_dir: Optional[Path], 

226 timestamp: str, 

227 output: str, 

228 force: bool, 

229): 

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

231 

232 apio_ctx = ApioContext( 

233 project_policy=ProjectPolicy.PROJECT_REQUIRED, 

234 remote_config_policy=RemoteConfigPolicy.CACHED_OK, 

235 packages_policy=PackagesPolicy.ENSURE_PACKAGES, 

236 project_dir_arg=project_dir, 

237 env_arg=env, 

238 ) 

239 

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

241 os.chdir(apio_ctx.project_dir) 

242 

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

244 top_dict = {} 

245 

246 # -- Append user timestamp if specified. 

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

248 top_dict["timestamp"] = timestamp 

249 

250 section_dict = {} 

251 

252 active_env_dict = {} 

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

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

255 section_dict["active-env"] = active_env_dict 

256 

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

258 

259 synth_srcs, test_srcs = get_project_source_files() 

260 section_dict["synth-files"] = synth_srcs 

261 section_dict["test-benches"] = test_srcs 

262 

263 pr = apio_ctx.project_resources 

264 

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

266 board_dict.update(pr.board_info) 

267 section_dict["board"] = board_dict 

268 

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

270 fpga_dict.update(pr.fpga_info) 

271 section_dict["fpga"] = fpga_dict 

272 

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

274 programmer_dict.update(pr.programmer_info) 

275 section_dict["programmer"] = programmer_dict 

276 

277 # -- Add section 

278 top_dict["project"] = section_dict 

279 

280 # -- Write out 

281 write_as_json_doc(top_dict, output, force) 

282 

283 

284# ------ apio api get-boards 

285 

286 

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

288APIO_API_GET_BOARDS_HELP = """ 

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

290JSON document. 

291 

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

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

294was indeed was generated by the same invocation. 

295 

296Examples:[code] 

297 apio api get-boards # Write to stdout 

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

299""" 

300 

301 

302@click.command( 

303 name="get-boards", 

304 cls=ApioCommand, 

305 short_help="Retrieve boards information.", 

306 help=APIO_API_GET_BOARDS_HELP, 

307) 

308@timestamp_option 

309@output_option 

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

311def _get_boards_cli( 

312 *, 

313 # Options 

314 timestamp: str, 

315 output: str, 

316 force: bool, 

317): 

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

319 

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

321 # -- change in the future. 

322 apio_ctx = ApioContext( 

323 project_policy=ProjectPolicy.NO_PROJECT, 

324 remote_config_policy=RemoteConfigPolicy.CACHED_OK, 

325 packages_policy=PackagesPolicy.ENSURE_PACKAGES, 

326 ) 

327 

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

329 top_dict = {} 

330 

331 # -- Append user timestamp if specified. 

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

333 top_dict["timestamp"] = timestamp 

334 

335 # -- Generate the boards section. 

336 section = {} 

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

338 # -- The board output dict. 

339 board_dict = {} 

340 

341 # -- Add board description 

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

343 

344 # -- Add board's fpga information. 

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

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

347 assert "id" not in fpga_info 

348 fpga_dict = {"id": fpga_id} 

349 fpga_dict.update(fpga_info) 

350 board_dict["fpga"] = fpga_dict 

351 

352 # -- Add board's programmer information. 

353 programmer_dict = {} 

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

355 programmer_dict["id"] = programmer_id 

356 board_dict["programmer"] = programmer_dict 

357 

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

359 section[board_id] = board_dict 

360 

361 top_dict["boards"] = section 

362 

363 # -- Write out 

364 write_as_json_doc(top_dict, output, force) 

365 

366 

367# ------ apio api get-fpgas 

368 

369 

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

371APIO_API_GET_FPGAS_HELP = """ 

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

373JSON document. 

374 

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

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

377was indeed was generated by the same invocation. 

378 

379Examples:[code] 

380 apio api get-fpgas # Write to stdout 

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

382""" 

383 

384 

385@click.command( 

386 name="get-fpgas", 

387 cls=ApioCommand, 

388 short_help="Retrieve FPGAs information.", 

389 help=APIO_API_GET_FPGAS_HELP, 

390) 

391@timestamp_option 

392@output_option 

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

394def _get_fpgas_cli( 

395 *, 

396 # Options 

397 timestamp: str, 

398 output: str, 

399 force: bool, 

400): 

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

402 

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

404 # -- change in the future. 

405 apio_ctx = ApioContext( 

406 project_policy=ProjectPolicy.NO_PROJECT, 

407 remote_config_policy=RemoteConfigPolicy.CACHED_OK, 

408 packages_policy=PackagesPolicy.ENSURE_PACKAGES, 

409 ) 

410 

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

412 top_dict = {} 

413 

414 # -- Append user timestamp if specified. 

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

416 top_dict["timestamp"] = timestamp 

417 

418 # -- Generate the fpgas section 

419 section = {} 

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

421 section[fpga_id] = fpga_info 

422 

423 top_dict["fpgas"] = section 

424 

425 # -- Write out 

426 write_as_json_doc(top_dict, output, force) 

427 

428 

429# ------ apio api get-programmers 

430 

431 

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

433APIO_API_GET_PROGRAMMERS_HELP = """ 

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

435as a JSON document. 

436 

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

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

439was indeed was generated by the same invocation. 

440 

441Examples:[code] 

442 apio api get-programmers # Write to stdout 

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

444""" 

445 

446 

447@click.command( 

448 name="get-programmers", 

449 cls=ApioCommand, 

450 short_help="Retrieve programmers information.", 

451 help=APIO_API_GET_PROGRAMMERS_HELP, 

452) 

453@timestamp_option 

454@output_option 

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

456def _get_programmers_cli( 

457 *, 

458 # Options 

459 timestamp: str, 

460 output: str, 

461 force: bool, 

462): 

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

464 

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

466 # -- change in the future. 

467 apio_ctx = ApioContext( 

468 project_policy=ProjectPolicy.NO_PROJECT, 

469 remote_config_policy=RemoteConfigPolicy.CACHED_OK, 

470 packages_policy=PackagesPolicy.ENSURE_PACKAGES, 

471 ) 

472 

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

474 top_dict = {} 

475 

476 # -- Append user timestamp if specified. 

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

478 top_dict["timestamp"] = timestamp 

479 

480 # -- Generate the 'programmers' section. 

481 section = {} 

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

483 section[programmer_id] = programmer_info 

484 

485 top_dict["programmers"] = section 

486 

487 # -- Write out 

488 write_as_json_doc(top_dict, output, force) 

489 

490 

491# ------ apio api get-examples 

492 

493 

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

495APIO_API_GET_EXAMPLES_HELP = """ 

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

497JSON document. 

498 

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

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

501was indeed was generated by the same invocation. 

502 

503Examples:[code] 

504 apio api get-examples # Write to stdout 

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

506""" 

507 

508 

509@click.command( 

510 name="get-examples", 

511 cls=ApioCommand, 

512 short_help="Retrieve examples information.", 

513 help=APIO_API_GET_EXAMPLES_HELP, 

514) 

515@timestamp_option 

516@output_option 

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

518def _get_examples_cli( 

519 *, 

520 # Options 

521 timestamp: str, 

522 output: str, 

523 force: bool, 

524): 

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

526 

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

528 # -- change in the future. 

529 apio_ctx = ApioContext( 

530 project_policy=ProjectPolicy.NO_PROJECT, 

531 remote_config_policy=RemoteConfigPolicy.CACHED_OK, 

532 packages_policy=PackagesPolicy.ENSURE_PACKAGES, 

533 ) 

534 

535 # -- Get examples infos. 

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

537 

538 # -- Group examples by boards 

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

540 for example in examples: 

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

542 board_examples.append(example) 

543 boards_examples[example.board_id] = board_examples 

544 

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

546 top_dict = {} 

547 

548 # -- Append user timestamp if specified. 

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

550 top_dict["timestamp"] = timestamp 

551 

552 # -- Generate the 'examples' section. 

553 section = {} 

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

555 board_dict = {} 

556 # -- Generate board examples 

557 for example_info in board_examples: 

558 example_dict = {} 

559 example_dict["description"] = example_info.description 

560 board_dict[example_info.example_name] = example_dict 

561 

562 section[board] = board_dict 

563 

564 top_dict["examples"] = section 

565 

566 # -- Write out 

567 write_as_json_doc(top_dict, output, force) 

568 

569 

570# ------ apio api get-commands 

571 

572 

573@dataclass(frozen=True) 

574class CmdInfo: 

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

576 

577 name: str 

578 path: List[str] 

579 cli: click.Command 

580 children: List[Self] 

581 

582 

583def scan_children(cmd_cli) -> Dict: 

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

585 result = {} 

586 

587 # -- Sanity check 

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

589 

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

591 if isinstance(cmd_cli, ApioCommand): 

592 return result 

593 

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

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

596 subgroups: List[ApioSubgroup] = cmd_cli.subgroups 

597 

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

599 subcommands_dict = {} 

600 result["commands"] = subcommands_dict 

601 

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

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

604 for subgroup in subgroups: 

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

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

607 for subcommand in subgroup.commands: 

608 subcommand_dict = scan_children(subcommand) 

609 subcommands_dict[subcommand.name] = subcommand_dict 

610 

611 # -- All done ok. 

612 return result 

613 

614 

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

616APIO_API_GET_COMMANDS_HELP = """ 

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

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

619documentation generators and tests. 

620 

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

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

623was indeed was generated by the same invocation. 

624 

625Examples:[code] 

626 apio api get-commands # Write to stdout 

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

628""" 

629 

630 

631@click.command( 

632 name="get-commands", 

633 cls=ApioCommand, 

634 short_help="Retrieve apio commands information.", 

635 help=APIO_API_GET_COMMANDS_HELP, 

636) 

637@click.pass_context 

638@timestamp_option 

639@output_option 

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

641def _get_commands_cli( 

642 # Click context 

643 cmd_ctx: ApioCmdContext, 

644 *, 

645 # Options 

646 timestamp: str, 

647 output: str, 

648 force: bool, 

649): 

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

651 

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

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

654 ctx = cmd_ctx 

655 while ctx.parent: 

656 ctx = ctx.parent 

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

658 top_cli = ctx.command 

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

660 

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

662 ApioContext( 

663 project_policy=ProjectPolicy.NO_PROJECT, 

664 remote_config_policy=RemoteConfigPolicy.CACHED_OK, 

665 packages_policy=PackagesPolicy.ENSURE_PACKAGES, 

666 ) 

667 

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

669 top_dict = {} 

670 

671 # -- Append user timestamp if specified. 

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

673 top_dict["timestamp"] = timestamp 

674 

675 section_dict = {} 

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

677 top_dict["commands"] = section_dict 

678 

679 # -- Write out 

680 write_as_json_doc(top_dict, output, force) 

681 

682 

683# ------ apio api scan-devices 

684 

685 

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

687APIO_API_SCAN_DEVICES_HELP = """ 

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

689serial devices. 

690 

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

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

693was indeed was generated by the same invocation. 

694 

695Examples:[code] 

696 apio api scan-devices # Write to stdout 

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

698""" 

699 

700 

701@click.command( 

702 name="scan-devices", 

703 cls=ApioCommand, 

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

705 help=APIO_API_SCAN_DEVICES_HELP, 

706) 

707@timestamp_option 

708@output_option 

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

710def _scan_devices_cli( 

711 *, 

712 # Options 

713 timestamp: str, 

714 output: str, 

715 force: bool, 

716): 

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

718 

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

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

721 # -- the packages. 

722 apio_ctx = ApioContext( 

723 project_policy=ProjectPolicy.NO_PROJECT, 

724 remote_config_policy=RemoteConfigPolicy.CACHED_OK, 

725 packages_policy=PackagesPolicy.ENSURE_PACKAGES, 

726 ) 

727 

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

729 top_dict = {} 

730 

731 # -- Append user timestamp if specified. 

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

733 top_dict["timestamp"] = timestamp 

734 

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

736 # packages.install_missing_packages_on_the_fly(apio_ctx.packages_context) 

737 

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

739 

740 # -- Scan and report usb devices. 

741 section = [] 

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

743 dev = {} 

744 dev["vid"] = device.vendor_id 

745 dev["pid"] = device.product_id 

746 dev["bus"] = device.bus 

747 dev["device"] = device.device 

748 dev["manufacturer"] = device.manufacturer 

749 dev["product"] = device.product 

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

751 dev["device_type"] = device.device_type 

752 

753 section.append(dev) 

754 

755 top_dict["usb-devices"] = section 

756 

757 # -- Scan and report serial devices. 

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

759 

760 section = [] 

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

762 dev = {} 

763 dev["port"] = device.port 

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

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

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

767 dev["manufacturer"] = device.manufacturer 

768 dev["product"] = device.product 

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

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

771 

772 section.append(dev) 

773 

774 top_dict["serial-devices"] = section 

775 

776 # -- Write out 

777 write_as_json_doc(top_dict, output, force) 

778 

779 

780# ------ apio api echo 

781 

782 

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

784APIO_API_ECHO_HELP = """ 

785The command 'apio api echo' allows external programs such as the Apio VS Code \ 

786extension to print a short message in a format that is consistent with \ 

787that Apio theme that is currently selected in the user preferences. 

788 

789The required option '--style' should have one of these values: OK, \ 

790INFO, WARNING, ERROR, TITLE, EMPH1, EMPH2, or EMPH3. The style colors can \ 

791be viewed with the command 'apio info themes'. 

792 

793Examples:[code] 

794 apio api echo -t "Hello world", -s "INFO" 

795 apio api echo -t "Task completed successfully", -s "OK" 

796 apio api echo -t "Task failed", -s "ERROR"[/code] 

797""" 

798 

799# -- Supported style names 

800STYLES = { 

801 "OK": SUCCESS, 

802 "INFO": INFO, 

803 "WARNING": WARNING, 

804 "ERROR": ERROR, 

805 "TITLE": TITLE, 

806 "EMPH1": EMPH1, 

807 "EMPH2": EMPH2, 

808 "EMPH3": EMPH3, 

809} 

810 

811text_option = click.option( 

812 "text", # Var name. 

813 "-t", 

814 "--text", 

815 type=str, 

816 metavar="MESSAGE", 

817 required=True, 

818 help="Set message to echo.", 

819 cls=cmd_util.ApioOption, 

820) 

821 

822 

823style_option = click.option( 

824 "style", # Var name. 

825 "-s", 

826 "--style", 

827 type=click.Choice(STYLES.keys()), 

828 metavar="STYLE", 

829 required=True, 

830 help="Set style to use.", 

831 cls=cmd_util.ApioOption, 

832) 

833 

834 

835@click.command( 

836 name="echo", 

837 cls=ApioCommand, 

838 short_help="Print a message in given format.", 

839 help=APIO_API_ECHO_HELP, 

840) 

841@text_option 

842@style_option 

843def _echo_cli( 

844 *, 

845 # Options 

846 text: str, 

847 style: str, 

848): 

849 """Implements the 'apio apio echo command.""" 

850 

851 # -- Instanatiate Apio context, project and packages are not needed. 

852 _ = ApioContext( 

853 project_policy=ProjectPolicy.NO_PROJECT, 

854 remote_config_policy=RemoteConfigPolicy.CACHED_OK, 

855 packages_policy=PackagesPolicy.ENSURE_PACKAGES, 

856 ) 

857 

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

859 

860 

861# ------ apio apio 

862 

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

864APIO_API_HELP = """ 

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

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

867directly by users. 

868""" 

869 

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

871SUBGROUPS = [ 

872 ApioSubgroup( 

873 "Subcommands", 

874 [ 

875 _get_system_cli, 

876 _get_project_cli, 

877 _get_boards_cli, 

878 _get_fpgas_cli, 

879 _get_programmers_cli, 

880 _get_examples_cli, 

881 _get_commands_cli, 

882 _scan_devices_cli, 

883 _echo_cli, 

884 ], 

885 ) 

886] 

887 

888 

889@click.command( 

890 name="api", 

891 cls=ApioGroup, 

892 subgroups=SUBGROUPS, 

893 short_help="Apio programmatic interface.", 

894 help=APIO_API_HELP, 

895) 

896def cli(): 

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

898 

899 # pass