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

62 statements  

« 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"""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 # -- Create the underlying scons env. 

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

44 

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

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

47 self.scons_env.SConsignFile( 

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

49 ) 

50 

51 # -- Since we ae not using the default environment, make sure it was 

52 # -- not used unintentionally, e.g. in tests that run create multiple 

53 # -- scons env in the same session. 

54 # -- 

55 assert ( 

56 SCons.Defaults._default_env is None 

57 ), "DefaultEnvironment already exists" 

58 

59 # Extra info for debugging. 

60 if self.is_debug(2): 

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

62 self.dump_env_vars() 

63 

64 @property 

65 def env_name(self): 

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

67 return self.params.apio_env_params.env_name 

68 

69 @property 

70 def env_build_path(self): 

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

72 dir.""" 

73 return env_build_path(self.env_name) 

74 

75 @property 

76 def is_windows(self): 

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

78 return self.params.environment.is_windows 

79 

80 def is_debug(self, level: int): 

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

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

83 

84 def targeting(self, *target_names) -> bool: 

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

86 scons command line.""" 

87 for target_name in target_names: 

88 if target_name in self.command_line_targets: 

89 return True 

90 return False 

91 

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

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

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

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

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

97 

98 def builder_target( 

99 self, 

100 *, 

101 builder_id: str, 

102 target, 

103 sources: List, 

104 extra_dependencies: Optional[List] = None, 

105 always_build: bool = False, 

106 ): 

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

108 

109 # pylint: disable=too-many-arguments 

110 

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

112 # -- new target. 

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

114 target = builder_wrapper(target, sources) 

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

116 if always_build: 

117 self.scons_env.AlwaysBuild(target) 

118 # -- Add extra dependencies, if any. 

119 if extra_dependencies: 

120 for dependency in extra_dependencies: 

121 self.scons_env.Depends(target, dependency) 

122 return target 

123 

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

125 """Creates a target with given dependencies""" 

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

127 if always_build: 

128 self.scons_env.AlwaysBuild(target) 

129 return target 

130 

131 def dump_env_vars(self) -> None: 

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

133 dictionary = self.scons_env.Dictionary() 

134 keys = list(dictionary.keys()) 

135 keys.sort() 

136 cout("") 

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

138 for key in keys: 

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

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