#!/usr/bin/env bash # Copyright 2019 The TensorFlow Authors. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== # External `common.sh` # Keeps Bazel versions of the build scripts. # LINT.IfChange LATEST_BAZEL_VERSION=7.7.0 # LINT.ThenChange( # //tf_keras/google/kokoro/pip/build_and_upload_pip_package.sh, # //tensorflow/opensource_only/.bazelversion, # //tensorflow/tools/ci_build/install/install_bazel.sh, # //tensorflow/tools/ci_build/install/install_bazel_from_source.sh, # //tensorflow/tools/toolchains/cross_compile/cc/cc_toolchain_config.bzl) # Run flaky functions with retries. # run_with_retry cmd function run_with_retry { eval "$1" # If the command fails retry again in 60 seconds. if [[ $? -ne 0 ]]; then sleep 60 eval "$1" fi } function die() { echo "$@" 1>&2 ; exit 1; } # A small utility to run the command and only print logs if the command fails. # On success, all logs are hidden. function readable_run { # Disable debug mode to avoid printing of variables here. set +x result=$("$@" 2>&1) || die "$result" echo "$@" echo "Command completed successfully at $(date)" set -x } # LINT.IfChange # Redirect bazel output dir b/73748835 function set_bazel_outdir { mkdir -p /tmpfs/bazel_output export TEST_TMPDIR=/tmpfs/bazel_output } # Downloads bazelisk to ~/bin as `bazel`. function install_bazelisk { date case "$(uname -s)" in Darwin) local name=bazelisk-darwin-amd64 ;; Linux) case "$(uname -m)" in x86_64) local name=bazelisk-linux-amd64 ;; aarch64) local name=bazelisk-linux-arm64 ;; *) die "Unknown machine type: $(uname -m)" ;; esac ;; *) die "Unknown OS: $(uname -s)" ;; esac mkdir -p "$HOME/bin" wget --no-verbose -O "$HOME/bin/bazel" \ "https://github.com/bazelbuild/bazelisk/releases/download/v1.11.0/$name" chmod u+x "$HOME/bin/bazel" if [[ ! ":$PATH:" =~ :"$HOME"/bin/?: ]]; then PATH="$HOME/bin:$PATH" fi set_bazel_outdir which bazel bazel version date } # Install the given bazel version on linux function update_bazel_linux { if [[ -z "$1" ]]; then BAZEL_VERSION=${LATEST_BAZEL_VERSION} else BAZEL_VERSION=$1 fi rm -rf ~/bazel mkdir ~/bazel pushd ~/bazel readable_run wget https://github.com/bazelbuild/bazel/releases/download/"${BAZEL_VERSION}"/bazel-"${BAZEL_VERSION}"-installer-linux-x86_64.sh chmod +x bazel-*.sh ./bazel-"${BAZEL_VERSION}"-installer-linux-x86_64.sh --user rm bazel-"${BAZEL_VERSION}"-installer-linux-x86_64.sh popd PATH="/home/kbuilder/bin:$PATH" set_bazel_outdir which bazel bazel version } # LINT.ThenChange() function install_ubuntu_16_pip_deps { PIP_CMD="pip" while true; do if [[ -z "${1}" ]]; then break fi if [[ "$1" == "pip"* ]]; then PIP_CMD="$1" fi shift done # First, upgrade pypi wheels "${PIP_CMD}" install --user --upgrade 'setuptools' pip wheel # LINT.IfChange(linux_pip_installations_orig) # Remove any historical keras package if they are installed. "${PIP_CMD}" list "${PIP_CMD}" uninstall -y keras "${PIP_CMD}" install --user -r tensorflow/tools/ci_build/release/requirements_ubuntu.txt # LINT.ThenChange(:mac_pip_installations) } # Gradually replace function install_ubuntu_16_pip_deps. # TODO(lpak): delete install_ubuntu_16_pip_deps when completely replaced. function install_ubuntu_16_python_pip_deps { PIP_CMD="pip" while true; do if [[ -z "${1}" ]]; then break fi if [[ "$1" == "pip"* ]]; then PIP_CMD="$1" fi if [[ "$1" == "python"* ]]; then PIP_CMD="${1} -m pip" fi shift done # First, upgrade pypi wheels ${PIP_CMD} install --user --upgrade 'setuptools' pip wheel # LINT.IfChange(linux_pip_installations) # Remove any historical keras package if they are installed. ${PIP_CMD} list ${PIP_CMD} uninstall -y keras ${PIP_CMD} install --user -r tensorflow/tools/ci_build/release/requirements_ubuntu.txt # LINT.ThenChange(:mac_pip_installations) } function install_ubuntu_pip_deps { # Install requirements in the python environment which python which pip PIP_CMD="python -m pip" ${PIP_CMD} list # auditwheel>=4 supports manylinux_2 and changes the output wheel filename # when upgrading auditwheel modify upload_wheel_cpu_ubuntu and upload_wheel_gpu_ubuntu # to match the filename generated. ${PIP_CMD} install --upgrade pip wheel auditwheel~=3.3.1 ${PIP_CMD} install -r tensorflow/tools/ci_build/release/${REQUIREMENTS_FNAME} ${PIP_CMD} list } function setup_venv_ubuntu () { # Create virtual env and install dependencies # First argument needs to be the python executable. ${1} -m venv ~/.venv/tf source ~/.venv/tf/bin/activate REQUIREMENTS_FNAME="requirements_ubuntu.txt" install_ubuntu_pip_deps } function remove_venv_ubuntu () { # Deactivate virtual environment and clean up deactivate rm -rf ~/.venv/tf } function install_ubuntu_pip_deps_novenv () { # Install on default python Env (No Virtual Env for pip packages) PIP_CMD="${1} -m pip" REQUIREMENTS_FNAME="requirements_ubuntu.txt" ${PIP_CMD} install --user --upgrade 'setuptools' pip wheel pyparsing auditwheel~=3.3.1 ${PIP_CMD} install --user -r tensorflow/tools/ci_build/release/${REQUIREMENTS_FNAME} ${PIP_CMD} list } function upload_wheel_cpu_ubuntu() { # Upload the built packages to pypi. for WHL_PATH in $(ls pip_pkg/tf_nightly_cpu-*dev*.whl); do WHL_DIR=$(dirname "${WHL_PATH}") WHL_BASE_NAME=$(basename "${WHL_PATH}") AUDITED_WHL_NAME="${WHL_DIR}"/$(echo "${WHL_BASE_NAME//linux/manylinux2010}") auditwheel repair --plat manylinux2010_x86_64 -w "${WHL_DIR}" "${WHL_PATH}" # test the whl pip package chmod +x tensorflow/tools/ci_build/builds/nightly_release_smoke_test.sh ./tensorflow/tools/ci_build/builds/nightly_release_smoke_test.sh ${AUDITED_WHL_NAME} RETVAL=$? # Upload the PIP package if whl test passes. if [ ${RETVAL} -eq 0 ]; then echo "Basic PIP test PASSED, Uploading package: ${AUDITED_WHL_NAME}" python -m pip install twine python -m twine upload -r pypi-warehouse "${AUDITED_WHL_NAME}" else echo "Basic PIP test FAILED, will not upload ${AUDITED_WHL_NAME} package" return 1 fi done } function upload_wheel_gpu_ubuntu() { # Upload the built packages to pypi. for WHL_PATH in $(ls pip_pkg/tf_nightly*dev*.whl); do WHL_DIR=$(dirname "${WHL_PATH}") WHL_BASE_NAME=$(basename "${WHL_PATH}") AUDITED_WHL_NAME="${WHL_DIR}"/$(echo "${WHL_BASE_NAME//linux/manylinux2010}") # Copy and rename for gpu manylinux as we do not want auditwheel to package in libcudart.so WHL_PATH=${AUDITED_WHL_NAME} cp "${WHL_DIR}"/"${WHL_BASE_NAME}" "${WHL_PATH}" echo "Copied manylinux2010 wheel file at: ${WHL_PATH}" # test the whl pip package chmod +x tensorflow/tools/ci_build/builds/nightly_release_smoke_test.sh ./tensorflow/tools/ci_build/builds/nightly_release_smoke_test.sh ${AUDITED_WHL_NAME} RETVAL=$? # Upload the PIP package if whl test passes. if [ ${RETVAL} -eq 0 ]; then echo "Basic PIP test PASSED, Uploading package: ${AUDITED_WHL_NAME}" python -m pip install twine python -m twine upload -r pypi-warehouse "${AUDITED_WHL_NAME}" else echo "Basic PIP test FAILED, will not upload ${AUDITED_WHL_NAME} package" return 1 fi done } function install_macos_pip_deps { PIP_CMD="python -m pip" # First, upgrade pypi wheels ${PIP_CMD} install --upgrade 'setuptools' pip wheel # LINT.IfChange(mac_pip_installations) # Remove any historical keras package if they are installed. ${PIP_CMD} list ${PIP_CMD} uninstall -y keras ${PIP_CMD} install -r tensorflow/tools/ci_build/release/requirements_mac.txt # LINT.ThenChange( # :linux_pip_installations_orig, # :install_macos_pip_deps_no_venv, # :linux_pip_installations) } # This hack is unfortunately necessary for MacOS builds that use pip_new.sh # You cannot deactivate a virtualenv from a subshell. function install_macos_pip_deps_no_venv { PIP_CMD="${1} -m pip" # First, upgrade pypi wheels ${PIP_CMD} install --user --upgrade 'setuptools' pip wheel # LINT.IfChange(mac_pip_installations) # Remove any historical keras package if they are installed. ${PIP_CMD} list ${PIP_CMD} uninstall -y keras ${PIP_CMD} install --user -r tensorflow/tools/ci_build/release/requirements_mac.txt # LINT.ThenChange(:install_macos_pip_deps) } function setup_venv_macos () { # First argument needs to be the python executable. ${1} -m pip install virtualenv ${1} -m virtualenv tf_build_env source tf_build_env/bin/activate install_macos_pip_deps } function activate_venv_macos () { source tf_build_env/bin/activate } function setup_python_from_pyenv_macos { if [[ -z "${1}" ]]; then PY_VERSION=3.9.1 else PY_VERSION=$1 fi git clone --branch v2.2.2 https://github.com/pyenv/pyenv.git PYENV_ROOT="$(pwd)/pyenv" export PYENV_ROOT export PATH="$PYENV_ROOT/bin:$PYENV_ROOT/shims:$PATH" eval "$(pyenv init -)" pyenv install -s "${PY_VERSION}" pyenv local "${PY_VERSION}" python --version } function maybe_skip_v1 { # If we are building with v2 by default, skip tests with v1only tag. if grep -q "build --config=v2" ".bazelrc"; then echo ",-v1only" else echo "" fi } # Copy and rename a wheel to a new project name. # Usage: copy_to_new_project_name , for example # copy_to_new_project_name test_dir/tf_nightly-1.15.0.dev20190813-cp35-cp35m-manylinux2010_x86_64.whl tf_nightly_cpu # will create a wheel with the same tags, but new project name under the same # directory at # test_dir/tf_nightly_cpu-1.15.0.dev20190813-cp35-cp35m-manylinux2010_x86_64.whl function copy_to_new_project_name { WHL_PATH="$1" NEW_PROJECT_NAME="$2" PYTHON_CMD="$3" ORIGINAL_WHL_NAME=$(basename "${WHL_PATH}") ORIGINAL_WHL_DIR=$(realpath "$(dirname "${WHL_PATH}")") ORIGINAL_PROJECT_NAME="$(echo "${ORIGINAL_WHL_NAME}" | cut -d '-' -f 1)" FULL_TAG="$(echo "${ORIGINAL_WHL_NAME}" | cut -d '-' -f 2-)" NEW_WHL_NAME="${NEW_PROJECT_NAME}-${FULL_TAG}" VERSION="$(echo "${FULL_TAG}" | cut -d '-' -f 1)" ORIGINAL_WHL_DIR_PREFIX="${ORIGINAL_PROJECT_NAME}-${VERSION}" NEW_WHL_DIR_PREFIX="${NEW_PROJECT_NAME}-${VERSION}" TMP_DIR="$(mktemp -d)" ${PYTHON_CMD} -m wheel unpack "${WHL_PATH}" mv "${ORIGINAL_WHL_DIR_PREFIX}" "${TMP_DIR}" pushd "${TMP_DIR}/${ORIGINAL_WHL_DIR_PREFIX}" mv "${ORIGINAL_WHL_DIR_PREFIX}.dist-info" "${NEW_WHL_DIR_PREFIX}.dist-info" if [[ -d "${ORIGINAL_WHL_DIR_PREFIX}.data" ]]; then mv "${ORIGINAL_WHL_DIR_PREFIX}.data" "${NEW_WHL_DIR_PREFIX}.data" fi ORIGINAL_PROJECT_NAME_DASH="${ORIGINAL_PROJECT_NAME//_/-}" NEW_PROJECT_NAME_DASH="${NEW_PROJECT_NAME//_/-}" # We need to change the name in the METADATA file, but we need to ensure that # all other occurrences of the name stay the same, otherwise things such as # URLs and depedencies might be broken (for example, replacing without care # might transform a `tensorflow_estimator` dependency into # `tensorflow_gpu_estimator`, which of course does not exist -- except by # manual upload of a manually altered `tensorflow_estimator` package) sed -i.bak "s/Name: ${ORIGINAL_PROJECT_NAME_DASH}/Name: ${NEW_PROJECT_NAME_DASH}/g" "${NEW_WHL_DIR_PREFIX}.dist-info/METADATA" ${PYTHON_CMD} -m wheel pack . mv *.whl "${ORIGINAL_WHL_DIR}" popd rm -rf "${TMP_DIR}" } # Create minimalist test XML for web view. It includes the pass/fail status # of each target, without including errors or stacktraces. # Remember to "set +e" before calling bazel or we'll only generate the XML for # passing runs. function test_xml_summary { set +x set +e mkdir -p "${KOKORO_ARTIFACTS_DIR}/${KOKORO_JOB_NAME}/summary" # First build the repeated inner XML blocks, since the header block needs to # report the number of test cases / failures / errors. # TODO(rsopher): handle build breakages # TODO(rsopher): extract per-test times as well TESTCASE_XML="$(sed -n '/INFO:\ Build\ completed/,/INFO:\ Build\ completed/p' \ /tmpfs/kokoro_build.log \ | grep -E '(PASSED|FAILED|TIMEOUT)\ in' \ | while read -r line; \ do echo ''"$( \ case "$(echo "${line}" | tr -s ' ' | cut -d ' ' -f 2)" in \ FAILED) echo '' ;; \ TIMEOUT) echo '' ;; \ esac; \ )"''; done; \ )" NUMBER_OF_TESTS="$(echo "${TESTCASE_XML}" | wc -l)" NUMBER_OF_FAILURES="$(echo "${TESTCASE_XML}" | grep -c ''\ ''\ ''\ "${TESTCASE_XML}"''\ > "${KOKORO_ARTIFACTS_DIR}/${KOKORO_JOB_NAME}/summary/sponge_log.xml" } # Create minimalist test XML for web view, then exit. # Ends script with value of previous command, meant to be called immediately # after bazel as the last call in the build script. function test_xml_summary_exit { RETVAL=$? test_xml_summary exit "${RETVAL}" } # Note: The Docker-based Ubuntu TF-nightly jobs do not use this list. They use # //tensorflow/tools/tf_sig_build_dockerfiles/devel.usertools/wheel_verification.bats # instead. See go/tf-devinfra/docker. # CPU size MAC_CPU_MAX_WHL_SIZE=240M WIN_CPU_MAX_WHL_SIZE=170M # GPU size WIN_GPU_MAX_WHL_SIZE=360M function test_tf_whl_size() { WHL_PATH=${1} # First, list all wheels with their sizes: echo "Found these wheels: " find $WHL_PATH -type f -exec ls -lh {} \; echo "====================" # Check CPU whl size. if [[ "$WHL_PATH" == *"_cpu"* ]]; then # Check MAC CPU whl size. if [[ "$WHL_PATH" == *"-macos"* ]] && [[ $(find $WHL_PATH -type f -size +${MAC_CPU_MAX_WHL_SIZE}) ]]; then echo "Mac CPU whl size has exceeded ${MAC_CPU_MAX_WHL_SIZE}. To keep within pypi's CDN distribution limit, we must not exceed that threshold." return 1 fi # Check Windows CPU whl size. if [[ "$WHL_PATH" == *"-win"* ]] && [[ $(find $WHL_PATH -type f -size +${WIN_CPU_MAX_WHL_SIZE}) ]]; then echo "Windows CPU whl size has exceeded ${WIN_CPU_MAX_WHL_SIZE}. To keep within pypi's CDN distribution limit, we must not exceed that threshold." return 1 fi elif [[ "$WHL_PATH" == *"_gpu"* ]]; then # Check Windows GPU whl size. if [[ "$WHL_PATH" == *"-win"* ]] && [[ $(find $WHL_PATH -type f -size +${WIN_GPU_MAX_WHL_SIZE}) ]]; then echo "Windows GPU whl size has exceeded ${WIN_GPU_MAX_WHL_SIZE}. To keep within pypi's CDN distribution limit, we must not exceed that threshold." return 1 fi fi }