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

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

12 

13import os 

14import sys 

15from pathlib import Path 

16from glob import glob 

17from typing import List, Union, Any, Tuple 

18import debugpy 

19 

20# -- A list with the file extensions of the source files. 

21SRC_SUFFIXES = [".v", ".sv"] 

22 

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

26 

27 

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 

32 

33 

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() 

58 

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

63 

64 

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] 

75 

76 

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 

82 

83 

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

91 

92 

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) 

99 

100 

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

114 

115 # -- Sort the files by directory and then by file name. 

116 files = sort_files(files) 

117 

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) 

131 

132 return (synth_srcs, test_srcs)