blob: 7aea21bb6ddd1038ba2e27ad62370b1bd51410d5 [file] [edit]
#!/bin/bash
# Copyright 2018 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
set -e
set -x
set -o pipefail
PREFIX="$1"
DEPS_PREFIX="$2"
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
# Transform the patch version into a "git tag" name that will be
# embedded into the python build info.
GITTAG_NAME=$(echo -n "$_3PP_PATCH_VERSION" \
| tr '[:upper:]' '[:lower:]' | tr -c '.[:alnum:]' '[-*]')
GITTAG="/bin/echo -n ${GITTAG_NAME}"
# The Python build system normally uses git to fill in part of the
# build id. However, when building from cached source, .git directories
# are not preserved. Always remove .git in order to get a consistent
# result.
rm -rf .git
# Make sure we don't pick up any modules from the host PYTHONPATH.
export PYTHONPATH=""
CPPFLAGS="-I$DEPS_PREFIX/include"
LDFLAGS="-L$DEPS_PREFIX/lib"
export CONFIG_ARGS="--host $CROSS_TRIPLE"
SETUP_LOCAL_SKIP=(
# `crypt` module is deprecated in python3.11 and slated for removal.
# See peps.python.org/pep-0594/#crypt.
"_crypt"
# `nis` module is deprecated in python3.11 and slated for removal.
# See peps.python.org/pep-0594/#nis.
"nis"
# These modules are broken, and seem to reference non-existent symbols
# at compile time.
"_testcapi"
"_testinternalcapi"
# We don't have these libraries on Mac, and don't want to depend on
# them on Linux either.
"_tkinter"
)
SETUP_LOCAL_ATTACH=(
"$DEPS_PREFIX/lib/libbz2.a"
"$DEPS_PREFIX/lib/libreadline.a"
"$DEPS_PREFIX/lib/libpanelw.a"
"$DEPS_PREFIX/lib/libncursesw.a"
"$DEPS_PREFIX/lib/libsqlite3.a"
"$DEPS_PREFIX/lib/libz.a"
"$DEPS_PREFIX/lib/liblzma.a"
"$DEPS_PREFIX/lib/libssl.a"
"$DEPS_PREFIX/lib/libcrypto.a"
"$DEPS_PREFIX/lib/libuuid.a"
# We always use the OSS ncurses headers; on OS X the system headers are weird
# and python's configure file works around that by not setting the
# XOPEN_SOURCE defines. Unfortunately that means that on OS X the configure
# script gets it wrong.
#
# We set the NCURSES_WIDECHAR variable explicitly, as that's the only intended
# side effect of setting the XOPEN_SOURCE defines. Setting XOPEN_SOURCE
# defines ourselves leads to problems in other headers which we still use :(.
"_curses:: -DNCURSES_WIDECHAR=1"
"_curses_panel:: -DNCURSES_WIDECHAR=1"
)
WITH_LIBS="-lpthread"
# TODO(iannucci): Remove this once the fleet is using GLIBC 2.25 and
# macOS 10.12 or higher.
#
# See comment in 3pp/openssl/install.sh for more detail.
export ac_cv_func_getentropy=0
# Never link against libcrypt, even if it is present, as it will not
# necessarily be present on the target system.
export ac_cv_search_crypt=no
export ac_cv_search_crypt_r=no
if [[ $_3PP_PLATFORM == mac* ]]; then
PYTHONEXE=python.exe
USE_SYSTEM_FFI=true
# Instruct Mac to prefer ".a" files in earlier library search paths
# rather than search all of the paths for a ".dylib" and then, failing
# that, do a second sweep for ".a".
LDFLAGS="$LDFLAGS -Wl,-search_paths_first"
# For use with cross-compiling.
if [[ $_3PP_TOOL_PLATFORM == mac-arm64 ]]; then
host_cpu="aarch64"
else
host_cpu="x86_64"
fi
EXTRA_CONFIGURE_ARGS="$EXTRA_CONFIGURE_ARGS --build=${host_cpu}-apple-darwin"
else
PYTHONEXE=python
USE_SYSTEM_FFI=
EXTRA_CONFIGURE_ARGS="--with-dbmliborder=bdb:gdbm"
# NOTE: This can break building on Mac builder, causing it to freeze
# during execution.
#
# Maybe look into this if we have time later.
# Also disable PGO when cross-compiling, since a profile can't be generated.
if [[ $_3PP_TOOL_PLATFORM == $_3PP_PLATFORM ]]; then
EXTRA_CONFIGURE_ARGS="$EXTRA_CONFIGURE_ARGS --enable-optimizations"
else
# We still want this flag, which is for some reason only used when
# PGO is enabled.
CFLAGS_NODIST="-fno-semantic-interposition"
LDFLAGS_NODIST="-fno-semantic-interposition"
fi
# TODO(iannucci) This assumes we're building for linux under docker (which is
# currently true).
EXTRA_CONFIGURE_ARGS="$EXTRA_CONFIGURE_ARGS --build=x86_64-linux-gnu"
# OpenSSL 1.1.1 depends on pthread, so it needs to come LAST. Python's
# Makefile has BASEMODLIBS which is used last when linking the final
# executable.
BASEMODLIBS="-lpthread"
# Linux requires -lrt.
WITH_LIBS+=" -lrt"
# sqlite3 requires -lm (log function).
WITH_LIBS+=" -lm"
# On Linux, we need to ensure that most symbols from our static-embedded
# libraries (notably OpenSSL) don't get exported. If they do, they can
# conflict with the same libraries from wheels or other dynamically
# linked sources.
#
# This set of symbols was determined by trial, see:
# - crbug.com/763792
#
# We use LDFLAGS_NODIST instead of LDFLAGS so that distutils doesn't use this
# for building extensions. It would break the build, as gnu_version_script.txt
# isn't available when we build wheels. It's not necessary there anyway.
LDFLAGS_NODIST=" -Wl,--version-script=$SCRIPT_DIR/gnu_version_script.txt"
if [[ $_3PP_PLATFORM != $_3PP_TOOL_PLATFORM ]]; then
# -pthread detection does not work when cross-compiling, but we need this
# flag in order for OpenSSL libraries to get their symbols resolved.
export ac_cv_pthread=yes
export ac_cv_cxx_thread=yes
export ac_cv_pthread_system_supported=yes
fi
fi
# Assert blindly that the target distro will have /dev/ptmx and not /dev/ptc.
# This is likely to be true, since Mac and all linuxes that we know of have this
# configuration.
export ac_cv_file__dev_ptmx=y
export ac_cv_file__dev_ptc=n
if [[ $USE_SYSTEM_FFI ]]; then
EXTRA_CONFIGURE_ARGS+=" --with-system-ffi"
SETUP_LOCAL_ATTACH+=("_ctypes::-lffi")
else
EXTRA_CONFIGURE_ARGS+=" --without-system-ffi"
SETUP_LOCAL_ATTACH+=("$DEPS_PREFIX/lib/libffi.a")
fi
if [[ $_3PP_PLATFORM != $_3PP_TOOL_PLATFORM ]]; then # cross compiling
BUILD_PYTHON=`which python3`
EXTRA_CONFIGURE_ARGS+=" --with-build-python=${BUILD_PYTHON}"
fi
# Avoid querying altstack size dynamically on armv6l because the dockcross
# image we are using don't have the sys/auxv.h in glibc. The autoconf doesn't
# take sys/auxv.h into account so we need to manually disable the detection of
# linux/auxvec.h. This can be removed when we move to a newer version of the
# glibc.
# See also: https://github.com/python/cpython/pull/31789
if [[ $_3PP_PLATFORM == "linux-armv6l" ]]; then
export ac_cv_header_linux_auxvec_h=n
fi
# Python tends to hard-code /usr/include and /usr/local/include in its setup.py
# file which can end up picking up headers and stuff from wherever.
sed -i \
"s+/usr/include+$DEPS_PREFIX/include+" \
setup.py
sed -i \
"s+/usr/include+$DEPS_PREFIX/include+" \
configure.ac
sed -i \
"s+/usr/local/include+$DEPS_PREFIX/include+" \
setup.py
sed -i \
"s+/usr/lib+$DEPS_PREFIX/lib+" \
setup.py
# Generate our configure script.
autoconf
export LDFLAGS
export LDFLAGS_NODIST
export CPPFLAGS
export CFLAGS_NODIST
# Configure our production Python build with our static configuration
# environment and generate our basic platform.
#
# We're going to use our bootstrap python interpreter to generate our static
# module list.
if ! ./configure --prefix "$PREFIX" --host="$CROSS_TRIPLE" \
--disable-shared --enable-ipv6 \
--with-openssl="$DEPS_PREFIX" --with-libs="$WITH_LIBS" \
--without-ensurepip \
$EXTRA_CONFIGURE_ARGS; then
# Show log when failed to run configure.
cat config.log
exit 1
fi
# These flags have been picked up by configure; unset them so they aren't
# appended again.
export LDFLAGS=
export LDFLAGS_NODIST=
export CPPFLAGS=
export CFLAGS_NODIST=
if [ ! $USE_SYSTEM_FFI ]; then
# Tweak Makefile to change LIBFFI_INCLUDEDIR=<TAB>path
sed -i \
$'s+^LIBFFI_INCLUDEDIR=\t.*+LIBFFI_INCLUDEDIR=\t'"$DEPS_PREFIX/include+" \
Makefile
fi
# Generate our "pybuilddir.txt" file. This also generates
# "_sysconfigdata.py" from our current Python, which we need to
# generate our module list, since it includes our "configure_env"'s
# CPPFLAGS, LDFLAGS, etc.
make -j $(nproc) GITTAG="${GITTAG}" platform
# Generate our static module list, "Modules/Setup.local". Python
# reads this during build and projects it into its Makefile.
#
# The "python_mod_gen.py" script extracts a list of modules by
# strategically invoking "setup.py", pretending that it's trying to
# build the modules, and capturing their output. It generates a
# "Setup.local" file.
#
# We need to run it with a Python interpreter that is compatible with
# this checkout. Enter the bootstrap interpreter! However, that is
# tailored to the bootstrap interpreter's environment ("bootstrap_dir"),
# not the production one ("checkout_dir"). We use the
# "python_build_bootstrap.py" script to strip that out and reorient
# it to point to our production directory prior to invoking
# "python_mod_gen.py".
#
# This is all a very elaborate (but adaptable) way to not hardcode
# "Setup.local" for each set of platforms that we support.
SETUP_LOCAL_FLAGS=()
for x in "${SETUP_LOCAL_SKIP[@]}"; do
SETUP_LOCAL_FLAGS+=(--skip "$x")
done
for x in "${SETUP_LOCAL_ATTACH[@]}"; do
SETUP_LOCAL_FLAGS+=(--attach "$x")
done
INTERP=python3
if [[ $_3PP_PLATFORM == $_3PP_TOOL_PLATFORM ]]; then # not cross compiling
INTERP=./$PYTHONEXE
fi
$INTERP -s -S "$SCRIPT_DIR/python_mod_gen.py" \
--pybuilddir $(cat pybuilddir.txt) \
--output ./Modules/Setup.local \
"${SETUP_LOCAL_FLAGS[@]}"
# Make sure we don't try to regenerate this file. When extracted from a
# release tarball, this would have the same timestamp as the inputs so it
# would not be updated, but this is not guaranteed when using git.
#
# This is especially problematic because the regeneration requires Python
# 3.10 or higher ( https://github.com/python/cpython/issues/104487 ).
touch Modules/_blake2/blake2s_impl.c
# Build production Python. BASEMODLIBS override allows -lpthread to be
# at the end of the linker command for old gcc's (like 4.9, still used on e.g.
# arm64 as of Nov 2019). This can likely go away when the dockcross base images
# update to gcc-6 or later.
make profile-clean-stamp # Seems to not be parallel-safe
make -j $(nproc) BASEMODLIBS=$BASEMODLIBS GITTAG="${GITTAG}"
make install BASEMODLIBS=$BASEMODLIBS GITTAG="${GITTAG}"
# Augment the Python installation.
# Read / augment / write the "ssl.py" module to implement custom SSL
# certificate loading logic.
#
# We do this here instead of "usercustomize.py" because the latter
# isn't propagated when a VirtualEnv is cut.
cat "$SCRIPT_DIR/ssl_suffix.py" >> $PREFIX/lib/python*/ssl.py
# Replace paths to the install location in _sysconfigdata with a
# placeholder string. This can then be reliably replaced with the
# real install location when we need to build wheels.
for f in "${PREFIX}/lib/python*/_sysconfigdata*.py"; do
sed -e "s?${PREFIX}?[INSTALL_PREFIX]?g" -i $f
done
# TODO: maybe strip python executable?
$INTERP "$(which pip_bootstrap.py)" "$PREFIX"
PYTHON_MAJOR=$(cd $PREFIX/lib && echo python*)
# Cleanup!
find $PREFIX -name '*.a' -delete -print
# For some reason, docker freezes when doing `rm -rf .../test`. Specifically,
# `rm` hangs at 100% CPU forever on my mac. However, deleting it in smaller
# chunks works just fine. IDFK.
find $PREFIX/lib/$PYTHON_MAJOR/test -type f -exec rm -vf '{}' ';'
rm -vrf $PREFIX/lib/$PYTHON_MAJOR/test
rm -vrf $PREFIX/lib/$PYTHON_MAJOR/config
rm -vrf $PREFIX/lib/pkgconfig
rm -vrf $PREFIX/share
rm -vrf $PREFIX/lib/$PYTHON_MAJOR/lib-dynload/*.{so,dylib} || true
touch $PREFIX/lib/$PYTHON_MAJOR/lib-dynload/.keep
# Don't distribute __pycache__. Because the file modification times are not
# preserved in the CIPD package, Python will try to regenerate the compiled
# code, but will not overwrite an existing read-only file, effectively
# disabling the compiled code cache.
find "$PREFIX" -name __pycache__ -exec rm -rf {} +