Coverage for apio/common/common_util.py: 86%
46 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-11-06 10:20 +0000
« prev ^ index » next coverage.py v7.11.0, created at 2025-11-06 10:20 +0000
1# -*- coding: utf-8 -*-
2# -- This file is part of the Apio project
3# -- (C) 2016-2018 FPGAwars
4# -- Author Jesús Arroyo
5# -- License GPLv2
6# -- Derived from:
7# ---- Platformio project
8# ---- (C) 2014-2016 Ivan Kravets <me@ikravets.com>
9# ---- License Apache v2
10"""Utilities that are available for both the apio (parent) process and the
11scons (child) process."""
13import os
14from pathlib import Path
15from glob import glob
16from typing import List, Union, Any, Tuple
17import debugpy
18from apio.common.apio_styles import EMPH3, SUCCESS
20# -- A list with the file extensions of the source files.
21SRC_SUFFIXES = [".v", ".sv"]
23# -- The root dir of all the env build directory. Relative to the
24# -- project dir. 'ALL' to distinguish from individual env build dirs.
25PROJECT_BUILD_PATH = Path("_build")
28def env_build_path(env_name: str) -> Path:
29 """Given an env name, return a relative path from the project dir to the
30 env build dir."""
31 return PROJECT_BUILD_PATH / env_name
34def maybe_wait_for_remote_debugger(env_var_name: str):
35 """A rendezvous point for a remote debugger. If the environment variable
36 of given name is set, the function will block until a remote
37 debugger (e.g. from Visual Studio Code) is attached.
38 """
39 if os.getenv(env_var_name) is not None: 39 ↛ 42line 39 didn't jump to line 42 because the condition on line 39 was never true
40 # NOTE: This function may be called before apio_console.py is
41 # initialized, so we use print() instead of cout().
42 print(f"Env var '{env_var_name}' was detected.")
43 port = 5678
44 print(f"Apio SCons for remote debugger on port localhost:{port}.")
45 debugpy.listen(port)
46 print(
47 "Attach Visual Studio Code python remote python debugger "
48 f"to port {port}.",
49 style=EMPH3,
50 )
51 # -- Block until the debugger connects.
52 debugpy.wait_for_client()
53 # -- Here the remote debugger is attached and the program continues.
54 print(
55 "Remote debugger is attached, program continues...",
56 style=SUCCESS,
57 )
60def file_sort_key_func(f: Union[str, Path]) -> Any:
61 """Given a file name or path, return a key to sort a file list.
62 The order is lexicography and case sensitive."""
63 path = Path(f)
64 # -- List of directory names in lower case.
65 parents: List[str] = [s.lower() for s in path.parent.parts]
66 # -- File name in lower case.
67 name: str = path.name.lower()
68 # -- Sort by directory and then by file name.
69 return [parents, name]
72def is_source_file(file_name: str) -> bool:
73 """Given a file name, determine by its extension if it's a verilog
74 source file (testbenches included)."""
75 _, ext = os.path.splitext(file_name)
76 return ext in SRC_SUFFIXES
79def has_testbench_name(file_name: str) -> bool:
80 """Given a file name, return true if it's base name indicates a
81 testbench. For example abc_tb.v or _build/abc_tb.out. The file extension
82 is ignored.
83 """
84 name, _ = os.path.splitext(file_name)
85 return name.lower().endswith("_tb")
88def sort_files(files: List[str]) -> List[str]:
89 """Sort a list of files by directory and then by file name.
90 A new sorted list is returned.
91 """
92 # -- Sort the files by directory and then by file name.
93 return sorted(files, key=file_sort_key_func)
96def get_project_source_files() -> Tuple[List[str], List[str]]:
97 """Get the list of source files in the directory tree under the current
98 directory, splitted into synth and testbench lists.
99 If source file has the suffix _tb it's is classified st a testbench,
100 otherwise as a synthesis file.
101 """
102 # -- Get a list of all source files in the project dir.
103 # -- Ideally we should use the scons env.Glob() method but it doesn't
104 # -- work with the recursive=True option. So we use the glob() function
105 # -- instead.
106 files: List[str] = []
107 for ext in SRC_SUFFIXES:
108 files.extend(glob(f"**/*{ext}", recursive=True))
110 # -- Sort the files by directory and then by file name.
111 files = sort_files(files)
113 # -- Split file names to synth files and testbench file lists
114 synth_srcs = []
115 test_srcs = []
116 for file in files:
117 if PROJECT_BUILD_PATH in Path(file).parents:
118 # -- Ignore source files from the _build directory.
119 continue
120 if has_testbench_name(file):
121 # -- Handle a testbench file.
122 test_srcs.append(file)
123 else:
124 # -- Handle a synthesis file.
125 synth_srcs.append(file)
127 return (synth_srcs, test_srcs)