tflite-micro/python/tflite_micro/pypi_build.sh
Ryan Kuester cdf028c7e3
fix(pypi): pass some env v'bles through during containerized whl build (#2258)
Pass a few environmental variables into the bazel build and test of the
tflite_micro package whl during the containerized build for PyPI. By default,
bazel cleans the environment.

Recent changes (presumably) to the package wrapt, installed as a dependency of
the package tensorflow, cause the containerized build of the package
tflite_micro for PyPI targeting Python 3.11 to fail. During wrapt's
installation, setuptools calls pathlib to determine the user's home directory.
pathlib tries HOME in the environment, but falls back on reading the Unix
password database for itself if necessary. This fallback is why
non-containerized environments don't exhibit the failure despite bazel cleaning
HOME from the environment. In the PyPI build container, however, the password
database doesn't contain the invoking user, and pathlib's fallback fails.

To fix, set HOME and pass it through to the action environment of the bazel
build and test. Also pass through a couple of other variables which were also
intended to affect the build and test (in minor ways, such as cache location
and cleaning up a warning).

BUG=fixes #2257
2023-10-02 16:42:47 +00:00

119 lines
4.1 KiB
Bash
Executable file

#!/bin/sh
# Copyright 2023 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.
set -e
OUT_DIR_DEFAULT=bazel-pypi-out
USAGE="$(basename $0) <python-tag> [<output-directory>]
Build a Python wheel for public release to PyPI using a special Docker build
container. Uses bazel, but does not pollute the WORKSPACE's default cache.
<python-tag> must be one of the supported interpreters:
cp310
cp311
<output-directory> defaults to $OUT_DIR_DEFAULT.
"
case "$1" in
cp310|cp311)
PY_TAG=$1
OUTDIR=$(realpath ${2:-$OUT_DIR_DEFAULT})
mkdir -p $OUTDIR
break
;;
*)
echo usage: "$USAGE" >&2
exit 1
esac
SRCDIR=$(realpath .)
if ! test -f $SRCDIR/WORKSPACE; then
echo "error: must run from the top of the source tree" >&2
exit 1
fi
# Remove Bazel's workspace symlinks so they'll be rewritten below, pointing into
# OUTDIR.
find . -maxdepth 1 -type l -name bazel-\* | xargs rm -f
# Build the Docker image from its source file. Don't pollute the public list of
# images by tagging; just use the image's ID.
DOCKERFILE=python/tflite_micro/pypi_build.dockerfile
IMAGE_ID_FILE=$OUTDIR/image-id
docker build - --iidfile $IMAGE_ID_FILE <$DOCKERFILE
IMAGE_ID=$(cat $IMAGE_ID_FILE)
# Build the Python package within an ephemeral container.
docker run \
--rm \
--interactive \
--mount type=bind,source=$SRCDIR,destination=$SRCDIR \
--mount type=bind,source=$OUTDIR,destination=$OUTDIR \
--workdir $SRCDIR \
--env USER=$(id -un) \
$IMAGE_ID \
/bin/bash -s -e -x -u \
<<EOF
# Setup the Python compatibility tags. The PY_ABI always matches the Python
# interpreter tag. The platform tag is supplied by the build image in the
# environment variable AUDITWHEEL_PLAT.
PY_ABI=$PY_TAG
PY_PLATFORM=\$AUDITWHEEL_PLAT
PY_COMPATIBILITY=${PY_TAG}_\${PY_ABI}_\${PY_PLATFORM}
# Link the desired Python version into the PATH, where bazel will find it.
# The build image contains many different Python versions as options.
ln -sf /opt/python/$PY_TAG-$PY_TAG/bin/* /usr/bin
# Bazelisk fails if it can't check HOME for a .rc file., and pip (in
# :whl_test) installation of some dependencies (e.g., wrapt) expects HOME.
export HOME=$OUTDIR
# Bazelisk, bazel, and pip all need a writable cache directory.
export XDG_CACHE_HOME=$OUTDIR/cache
# Relocate the bazel root to keep the cache used for each Python toolchain
# separate. Drop root privledges and run as the invoking user.
call_bazel() {
setpriv --reuid=$(id -u) --regid=$(id -g) --clear-groups \
bazel \
--output_user_root=$OUTDIR/$PY_TAG-out \
"\$@" \
--action_env=HOME `# help setuptools find HOME in container` \
--action_env=USER `# bazel reads USER via whoami` \
--action_env=XDG_CACHE_HOME `# locate pip's cache inside OUTDIR`
}
# Build the wheel via bazel, using the Python compatibility tag matching the
# build environment.
call_bazel build //python/tflite_micro:whl.dist \
--//python/tflite_micro:compatibility_tag=\$PY_COMPATIBILITY
# Test, in the container environment.
call_bazel test //python/tflite_micro:whl_test \
--//python/tflite_micro:compatibility_tag=\$PY_COMPATIBILITY
EOF
# Make the output directory tree writable so it can be removed easily by the
# user with `rm -rf $OUTDIR`. Bazel leaves it write-protected.
chmod -R +w $OUTDIR
# Copy the generated wheel file to the root of the $OUTDIR.
cp bazel-bin/python/tflite_micro/whl_dist/*.whl $OUTDIR
echo "Output:\n$(ls $OUTDIR/*.whl)"