xref: /aosp_15_r20/external/pigweed/pw_env_setup/util.sh (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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
15# Just in case PATH isn't already exported.
16export PATH
17
18# Note: Colors are unfortunately duplicated in several places; and removing the
19# duplication is not easy. Their locations are:
20#
21#   - bootstrap.sh
22#   - pw_cli/color.py
23#   - pw_env_setup/py/pw_env_setup/colors.py
24#   - pw_env_setup/util.fish
25#   - pw_env_setup/util.sh
26#
27# So please keep them matching then modifying them.
28pw_none() {
29  echo -e "$*"
30}
31
32pw_red() {
33  echo -e "\033[0;31m$*\033[0m"
34}
35
36pw_bold_red() {
37  echo -e "\033[1;31m$*\033[0m"
38}
39
40pw_yellow() {
41  echo -e "\033[0;33m$*\033[0m"
42}
43
44pw_bold_yellow() {
45  echo -e "\033[1;33m$*\033[0m"
46}
47
48pw_green() {
49  echo -e "\033[0;32m$*\033[0m"
50}
51
52pw_bold_green() {
53  echo -e "\033[1;32m$*\033[0m"
54}
55
56pw_blue() {
57  echo -e "\033[1;34m$*\033[0m"
58}
59
60pw_cyan() {
61  echo -e "\033[1;36m$*\033[0m"
62}
63
64pw_magenta() {
65  echo -e "\033[0;35m$*\033[0m"
66}
67
68pw_bold_white() {
69  echo -e "\033[1;37m$*\033[0m"
70}
71
72pw_error() {
73  echo -e "\033[1;31m$*\033[0m" >& /dev/stderr
74}
75
76pw_error_info() {
77  echo -e "\033[0;31m$*\033[0m" >& /dev/stderr
78}
79
80pw_eval_sourced() {
81  if [ "$1" -eq 0 ]; then
82    # TODO(pwbug/354) Remove conditional after all downstream projects have
83    # changed to passing in second argument.
84    if [ -n "$2" ]; then
85      _PW_NAME=$(basename "$2" .sh)
86    else
87      _PW_NAME=$(basename "$_BOOTSTRAP_PATH" .sh)
88    fi
89    pw_error "Error: Attempting to $_PW_NAME in a subshell"
90    pw_error_info "  Since $_PW_NAME.sh modifies your shell's environment"
91    pw_error_info "  variables, it must be sourced rather than executed. In"
92    pw_error_info "  particular, 'bash $_PW_NAME.sh' will not work since the "
93    pw_error_info "  modified environment will get destroyed at the end of the"
94    pw_error_info "  script. Instead, source the script's contents in your"
95    pw_error_info "  shell:"
96    pw_error_info ""
97    pw_error_info "    \$ source $_PW_NAME.sh"
98    exit 1
99  fi
100}
101
102pw_check_root() {
103  _PW_ROOT="$1"
104  if [[ "$_PW_ROOT" = *" "* ]]; then
105    pw_error "Error: The Pigweed path contains spaces\n"
106    pw_error_info "  The path '$_PW_ROOT' contains spaces. "
107    pw_error_info "  Pigweed's Python environment currently requires Pigweed to"
108    pw_error_info "  be at a path without spaces. Please checkout Pigweed in a"
109    pw_error_info "  directory without spaces and retry running bootstrap."
110    return -1
111  fi
112}
113
114pw_get_env_root() {
115  # PW_ENVIRONMENT_ROOT allows callers to specify where the environment should
116  # be installed. bootstrap.sh scripts should not use that variable to store the
117  # result of this function. This separation allows scripts to assume
118  # PW_ENVIRONMENT_ROOT came from the caller and not from a previous bootstrap
119  # possibly from another workspace. PW_ENVIRONMENT_ROOT will be cleared after
120  # environment setup completes.
121  if [ -n "$PW_ENVIRONMENT_ROOT" ]; then
122    echo "$PW_ENVIRONMENT_ROOT"
123    return
124  fi
125
126  # Determine project-level root directory.
127  if [ -n "$PW_PROJECT_ROOT" ]; then
128    _PW_ENV_PREFIX="$PW_PROJECT_ROOT"
129  else
130    _PW_ENV_PREFIX="$PW_ROOT"
131  fi
132
133  # If <root>/environment exists, use it. Otherwise, if <root>/.environment
134  # exists, use it. Finally, use <root>/environment.
135  _PW_DOTENV="$_PW_ENV_PREFIX/.environment"
136  _PW_ENV="$_PW_ENV_PREFIX/environment"
137
138  if [ -d "$_PW_DOTENV" ]; then
139    if [ -d "$_PW_ENV" ]; then
140      pw_error "Error: both possible environment directories exist."
141      pw_error_info "  $_PW_DOTENV"
142      pw_error_info "  $_PW_ENV"
143      pw_error_info "  If only one of these folders exists it will be used for"
144      pw_error_info "  the Pigweed environment. If neither exists"
145      pw_error_info "  '<...>/environment' will be used. Since both exist,"
146      pw_error_info "  bootstrap doesn't know which to use. Please delete one"
147      pw_error_info "  or both and rerun bootstrap."
148      exit 1
149    fi
150  fi
151
152  if [ -d "$_PW_ENV" ]; then
153    echo "$_PW_ENV"
154  elif [ -d "$_PW_DOTENV" ]; then
155    echo "$_PW_DOTENV"
156  else
157    echo "$_PW_ENV"
158  fi
159}
160
161# Note: This banner is duplicated in three places; which is a lesser evil than
162# the contortions that would be needed to share this snippet across shell,
163# batch, and Python. Locations:
164#
165#   - pw_env_setup/util.fish
166#   - pw_env_setup/util.sh
167#   - pw_cli/branding.py
168#   - pw_env_setup/py/pw_env_setup/windows_env_start.py
169#
170_PW_BANNER=$(cat <<EOF
171 ▒█████▄   █▓  ▄███▒  ▒█    ▒█ ░▓████▒ ░▓████▒ ▒▓████▄
172  ▒█░  █░ ░█▒ ██▒ ▀█▒ ▒█░ █ ▒█  ▒█   ▀  ▒█   ▀  ▒█  ▀█▌
173  ▒█▄▄▄█░ ░█▒ █▓░ ▄▄░ ▒█░ █ ▒█  ▒███    ▒███    ░█   █▌
174  ▒█▀     ░█░ ▓█   █▓ ░█░ █ ▒█  ▒█   ▄  ▒█   ▄  ░█  ▄█▌
175  ▒█      ░█░ ░▓███▀   ▒█▓▀▓█░ ░▓████▒ ░▓████▒ ▒▓████▀
176EOF
177)
178
179_pw_banner() {
180  pw_magenta "$_PW_BANNER\n"
181}
182
183_PW_BANNER_FUNC="_pw_banner"
184
185_pw_hello() {
186  _PW_TEXT="$1"
187  # If $PW_BANNER_FUNC is defined, use that as the banner function
188  # instead of the default.
189  if [ -n "$PW_BANNER_FUNC" ]; then
190    _PW_BANNER_FUNC="$PW_BANNER_FUNC"
191  fi
192  # Display the banner unless PW_ENVSETUP_QUIET or
193  # PW_ENVSETUP_NO_BANNER is set.
194  if [ -z "$PW_ENVSETUP_QUIET" ] && [ -z "$PW_ENVSETUP_NO_BANNER" ]; then
195    pw_green "\n  WELCOME TO...\n"
196    "$_PW_BANNER_FUNC"
197    pw_green "$_PW_TEXT"
198  fi
199}
200
201pw_deactivate() {
202  # Assume PW_ROOT and PW_PROJECT_ROOT have already been set and we need to
203  # preserve their values.
204  _NEW_PW_ROOT="$PW_ROOT"
205  _NEW_PW_PROJECT_ROOT="$PW_PROJECT_ROOT"
206
207  # Find deactivate script, run it, and then delete it. This way if the
208  # deactivate script is doing something wrong subsequent bootstraps still
209  # have a chance to pass.
210  _PW_DEACTIVATE_SH="$_PW_ACTUAL_ENVIRONMENT_ROOT/deactivate.sh"
211  if [ -f "$_PW_DEACTIVATE_SH" ]; then
212    . "$_PW_DEACTIVATE_SH"
213    rm -f "$_PW_DEACTIVATE_SH" &> /dev/null
214  fi
215
216  # If there's a _pw_deactivate function run it. Redirect output to /dev/null
217  # in case _pw_deactivate doesn't exist. Remove _pw_deactivate when complete.
218  if [ -n "$(command -v _pw_deactivate)" ]; then
219    _pw_deactivate > /dev/null 2> /dev/null
220    unset -f _pw_deactivate
221  fi
222
223  # Restore.
224  PW_ROOT="$_NEW_PW_ROOT"
225  export PW_ROOT
226  PW_PROJECT_ROOT="$_NEW_PW_PROJECT_ROOT"
227  export PW_PROJECT_ROOT
228}
229
230deactivate() {
231  pw_deactivate
232  unset -f pw_deactivate
233  unset -f deactivate
234  unset PW_ROOT
235  unset PW_PROJECT_ROOT
236  unset PW_BRANDING_BANNER
237  unset PW_BRANDING_BANNER_COLOR
238}
239
240# The next three functions use the following variables.
241# * PW_BANNER_FUNC: function to print banner
242# * PW_BOOTSTRAP_PYTHON: specific Python interpreter to use for bootstrap
243# * PW_ROOT: path to Pigweed root
244# * PW_ENVSETUP_QUIET: limit output if "true"
245#
246# All arguments passed in are passed on to env_setup.py in pw_bootstrap,
247# pw_activate takes no arguments, and pw_finalize takes the name of the script
248# "bootstrap" or "activate" and the path to the setup script written by
249# bootstrap.sh.
250pw_bootstrap() {
251  _pw_hello "  BOOTSTRAP! Bootstrap may take a few minutes; please be patient.\n"
252
253  local _pw_alias_check=0
254  alias python > /dev/null 2> /dev/null || _pw_alias_check=$?
255  if [ "$_pw_alias_check" -eq 0 ]; then
256    pw_error "Error: 'python' is an alias"
257    pw_error_info "The shell has a 'python' alias set. This causes many obscure"
258    pw_error_info "Python-related issues both in and out of Pigweed. Please"
259    pw_error_info "remove the Python alias from your shell init file or at"
260    pw_error_info "least run the following command before bootstrapping"
261    pw_error_info "Pigweed."
262    pw_error_info
263    pw_error_info "  unalias python"
264    pw_error_info
265    return
266  fi
267
268  # Allow forcing a specific version of Python for testing pursposes.
269  if [ -n "$PW_BOOTSTRAP_PYTHON" ]; then
270    _PW_PYTHON="$PW_BOOTSTRAP_PYTHON"
271  elif command -v python3 > /dev/null 2> /dev/null; then
272    _PW_PYTHON=python3
273  elif command -v python > /dev/null 2> /dev/null; then
274    _PW_PYTHON=python
275  else
276    pw_error "Error: No system Python present\n"
277    pw_error_info "  Pigweed's bootstrap process requires a local system"
278    pw_error_info "  Python. Please install Python on your system, add it to "
279    pw_error_info "  your PATH and re-try running bootstrap."
280    return
281  fi
282
283  local _pw_python_major_version=$($_PW_PYTHON -c 'import sys; print(sys.version_info[0])')
284  if [ "$_pw_python_major_version" != "3" ]; then
285    pw_error "Error: System Python is not Python 3\n"
286    pw_error_info "  The system Python is not Python 3. Please install Python 3"
287    pw_error_info "  and rerun bootstrap. Note that you may need to open a new"
288    pw_error_info "  terminal to see the newly installed Python."
289    return
290  fi
291
292  if [ -n "$_PW_ENV_SETUP" ]; then
293    "$_PW_ENV_SETUP" "$@"
294    _PW_ENV_SETUP_STATUS="$?"
295  else
296    "$_PW_PYTHON" "$PW_ROOT/pw_env_setup/py/pw_env_setup/env_setup.py" "$@"
297    _PW_ENV_SETUP_STATUS="$?"
298  fi
299
300  # Write the directory path at bootstrap time into the directory. This helps
301  # us double-check things are still in the same space when calling activate.
302  _PW_ENV_ROOT_TXT="$_PW_ACTUAL_ENVIRONMENT_ROOT/env_root.txt"
303  echo "$_PW_ACTUAL_ENVIRONMENT_ROOT" > "$_PW_ENV_ROOT_TXT" 2> /dev/null
304
305  # Create the environment README file. Use quotes to prevent alias expansion.
306  "cp" "$PW_ROOT/pw_env_setup/destination.md" "$_PW_ACTUAL_ENVIRONMENT_ROOT/README.md" &> /dev/null
307}
308
309pw_activate() {
310  _pw_hello "  ACTIVATOR! This sets your shell environment variables.\n"
311  _PW_ENV_SETUP_STATUS=0
312}
313
314pw_finalize() {
315  _PW_NAME="$1"
316  _PW_SETUP_SH="$2"
317
318  # Check that the environment directory agrees that the path it's at matches
319  # where it thinks it should be. If not, bail.
320  _PW_ENV_ROOT_TXT="$_PW_ACTUAL_ENVIRONMENT_ROOT/env_root.txt"
321  if [ -f "$_PW_ENV_ROOT_TXT" ]; then
322    _PW_PREV_ENV_ROOT="$(cat $_PW_ENV_ROOT_TXT)"
323    if [ "$_PW_ACTUAL_ENVIRONMENT_ROOT" != "$_PW_PREV_ENV_ROOT" ]; then
324      pw_error "Error: Environment directory moved"
325      pw_error_info "This Pigweed environment was created at"
326      pw_error_info
327      pw_error_info "    $_PW_PREV_ENV_ROOT"
328      pw_error_info
329      pw_error_info "But it is now being activated from"
330      pw_error_info
331      pw_error_info "    $_PW_ACTUAL_ENVIRONMENT_ROOT"
332      pw_error_info
333      pw_error_info "This is likely because the checkout moved. After moving "
334      pw_error_info "the checkout a full '. ./bootstrap.sh' is required."
335      pw_error_info
336      _PW_ENV_SETUP_STATUS=1
337    fi
338  fi
339
340  if [ "$_PW_ENV_SETUP_STATUS" -ne 0 ]; then
341     return
342  fi
343
344  if [ -f "$_PW_SETUP_SH" ]; then
345    . "$_PW_SETUP_SH"
346
347    if [ "$?" -eq 0 ]; then
348      if [ "$_PW_NAME" = "bootstrap" ] && [ -z "$PW_ENVSETUP_QUIET" ]; then
349        echo "To reactivate this environment in the future, run this in your "
350        echo "terminal:"
351        echo
352        pw_green "  source ./activate.sh"
353        echo
354        echo "To deactivate this environment, run this:"
355        echo
356        pw_green "  deactivate"
357        echo
358      fi
359    else
360      pw_error "Error during $_PW_NAME--see messages above."
361    fi
362  else
363    pw_error "Error during $_PW_NAME--see messages above."
364  fi
365}
366
367pw_install_post_checkout_hook() {
368  cp "$PW_ROOT/pw_env_setup/post-checkout-hook.sh" "$PW_PROJECT_ROOT/.git/hooks/post-checkout"
369}
370
371pw_cleanup() {
372  unset _PW_BANNER
373  unset _PW_BANNER_FUNC
374  unset PW_BANNER_FUNC
375  unset _PW_ENV_SETUP
376  unset _PW_NAME
377  unset PW_ENVIRONMENT_ROOT
378  unset _PW_PYTHON
379  unset _PW_ENV_ROOT_TXT
380  unset _PW_PREV_ENV_ROOT
381  unset _PW_SETUP_SH
382  unset _PW_DEACTIVATE_SH
383  unset _NEW_PW_ROOT
384  unset _PW_ENV_SETUP_STATUS
385  unset _PW_ENV_PREFIX
386  unset _PW_ENV
387  unset _PW_DOTENV
388
389  unset -f pw_none
390  unset -f pw_red
391  unset -f pw_bold_red
392  unset -f pw_yellow
393  unset -f pw_bold_yellow
394  unset -f pw_green
395  unset -f pw_bold_green
396  unset -f pw_blue
397  unset -f pw_cyan
398  unset -f pw_magenta
399  unset -f pw_bold_white
400  unset -f pw_eval_sourced
401  unset -f pw_check_root
402  unset -f pw_get_env_root
403  unset -f _pw_banner
404  unset -f pw_bootstrap
405  unset -f pw_activate
406  unset -f pw_finalize
407  unset -f pw_install_post_checkout_hook
408  unset -f pw_cleanup
409  unset -f _pw_hello
410  unset -f pw_error
411  unset -f pw_error_info
412}
413