Coverage for apio / common / common_util.py: 80%
50 statements
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-24 01:53 +0000
« 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-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
14import sys
15from pathlib import Path
16from glob import glob
17from typing import List, Union, Any, Tuple
18import debugpy
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 print(
44 "Setting PYDEVD_DISABLE_FILE_VALIDATION=1 to disable frozen "
45 "modules warning."
46 )
47 os.environ["PYDEVD_DISABLE_FILE_VALIDATION"] = "1"
48 port = 5678
49 print(f"Apio SCons for remote debugger on port localhost:{port}.")
50 debugpy.listen(port)
51 print(
52 "Attach Visual Studio Code python remote python debugger "
53 f"to port {port}."
54 )
55 # -- Make sure the messages to the user are flushed.
56 sys.stdout.flush()
57 sys.stderr.flush()
59 # -- Block until the debugger connects.
60 debugpy.wait_for_client()
61 # -- Here the remote debugger is attached and the program continues.
62 print("Remote debugger is attached, program continues...")
65def file_sort_key_func(f: Union[str, Path]) -> Any:
66 """Given a file name or path, return a key to sort a file list.
67 The order is lexicography and case sensitive."""
68 path = Path(f)
69 # -- List of directory names in lower case.
70 parents: List[str] = [s.lower() for s in path.parent.parts]
71 # -- File name in lower case.
72 name: str = path.name.lower()
73 # -- Sort by directory and then by file name.
74 return [parents, name]
77def is_source_file(file_name: str) -> bool:
78 """Given a file name, determine by its extension if it's a verilog
79 source file (testbenches included)."""
80 _, ext = os.path.splitext(file_name)
81 return ext in SRC_SUFFIXES
84def has_testbench_name(file_name: str) -> bool:
85 """Given a file name, return true if it's base name indicates a
86 testbench. For example abc_tb.v or _build/abc_tb.out. The file extension
87 is ignored.
88 """
89 name, _ = os.path.splitext(file_name)
90 return name.lower().endswith("_tb")
93def sort_files(files: List[str]) -> List[str]:
94 """Sort a list of files by directory and then by file name.
95 A new sorted list is returned.
96 """
97 # -- Sort the files by directory and then by file name.
98 return sorted(files, key=file_sort_key_func)
101def get_project_source_files() -> Tuple[List[str], List[str]]:
102 """Get the list of source files in the directory tree under the current
103 directory, splitted into synth and testbench lists.
104 If source file has the suffix _tb it's is classified st a testbench,
105 otherwise as a synthesis file.
106 """
107 # -- Get a list of all source files in the project dir.
108 # -- Ideally we should use the scons env.Glob() method but it doesn't
109 # -- work with the recursive=True option. So we use the glob() function
110 # -- instead.
111 files: List[str] = []
112 for ext in SRC_SUFFIXES:
113 files.extend(glob(f"**/*{ext}", recursive=True))
115 # -- Sort the files by directory and then by file name.
116 files = sort_files(files)
118 # -- Split file names to synth files and testbench file lists
119 synth_srcs = []
120 test_srcs = []
121 for file in files:
122 if PROJECT_BUILD_PATH in Path(file).parents:
123 # -- Ignore source files from the _build directory.
124 continue
125 if has_testbench_name(file):
126 # -- Handle a testbench file.
127 test_srcs.append(file)
128 else:
129 # -- Handle a synthesis file.
130 synth_srcs.append(file)
132 return (synth_srcs, test_srcs)