• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2020 The Pigweed Authors
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may not
4# use this file except in compliance with the License. You may obtain a copy of
5# the License at
6#
7#     https://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations under
13# the License.
14"""The env module defines the environment variables used by Pigweed."""
15
16from pathlib import Path
17import os
18import sys
19
20from pw_cli import envparse
21from pw_cli.allowed_caller import AllowedCaller, check_caller_in
22
23
24def pigweed_environment_parser() -> envparse.EnvironmentParser:
25    """Defines Pigweed's environment variables on an EnvironmentParser."""
26    parser = envparse.EnvironmentParser(prefix='PW_')
27
28    parser.add_var('PW_BOOTSTRAP_PYTHON')
29    parser.add_var('PW_ENABLE_PRESUBMIT_HOOK_WARNING', default=False)
30    parser.add_var('PW_EMOJI', type=envparse.strict_bool, default=False)
31    parser.add_var('PW_ENVSETUP')
32    parser.add_var('PW_ENVSETUP_FULL')
33    parser.add_var(
34        'PW_ENVSETUP_NO_BANNER', type=envparse.strict_bool, default=False
35    )
36    parser.add_var(
37        'PW_ENVSETUP_QUIET', type=envparse.strict_bool, default=False
38    )
39    parser.add_var('PW_ENVIRONMENT_ROOT', type=Path)
40    parser.add_var('PW_PACKAGE_ROOT', type=Path)
41    parser.add_var('PW_PROJECT_ROOT', type=Path)
42    parser.add_var('PW_ROOT', type=Path)
43    parser.add_var(
44        'PW_DISABLE_ROOT_GIT_REPO_CHECK',
45        type=envparse.strict_bool,
46        default=False,
47    )
48    parser.add_var('PW_SKIP_BOOTSTRAP')
49    parser.add_var('PW_SUBPROCESS', type=envparse.strict_bool, default=False)
50    parser.add_var('PW_USE_COLOR', type=envparse.strict_bool, default=True)
51    parser.add_var('PW_USE_GCS_ENVSETUP', type=envparse.strict_bool)
52
53    parser.add_allowed_suffix('_CIPD_INSTALL_DIR')
54
55    parser.add_var(
56        'PW_ENVSETUP_DISABLE_SPINNER', type=envparse.strict_bool, default=False
57    )
58    parser.add_var('PW_DOCTOR_SKIP_CIPD_CHECKS')
59    parser.add_var(
60        'PW_ACTIVATE_SKIP_CHECKS', type=envparse.strict_bool, default=False
61    )
62
63    parser.add_var('PW_BANNER_FUNC')
64    parser.add_var('PW_BRANDING_BANNER')
65    parser.add_var('PW_BRANDING_BANNER_COLOR', default='magenta')
66
67    parser.add_var(
68        'PW_PRESUBMIT_DISABLE_SUBPROCESS_CAPTURE', type=envparse.strict_bool
69    )
70
71    parser.add_var('PW_CONSOLE_CONFIG_FILE')
72    parser.add_var('PW_ENVIRONMENT_NO_ERROR_ON_UNRECOGNIZED')
73
74    parser.add_var('PW_NO_CIPD_CACHE_DIR')
75    parser.add_var('PW_CIPD_SERVICE_ACCOUNT_JSON')
76
77    # RBE environment variables
78    parser.add_var('PW_USE_RBE', default=False)
79    parser.add_var('PW_RBE_DEBUG', default=False)
80    parser.add_var('PW_RBE_CLANG_CONFIG', default='')
81    parser.add_var('PW_RBE_ARM_GCC_CONFIG', default='')
82
83    parser.add_var(
84        'PW_DISABLE_CLI_ANALYTICS', type=envparse.strict_bool, default=False
85    )
86
87    return parser
88
89
90# Internal: memoize environment parsing to avoid unnecessary computation in
91# multiple calls to pigweed_environment().
92_memoized_environment: envparse.EnvNamespace | None = None
93
94
95def pigweed_environment() -> envparse.EnvNamespace:
96    """Returns Pigweed's parsed environment."""
97    global _memoized_environment  # pylint: disable=global-statement
98
99    if _memoized_environment is None:
100        _memoized_environment = pigweed_environment_parser().parse_env()
101
102    return _memoized_environment
103
104
105_BAZEL_PROJECT_ROOT_ALLOW_LIST = [
106    AllowedCaller(
107        filename='pw_build/py/pw_build/pigweed_upstream_build.py',
108        name='__main__',
109        function='<module>',
110    ),
111    AllowedCaller(
112        filename='pw_build/py/pw_build/project_builder.py',
113        name='*',
114        function='__init__',
115        self_class='ProjectBuilder',
116    ),
117    AllowedCaller(
118        filename='pw_build/py/pw_build/project_builder_presubmit_runner.py',
119        name='pw_build.project_builder_presubmit_runner',
120        function='main',
121    ),
122    AllowedCaller(
123        filename='pw_watch/py/pw_watch/watch.py',
124        name='__main__',
125        function='watch_setup',
126    ),
127    AllowedCaller(
128        filename='pw_watch/py/pw_watch/run.py',
129        name='__main__',
130        function='_parse_args',
131    ),
132]
133
134
135_PROJECT_ROOT_ERROR_MESSAGE = '''
136Error: Unable to determine the project root directory. Expected environment
137variables are not set. Either $BUILD_WORKSPACE_DIRECTORY for bazel or
138$PW_PROJECT_ROOT for Pigweed bootstrap are required.
139
140Please re-run with either of these scenarios:
141
142  1. Under bazel with "bazelisk run ..." or "bazel run ..."
143  2. After activating a Pigweed bootstrap environment with ". ./activate.sh"
144'''
145
146
147def project_root(env: envparse.EnvNamespace | None = None) -> Path:
148    """Returns the project root by checking bootstrap and bazel env vars.
149
150    Please do not use this function unless the Python script must escape the
151    bazel sandbox. For example, an interactive tool that operates on the project
152    source code like code formatting.
153    """
154
155    if running_under_bazel():
156        bazel_source_dir = os.environ.get('BUILD_WORKSPACE_DIRECTORY', '')
157
158        # Ensure this function is only callable by functions in the allow list.
159        check_caller_in(_BAZEL_PROJECT_ROOT_ALLOW_LIST)
160
161        root = Path(bazel_source_dir)
162    else:
163        # Running outside bazel (via GN or bootstrap env).
164        if env is None:
165            env = pigweed_environment()
166        root = env.PW_PROJECT_ROOT
167
168    if not root:
169        print(_PROJECT_ROOT_ERROR_MESSAGE, file=sys.stderr)
170        sys.exit(1)
171
172    return root
173
174
175def running_under_bazel() -> bool:
176    """Returns True if any bazel script environment variables are set.
177
178    For more info on which variables are set when running executables in bazel
179    see: https://bazel.build/docs/user-manual#running-executables
180    """
181    # The root of the workspace where the build was run.
182    bazel_source_dir = os.environ.get('BUILD_WORKSPACE_DIRECTORY', '')
183    # The current working directory where Bazel was run from.
184    bazel_working_dir = os.environ.get('BUILD_WORKING_DIRECTORY', '')
185
186    return bool(bazel_source_dir or bazel_working_dir)
187