blob: dec7f4bdff4fc84c51821aa4564f15e1ac322679 [file] [log] [blame]
#!/bin/bash
# This script creates and (possibly) uploads a Caffe2 Anaconda package, and
# then (optionally) installs the package locally into the current activated
# conda environment. This script handles flags needed by CUDA and gcc versions,
# and has good default behavior if called with no arguments.
#
# Usage:
# ./build_anaconda.sh [--cuda X.Y] [--cudnn Z] [--conda <flag forwared to conda-build>]... [<flags forwarded to cmake>]...
#
# Parameters can also be passed through the BUILD_ENVIRONMENT environment
# variable, e.g.
# BUILD_ENVIRONMENT=conda2-cuda8.0-cudnn7-gcc4.8 ./scripts/build_anaconda.sh
# - Parameters parsed from the BUILD_ENVIRONMENT will be overridden by command
# line parameters.
# - The conda version and gcc version given in BUILD_ENVIRONMENT are ignored.
# These versions are determined by calling the binaries with --version
#
# The special flags SKIP_CONDA_TESTS, CAFFE2_ANACONDA_ORG_ACCESS_TOKEN, and
# ANACONDA_USERNAME can only be passed in as environment variables.
set -ex
#
# Functions used in this script
#
# portable_sed: A wrapper around sed that works on both mac and linux, used to
# alter conda-build files such as the meta.yaml. It always adds the inplace
# flag
# portable_sed <full regex string> <file>
portable_sed () {
if [ "$(uname)" == 'Darwin' ]; then
sed -i '' "$1" "$2"
else
sed -i "$1" "$2"
fi
}
# remove_package: Given a string, removes any line that mentions that line from
# the meta.yaml
remove_package () {
portable_sed "/$1/d" "${META_YAML}"
}
# add_package: Takes a package name and a version and finagles the
# meta.yaml to ask for that version specifically.
# NOTE: this assumes that $META_YAML has already been set
# The \\"$'\n' is a properly escaped new line
# Those 4 spaces are there to properly indent the comment
add_package () {
remove_package $1
# This magic string _M_STR is in the requirements sections of the meta.yaml
local _M_STR='# other packages here'
portable_sed "s/$_M_STR/- ${1} ${2}\\"$'\n'" $_M_STR/" "${META_YAML}"
}
# add_feature: Adds a given feature tag to the build section. Takes care to
# only add the feature section once. Assumes that no feature section exists yet
add_feature() {
local _M_STR='# features go here'
if [[ -z $ADDED_A_FEATURE ]]; then
portable_sed "s/$_M_STR/features:\\"$'\n'" $_M_STR/" "${META_YAML}"
ADDED_A_FEATURE=YES
fi
portable_sed "s/$_M_STR/- ${1}\\"$'\n'" $_M_STR/" "${META_YAML}"
}
#
# Parse options from both command line and from BUILD_ENVIRONMENT
#
CONDA_BUILD_ARGS=()
CMAKE_BUILD_ARGS=()
CAFFE2_CONDA_CHANNEL=()
if [[ $BUILD_ENVIRONMENT == *cuda* ]]; then
CUDA_VERSION="$(echo $BUILD_ENVIRONMENT | grep --only-matching -P '(?<=cuda)[0-9]\.[0-9]')"
fi
if [[ $BUILD_ENVIRONMENT == *cudnn* ]]; then
CUDNN_VERSION="$(echo $BUILD_ENVIRONMENT | grep --only-matching -P '(?<=cudnn)[0-9](\.[0-9])?')"
fi
while [[ $# -gt 0 ]]; do
case "$1" in
--cuda)
shift
CUDA_VERSION="$1"
;;
--cudnn)
shift
CUDNN_VERSION="$1"
;;
--conda)
shift
CONDA_BUILD_ARGS+=("$1")
;;
*)
CMAKE_BUILD_ARGS+=("$1")
;;
esac
shift
done
# Verify that the CUDA version is supported
if [[ -n $CUDA_VERSION ]]; then
if [[ $CUDA_VERSION == 9.1* ]]; then
CUDA_FEATURE_NAME=cuda91
elif [[ $CUDA_VERSION == 9.0* ]]; then
CUDA_FEATURE_NAME=cuda90
elif [[ $CUDA_VERSION == 8.0* ]]; then
CUDA_FEATURE_NAME=cuda80
else
echo "Unsupported CUDA version $CUDA_VERSION"
echo "Changes have already been made to the meta.yaml, you may have to revert them"
exit 1
fi
if [[ -z $CUDNN_VERSION ]]; then
echo "No CuDNN version given. Caffe2 will still build against whatever"
echo "CuDNN that it finds first, and will break if there is no CuDNN found."
fi
echo "Detected CUDA_VERSION of $CUDA_VERSION"
fi
#
# Read python and gcc version
#
# Read the gcc version to see what ABI to build for
if [[ "$(uname)" != 'Darwin' ]]; then
GCC_VERSION="$(gcc --version | grep --only-matching '[0-9]\.[0-9]\.[0-9]*' | head -1)"
fi
if [[ "$GCC_VERSION" == 4* ]]; then
GCC_USE_C11=0
else
GCC_USE_C11=1
fi
# Read the python version
# Specifically 3.6 because the latest Anaconda version is 3.6, and so it's site
# packages have 3.6 in the name
PYTHON_VERSION="$(python --version 2>&1 | grep --only-matching '[0-9]\.[0-9]\.[0-9]*')"
if [[ "$PYTHON_VERSION" == 3.6* ]]; then
# This is needed or else conda tries to move packages to python3/site-packages
# isntead of python3.6/site-packages
CONDA_BUILD_ARGS+=(" --python 3.6")
fi
#
# Pick the correct conda-build folder
#
PYTORCH_ROOT="$( cd "$(dirname "$0")"/.. ; pwd -P)"
CAFFE2_CONDA_BUILD_DIR="${PYTORCH_ROOT}/conda/caffe2"
if [[ "${BUILD_ENVIRONMENT}" == *full* ]]; then
CAFFE2_CONDA_BUILD_DIR="${CAFFE2_CONDA_BUILD_DIR}/full"
else
CAFFE2_CONDA_BUILD_DIR="${CAFFE2_CONDA_BUILD_DIR}/normal"
fi
META_YAML="${CAFFE2_CONDA_BUILD_DIR}/meta.yaml"
#
# Build the name of the package depending on CUDA and gcc
#
CAFFE2_PACKAGE_NAME="caffe2"
if [[ $BUILD_ENVIRONMENT == *cuda* ]]; then
# CUDA 9.0 and 9.1 are not in conda, and cuDNN is not in conda, so instead of
# pinning CUDA and cuDNN versions in the conda_build_config and then setting
# the package name in meta.yaml based off of these values, we let Caffe2
# take the CUDA and cuDNN versions that it finds in the build environment,
# and manually set the package name ourself.
CAFFE2_PACKAGE_NAME="${CAFFE2_PACKAGE_NAME}-cuda${CUDA_VERSION}-cudnn${CUDNN_VERSION}"
fi
if [[ "$(uname)" != 'Darwin' ]]; then
if [[ $GCC_USE_C11 -eq 0 ]]; then
# gcc compatibility is not tracked by conda-forge, so we track it ourselves
CAFFE2_PACKAGE_NAME="${CAFFE2_PACKAGE_NAME}-gcc${GCC_VERSION:0:3}"
fi
fi
if [[ $BUILD_ENVIRONMENT == *full* ]]; then
CAFFE2_PACKAGE_NAME="${CAFFE2_PACKAGE_NAME}-full"
fi
portable_sed "s/name: caffe2.*\$/name: ${CAFFE2_PACKAGE_NAME}/" "${META_YAML}"
#
# Handle skipping tests and uploading built packages to Anaconda.org
#
# If skipping tests, remove the test related lines from the meta.yaml and don't
# upload to Anaconda.org
if [ -n "$SKIP_CONDA_TESTS" ]; then
portable_sed '/test:/d' "${META_YAML}"
portable_sed '/imports:/d' "${META_YAML}"
portable_sed '/caffe2.python.core/d' "${META_YAML}"
elif [ -n "$UPLOAD_TO_CONDA" ]; then
# Upload to Anaconda.org if needed. This is only allowed if testing is
# enabled
CONDA_BUILD_ARGS+=(" --user ${ANACONDA_USERNAME}")
CONDA_BUILD_ARGS+=(" --token ${CAFFE2_ANACONDA_ORG_ACCESS_TOKEN}")
fi
#
# Set flags and package requirements
#
# Add normal packages for all builds
add_package glog
add_package gflags
add_package leveldb
add_package lmdb
add_package opencv
#
# Handle CUDA related flags
if [[ -n $CUDA_VERSION ]]; then
CMAKE_BUILD_ARGS+=("-DUSE_CUDA=ON")
CMAKE_BUILD_ARGS+=("-DUSE_NCCL=ON")
#add_feature $CUDA_FEATURE_NAME
#add_package $CUDA_FEATURE_NAME
#add_feature nccl2
#CAFFE2_CONDA_CHANNEL+=('-c pytorch')
else
CMAKE_BUILD_ARGS+=("-DUSE_CUDA=OFF")
CMAKE_BUILD_ARGS+=("-DUSE_NCCL=OFF")
CMAKE_BUILD_ARGS+=("-DBLAS=MKL")
add_package 'mkl'
add_package 'mkl-include'
fi
#
# Change flags based on target gcc ABI
if [[ "$(uname)" != 'Darwin' ]]; then
if [ "$GCC_USE_C11" -eq 0 ]; then
# opencv 3.3.1 in conda-forge doesn't have imgcodecs, and opencv 3.1.0
# requires numpy 1.12
add_package 'opencv' '==3.1.0'
if [[ "$PYTHON_VERSION" == 3.* ]]; then
add_package 'numpy' '>1.11'
fi
# Default conda channels use gcc 7.2 (for recent packages), conda-forge uses
# gcc 4.8.5
CMAKE_BUILD_ARGS+=("-DCMAKE_CXX_FLAGS=-D_GLIBCXX_USE_CXX11_ABI=0")
CAFFE2_CONDA_CHANNEL+=('-c conda-forge')
fi
fi
#
# Build Caffe2 with conda-build
#
# If --user and --token are set, then this will also upload the built package
# to Anaconda.org, provided there were no failures and all the tests passed
CONDA_CMAKE_BUILD_ARGS=${CMAKE_BUILD_ARGS[@]} conda build "${CAFFE2_CONDA_BUILD_DIR}" $CAFFE2_CONDA_CHANNEL ${CONDA_BUILD_ARGS[@]} "$@"
# Install Caffe2 from the built package into the local conda environment
if [ -n "$CONDA_INSTALL_LOCALLY" ]; then
conda install -y $CAFFE2_CONDA_CHANNEL "${CAFFE2_PACKAGE_NAME}" --use-local
fi