Coverage for apio/scons/apio_env.py: 81%
68 statements
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-23 03:53 +0000
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-23 03: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"""A class with common services for the apio scons handlers."""
12import os
13from typing import Any
14from SCons.Script.SConscript import SConsEnvironment
15from SCons.Environment import BuilderWrapper
16import SCons.Defaults
17from apio.common.debug_util import is_debug
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_scons_pb2 import SconsParams
24class ApioEnv:
25 """Provides abstracted scons env and other user services."""
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
36 # -- Create the base target.
37 self.target = str(self.env_build_path / "hardware")
39 # -- Create the target for the graph files (.dot, .svg, etc)
40 self.graph_target = str(self.env_build_path / "graph")
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=[])
51 # -- Create the underlying scons env.
52 self.scons_env = SConsEnvironment(ENV=os.environ, tools=[])
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 )
60 # Extra info for debugging.
61 if is_debug(2): 61 ↛ 62line 61 didn't jump to line 62 because the condition on line 61 was never true
62 cout(f"command_line_targets: {command_line_targets}")
63 self.dump_env_vars()
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
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)
76 @property
77 def is_windows(self):
78 """Returns True if we run on windows."""
79 return self.params.environment.is_windows
81 @property
82 def platform_id(self):
83 """Returns the platform id."""
84 return self.params.environment.platform_id
86 @property
87 def scons_shell_id(self):
88 """Returns the shell id that scons is expected to use.."""
89 return self.params.environment.scons_shell_id
91 def targeting_one_of(self, *target_names) -> bool:
92 """Returns true if the any of the named target was specified in the
93 scons command line."""
94 for target_name in target_names:
95 if target_name in self.command_line_targets:
96 return True
97 return False
99 def add_builder(self, builder_id: str, builder):
100 """Append to the scons env a builder with given id. The env
101 adds it to the BUILDERS dict and also adds to itself an attribute with
102 that name that contains a wrapper to that builder."""
103 self.scons_env.Append(BUILDERS={builder_id: builder})
105 def add_builder_target(
106 self,
107 *,
108 builder_id: str,
109 target,
110 sources: list[Any],
111 extra_dependencies: list | None = None,
112 always_build: bool = False,
113 ):
114 """Creates an return a target that uses the builder with given id."""
116 # pylint: disable=too-many-arguments
118 # -- Scons wraps the builder with a wrapper. We use it to create the
119 # -- new target.
120 builder_wrapper: BuilderWrapper = getattr(self.scons_env, builder_id)
121 target = builder_wrapper(target, sources)
122 # -- Mark as 'always build' if requested.
123 if always_build:
124 self.scons_env.AlwaysBuild(target)
125 # -- Add extra dependencies, if any.
126 if extra_dependencies:
127 for dependency in extra_dependencies:
128 self.scons_env.Depends(target, dependency)
129 return target
131 def add_alias(
132 self, name, *, source, action=None, always_build: bool = False
133 ):
134 """Creates a target with given dependencies"""
135 target = self.scons_env.Alias(name, source, action)
136 if always_build:
137 self.scons_env.AlwaysBuild(target)
138 return target
140 def dump_env_vars(self) -> None:
141 """Prints a list of the environment variables. For debugging."""
142 sc = self.scons_env
143 dictionary: dict = sc.Dictionary()
144 keys = list(dictionary.keys())
145 keys.sort()
146 cout("")
147 cout(">>> Env vars BEGIN", style=EMPH3)
148 for key in keys:
149 cout(f"{key} = {self.scons_env[key]}")
150 cout("<<< Env vars END\n", style=EMPH3)