1#!/usr/bin/env bash 2# Copyright 2016 The TensorFlow Authors. All Rights Reserved. 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15# ============================================================================== 16# 17# Build the Python PIP installation package for TensorFlow and install 18# the package. 19# The PIP installation is done using the --user flag. 20# 21# Usage: 22# pip.sh CONTAINER_TYPE [--test_tutorials] [--integration_tests] [bazel flags] 23# 24# When executing the Python unit tests, the script obeys the shell 25# variables: TF_BUILD_BAZEL_CLEAN, TF_BUILD_INSTALL_EXTRA_PIP_PACKAGES, 26# NO_TEST_ON_INSTALL, PIP_TEST_ROOT, TF_NIGHTLY 27# 28# TF_BUILD_BAZEL_CLEAN, if set to any non-empty and non-0 value, directs the 29# script to perform bazel clean prior to main build and test steps. 30# 31# TF_BUILD_INSTALL_EXTRA_PIP_PACKAGES overrides the default extra pip packages 32# to be installed in virtualenv before run_pip_tests.sh is called. Multiple 33# pakcage names are separated with spaces. 34# 35# If NO_TEST_ON_INSTALL has any non-empty and non-0 value, the test-on-install 36# part will be skipped. 37# 38# If NO_TEST_USER_OPS has any non-empty and non-0 value, the testing of user- 39# defined ops against the installation will be skipped. 40# 41# If NO_TEST_TFDBG_BINARIES has any non-empty and non-0 value, the testing of 42# TensorFlow Debugger (tfdbg) binaries and examples will be skipped. 43# 44# If PIP_TEST_ROOT has a non-empty and a non-0 value, the whl files will be 45# placed in that directory. 46# 47# If TF_NIGHTLY has a non-empty and a non-0 value, the name of the project will 48# be changed to tf_nightly or tf_nightly_gpu. 49# 50# Any flags not listed in the usage above will be passed directly to Bazel. 51# 52# If the --test_tutorials flag is set, it will cause the script to run the 53# tutorial tests (see test_tutorials.sh) after the PIP 54# installation and the Python unit tests-on-install step. Likewise, 55# --integration_tests will cause the integration tests (integration_tests.sh) 56# to run. 57# 58 59# Helper function: Strip leading and trailing whitespaces 60str_strip () { 61 echo -e "$1" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' 62} 63 64# Fixed naming patterns for wheel (.whl) files given different python versions 65if [[ $(uname) == "Linux" ]]; then 66 declare -A WHL_TAGS 67 WHL_TAGS=(["2.7"]="cp27-none" ["3.4"]="cp34-cp34m" ["3.5"]="cp35-cp35m") 68fi 69 70 71INSTALL_EXTRA_PIP_PACKAGES=${TF_BUILD_INSTALL_EXTRA_PIP_PACKAGES} 72 73 74# Script directory 75SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 76source "${SCRIPT_DIR}/builds_common.sh" 77 78 79SKIP_RETURN_CODE=112 80 81 82# Get the command line arguments 83CONTAINER_TYPE=$( echo "$1" | tr '[:upper:]' '[:lower:]' ) 84shift 85 86if [[ -n "${TF_BUILD_BAZEL_CLEAN}" ]] && \ 87 [[ "${TF_BUILD_BAZEL_CLEAN}" != "0" ]]; then 88 echo "TF_BUILD_BAZEL_CLEAN=${TF_BUILD_BAZEL_CLEAN}: Performing 'bazel clean'" 89 bazel clean 90fi 91 92DO_TEST_USER_OPS=1 93if [[ -n "${NO_TEST_USER_OPS}" ]] && \ 94 [[ "${NO_TEST_USER_OPS}" != "0" ]]; then 95 echo "NO_TEST_USER_OPS=${NO_TEST_USER_OPS}: Will skip testing of user ops" 96 DO_TEST_USER_OPS=0 97fi 98 99DO_TEST_TFDBG_BINARIES=1 100if [[ -n "${NO_TEST_TFDBG_BINARIES}" ]] && \ 101 [[ "${NO_TEST_TFDBG_BINARIES}" != "0" ]]; then 102 echo "NO_TEST_TFDBG_BINARIES=${NO_TEST_TFDBG_BINARIES}: Will skip testing of tfdbg binaries" 103 DO_TEST_TFDBG_BINARIES=0 104fi 105 106DO_TEST_TUTORIALS=0 107DO_INTEGRATION_TESTS=0 108BAZEL_FLAGS="" 109while true; do 110 if [[ "${1}" == "--test_tutorials" ]]; then 111 DO_TEST_TUTORIALS=1 112 elif [[ "${1}" == "--integration_tests" ]]; then 113 DO_INTEGRATION_TESTS=1 114 else 115 BAZEL_FLAGS="${BAZEL_FLAGS} ${1}" 116 fi 117 118 shift 119 if [[ -z "${1}" ]]; then 120 break 121 fi 122done 123 124BAZEL_FLAGS=$(str_strip "${BAZEL_FLAGS}") 125 126if [[ -z "$GIT_TAG_OVERRIDE" ]]; then 127 BAZEL_FLAGS+=" --action_env=GIT_TAG_OVERRIDE" 128fi 129 130echo "Using Bazel flags: ${BAZEL_FLAGS}" 131 132PIP_BUILD_TARGET="//tensorflow/tools/pip_package:build_pip_package" 133GPU_FLAG="" 134if [[ ${CONTAINER_TYPE} == "cpu" ]] || \ 135 [[ ${CONTAINER_TYPE} == "rocm" ]] || \ 136 [[ ${CONTAINER_TYPE} == "debian.jessie.cpu" ]]; then 137 bazel build ${BAZEL_FLAGS} ${PIP_BUILD_TARGET} || \ 138 die "Build failed." 139elif [[ ${CONTAINER_TYPE} == "gpu" ]]; then 140 bazel build ${BAZEL_FLAGS} ${PIP_BUILD_TARGET} || \ 141 die "Build failed." 142 GPU_FLAG="--gpu" 143else 144 die "Unrecognized container type: \"${CONTAINER_TYPE}\"" 145fi 146 147MAC_FLAG="" 148if [[ $(uname) == "Darwin" ]]; then 149 MAC_FLAG="--mac" 150fi 151 152 153# Check if in a virtualenv 154IN_VENV=$(python -c 'import sys; print("1" if hasattr(sys, "real_prefix") else "0")') 155# If still in a virtualenv, deactivate it first 156if [[ "$IN_VENV" == "1" ]]; then 157 echo "It appears that we are already in a virtualenv. Deactivating..." 158 deactivate || die "FAILED: Unable to deactivate from existing virtualenv" 159fi 160 161# Obtain the path to Python binary 162source tools/python_bin_path.sh 163 164# Assume: PYTHON_BIN_PATH is exported by the script above 165if [[ -z "$PYTHON_BIN_PATH" ]]; then 166 die "PYTHON_BIN_PATH was not provided. Did you run configure?" 167fi 168 169# Determine the major and minor versions of Python being used (e.g., 2.7) 170# This info will be useful for determining the directory of the local pip 171# installation of Python 172PY_MAJOR_MINOR_VER=$(${PYTHON_BIN_PATH} -V 2>&1 | awk '{print $NF}' | cut -d. -f-2) 173if [[ -z "${PY_MAJOR_MINOR_VER}" ]]; then 174 die "ERROR: Unable to determine the major.minor version of Python" 175fi 176 177echo "Python binary path to be used in PIP install: ${PYTHON_BIN_PATH} "\ 178"(Major.Minor version: ${PY_MAJOR_MINOR_VER})" 179 180# Create a TF_NIGHTLY argument if this is a nightly build 181PROJECT_NAME="tensorflow" 182NIGHTLY_FLAG="" 183if [ -n "$TF_NIGHTLY" ]; then 184 PROJECT_NAME="tf_nightly" 185 NIGHTLY_FLAG="--nightly_flag" 186fi 187 188# Build PIP Wheel file 189# Set default pip file folder unless specified by env variable 190if [ -z "$PIP_TEST_ROOT" ]; then 191 PIP_TEST_ROOT="pip_test" 192fi 193PIP_WHL_DIR="${PIP_TEST_ROOT}/whl" 194PIP_WHL_DIR=$(realpath ${PIP_WHL_DIR}) # Get absolute path 195rm -rf ${PIP_WHL_DIR} && mkdir -p ${PIP_WHL_DIR} 196bazel-bin/tensorflow/tools/pip_package/build_pip_package ${PIP_WHL_DIR} ${GPU_FLAG} ${NIGHTLY_FLAG} || \ 197 die "build_pip_package FAILED" 198 199WHL_PATH=$(ls ${PIP_WHL_DIR}/${PROJECT_NAME}*.whl) 200if [[ $(echo ${WHL_PATH} | wc -w) -ne 1 ]]; then 201 die "ERROR: Failed to find exactly one built TensorFlow .whl file in "\ 202"directory: ${PIP_WHL_DIR}" 203fi 204 205# Print the size of the PIP wheel file. 206echo 207echo "Size of the PIP wheel file built: $(ls -l ${WHL_PATH} | awk '{print $5}')" 208echo 209 210# Rename the whl file properly so it will have the python 211# version tags and platform tags that won't cause pip install issues. 212if [[ $(uname) == "Linux" ]]; then 213 PY_TAGS=${WHL_TAGS[${PY_MAJOR_MINOR_VER}]} 214 PLATFORM_TAG=$(to_lower "$(uname)_$(uname -m)") 215# MAC has bash v3, which does not have associative array 216elif [[ $(uname) == "Darwin" ]]; then 217 if [[ ${PY_MAJOR_MINOR_VER} == "2.7" ]]; then 218 PY_TAGS="py2-none" 219 elif [[ ${PY_MAJOR_MINOR_VER} == "3.5" ]]; then 220 PY_TAGS="py3-none" 221 elif [[ ${PY_MAJOR_MINOR_VER} == "3.6" ]]; then 222 PY_TAGS="py3-none" 223 fi 224 PLATFORM_TAG="any" 225fi 226 227WHL_DIR=$(dirname "${WHL_PATH}") 228WHL_BASE_NAME=$(basename "${WHL_PATH}") 229 230if [[ -n "${PY_TAGS}" ]]; then 231 NEW_WHL_BASE_NAME=$(echo ${WHL_BASE_NAME} | cut -d \- -f 1)-\ 232$(echo ${WHL_BASE_NAME} | cut -d \- -f 2)-${PY_TAGS}-${PLATFORM_TAG}.whl 233 234 if [[ ! -f "${WHL_DIR}/${NEW_WHL_BASE_NAME}" ]]; then 235 if cp "${WHL_DIR}/${WHL_BASE_NAME}" "${WHL_DIR}/${NEW_WHL_BASE_NAME}" 236 then 237 echo "Copied wheel file: ${WHL_BASE_NAME} --> ${NEW_WHL_BASE_NAME}" 238 else 239 die "ERROR: Failed to copy wheel file to ${NEW_WHL_BASE_NAME}" 240 fi 241 fi 242fi 243 244if [[ $(uname) == "Linux" ]]; then 245 AUDITED_WHL_NAME="${WHL_DIR}/$(echo ${WHL_BASE_NAME//linux/manylinux1})" 246 247 # Repair the wheels for cpu manylinux1 248 if [[ ${CONTAINER_TYPE} == "cpu" ]]; then 249 echo "auditwheel repairing ${WHL_PATH}" 250 auditwheel repair -w ${WHL_DIR} ${WHL_PATH} 251 252 if [[ -f ${AUDITED_WHL_NAME} ]]; then 253 WHL_PATH=${AUDITED_WHL_NAME} 254 echo "Repaired manylinx1 wheel file at: ${WHL_PATH}" 255 else 256 die "ERROR: Cannot find repaired wheel." 257 fi 258 # Copy and rename for gpu manylinux as we do not want auditwheel to package in libcudart.so 259 elif [[ ${CONTAINER_TYPE} == "gpu" ]] || \ 260 [[ ${CONTAINER_TYPE} == "rocm" ]]; then 261 WHL_PATH=${AUDITED_WHL_NAME} 262 cp ${WHL_DIR}/${WHL_BASE_NAME} ${WHL_PATH} 263 echo "Copied manylinx1 wheel file at ${WHL_PATH}" 264 fi 265fi 266 267 268create_activate_virtualenv_and_install_tensorflow() { 269 # Create and activate a virtualenv; then install tensorflow pip package in it. 270 # 271 # Usage: 272 # create_activate_virtualenv_and_install_tensorflow [--clean] \ 273 # <VIRTUALENV_DIR> <TF_WHEEL_PATH> 274 # 275 # Arguments: 276 # --clean: Create a clean virtualenv, i.e., without --system-site-packages. 277 # VIRTUALENV_DIR: virtualenv directory to be created. 278 # TF_WHEEL_PATH: Path to the tensorflow wheel file to be installed in the 279 # virtualenv. 280 281 VIRTUALENV_FLAGS="--system-site-packages" 282 if [[ "$1" == "--clean" ]]; then 283 VIRTUALENV_FLAGS="" 284 shift 285 fi 286 287 VIRTUALENV_DIR="$1" 288 TF_WHEEL_PATH="$2" 289 if [[ -d "${VIRTUALENV_DIR}" ]]; then 290 if rm -rf "${VIRTUALENV_DIR}" 291 then 292 echo "Removed existing virtualenv directory: ${VIRTUALENV_DIR}" 293 else 294 die "Failed to remove existing virtualenv directory: ${VIRTUALENV_DIR}" 295 fi 296 fi 297 298 if mkdir -p "${VIRTUALENV_DIR}" 299 then 300 echo "Created virtualenv directory: ${VIRTUALENV_DIR}" 301 else 302 die "FAILED to create virtualenv directory: ${VIRTUALENV_DIR}" 303 fi 304 305 # Use the virtualenv from the default python version (i.e., python-virtualenv) 306 # to create the virtualenv directory for testing. Use the -p flag to specify 307 # the python version inside the to-be-created virtualenv directory. 308 ${PYTHON_BIN_PATH} -m virtualenv -p "${PYTHON_BIN_PATH}" ${VIRTUALENV_FLAGS} \ 309 "${VIRTUALENV_DIR}" || \ 310 die "FAILED: Unable to create virtualenv" 311 312 source "${VIRTUALENV_DIR}/bin/activate" || \ 313 die "FAILED: Unable to activate virtualenv in ${VIRTUALENV_DIR}" 314 315 # Install the pip file in virtual env. 316 317 # Upgrade pip so it supports tags such as cp27mu, manylinux1 etc. 318 echo "Upgrade pip in virtualenv" 319 320 # NOTE: pip install --upgrade pip leads to a documented TLS issue for 321 # some versions in python 322 curl https://bootstrap.pypa.io/get-pip.py | python 323 324 # Force upgrade of setuptools. This must happen before the pip install of the 325 # WHL_PATH, which pulls in absl-py, which uses install_requires notation 326 # introduced in setuptools >=20.5. The default version of setuptools is 5.5.1, 327 # which is too old for absl-py. 328 pip install --upgrade setuptools==39.1.0 329 330 # Force tensorflow reinstallation. Otherwise it may not get installed from 331 # last build if it had the same version number as previous build. 332 PIP_FLAGS="--upgrade --force-reinstall" 333 pip install -v ${PIP_FLAGS} ${WHL_PATH} || \ 334 die "pip install (forcing to reinstall tensorflow) FAILED" 335 echo "Successfully installed pip package ${TF_WHEEL_PATH}" 336 337 # Force downgrade of setuptools. This must happen after the pip install of the 338 # WHL_PATH, which ends up upgrading to the latest version of setuptools. 339 # Versions of setuptools >= 39.1.0 will cause tests to fail like this: 340 # ImportError: cannot import name py31compat 341 pip install --upgrade setuptools==39.1.0 342} 343 344################################################################################ 345# Smoke test of tensorflow install in clean virtualenv 346################################################################################ 347do_clean_virtualenv_smoke_test() { 348 if [[ -n "${NO_TEST_ON_INSTALL}" ]] && 349 [[ "${NO_TEST_ON_INSTALL}" != "0" ]]; then 350 echo "NO_TEST_ON_INSTALL=${NO_TEST_ON_INSTALL}:" 351 echo " Skipping smoke test of tensorflow install in clean virtualenv" 352 return ${SKIP_RETURN_CODE} 353 fi 354 355 CLEAN_VENV_DIR="${PIP_TEST_ROOT}/venv_clean" 356 create_activate_virtualenv_and_install_tensorflow --clean \ 357 "${CLEAN_VENV_DIR}" "${WHL_PATH}" 358 359 # cd to a temporary directory to avoid picking up Python files in the source 360 # tree. 361 TMP_DIR=$(mktemp -d) 362 pushd "${TMP_DIR}" 363 if [[ $(python -c "import tensorflow as tf; print(tf.Session().run(tf.constant(42)))") == 42 ]]; 364 then 365 echo "Smoke test of tensorflow install in clean virtualenv PASSED." 366 else 367 echo "Smoke test of tensorflow install in clean virtualenv FAILED." 368 return 1 369 fi 370 371 deactivate 372 if [[ $? != 0 ]]; then 373 echo "FAILED: Unable to deactivate virtualenv from ${CLEAN_VENV_DIR}" 374 return 1 375 fi 376 377 popd 378 rm -rf "${TMP_DIR}" "${CLEAN_VENV_DIR}" 379} 380 381################################################################################ 382# Perform installation of tensorflow in "non-clean" virtualenv and tests against 383# the install. 384################################################################################ 385do_virtualenv_pip_test() { 386 # Create virtualenv directory for install test 387 VENV_DIR="${PIP_TEST_ROOT}/venv" 388 create_activate_virtualenv_and_install_tensorflow \ 389 "${VENV_DIR}" "${WHL_PATH}" 390 391 # Install extra pip packages required by the test-on-install 392 for PACKAGE in ${INSTALL_EXTRA_PIP_PACKAGES}; do 393 echo "Installing extra pip package required by test-on-install: ${PACKAGE}" 394 395 pip install ${PACKAGE} 396 if [[ $? != 0 ]]; then 397 echo "pip install ${PACKAGE} FAILED" 398 return 1 399 fi 400 done 401 402 if [[ -n "${NO_TEST_ON_INSTALL}" ]] && 403 [[ "${NO_TEST_ON_INSTALL}" != "0" ]]; then 404 echo "NO_TEST_ON_INSTALL=${NO_TEST_ON_INSTALL}:" 405 echo " Skipping ALL Python unit tests on install" 406 return ${SKIP_RETURN_CODE} 407 else 408 # Call run_pip_tests.sh to perform test-on-install 409 "${SCRIPT_DIR}/run_pip_tests.sh" --virtualenv ${GPU_FLAG} ${MAC_FLAG} 410 if [[ $? != 0 ]]; then 411 echo "PIP tests-on-install FAILED" 412 return 1 413 fi 414 fi 415} 416 417################################################################################ 418# Run tests tagged with oss_serial against the virtualenv install. 419################################################################################ 420do_virtualenv_oss_serial_pip_test() { 421 if [[ -n "${NO_TEST_ON_INSTALL}" ]] && 422 [[ "${NO_TEST_ON_INSTALL}" != "0" ]]; then 423 echo "NO_TEST_ON_INSTALL=${NO_TEST_ON_INSTALL}:" 424 echo " Skipping Python unit tests on install tagged with oss_serial" 425 return ${SKIP_RETURN_CODE} 426 else 427 # Call run_pip_tests.sh to perform test-on-install 428 "${SCRIPT_DIR}/run_pip_tests.sh" \ 429 --virtualenv ${GPU_FLAG} ${MAC_FLAG} --oss_serial 430 if [[ $? != 0 ]]; then 431 echo "PIP tests-on-install (oss_serial) FAILED" 432 return 1 433 fi 434 fi 435} 436 437################################################################################ 438# Test user ops (optional). 439################################################################################ 440do_test_user_ops() { 441 if [[ "${DO_TEST_USER_OPS}" == "1" ]]; then 442 "${SCRIPT_DIR}/test_user_ops.sh" --virtualenv ${GPU_FLAG} 443 if [[ $? != 0 ]]; then 444 echo "PIP user-op tests-on-install FAILED" 445 return 1 446 fi 447 else 448 echo "Skipping user-op test-on-install due to DO_TEST_USER_OPS = ${DO_TEST_USER_OPS}" 449 return ${SKIP_RETURN_CODE} 450 fi 451} 452 453################################################################################ 454# Test TensorFlow Debugger (tfdbg) binaries (optional). 455################################################################################ 456do_test_tfdbg_binaries() { 457 if [[ "${DO_TEST_TFDBG_BINARIES}" == "1" ]]; then 458 # cd to a temporary directory to avoid picking up Python files in the source 459 # tree. 460 TMP_DIR=$(mktemp -d) 461 pushd "${TMP_DIR}" 462 463 "${SCRIPT_DIR}/../../../python/debug/examples/examples_test.sh" \ 464 --virtualenv 465 if [[ $? != 0 ]]; then 466 echo "PIP tests-on-install of tfdbg binaries FAILED" 467 return 1 468 fi 469 popd 470 else 471 echo "Skipping test of tfdbg binaries due to DO_TEST_TFDBG_BINARIES = ${DO_TEST_TFDBG_BINARIES}" 472 return ${SKIP_RETURN_CODE} 473 fi 474} 475 476################################################################################ 477# Test tutorials (optional). 478################################################################################ 479do_test_tutorials() { 480 if [[ "${DO_TEST_TUTORIALS}" == "1" ]]; then 481 "${SCRIPT_DIR}/test_tutorials.sh" --virtualenv 482 if [[ $? != 0 ]]; then 483 echo "PIP tutorial tests-on-install FAILED" 484 return 1 485 fi 486 else 487 echo "Skipping tutorial tests-on-install due to DO_TEST_TUTORIALS = ${DO_TEST_TUTORIALS}" 488 return ${SKIP_RETURN_CODE} 489 fi 490} 491 492################################################################################ 493# Integration test for ffmpeg (optional). 494################################################################################ 495do_ffmpeg_integration_test() { 496 # Optional: Run integration tests 497 if [[ "${DO_INTEGRATION_TESTS}" == "1" ]]; then 498 "${SCRIPT_DIR}/integration_tests.sh" --virtualenv 499 if [[ $? != 0 ]]; then 500 echo "Integration tests on install FAILED" 501 return 1 502 fi 503 else 504 echo "Skipping ffmpeg integration due to DO_INTEGRATION_TESTS = ${DO_INTEGRATION_TESTS}" 505 return ${SKIP_RETURN_CODE} 506 fi 507} 508 509 510# List of all PIP test tasks and their descriptions. 511PIP_TASKS=("do_clean_virtualenv_smoke_test" "do_virtualenv_pip_test" "do_virtualenv_oss_serial_pip_test" "do_test_user_ops" "do_test_tfdbg_binaries" "do_test_tutorials" "do_ffmpeg_integration_test") 512PIP_TASKS_DESC=("Smoke test of pip install in clean virtualenv" "PIP tests in virtualenv" "PIP test in virtualenv (tag: oss_serial)" "User ops test" "TensorFlow Debugger (tfdbg) binaries test" "Tutorials test" "ffmpeg integration test") 513 514 515# Execute all the PIP test steps. 516COUNTER=0 517FAIL_COUNTER=0 518PASS_COUNTER=0 519SKIP_COUNTER=0 520while [[ ${COUNTER} -lt "${#PIP_TASKS[@]}" ]]; do 521 INDEX=COUNTER 522 ((INDEX++)) 523 524 echo 525 printf "${COLOR_BOLD}=== PIP test step ${INDEX} of ${#PIP_TASKS[@]}: "\ 526"${PIP_TASKS[COUNTER]} (${PIP_TASKS_DESC[COUNTER]}) ===${COLOR_NC}" 527 echo 528 529 ${PIP_TASKS[COUNTER]} 530 RESULT=$? 531 532 if [[ ${RESULT} == ${SKIP_RETURN_CODE} ]]; then 533 ((SKIP_COUNTER++)) 534 elif [[ ${RESULT} != "0" ]]; then 535 ((FAIL_COUNTER++)) 536 else 537 ((PASS_COUNTER++)) 538 fi 539 540 STEP_EXIT_CODES+=(${RESULT}) 541 542 echo "" 543 ((COUNTER++)) 544done 545 546deactivate || die "FAILED: Unable to deactivate virtualenv from ${VENV_DIR}" 547 548 549# Print summary of build results 550COUNTER=0 551echo "==== Summary of PIP test results ====" 552while [[ ${COUNTER} -lt "${#PIP_TASKS[@]}" ]]; do 553 INDEX=COUNTER 554 ((INDEX++)) 555 556 echo "${INDEX}. ${PIP_TASKS[COUNTER]}: ${PIP_TASKS_DESC[COUNTER]}" 557 if [[ ${STEP_EXIT_CODES[COUNTER]} == ${SKIP_RETURN_CODE} ]]; then 558 printf " ${COLOR_LIGHT_GRAY}SKIP${COLOR_NC}\n" 559 elif [[ ${STEP_EXIT_CODES[COUNTER]} == "0" ]]; then 560 printf " ${COLOR_GREEN}PASS${COLOR_NC}\n" 561 else 562 printf " ${COLOR_RED}FAIL${COLOR_NC}\n" 563 fi 564 565 ((COUNTER++)) 566done 567 568echo 569echo "${SKIP_COUNTER} skipped; ${FAIL_COUNTER} failed; ${PASS_COUNTER} passed." 570 571echo 572if [[ ${FAIL_COUNTER} == "0" ]]; then 573 printf "PIP test ${COLOR_GREEN}PASSED${COLOR_NC}\n" 574else 575 printf "PIP test ${COLOR_RED}FAILED${COLOR_NC}\n" 576 exit 1 577fi 578