Coverage for apio / scons / apio_env.py: 98%

68 statements  

« prev     ^ index     » next       coverage.py v7.13.3, created at 2026-02-08 02:47 +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"""A class with common services for the apio scons handlers.""" 

11 

12 

13import os 

14from typing import List, Optional 

15from SCons.Script.SConscript import SConsEnvironment 

16from SCons.Environment import BuilderWrapper 

17import SCons.Defaults 

18from apio.common.apio_console import cout 

19from apio.common.apio_styles import EMPH3 

20from apio.common.common_util import env_build_path 

21from apio.common.proto.apio_pb2 import SconsParams 

22 

23 

24class ApioEnv: 

25 """Provides abstracted scons env and other user services.""" 

26 

27 def __init__( 

28 self, 

29 command_line_targets: List[str], 

30 scons_params: SconsParams, 

31 ): 

32 # -- Save the arguments. 

33 self.command_line_targets = command_line_targets 

34 self.params = scons_params 

35 

36 # -- Create the base target. 

37 self.target = str(self.env_build_path / "hardware") 

38 

39 # -- Create the target for the graph files (.dot, .svg, etc) 

40 self.graph_target = str(self.env_build_path / "graph") 

41 

42 # -- Initialized the scons default environment with no tools even 

43 # -- though we don't use it. This is to avoid the issue reported 

44 # -- at https://github.com/FPGAwars/apio/issues/802 in which scons 

45 # -- triggers a gcc installation dialog box on MacOs. 

46 # 

47 # -- Note that DefaultEnvironment is a funny function that replaces 

48 # -- itself with _fetch_DefaultEnvironment() after the first call. 

49 SCons.Defaults.DefaultEnvironment(ENV=os.environ, tools=[]) 

50 

51 # -- Create the underlying scons env. 

52 self.scons_env = SConsEnvironment(ENV=os.environ, tools=[]) 

53 

54 # -- Set the location of the scons incremental build database. 

55 # -- By default it would be stored in project root dir. 

56 self.scons_env.SConsignFile( 

57 self.env_build_path.absolute() / "sconsign.dblite" 

58 ) 

59 

60 # Extra info for debugging. 

61 if self.is_debug(2): 

62 cout(f"command_line_targets: {command_line_targets}") 

63 self.dump_env_vars() 

64 

65 @property 

66 def env_name(self): 

67 """Return the action apio env name for this invocation.""" 

68 return self.params.apio_env_params.env_name 

69 

70 @property 

71 def env_build_path(self): 

72 """Returns a relative path from the project dir to the env build 

73 dir.""" 

74 return env_build_path(self.env_name) 

75 

76 @property 

77 def is_windows(self): 

78 """Returns True if we run on windows.""" 

79 return self.params.environment.is_windows 

80 

81 def is_debug(self, level: int): 

82 """Returns true if we run in debug mode.""" 

83 return self.params.environment.debug_level >= level 

84 

85 @property 

86 def platform_id(self): 

87 """Returns the platform id.""" 

88 return self.params.environment.platform_id 

89 

90 @property 

91 def scons_shell_id(self): 

92 """Returns the shell id that scons is expected to use..""" 

93 return self.params.environment.scons_shell_id 

94 

95 def targeting_one_of(self, *target_names) -> bool: 

96 """Returns true if the any of the named target was specified in the 

97 scons command line.""" 

98 for target_name in target_names: 

99 if target_name in self.command_line_targets: 

100 return True 

101 return False 

102 

103 def builder(self, builder_id: str, builder): 

104 """Append to the scons env a builder with given id. The env 

105 adds it to the BUILDERS dict and also adds to itself an attribute with 

106 that name that contains a wrapper to that builder.""" 

107 self.scons_env.Append(BUILDERS={builder_id: builder}) 

108 

109 def builder_target( 

110 self, 

111 *, 

112 builder_id: str, 

113 target, 

114 sources: List, 

115 extra_dependencies: Optional[List] = None, 

116 always_build: bool = False, 

117 ): 

118 """Creates an return a target that uses the builder with given id.""" 

119 

120 # pylint: disable=too-many-arguments 

121 

122 # -- Scons wraps the builder with a wrapper. We use it to create the 

123 # -- new target. 

124 builder_wrapper: BuilderWrapper = getattr(self.scons_env, builder_id) 

125 target = builder_wrapper(target, sources) 

126 # -- Mark as 'always build' if requested. 

127 if always_build: 

128 self.scons_env.AlwaysBuild(target) 

129 # -- Add extra dependencies, if any. 

130 if extra_dependencies: 

131 for dependency in extra_dependencies: 

132 self.scons_env.Depends(target, dependency) 

133 return target 

134 

135 def alias(self, name, *, source, action=None, always_build: bool = False): 

136 """Creates a target with given dependencies""" 

137 target = self.scons_env.Alias(name, source, action) 

138 if always_build: 

139 self.scons_env.AlwaysBuild(target) 

140 return target 

141 

142 def dump_env_vars(self) -> None: 

143 """Prints a list of the environment variables. For debugging.""" 

144 dictionary = self.scons_env.Dictionary() 

145 keys = list(dictionary.keys()) 

146 keys.sort() 

147 cout("") 

148 cout(">>> Env vars BEGIN", style=EMPH3) 

149 for key in keys: 

150 cout(f"{key} = {self.scons_env[key]}") 

151 cout("<<< Env vars END\n", style=EMPH3)