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='get_common_excludes', 126 ), 127] 128 129 130_PROJECT_ROOT_ERROR_MESSAGE = ''' 131Error: Unable to determine the project root directory. Expected environment 132variables are not set. Either $BUILD_WORKSPACE_DIRECTORY for bazel or 133$PW_PROJECT_ROOT for Pigweed bootstrap are required. 134 135Please re-run with either of these scenarios: 136 137 1. Under bazel with "bazelisk run ..." or "bazel run ..." 138 2. After activating a Pigweed bootstrap environment with ". ./activate.sh" 139''' 140 141 142def project_root(env: envparse.EnvNamespace | None = None) -> Path: 143 """Returns the project root by checking bootstrap and bazel env vars. 144 145 Please do not use this function unless the Python script must escape the 146 bazel sandbox. For example, an interactive tool that operates on the project 147 source code like code formatting. 148 """ 149 150 if running_under_bazel(): 151 bazel_source_dir = os.environ.get('BUILD_WORKSPACE_DIRECTORY', '') 152 153 # Ensure this function is only callable by functions in the allow list. 154 check_caller_in(_BAZEL_PROJECT_ROOT_ALLOW_LIST) 155 156 root = Path(bazel_source_dir) 157 else: 158 # Running outside bazel (via GN or bootstrap env). 159 if env is None: 160 env = pigweed_environment() 161 root = env.PW_PROJECT_ROOT 162 163 if not root: 164 print(_PROJECT_ROOT_ERROR_MESSAGE, file=sys.stderr) 165 sys.exit(1) 166 167 return root 168 169 170def running_under_bazel() -> bool: 171 """Returns True if any bazel script environment variables are set. 172 173 For more info on which variables are set when running executables in bazel 174 see: https://bazel.build/docs/user-manual#running-executables 175 """ 176 # The root of the workspace where the build was run. 177 bazel_source_dir = os.environ.get('BUILD_WORKSPACE_DIRECTORY', '') 178 # The current working directory where Bazel was run from. 179 bazel_working_dir = os.environ.get('BUILD_WORKING_DIRECTORY', '') 180 181 return bool(bazel_source_dir or bazel_working_dir) 182